Project

General

Profile

Actions

Bug #19253

closed

`Time` objects can't be efficiently and precisely serialized without Marshal

Added by byroot (Jean Boussier) over 1 year ago. Updated over 1 year ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:111392]

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0