Converting UTC time to local standard time in R
First, POSIXct date-times are always UCT internally. The print.POSIXt
and format.POSIXt
methods will appropriately make the TZ shift on output from their internal representations:
pb.date <- as.POSIXct(Sys.Date())
Sys.Date()
#[1] "2015-07-09"
So that was midnight of the current date in Greenwich:
format(pb.date, tz="America/Los_Angeles",usetz=TRUE)
#[1] "2015-07-08 17:00:00 PDT"
When it's midnight in Greenwich, it's 5PM Daylight Time in the previous day on the Left Coast of the US. You need to use the correct character values for your TZ (and your OS) both of which at the moment are unspecified.
The US Pacific timezone is 8 hours behind GMT (in winter months) so you can use a timezone that is Standard/Daylight-agnostic:
> format(pb.date,usetz=TRUE, tz="Etc/GMT+8")
[1] "2015-07-08 16:00:00 GMT+8"
(Notice the reversal of + with "behind" and - with "ahead".)
How to convert date and time from UTC to local time in R?
The default format of as.POSIXct
expects an date ordered by Year-Month-Day. Therefore the date 01/04/2020 is translated into the 20th April of Year 1.
You just need to add your timeformat to as.POSIXct
:
vlinder$dateandtime <- as.POSIXct(vlinder$dateandtime, tz = "UTC", format = "%d/%m/%Y %H:%M:%S")
format(vlinder$dateandtime, tz = "Europe/Brussels", usetz = TRUE)
Convert UTC datetime to local datetime with timezone
Unfortunately you cannot have different time zones within a single POSIXct vector, because the timezone is stored as a single atomic attribute that applies to the whole vector. If you try to write multiple timezones to this attribute, the S3 methods for POSIXct will stop working.
If this is something you are very keen to pursue you can write a new S3 class to handle this kind of problem. The very bare bones of such a class would look something like this:
POSIX_multi_tz <- function(UTC_times, time_zones)
{
structure(as.numeric(UTC_times),
class = c("POSIXmulti", "POSIXt"),
tz = time_zones)
}
format.POSIXmulti <- function(x, ...)
{
unlist(mapply(function(a, b) {
format(as.POSIXct(a, origin = "1970-01-01"), tz = b, usetz = TRUE)
}, a = x, b = attr(x, "tz"), SIMPLIFY = FALSE))
}
print.POSIXmulti <- function(x, ...)
{
print(format(x, ...))
}
This would allow for the following behaviour:
df$new_time <- POSIX_multi_tz(df$start_time, df$timezone)
df
#> start_time timezone new_time
#> 1 2020-07-08 00:01:15 Europe/Dublin 2020-07-08 00:01:15 IST
#> 2 2020-07-08 05:01:28 America/Los_Angeles 2020-07-07 21:01:28 PDT
#> 3 2020-07-20 20:45:33 America/New_York 2020-07-20 15:45:33 EDT
#> 4 2020-07-25 00:00:32 America/Los_Angeles 2020-07-24 16:00:32 PDT
#> 5 2020-07-09 22:00:39 Europe/London 2020-07-09 22:00:39 BST
#> 6 2020-07-17 04:30:30 America/Los_Angeles 2020-07-16 20:30:30 PDT
#> 7 2020-07-29 22:03:09 Europe/London 2020-07-29 22:03:09 BST
#> 8 2020-07-28 04:59:32 America/Los_Angeles 2020-07-27 20:59:32 PDT
#> 9 2020-07-21 00:09:54 America/Denver 2020-07-20 17:09:54 MDT
#> 10 2020-07-21 17:51:04 Europe/Dublin 2020-07-21 17:51:04 IST
Beware though - you would still have a bit of work to do to be able to use this class the way you would use POSIXct objects. You can still use arithmetic functions to add and substract seconds, but if you use the lubridate
package or similar, many of the methods will not work for this class unless you define various Ops
to handle adding durations, periods etc.
How do you convert dates/times from one time zone to another in R?
First, convert the London time to a POSIXct
object:
pb.txt <- "2009-06-03 19:30"
pb.date <- as.POSIXct(pb.txt, tz="Europe/London")
Then use format
to print the date in another time zone:
> format(pb.date, tz="America/Los_Angeles",usetz=TRUE)
[1] "2009-06-03 11:30:00 PDT"
There are some tricks to finding the right time zone identifier to use. More details in this post at the Revolutions blog: Converting time zones in R: tips, tricks and pitfalls
convert local dateTime to UTC in R
If you want to shift a datetime from your current timezone to UTC, you need to
import in your local timezone, then just shift the display timezone to "UTC". e.g.: in Australian EST I am UTC+10.
out <- as.POSIXct("12/31/2014 6:42:52 PM", format="%m/%d/%Y %H:%M:%S")
out
#"2014-12-31 06:42:52 EST"
#(Australian Eastern Standard Time)
as.numeric(out)
#[1] 1419972172
Now shift the timezone for display purposes:
attr(out, "tzone") <- "UTC"
out
#[1] "2014-12-30 20:42:52 UTC"
# display goes 10 hours backwards as I'm UTC+10
as.numeric(out)
#[1] 1419972172
Note that this doesn't affect the underlying numeric data (seconds since 1970-01-01), it only changes what is displayed.
Converting local date-times from a large dataset with multiple time zones into UTC
The error is due to the timeZone
column being a factor instead of a character vector, use stringsAsFactors = FALSE
when defining the data.frame to specify timeZone
as a character column. You can also avoid any loops by using vectorized functions from the lubridate
package:
library(lubridate)
df <- data.frame(
"localDate" = c("2015-04-20","2016-07-17","2015-08-06"),
"localTime" = c("14:00", "14:46", "10:35"),
"timeZone" = c("Pacific/Pago_Pago", "Pacific/Saipan", "Pacific/Honolulu"),
stringsAsFactors = FALSE
)
df$eventDate <- force_tzs(ymd_hm(with(df, paste(localDate, localTime))), tzones = df$timeZone)
df
#> localDate localTime timeZone eventDate
#> 1 2015-04-20 14:00 Pacific/Pago_Pago 2015-04-21 01:00:00
#> 2 2016-07-17 14:46 Pacific/Saipan 2016-07-17 04:46:00
#> 3 2015-08-06 10:35 Pacific/Honolulu 2015-08-06 20:35:00
Edit: in case of missing values, check for each row whether it can be converted and if not return NA
. Below an example solution using base R:
df <- data.frame(
"localDate" = c("2015-04-20","2016-07-17","2015-08-06", "2019-01-01", "2019-01-01"),
"localTime" = c("14:00", "14:46", "10:35", NA, "00:00"),
"timeZone" = c("Pacific/Pago_Pago", "Pacific/Saipan", "Pacific/Honolulu",
"Pacific/Honolulu", NA),
stringsAsFactors = FALSE
)
df$eventDate <- apply(df, 1, function(row) {
ifelse(any(is.na(row)), NA_character_,
format(as.POSIXct(paste(row["localDate"], row["localTime"]), "%Y-%m-%d %H:%M",
tz = row["timeZone"]), tz = "UTC", usetz = TRUE)
)
})
df
#> localDate localTime timeZone eventDate
#> 1 2015-04-20 14:00 Pacific/Pago_Pago 2015-04-21 01:00:00 UTC
#> 2 2016-07-17 14:46 Pacific/Saipan 2016-07-17 04:46:00 UTC
#> 3 2015-08-06 10:35 Pacific/Honolulu 2015-08-06 20:35:00 UTC
#> 4 2019-01-01 <NA> Pacific/Honolulu <NA>
#> 5 2019-01-01 00:00 <NA> <NA>
R Change from stardard UTC time zone to multiple local time zones
Multiple problems here:
format
(and other time-related functions) only takes a length-1 argument fortz
;- time zones recognized by R do not include the popular
"CST"
,"PST"
, etc.
To fix the first, the use of Map
or mapply
will suffice.
The second requires a little more research, unfortunately. Zones like "PST"
and such, though popular in at least the US if not other countries, are not valid time zone strings (ref: CCTZ, a C++ library for translating between time zones, says so). Neither are "GMT-7"
, et al, though the latter can be faked by prepending with Etc/
, as in: "Etc/GMT-7"
. Or you can go with the alternatives of "America/New_York"
or "US/Eastern"
.
df$time_zone <- c("US/Eastern", "US/Pacific", "US/Central", "US/Eastern")
df
# day time_zone
# 1 2018-12-06 15:40:29 US/Eastern
# 2 2018-12-06 15:25:28 US/Pacific
# 3 2018-12-06 15:25:28 US/Central
# 4 2018-12-06 14:09:09 US/Eastern
mapply(format, df$day, tz = "GMT")
# [1] "2018-12-06 15:40:29" "2018-12-06 15:25:28" "2018-12-06 15:25:28"
# [4] "2018-12-06 14:09:09"
mapply(format, df$day, tz = df$time_zone)
# [1] "2018-12-06 10:40:29" "2018-12-06 07:25:28" "2018-12-06 09:25:28"
# [4] "2018-12-06 09:09:09"
All of the immediately-recognizable formats for R's time zones are found in a 594-element vector:
str(OlsonNames())
# chr [1:592] "Africa/Abidjan" "Africa/Accra" "Africa/Addis_Ababa" ...
# - attr(*, "Version")= chr "2018e"
set.seed(2)
sample(OlsonNames(), size=8)
# [1] "America/El_Salvador" "Etc/GMT+8" "Atlantic/Madeira"
# [4] "America/Creston" "Pacific/Port_Moresby" "Pacific/Ponape"
# [7] "America/Atka" "GB-Eire"
grep("US/", OlsonNames(), value = TRUE)
# [1] "US/Alaska" "US/Aleutian" "US/Arizona"
# [4] "US/Central" "US/East-Indiana" "US/Eastern"
# [7] "US/Hawaii" "US/Indiana-Starke" "US/Michigan"
# [10] "US/Mountain" "US/Pacific" "US/Pacific-New"
# [13] "US/Samoa"
In this example, you'll see one of the alternatives you can use: "Etc/GMT+8"
. Notice that +
is to the west of the prime meridian, so
mapply(format, df$day, tz = "US/Eastern")
# [1] "2018-12-06 10:40:29" "2018-12-06 10:25:28" "2018-12-06 10:25:28"
# [4] "2018-12-06 09:09:09"
mapply(format, df$day, tz = "Etc/GMT+5")
# [1] "2018-12-06 10:40:29" "2018-12-06 10:25:28" "2018-12-06 10:25:28"
# [4] "2018-12-06 09:09:09"
Caveat emptor: using "US/Eastern"
should take into account daylight savings where appropriate; "Etc/GMT+5"
does not, I believe.
Related Topics
Naive Bayes in Quanteda VS Caret: Wildly Different Results
Scientific Notation Issue in R
Programming with Ggplot2 and Dplyr
How to Calculate the Median on Grouped Dataset
Caret: There Were Missing Values in Resampled Performance Measures
Simple Comparing of Two Texts in R
Forest Plot with Table Ggplot Coding
Add Hline with Population Median for Each Facet
Tidyr Separate Only First N Instances
Control Number Formatting in Shiny's Implementation of Datatable
Math of Tm::Findassocs How Does This Function Work
Remove Zombie Processes Using Parallel Package
Nls Troubles: Missing Value or an Infinity Produced When Evaluating the Model
Equation Numbering in Rmarkdown - for Export to Word
Subtract Values in One Dataframe from Another
Making Binned Scatter Plots for Two Variables in Ggplot2 in R
How to Run a Job Array in R Using the Rscript Command from the Command Line