R: date formatting ignores timezone for POSIXlt objects - r

I cannot get R to format POSIXlt objects in the desired timezone. POSIXct works as expected. Is this a bug or am I missing something?
date.str = "2015-12-09 13:30"
from = "Europe/London"
to = "America/Los_Angeles"
lt = as.POSIXlt(date.str, tz=from)
format(lt, tz=to, usetz=TRUE)
#[1] "2015-12-09 13:30:00 GMT"
ct = as.POSIXct(date.str, tz=from)
format(ct, tz=to, usetz=TRUE)
#[1] "2015-12-09 05:30:00 PST"
The tzone attributes are the same:
attributes(ct)$tzone
#[1] "Europe/London"
attributes(lt)$tzone
#[1] "Europe/London"
Solution
As pointed out by #nicola, format.POSIXlt has no tz parameter. To print a POSIXlt date in another timezone one can use lubridate package to convert a POSIXlt object to the desired timezone first:
require(lubridate)
lt.changed = with_tz(lt, tz=to)
format(lt.changed, usetz=TRUE)
#[1] "2015-12-09 05:30:00 PST"

Related

as.POSIXCt returning incorrect date value in R

I am trying to convert the Eopch time interval using as.POSIXct function in R
into the local timezone and in the Europe/Vienna timezone. But for both the timezone its displaying a weird date
as.POSIXct(1385856600000, origin = "1970-01-01", tz='CET')
[1] "45886-01-17 23:40:00 CET"
> as.POSIXct(1385856600000, origin = "1970-01-01")
[1] "45886-01-18 04:10:00 IST"
What i am typing wrong here ?
you can use anytime package too.. much simpler to use than as.POSIXCt
anytime(1385856600000/1000)
[1] "2013-12-01 05:40:00 IST"
I think that your time is in milliseconds. If you divide by 1000, you get:
as.POSIXct(1385856600, origin = "1970-01-01")
[1] "2013-12-01 05:40:00 IST"

Formatting the output of Sys.time() with timezone in R using format.Posix*

I am trying to figure out how to format the output of Sys.time() in R.
For example:
t <- Sys.time()
print(t)
# [1] "2017-07-26 09:41:29 CEST"
which is correct.
I want to make a string out of t made of the date, the hour and minute and the timezone.
I can use
format(t, format = "%F %R %Z")
# [1] "2017-07-26 09:41 CEST"
Which is what I expect.
However, I am having a hard time understanding the output if I set the timezone explicitly. For example:
format(t, format = "%F %R %Z", tz = "Europe/Stockholm")
# [1] "2017-07-26 09:41 CEST"
produces what I expect, but:
format(t, format = "%F %R %Z", tz = "CEST")
# [1] "2017-07-26 07:41 CEST"
which I think is wrong, I would have expected the output to be "2017-07-26 09:41 CEST" or "2017-07-26 09:41 Europe/Stockholm"
Also
format(t, format = "%F %R %Z", tz = "UTC+02:00")
# [1] "2017-07-26 05:41 UTC"
which I find even weirder, since I would have expected the output to be "2017-07-26 10:41 UTC+02:00"
In the answer I would like to know two things:
why my examples give the output they give?
is there any way to have the timezone always written like "2017-07-26 10:41 UTC+02:00" or "2017-07-26 10:41 Europe/Stockholm"?
Even though R displays time zone in the console as "CEST" (which is %Z), there is no valid timezone by that name. You can check OlsonNames() for valid timezone names.
any(grepl("CEST", OlsonNames()))
#[1] FALSE
For cases when the timezone is displayed as CEST, it is still stored as "Europe/Stockholm" internally. We can check using dput
as.POSIXct("2017-07-26 10:46:12", tz = "Europe/Stockholm")
#[1] "2017-07-26 10:46:12 CEST"
dput(as.POSIXct("2017-07-26 10:46:12", tz = "Europe/Stockholm"))
#structure(1501058772, class = c("POSIXct", "POSIXt"), tzone = "Europe/Stockholm")
Note that %Z is only for output and is not reliable for input. CEST is not a valid value for tz and if you use invalid values for tz, they will commonly be treated as UTC (Read more at ?format.POSIXct or ?strptime). That is why you get unexpected output with format(t, format = "%F %R %Z", tz = "CEST")
Just use "Europe/Stockholm" explicitly.
any(grepl("Europe/Stockholm", OlsonNames()))
#[1] TRUE
As for formatting time in the specific format, try
format(as.POSIXct("2017-07-26 10:46:12", tz = "UTC"), "%F %R UTC%z")
#[1] "2017-07-26 10:46 UTC+0000"

R : how to define date and time format

I have a following time stamps.
outlist
"19980202_0810" "19980202_0815" "19980202_0820"
I want to have
"1998-02-02T08:10:00Z" "1998-02-02T08:15:00Z" "1998-02-02T08:20:00Z"
All I can do either "1998-02-02 08:10:00 PST".
time_obj = strptime(outlist, format = "%Y%m%d_%H%M")
strptime is the import function; strftime is the format (output) function although I think most people would just use format:
outlist <- c("19980202_0810", "19980202_0815", "19980202_0820")
time_obj=strptime(outlist, format = "%Y%m%d_%H%M")
format( time_obj , format="%Y-%m-%dT%H:%M:%SZ")
#[1] "1998-02-02T08:10:00Z" "1998-02-02T08:15:00Z" "1998-02-02T08:20:00Z"
At the moment these are POSIXlt items:
> class(time_obj)
[1] "POSIXlt" "POSIXt"
It would be safer to convert to POSIXct:
> time_obj=as.POSIXct(outlist, format = "%Y%m%d_%H%M")
> time_obj
[1] "1998-02-02 08:10:00 PST" "1998-02-02 08:15:00 PST" "1998-02-02 08:20:00 PST"
> class(time_obj)
[1] "POSIXct" "POSIXt"
POSIXlt objects are not handled well in data.frames.
Edit: The print method for POSIXct objects is documented in ?DateTimeClasses and the help page for format.POSIXct is at``srtftime` which I already cited:
strftime(time_obj, tz = "UTC")
[1] "1998-02-02 16:10:00" "1998-02-02 16:15:00" "1998-02-02 16:20:00"

Modifying timezone of a POSIXct object without changing the display

I have a POSIXct object and would like to change it's tz attribute WITHOUT R to interpret it (interpret it would mean to change how the datetime is displayed on the screen).
Some background: I am using the fasttime package from S.Urbanek, which take strings and cast it to POSIXct very quickly. Problem is that the string should represent a datetime in "GMT" and it's not the case of my data.
I end up with a POSIXct object with tz=GMT, in reality it is tz=GMT+1, if I change the timezone with
attr(datetime, "tzone") <- "Europe/Paris";
datetime <- .POSIXct(datetime,tz="Europe/Paris");
then it will be "displayed" as GMT+2 (the underlying value never change).
EDIT: Here is an example
datetime=as.POSIXct("2011-01-01 12:32:23.234",tz="GMT")
attributes(datetime)
#$tzone
#[1] "GMT"
datetime
#[1] "2011-01-01 12:32:23.233 GMT"
How can I change this attribute without R to interpret it aka how can I change tzone and still have datetime displayed as "2011-01-01 12:32:23.233" ?
EDIT/SOLUTION, #GSee's solution is reasonably fast, lubridate::force_tz very slow
datetime=rep(as.POSIXct("2011-01-01 12:32:23.234",tz="GMT"),1e5)
f <- function(x,tz) return(as.POSIXct(as.numeric(x), origin="1970-01-01", tz=tz))
> system.time(datetime2 <- f(datetime,"Europe/Paris"))
user system elapsed
0.01 0.00 0.02
> system.time(datetime3 <- force_tz(datetime,"Europe/Paris"))
user system elapsed
5.94 0.02 5.98
identical(datetime2,datetime3)
[1] TRUE
To change the tz attribute of a POSIXct variable it is not best practice to convert to character or numeric and then back to POSIXct. Instead you could use the force_tz function of the lubridate package
library(lubridate)
datetime2 <- force_tz(datetime, tzone = "CET")
datetime2
attributes(datetime2)
EDITED:
My previous solution was passing a character value to origin (i.e.origin="1970-01-01"). That only worked here because of a bug (#PR14973) that has now been fixed in R-devel.
origin was being coerced to POSIXct using the tz argument of the as.POSIXct call, and not "GMT" as it was documented to do. The behavior has been changed to match the documentation which, in this case, means that you have to specify your timezone for both the origin and the as.POSIXct call.
datetime
#[1] "2011-01-01 12:32:23.233 GMT"
as.POSIXct(as.numeric(datetime), origin=as.POSIXct("1970-01-01", tz="Europe/Paris"),
tz="Europe/Paris")
#[1] "2011-01-01 12:32:23.233 CET"
This will also works in older versions of R.
An alternative to the lubridate package is via conversion to and back from character type:
recastTimezone.POSIXct <- function(x, tz) return(
as.POSIXct(as.character(x), origin = as.POSIXct("1970-01-01"), tz = tz))
(Adapted from GSee's answer)
Don't know if this is efficient, but it would work for time zones with daylight savings.
Test code:
x <- as.POSIXct('2003-01-03 14:00:00', tz = 'Etc/UTC')
x
recastTimezone.POSIXct(x, tz = 'Australia/Melbourne')
Output:
[1] "2003-01-03 14:00:00 UTC"
[1] "2003-01-03 14:00:00 AEDT" # Nothing is changed apart from the time zone.
Output if I replaced as.character() by as.numeric() (as GSee had done):
[1] "2003-01-03 14:00:00 UTC"
[1] "2003-01-03 15:00:00 AEDT" # An hour is added.

as.POSIXct gives an unexpected timezone

I'm trying to convert a yearmon date (from the zoo package) to a POSIXct in the UTC timezone.
This is what I tried to do:
> as.POSIXct(as.yearmon("2010-01-01"), tz="UTC")
[1] "2010-01-01 01:00:00 CET"
I get the same when I convert a Date:
> as.POSIXct(as.Date("2010-01-01"),tz="UTC")
[1] "2010-01-01 01:00:00 CET"
The only way to get it to work is to pass a character as an argument:
> as.POSIXct("2010-01-01", tz="UTC")
[1] "2010-01-01 UTC"
I looked into the documentation of DateTimeClasses, tzset and timezones. My /etc/localtime is set to Europe/Amsterdam. I couldn't find a way to set the tz to UTC, other than setting the TZ environment variable:
> Sys.setenv(TZ="UTC")
> as.POSIXct(as.Date("2010-01-01"),tz="UTC")
[1] "2010-01-01 UTC"
Is it possible to directly set the timezone when creating a POSIXct from a yearmon or Date?
Edit:
I checked the functions as.POSIXct.yearmon. This one passes to the as.POSIXct.Date.
> zoo:::as.POSIXct.yearmon
function (x, tz = "", ...)
as.POSIXct(as.Date(x), tz = tz, ...)
<environment: namespace:zoo>
So like Joshua says the timezone gets lost in the as.POSIXct.Date. For now I'll use Richies suggestion to set the tzone by hand using:
attr(x, "tzone") <- 'UTC'
This solves the issue of the lost tzone, which is only used for presentation and not internally like Grothendieck and Dwin suggested.
This is because as.POSIXct.Date doesn't pass ... to .POSIXct.
> as.POSIXct.Date
function (x, ...)
.POSIXct(unclass(x) * 86400)
<environment: namespace:base>
You are setting the timezone correctly in your code. The problem you are perceiving is only at the output stage. POSIX values are all referenced to UTC/GMT. Dates are assumed to be midnight times. Midnight UTC is 1 AM CET ( which is apparently where you are).
> as.POSIXct(as.yearmon("2010-01-01"), tz="UTC")
[1] "2009-12-31 19:00:00 EST" # R reports the time in my locale's timezone
> dtval <- as.POSIXct(as.yearmon("2010-01-01"), tz="UTC")
> format(dtval, tz="UTC") # report the date in UTC note it is the correct date ... there
[1] "2010-01-01"
> format(dtval, tz="UTC", format="%Y-%m-%d ")
[1] "2010-01-01 " # use a format string
> format(dtval, tz="UTC", format="%Y-%m-%d %OS3")
[1] "2010-01-01 00.000" # use decimal time
See ?strptime for many, many other format possibilities.
In the help page ?as.POSIXct, for the tz argument it says
A timezone specification to be used
for the conversion, if one is
required. System-specific (see time
zones), but ‘""’ is the current
timezone, and ‘"GMT"’ is UTC
(Universal Time, Coordinated).
Does as.POSIXct(as.yearmon("2010-01-01"), tz="GMT") work for you?
After more perusal of the documentation, in the details section we see:
Dates without times are treated as
being at midnight UTC.
So in your example, the tz argument is ignored. If you use as.POSIXlt it is easier to see what happens with the timezone. The following should all give the same answer, with UTC as the timezone.
unclass(as.POSIXlt(as.yearmon("2010-01-01")))
unclass(as.POSIXlt(as.yearmon("2010-01-01"), tz = "UTC"))
unclass(as.POSIXlt(as.yearmon("2010-01-01"), tz = "GMT"))
unclass(as.POSIXlt(as.yearmon("2010-01-01"), tz = "CET"))
In fact, since you are using as.yearmon (which strips the time out) you will never get to set the timezone. Compare, e.g.,
unclass(as.POSIXlt(as.yearmon("2010-01-01 12:00:00"), tz = "CET"))
unclass(as.POSIXlt("2010-01-01 12:00:00", tz = "CET"))
This seems to be an oddity with the date/time "POSIXct" class methods. Try formatting the "Date" or "yearmon" variable first so that as.POSIXct.character rather than as.POSIXct.{Date, yearmon} is dispatched:
Date
> d <- as.Date("2010-01-01")
> as.POSIXct(format(d), tz = "UTC")
[1] "2010-01-01 UTC"
yearmon
> library(zoo)
> y <- as.yearmon("2010-01")
> as.POSIXct(format(y, format = "%Y-%m-01"), tz = "UTC")
[1] "2010-01-01 UTC"
> # or
> as.POSIXct(format(as.Date(y)), tz = "UTC")
[1] "2010-01-01 UTC"

Resources