Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I've run into this problem, and so started looking for a solution. @Aaron's answer is good, but still breaks for large dates.</p> <p>Here is code that rounds the seconds properly, according to <code>format</code> or <code>option("digits.secs")</code>:</p> <pre><code>form &lt;- function(x, format = "", tz= "", ...) { # From format.POSIXct if (!inherits(x, "POSIXct")) stop("wrong class") if (missing(tz) &amp;&amp; !is.null(tzone &lt;- attr(x, "tzone"))) tz &lt;- tzone # Find the number of digits required based on the format string if (length(format) &gt; 1) stop("length(format) &gt; 1 not supported") m &lt;- gregexpr("%OS[[:digit:]]?", format)[[1]] l &lt;- attr(m, "match.length") if (l == 4) { d &lt;- as.integer(substring(format, l+m-1, l+m-1)) } else { d &lt;- unlist(options("digits.secs")) if (is.null(d)) { d &lt;- 0 } } secs.since.origin &lt;- unclass(x) # Seconds since origin secs &lt;- round(secs.since.origin %% 60, d) # Seconds within the minute mins &lt;- floor(secs.since.origin / 60) # Minutes since origin # Fix up overflow on seconds if (secs &gt;= 60) { secs &lt;- secs - 60 mins &lt;- mins + 1 } # Represents the prior minute lt &lt;- as.POSIXlt(60 * mins, tz=tz, origin=ISOdatetime(1970,1,1,0,0,0,tz="GMT")); lt$sec &lt;- secs + 10^(-d-1) # Add in the seconds, plus a fudge factor. format.POSIXlt(as.POSIXlt(lt), format, ...) } </code></pre> <p>The fudge factor of 10^(-d-1) is from here: <a href="https://stackoverflow.com/questions/15383057/15391386">Accurately converting from character-&gt;POSIXct-&gt;character with sub millisecond datetimes</a> by Aaron.</p> <p>Some examples:</p> <pre><code>f &lt;- "%Y-%m-%d %H:%M:%OS" f3 &lt;- "%Y-%m-%d %H:%M:%OS3" f6 &lt;- "%Y-%m-%d %H:%M:%OS6" </code></pre> <p>From a nearly identical question:</p> <pre><code>x &lt;- as.POSIXct("2012-12-14 15:42:04.577895") &gt; format(x, f6) [1] "2012-12-14 15:42:04.577894" &gt; form(x, f6) [1] "2012-12-14 15:42:04.577895" &gt; myformat.POSIXct(x, 6) [1] "2012-12-14 15:42:04.577895" </code></pre> <p>From above:</p> <pre><code>&gt; format(t1) [1] "2011-10-11 07:49:36.2" &gt; myformat.POSIXct(t1,1) [1] "2011-10-11 07:49:36.3" &gt; form(t1) [1] "2011-10-11 07:49:36.3" &gt; format(t2) [1] "2011-10-11 23:59:59.9" &gt; myformat.POSIXct(t2,0) [1] "2011-10-12 00:00:00" &gt; myformat.POSIXct(t2,1) [1] "2011-10-12 00:00:00.0" &gt; form(t2) [1] "2011-10-12" &gt; form(t2, f) [1] "2011-10-12 00:00:00.0" </code></pre> <p>The real fun comes in 2038 for some dates. I assume this is because we lose one more bit of precision in the mantissa. Note the value of the seconds field.</p> <pre><code>&gt; t3 &lt;- as.POSIXct('2038-12-14 15:42:04.577895') &gt; format(t3) [1] "2038-12-14 15:42:05.5" &gt; myformat.POSIXct(t3, 1) [1] "2038-12-14 15:42:05.6" &gt; form(t3) [1] "2038-12-14 15:42:04.6" </code></pre> <p>This code seems to work for other edge cases that I've tried. The common thing between <code>format.POSIXct</code> and <code>myformat.POSIXct</code> in Aaron's answer is the conversion to from <code>POSIXct</code> to <code>POSIXlt</code> with the seconds field intact.</p> <p>This points to a bug in that conversion. I'm not using any data that isn't available to <code>as.POSIXlt()</code>.</p> <p><strong>Update</strong></p> <p>The bug is in <code>src/main/datetime.c:434</code> in the static function <code>localtime0</code>, but I am not sure yet of the correct fix:</p> <p>Lines 433-434:</p> <pre><code>day = (int) floor(d/86400.0); left = (int) (d - day * 86400.0 + 0.5); </code></pre> <p>The extra <code>0.5</code> for rounding the value is the culprit. Note that the subsecond value of <code>t3</code> above exceeds .5. <code>localtime0</code> deals with seconds only, and the subseconds are added in after <code>localtime0</code> returns.</p> <p><code>localtime0</code> returns correct results if the double presented is an integer value.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload