POSIXct Strips Seconds from 12-hour Timestamp - r

I'm trying to convert a 12-hour timestamp to a POSIXct object in R. For some reason it strips away the seconds after the conversion.
## timestamp
chk = '17-MAY-16 04.51.34.000000000 PM'
## convert
as.POSIXct(chk, format = '%d-%b-%y %I.%M.%S.%OS %p', tz = 'America/New_York')
[1] "2016-05-17 16:51:00 EDT"
Am I doing something incorrectly?

It does not strip the seconds. It simply adheres to a default for printing and formatting which does not include subseconds.
Witness an example that
actually has subsecond entries
runs in a session with options(digits.secs) set correctly
corrects one error you had in the format string
Demo:
R> options(digits.secs=6) # important to tell R we want subsecs
R> input <- '17-MAY-16 04.51.34.123456 PM'
R> as.POSIXct(input, '%d-%b-%y %I.%M.%OS %p', tz = 'America/New_York')
[1] "2016-05-17 16:51:34.123456 EDT"
R>
If we reset digits.secs=0 it falls back to whole seconds only (which is after all a good default for many settings, though one may argue that %0S could override it...)
R> options(digits.secs=0) # reset
R> as.POSIXct(input, '%d-%b-%y %I.%M.%OS %p', tz = 'America/New_York')
[1] "2016-05-17 16:51:34 EDT"
R>
Also note the small change to the format string. Don't use both %S and %OS.

Related

Retrieving day and time (in minutes) from character timestamp

I have a timestamp in character in the following format with a suffixed Z:
"2022-03-01T00:00:00Z"
I wanted to retrieve the day in numbers and the time in minutes (should return 0 from the above example).
I tried the execute the following code but my output only retrieve the year-month-day and not the time format.
time_stamp <- as.POSIXct(participant1_data$timestamp[1], format="%Y-%m-%dT%H:%M")
str(time_stamp)
Output: POSIXct[1:1], format: "2022-03-01"
Is there anything wrong with the code that does not retrieve the time format?
As noted in comments, use tz="UTC", otherwise the "Z" == UTC (zulu) information gets lost, also see this answer.
If time is exactly midnight, the output is omitted.
as.POSIXct('2022-03-01T00:00:00Z', tz="UTC", "%Y-%m-%dT%H:%M:%OSZ")
# [1] "2022-03-01 UTC"
as.POSIXct('2022-03-01T11:11:11Z', tz="UTC", "%Y-%m-%dT%H:%M:%OSZ")
# [1] "2022-03-01 11:11:11 UTC"
You have now read in the time correctly and got "POSIXct" class. To achieve a different output from it, you need to format it to character with the desired format using strftime which allows to specify the correct timezone tz=, e.g. "CET".
strftime(as.POSIXct('2022-03-01T00:00:00Z', tz="UTC", "%Y-%m-%dT%H:%M:%OSZ"),
'%F %R', tz='CET')
# [1] "2022-03-01 01:00"
strftime(as.POSIXct('2022-03-01T11:11:11Z', tz="UTC", "%Y-%m-%dT%H:%M:%OSZ"),
'%F %R', tz='CET')
# [1] "2022-03-01 12:11"
Or if I understand "day in numbers and the time in minutes correctly, you maybe want this:
strftime(as.POSIXct('2022-03-01T11:11:11Z', tz="UTC", "%Y-%m-%dT%H:%M:%OSZ"),
'%d %M', tz='CET')
# [1] "01 11"
Type help('strftime') or short ?strftime into the R console and read the help page for possible in/output options.

Sys.Date() with as.POSIXct()

Trying to get current date in a POSIXct class. I have tried the following:
as.POSIXct(Sys.Date(), format = "%m/%d/%y", tz = "EST")
and got
[1] "2021-02-12 19:00:00 EST"
and I wish to only get the date without the time but in POSIXct class. For instance:
[1] "2021-02-12"
Convert the Date class object to character first:
as.POSIXct(format(Sys.Date()))
## [1] "2021-02-13 EST"
Even shorter is:
trunc(Sys.time(), "day")
## [1] "2021-02-13 EST"
Note:
POSIXct objects are stored internally as seconds since the Epoch and not as separate date and time so they always have times; however, if the time is midnight as it is here then it does not display when printed using the default formatting.
if you only need the Date it is normally better to use Date class since using POSIXct class can result in subtle time zone errors if you are not careful and there is typically no reason to expose yourself to that potential if you don't need to.
if you change the session's time zone then it won't display without the time because midnight in one time zone is not midnight other time zones.
x <- as.POSIXct(format(Sys.Date()))
x
## [1] "2021-02-13 EST"
# change time zone
Sys.setenv(tz = "GMT")
x
## [1] "2021-02-13 05:00:00 GMT"
# change back
Sys.setenv(tz = "")
x
## [1] "2021-02-13 EST"

