Bug #9951

DateTime.strftime and Time.strftime differ in how they treat "%L"

Added by dchelimsky (David Chelimsky) over 3 years ago. Updated over 3 years ago.

Target version:
ruby -v:
1.9.3 - 2.1.1

# => "678",1,2,3,4,5.678).strftime("%L")
# => "677"

I think these should both produce "678", but at the very least they should produce the same number so users don't have to special case one or the other. I realize there is floating point math under the hood here, but that's an implementation detail I don't think users should care about in this case.


#1 [ruby-core:63212] Updated by nobu (Nobuyoshi Nakada) over 3 years ago

  • Description updated (diff)
  • Status changed from Open to Assigned
  • Assignee set to akr (Akira Tanaka)

DateTime seems to round the fraction of second in nanoseconds at initialization.

#2 [ruby-core:63213] Updated by phasis68 (Heesob Park) over 3 years ago

Time seems to truncate the fraction of second in strftime.

Here is a patch

diff --git a/strftime.c b/strftime.c
index 83550e9..a0a7de5 100644
--- a/strftime.c
+++ b/strftime.c
@@ -705,7 +705,7 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
                                 else {
                                         int i;
                                         for (i = 0; i < 9-precision; i++)
-                                                subsec /= 10;
+                                                subsec = (int)(subsec/10.0+0.5);
                                         snprintf(s, endp - s, "%0*ld", precision, subsec);
                                         s += precision;
@@ -725,7 +725,7 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
                                         n *= 10;
                                 if (n != 1)
                                         subsec = mul(subsec, INT2FIX(n));
-                                subsec = div(subsec, INT2FIX(1));
+                                subsec = div(add(subsec,DBL2NUM(0.5)), INT2FIX(1));

                                 if (FIXNUM_P(subsec)) {
                                         (void)snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec));

#3 [ruby-core:63214] Updated by akr (Akira Tanaka) over 3 years ago

  • Status changed from Assigned to Rejected

The problem of float is ruby can not know what user want to
specify 5.678 or 5.67799999999999993605115378159098327159881591796875.

DateTime's rounding to nanosecond means DateTime suppose
users don't want to specify under-nanosecond time.
I don't want to such assumption for Time.

%L uses floor because 0.9999 should be shown as 999, not 1000.

So, I don't have idea to solve this issue in Ruby side.

Please use rational, such as 5.678r.

% ruby -e 'p,1,2,3,4,5.678r).strftime("%L")' 

#4 [ruby-core:63218] Updated by dchelimsky (David Chelimsky) over 3 years ago

Akira, thanks for the "rational" suggestion. That appears to solve the problem for me.

Also available in: Atom PDF