54

If I have a date like this in London time: "2009-06-03 19:30", how can I convert it to the equivalent time in the US West Coast?

David Smith
  • 2,114
  • 3
  • 18
  • 17

5 Answers5

62

Package lubridate holds two functions to convert timezones. According to the help pages:


force_tz returns a date-time that has the same clock time as x in the new time zone.

force_tz(time, tzone = "America/Los_Angeles")


with_tz changes the time zone in which an instant is displayed. The clock time displayed for the instant changes, but the moment of time described remains the same.

with_tz(time, tzone = "America/Los_Angeles")
nnn
  • 4,985
  • 4
  • 24
  • 34
59

Use attr() (base R) or lubridate::with_tz() (tidyverse) to view the same moment of time in a different time zone.

The internal value of a POSIXct object is always in UTC (number of seconds since the beginning of 1970; see ?DateTimeClasses, but the displayed time when printed is determined by the tzone attribute. So change the tzone attribute (via attr() or lubridate::with_tz()) to display/print the same moment of time in a different time zone.

For example:

pb.txt <- "2009-06-03 19:30"  
pb.date <- as.POSIXct(pb.txt, tz="Europe/London") 

Note tzone attribute determines time zone used when displayed/printed:

attributes(pb.date)$tzone
[1] "Europe/London"

pb.date
[1] "2009-06-03 19:30:00 BST"

Note that internal value is seconds since origin in UTC, regardless of tzone attribute:

as.numeric(pb.date)
[1] 1244053800

In base R, the tzone attribute can be set/changed using attr():

attr(pb.date, "tzone") <- "America/Los_Angeles"

pb.date  
[1] "2009-06-03 11:30:00 PDT"  

Equivalently, use lubridate::with_tz() (which also uses attr):

pb.date <- lubridate::with_tz(pb.date, "America/Los_Angeles")

Note that display time is now local clock time in PDT:

pb.date  
[1] "2009-06-03 11:30:00 PDT"

but internal value has not changed:

as.numeric(pb.date)
[1] 1244053800

so tzone does not affect any operations, only the displayed value when printed.

Chris Holbrook
  • 2,531
  • 1
  • 17
  • 30
  • Don't forget to check the link posted in the accepted answer's comments (http://blog.revolution-computing.com/2009/06/converting-time-zones.html). I used EST and thought this answer didn't work! – kaoD Jun 23 '13 at 19:33
  • 1
    Alternatively, `attr(pb.date, "tzone") <- "America/Los_Angeles"` – Leonardo Fontenelle Dec 06 '19 at 19:38
  • @ChrisHolbrook but during daylight saving time, when we want to convert EST to UTC, it makes mistake? as it is asked [here](https://stackoverflow.com/questions/13156836/character-posixct-conversion-in-r-causes-wrong-timezone-values-on-daylight-savin?noredirect=1&lq=1). Could you please let me know how to solve this issue? Thanks. – ebrahimi Feb 07 '23 at 05:41
  • @ebrahimi it only makes a mistake when the input to `as.POSIXct` does not contain the information needed to resolve actual moment in time (i.e., in `UTC`). In [the question you linked](https://stackoverflow.com/questions/13156836/character-posixct-conversion-in-r-causes-wrong-timezone-values-on-daylight-savin), time zone information is correctly present in `time_seq_01` (verify with `as.numeric(time_seq_01)`); but is lost on conversion to `time_seq_02`.... (continued below) – Chris Holbrook Feb 09 '23 at 22:00
  • Although we can see the difference with our eyes on the screen (e.g., "2012-10-28 02:00:00 CEST" vs. "2012-10-28 02:00:00 EST"), those time zone abbreviations are not used by `as.POSIXct`, and since they are ambiguous (cannot resolve "2012-10-28 02:00:00" as explained in Details in `?as.POSIXct`), the result OS-specific and unreliable. – Chris Holbrook Feb 09 '23 at 22:11
  • So to prevent this issue when converting from POSIXct to character, use an output specification for timezone that is handled reliably by `as.POSIXct` (e.g., `format(time_seq_01, tz = "UTC")` or `format(time_seq_01, format = "%Y-%m-%d %H:%M:%S %z")`). – Chris Holbrook Feb 09 '23 at 22:21
26

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

David Smith
  • 2,114
  • 3
  • 18
  • 17
9

If you'd like to do this in one line, recall that any POSIXct object in R is really just a number (seconds UTC since epoch beginning), and that the "timezone" is just an attribute that determines how that number is printed.

Therefore, we can use the .POSIXct helper function as follows:

x = as.POSIXct("2009-06-03 19:30", tz = "Europe/London")
.POSIXct(as.numeric(x), tz = 'America/Los_Angeles')
# [1] "2009-06-03 11:30:00 PDT"

as.numeric strips the class and attributes of x, and .POSIXct is shorthand for constructing a POSIXct object.

Note that I'm using as.numeric() and not as.integer() to avoid truncating milliseconds.

MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
  • Just fixed a typo, and add origin to prevent error x = as.POSIXct("2009-06-03 19:30", tz = "Europe/London") x = as.POSIXct(as.integer(x), tz = 'America/Los_Angeles', origin = '1970-01-01') x – skysign Sep 03 '19 at 08:01
  • @MichaelChirico but during daylight saving time, when we want to convert EST to UTC, it makes mistake? as it is asked [here](https://stackoverflow.com/questions/13156836/character-posixct-conversion-in-r-causes-wrong-timezone-values-on-daylight-savin?noredirect=1&lq=1). Could you please let me know how to solve this issue? Thanks. – ebrahimi Feb 07 '23 at 05:42
0

Another, perhaps more clunky, way to do this is to create two new columns for "date" and "time" by extracting that information out of your "datetime" column and then combining those two columns into a new column and using POSIXct to choose the time zone you want the dates to be in.

#Extract the date and year from your "datetime" column to create two new columns.
data$date <- substr(data$datetime, 1, 10) #column for date (selects characters 1-10 from your "datetime" column and pastes them into the "year" column.
data$time <- substr(data$datetime, 12, 19) #column for year (same as above).
    
#Combine the two columns to create a new column "new.datetime" and select your timezone of choice.
data$new.datetime <- as.POSIXct(paste(data$year, data$time), format = 
"%Y-%m-%d %H:%M:%S", tz = "America/Toronto").
    
#Confirm the timezone is in the proper format.
attr(data$new.datetime, "tzone")
[1] "America/Toronto"
Marco
  • 2,368
  • 6
  • 22
  • 48
Ryan
  • 1