Description
The original report for Bug 17350 queried whether fractions of a second for a POSIXct were displayed correctly when printed. The discussion started to pin down some issues which I explore further here.
We'll use Sebastian Meyer's (@bastistician's) example code:
x <- as.POSIXct("2009-08-03 12:01:59") + 0:3/10
As Sebastian notes, print.POSIXct(x)
calls format.POSIXct(x)
which is a wrapper around format.POSIXlt()
. If we check the seconds after converting x
to POSIXct, we see there are lots of digits due to the floating point arithmetic (i.e. adding 0, 0.1, 0.2, 0.3 does not give exactly 59.0, 59.1, 59.2, 59.3).
secs <- as.POSIXlt(x)$sec
print(secs, digits = 15)
# [1] 59.0000000000000 59.0999999046326 59.2000000476837 59.2999999523163
Main Bug: inconsistent formatting of digits for fractional seconds
As documented in ?format.POSIXlt
the default format is "%Y-%m-%d %H:%M:%S" when the global option digits.secs
is NULL (or 0). In other words, no fractional seconds are displayed.
options(digits.secs = 0)
format(x)
# [1] "2009-08-03 12:01:59" "2009-08-03 12:01:59" "2009-08-03 12:01:59" "2009-08-03 12:01:59"
If we use the "%OS" format, 0:6 digits are used for the fractional seconds, with the number taken from the global option digits.secs
, e.g.
options(digits.secs = 3)
format(x, "%Y-%m-%d %H:%M:%OS")
# [1] "2009-08-03 12:01:59.000" "2009-08-03 12:01:59.099" "2009-08-03 12:01:59.200"
# [4] "2009-08-03 12:01:59.299"
If we have set the global option digit.sec > 0
and use the default format, however, we only get 1 decimal place:
options(digits.secs = 3)
format(x)
# [1] "2009-08-03 12:01:59.0" "2009-08-03 12:01:59.0" "2009-08-03 12:01:59.2" "2009-08-03 12:01:59.2"
The same happens if the global option is at the default and we set digits = 3
options(digits.secs = 0)
format(x, digits = 3)
# [1] "2009-08-03 12:01:59.0" "2009-08-03 12:01:59.0" "2009-08-03 12:01:59.2" "2009-08-03 12:01:59.2"
This is due to a block of code in format.POSIXlt()
(L385-400) that is only run when the default format is used (format == ""
) and adjusts the number of digits to avoid trailing zeroes.
The task here is to discuss how format.POSIXlt(x)
could be modified to be more consistent and to prepare a corresponding patch.
Side issue: digits ignored when "%OS" format used
A side issue here is that when specifying "%OS" format directly, only the global digit.secs
option has an effect:
options(digits.secs = 3)
format(x, "%Y-%m-%d %H:%M:%OS", digits = 2)
# [1] "2009-08-03 12:01:59.000" "2009-08-03 12:01:59.099" "2009-08-03 12:01:59.200"
# [4] "2009-08-03 12:01:59.299"
options(digits.secs = 0)
format(x, "%Y-%m-%d %H:%M:%OS", digits = 2)
# [1] "2009-08-03 12:01:59" "2009-08-03 12:01:59" "2009-08-03 12:01:59"
# [4] "2009-08-03 12:01:59"
This is inconsistent with how options in R usually behave, e.g.
options(digits = 2)
print(secs)
# [1] 59 59 59 59
print(secs, digits = 15)
# 59.0000000000000 59.0999999046326 59.2000000476837 59.2999999523163
and is due to the C code looking up the option to get the digits datetime.c#L986, rather than taking an argument.
So, an extension to this task could be adjusting the R or C code to respect the digits
argument.
Other
Potentially the help file for format.POSIXlt()
needs to be updated to document the behaviour of the function, e.g. the help for the format
and digits
arguments could be reviewed.