6
options(digits.secs = 3);

> strptime("2007-03-30 15:00:00.007", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:00.007"
> strptime("2007-03-30 15:00:00.008", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:00.008"
> strptime("2007-03-30 15:00:00.009", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:00.008"
> strptime("2007-03-30 15:00:00.010", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:00.01"
> strptime("2007-03-30 15:00:00.011", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:00.010"
> strptime("2007-03-30 15:00:00.999", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:00.998"

I'm confused why there's one millisecond's difference from "009", then again from "011".

Martin G
  • 17,357
  • 9
  • 82
  • 98
sunt
  • 303
  • 2
  • 5
  • 12

2 Answers2

9

This is related to R-FAQ 7.31, though it takes a different-than-usual guise.

The behavior you are seeing results from a combination of: (a) the inexact representation of (most) decimal values by binary computers; and (b) the documented behavior of strftime and strptime, which is to truncate rather than round the fractional parts of seconds, to the specified number of decimal places.

From the ?strptime help file (the key word being 'truncated'):

Specific to R is ‘%OSn’, which for output gives the seconds truncated to ‘0 <= n <= 6’ decimal places (and if ‘%OS’ is not followed by a digit, it uses the setting of ‘getOption("digits.secs")’, or if that is unset, ‘n = 3’).

An example will probably illustrate what's going on more effectively than further explanation:

strftime('2011-10-11 07:49:36.3', format="%Y-%m-%d %H:%M:%OS6")
[1] "2011-10-11 07:49:36.299999"

strptime('2012-01-16 12:00:00.3', format="%Y-%m-%d %H:%M:%OS1")
[1] "2012-01-16 12:00:00.2"

In the example above, the fractional '.3' must be best approximated by a binary number that is slightly less than '0.300000000000000000' -- something like '0.29999999999999999'. Because strptime and strftime truncate rather than round to the specified decimal place, 0.3 will be converted to 0.2, if the number of decimal places is set to 1. The same logic holds for your example times, of which half exhibit this behavior, as would (on average) be expected.

Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • Out of curiosity, do you have any idea why Joshua and I couldn't reproduce it on Ubuntu and OSX? – joran Jan 17 '12 at 07:11
  • @joran: could it be a problem of 32bit vs 64bit architecture? I can reproduce the issue on FC16 32bit, so it seems unlikely it is an OS issue... – nico Jan 17 '12 at 07:15
  • @joran -- No, I'm not that knowledgeable, though I am intrigued. I wonder if this, from further down in the `?strftime` help file is a clue: `The behaviour of other conversion specifications (and even if other character sequences commencing with ‘%’ _are_ conversion specifications) is system-specific.` – Josh O'Brien Jan 17 '12 at 07:18
  • @nico -- That's an interesting thought. Still, I'd think the same fundamental limitations should hold for binary representations on 64-bit machines, although the particular values which would be truncated down could easily be different. I wonder if joran or Joshua Ulrich see truncation downward for **any** of the fractional seconds in the OP's question. – Josh O'Brien Jan 17 '12 at 07:23
  • I can't replicate it (for any of the examples) in OSX (Lion or SL) using R 2.14.0 or 2.14.1, both 64bit. So maybe it is a 32 vs 64 bit thing. I'll have to try OSX with a 32bit build... – joran Jan 17 '12 at 17:29
  • @joran -- Very interesting. Though I didn't say it, my machine is 32 bit. I wonder where a difference between 64bit and 32bit treatment of POSIX date-times would be documented (if that's what this is). I have a feeling Dirk Eddelbuettel might know the answer, and may post this as a question if you find that a 32 bit OSX **does** replicate the examples. – Josh O'Brien Jan 17 '12 at 21:30
3

I know it's been "answered" but this issues does still exist for 32 bit R, there is an inconsistency in implementation between the 32 bit and 64 bit versions. The truncation issue is partially true but it is not as a result of the strptime function but the print.POSIXlt method in this particular case.

This can be demonstrated by overwriting the function with a function that produces the expected behaviour. E.g.

print.POSIXlt = function(posix) {
    print(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
       sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
    }

Now the time displays as expected:

> strptime("2007-03-30 15:00:00.009", format = "%Y-%m-%d %H:%M:%OS");
[1] "2007-03-30 15:00:0.009"

For further details, I've covered this here R issue with rounding milliseconds

Community
  • 1
  • 1