Bug #10135
closedTime.at is inaccurate
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?
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) about 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) about 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