Bug #19253
closed`Time` objects can't be efficiently and precisely serialized without Marshal
Description
Context¶
In our application we try to avoid to use Marshal
for serializing cache payloads because we want to be strict on what types we allow to be cached. (Full context in this post).
As such we need to be able to break Time
objects into a list of primitive types supported by our serialization format (msgpack).
Problem¶
Maybe I'm missing something, but I as far as I can tell Time
instance can't be recreated in the exact same state.
>> t = Time.now
=> 2022-12-23 09:44:05.693688 +0100
>> Time.at(t.sec, t.nsec, :nanosecond)
=> 1970-01-01 01:00:05.693688 +0100
>> t == Time.at(t.sec, t.nsec, :nanosecond)
=> false
>> t == Time.at(t.sec, t.subsec)
=> false
Additionally, Time
objects created with Time.now
have a String
as Time#zone
, and as far as I can tell that can't be reproduced either:
>> t = Time.now
=> 2022-12-23 09:46:22.452771 +0100
>> t.zone
=> "CET"
>> Time.at(t.sec, t.subsec, in: t.utc_offset).zone
=> nil
>> Time.at(t.sec, t.subsec, in: t.zone)
<internal:timev>:274:in `at': "+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: CET (ArgumentError)
>> Time.at(t.sec, t.subsec, in: TZInfo::Timezone.get(t.zone)).zone
=> #<TZInfo::DataTimezone: CET>
Updated by mame (Yusuke Endoh) over 1 year ago
How about:
irb(main):001:0> t = Time.now
=> 2022-12-19 16:24:11.749470645 +0900
irb(main):002:0> Time.at(t.to_r, in: t.utc_offset)
=> 2022-12-19 16:24:11.749470645 +0900
irb(main):003:0> Time.at(t.to_r, in: t.utc_offset) == t
=> true
?
Updated by byroot (Jean Boussier) over 1 year ago
Interesting, to_r
is what we were currently using: https://github.com/Shopify/paquito/pull/28.
The only part missing is the zone
property:
>> t.zone
=> "CET"
>> Time.at(t.to_r, in: t.utc_offset).zone
=> nil
It probably isn't a huge deal as it's very unlikely to be used, but would be preferable if it could be restored too.
Updated by byroot (Jean Boussier) over 1 year ago
- Status changed from Open to Closed
Ok, I suppose the #zone
isn't that important.
Thank you @mame (Yusuke Endoh).
Updated by Eregon (Benoit Daloze) over 1 year ago
to_r
seems pretty inefficient, I don't think it's a good solution.
I remember seeing Time#to_r
as a bottleneck in karafka or some dep or so.
Also indeed there should be a way to preserve the zone.
The first example from the description is wrong because it needs to use tv_sec
(the timestamp) and not just sec
(which is seconds of the hour)
> t = Time.now
> t == Time.at(t.tv_sec, t.nsec, :nanosecond)
=> true
> t == Time.at(t.tv_sec, t.tv_nsec, :nanosecond)
=> true
So that seems better and a lot more efficient than to_r
at least.
Of course it doesn't preserve the zone either.
It doesn't care about subsec values below the nanosecond but no built-in clock (i.e. clock_gettime) provides that AFAIK and TruffleRuby & the JVM java.time don't support below nanosecond either.