66

Playing with dateTimes and timezone can be tricky in R. Here is my question: I want to change the time-zone on a POSIXct object

R) data <- data.frame(x=c(1,2),dateTime=as.POSIXct(c("2010-03-11 03:30:00.432","2010-03-15 03:30:00.432"),format="%Y-%m-%d %H:%M:%S",tz="America/Montreal"))
R) data
  x            dateTime
1 1 2010-03-11 03:30:00
2 2 2010-03-15 03:30:00
R) str(data)
'data.frame':   2 obs. of  2 variables:
 $ x       : num  1 2
 $ dateTime: POSIXct, format: "2010-03-11 03:30:00" "2010-03-15 03:30:00"

But if I want to change the timezone, the only thing I found is:

R) data$dateTime2 = format(data$dateTime,tz="Europe/Paris")
R) str(data)
'data.frame':   2 obs. of  3 variables:
 $ x        : num  1 2
 $ dateTime : POSIXct, format: "2010-03-11 03:30:00" "2010-03-15 03:30:00"
 $ dateTime2: chr  "2010-03-11 09:30:00" "2010-03-15 08:30:00"

Or

R) data$dateTime2 = as.POSIXlt(data$dateTime,tz="Europe/Paris")
R) str(data)
'data.frame':   2 obs. of  3 variables:
 $ x        : num  1 2
 $ dateTime : POSIXct, format: "2010-03-11 03:30:00" "2010-03-15 03:30:00"
 $ dateTime2: POSIXlt, format: "2010-03-11 09:30:00" "2010-03-15 08:30:00"

God (or somebody on SO) knows why it doesn't work with POSIXct

R) data$dateTime2 = as.POSIXct(data$dateTime,tz="Europe/Paris")
R) str(data)
'data.frame':   2 obs. of  3 variables:
 $ x        : num  1 2
 $ dateTime : POSIXct, format: "2010-03-11 03:30:00" "2010-03-15 03:30:00"
 $ dateTime2: POSIXct, format: "2010-03-11 03:30:00" "2010-03-15 03:30:00"

Do I need to convert to character and cast back to POSIXct ?

Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
statquant
  • 13,672
  • 21
  • 91
  • 162

3 Answers3

81

It doesn't work with POSIXct because base::as.POSIXct.default simply returns x if it's already POSIXct. You can change the timezone via the tzone attribute:

attr(data$dateTime, "tzone") <- "Europe/Paris"
Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
  • There is a setter function `indexTZ()` that does this too: `indexTZ(data$dateTime) <- "Europe/Paris"` – Dirk Eddelbuettel Jan 03 '13 at 15:15
  • 4
    `.POSIXct(data$dateTime, tz="Europe/Paris")` also does it, but it's an .Internal function so it _probably_ shouldn't be used in a CRAN package. – GSee Jan 03 '13 at 15:16
  • 2
    @DirkEddelbuettel: that's to change the timezone of an xts object though, not a POSIXct object. – Joshua Ulrich Jan 03 '13 at 15:17
  • Aha! POSIXct and POSIXlt objects have a 'tzone' attribute, and the downstream processes use it differently. – Dave X Jun 08 '16 at 17:55
  • 13
    Note that if `dateTime` is a `data.table` column, you should use `setattr(data$dateTime, 'tzone', 'Europe/Paris')` since `attr<-` can inadvertently create copies of `data` – MichaelChirico Jul 19 '18 at 04:27
45

In the lubridate package there is a function with_tz which changes the timezone attribute (effectively what Joshua described).

dttm <- as.POSIXct("2016-01-01 10:10:10", tz = "UTC")
dttm
[1] "2016-01-01 10:10:10 UTC"

Change timezone from UTC to CET

with_tz(dttm, "CET")
[1] "2016-01-01 11:10:10 CET"
Mark Heckmann
  • 10,943
  • 4
  • 56
  • 88
  • 18
    `force_tz` is also a useful function in the `lubridate` package. Useful when you read data from a file and the system assumes a certain timezone. `force_tz` changes the time zone without changing the clock time. – CJB Sep 28 '17 at 10:07
1

As of 7/23/2021. indexTZ is deprecated. tzone is good to use.

tzone(data$dateTime) <- "Europe/Paris"

ay__ya
  • 423
  • 1
  • 5
  • 12
  • It may be "deprecated" in the sense of being the older, original accessor function, but it is still the header on the help page, so when you attempt to find it with `??` you only see 'xts::indexTZ' on the results page. Or maybe it's only a soft deprecation? The help page does NOT say it's deprecated, but rather implies that "things will remain the same" with the exception of the addition of `tzone` and `tzone<-` functions – IRTFM Sep 19 '21 at 20:29