Bug #10135

Time.at is inaccurate

Added by John Smart 8 months ago. Updated 7 months ago.

[ruby-core:64369]
Status:Rejected
Priority:Low
Assignee:Akira Tanaka
ruby -v:ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0] Backport:2.0.0: UNKNOWN, 2.1: UNKNOWN

Description

I believe I have found an issue with Time.at. Many runtimes and DBs use milliseconds since epoch for recording time. Currently, the only way to generate a Time in ruby with milliseconds is:

Time.at(milliseconds / 1000.0)

However, this is inaccurate:

> Time.at(1381089302195 / 1000.0).to_f
=> 1381089302.1949997
> Time.at(1381089302195 / 1000.0).strftime('%3N')
=> "194"

This doesn't make much sense because you would expect:

> 1381089302195 / 1000.0
=> 1381089302.195
> (1381089302195 / 1000.0).to_f
=> 1381089302.195

It seems that somewhere in the MRI, someone is using floating-point math to represent a Time.

Is there any other way (workaround) to construct Time with millisecond accuracy?


Related issues

Related to Ruby trunk - Bug #10136: printf("%.60f\n", 0.1r) shows 0.100000000000000005551115123125782702118158340454101562500000 Closed 08/15/2014

History

#1 Updated by Tsuyoshi Sawada 8 months ago

Your way of presentation may not be clear. I suppose the division is irrelevant here. Your point seems to be that while to_f is idempotent with:

    1381089302.195.to_f
    #=> 1381089302.195

the float stored by Time.at is not the same float:

    Time.at(1381089302.195).to_f
    #=> 1381089302.1949997

#2 Updated by Eric Wong 8 months ago

I didn't know this before, but I investigated this and see Time.at
may take a second argument for microseconds. You may try:

Time.at(seconds, microseconds_with_frac) -> time
n = 1381089302195
p Time.at(n / 1000, (n % 1000) * 1_000).strftime('%3N')

#3 Updated by Yui NARUSE 8 months ago

  • Related to Bug #10136: printf("%.60f\n", 0.1r) shows 0.100000000000000005551115123125782702118158340454101562500000 added

#4 Updated by Nobuyoshi Nakada 7 months ago

  • Priority changed from High to Low
  • Assignee changed from ruby-core to Akira Tanaka

1381089302.195 cannot be represented in binary format.
AFAIK, it should be a spec by akr that Time#strftime('%N') truncates but doesn't round.

#5 Updated by David MacMahon 7 months ago

John Smart wrote:

I believe I have found an issue with Time.at. Many runtimes and DBs use milliseconds since epoch for recording time. Currently, the only way to generate a Time in ruby with milliseconds is:

Time.at(milliseconds / 1000.0)

Another (better) way is:

Time.at(milliseconds.to_r / 1000)

That passes the non-integer seconds as a Rational rather than a Float so precision is maintained:

>> Time.at(1381089302195.to_r / 1000).strftime('%N')
=> "195000000"

# Modern Ruby (since 2.x?) can use 'r' suffix for literal Rationals
>> Time.at(1381089302195r / 1000).strftime('%N')
=> "195000000"

It seems that somewhere in the MRI, someone is using floating-point math to represent a Time.

Actually, you were the one who started using floating point math to represent Time when you divided milliseconds by 1000.0! :-)

#6 Updated by Akira Tanaka 7 months ago

  • Status changed from Open to Rejected

John Smart wrote:

It seems that somewhere in the MRI, someone is using floating-point math to represent a Time.

You use floating point math in 1381089302195 / 1000.0 which is
1381089302.1949999332427978515625.

The Time instance contains the value and strftime('%3N') truncates digits
after 3 digits, as documented (with a reason).

Floating point math is not used except conversion from the given argument to
rational.

Is there any other way (workaround) to construct Time with millisecond accuracy?

You can use the rational or the second argument of Time.at as others explained.

#7 Updated by Akira Tanaka 7 months ago

Tsuyoshi Sawada wrote:

the float stored by Time.at is not the same float:

    Time.at(1381089302.195).to_f
    #=> 1381089302.1949997

This is another story.

Time#to_f is not accurate.

There are workaround, though:

Time.at(1381089302.195).to_r.to_f
#=> 1381089302.195

Also available in: Atom PDF