Project

General

Profile

Actions

Bug #10135

closed

Time.at is inaccurate

Added by thesmart (John Smart) over 10 years ago. Updated about 10 years ago.

Status:
Rejected
Target version:
ruby -v:
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
[ruby-core:64369]

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 1 (0 open1 closed)

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

Updated by sawa (Tsuyoshi Sawada) over 10 years 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

Updated by normalperson (Eric Wong) over 10 years 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')

Updated by naruse (Yui NARUSE) over 10 years ago

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

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

  • Assignee changed from core to akr (Akira Tanaka)
  • Priority changed from 5 to 3

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.

Updated by david_macmahon (David MacMahon) over 10 years 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! :-)

Updated by akr (Akira Tanaka) about 10 years 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.

Updated by akr (Akira Tanaka) about 10 years 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
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0