formatting time in R error

I have a Time column in my df with value 1.01.2016 0:00:05. I want it without the seconds and therefore used df$Time <- as.POSIXct(df$Time, format = "%d.%m.%Y :%H:%M", tz = "Asia/Kolkata"). But I get NA value. What is the problem here?
I suspect there are two things working here: the storage of a time object (POSIXt), and the representation of that object.
The string you present is (I believe) not a proper POSIXt (whether POSIXct or POSIXlt) object for R, which means it is just a character string. In that case, you can remove it with:
gsub(':[^:]*$', '', '1.01.2016 0:00:05')
# [1] "1.01.2016 0:00"
However, that is still just a string, not a date or time object. If you parse it into a time-object that R knows about:
as.POSIXct("1.01.2016 0:00:05", format = "%d.%m.%Y %H:%M:%S", tz = "Asia/Kolkata")
# [1] "2016-01-01 00:00:05 IST"
then you now have a time object that R knows something about ... and it defaults to representing it (printing it on the console) with seconds-precision. Typically, all that is available to change for the console-printing is the precision of the seconds, as in
options("digits.secs")
# $digits.secs
# NULL
Sys.time()
# [1] "2018-06-26 18:21:06 PDT"
options("digits.secs"=3)
Sys.time()
# [1] "2018-06-26 18:21:10.090 PDT"
then you can get more. But alas, I do know think there is an R-option to say "always print my POSIXt objects in this way". So your only choice is (at the point where you no longer need it to be a time-like object) to change it back into a string with no time-like value:
x <- as.POSIXct("1.01.2016 0:00:05", format = "%d.%m.%Y %H:%M:%S", tz = "Asia/Kolkata")
x
# [1] "2016-01-01 00:00:05 IST"
?strptime
# see that day-of-month can either be "%d" for 01-31 or "%e" for 1-31
format(x, format="%e.%m.%Y %H:%M")
# [1] " 1.01.2016 00:00"
(This works equally well for a vector.)
Part of me suggests convert to POSIXt and back to string as opposed to my gsub example because using as.POSIXct will tell you when the string does not match the date-time-like object you are expecting, whereas gsub will happily do something wrong or nothing.
Try asPOSIXlt:
> test <- "1.01.2016 0:00:05"
> as.POSIXlt(test, "%d.%m.%Y %H:%M:%S", tz="Asia/Kolkata")
[1] "2016-01-01 00:00:05 IST"

System date in Posixlt and Posixct

I am trying to get the last minute of yesterday using Sys.Date() in Posix time.
force_tz(as.POSIXlt(Sys.Date()-1), tz = 'America/New_York') + 86399
# [1] "2018-01-12 23:59:59 EST"
CORRECT
force_tz(as.POSIXct(Sys.Date()-1), tz = 'America/New_York') + 86399
# [1] "2018-01-12 15:59:59 EST"
INCORRECT
Sys.Date()
# [1] "2018-01-13"
Why does as.Posixct and as.Posixlt return two different values using Sys.Date() and why is the difference 8 hours even after applying force_tz from lubridate ?
As ever, debugonce is your friend. Running debugonce(force_tz), you can see that the difference in output comes from when force_tz hits the branches checking first is.POSIXct(time) (in which case the default tzone = "" is applied); in the POSIXlt case, the default branch is hit, where as.POSIXct is applied to time and tz(time) (which comes out as UTC for a POSIXlt object) is used as the time zone.
This comes down to something subtle happening; from ?as.POSIXlt.Date:
Dates without times are treated as being at midnight UTC.
Hence
tz(as.POSIXlt(Sys.Date()-1))
# [1] "UTC"
But
tz(as.POSIXct(Sys.Date()-1))
# [1] ""
What's peculiar is this can't be overridden -- as.POSIXlt.Date doesn't accept a tz argument:
formals(as.POSIXlt.Date)
# $x
# $...
If you want to use POSIXct, how about the following?
force_tz(as.POSIXct(sprintf('%s 00:00:00', Sys.Date())), 'America/New_York') - 1L
# [1] "2018-01-12 23:59:59 EST"

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.

Resources