[ruby-core:111392] [Ruby master Bug#19253] `Time` objects can't be efficiently and precisely serialized without Marshal

Issue #19253 has been reported by byroot (Jean Boussier). ---------------------------------------- Bug #19253: `Time` objects can't be efficiently and precisely serialized without Marshal https://bugs.ruby-lang.org/issues/19253 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- ### 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](https://shopify.engineering/caching-without-marshal-part-one)). 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. ```ruby
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:
```ruby
>> 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>

Issue #19253 has been updated by mame (Yusuke Endoh). 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 ``` ? ---------------------------------------- Bug #19253: `Time` objects can't be efficiently and precisely serialized without Marshal https://bugs.ruby-lang.org/issues/19253#change-100767 * Author: byroot (Jean Boussier) * Status: Open * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- ### 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](https://shopify.engineering/caching-without-marshal-part-one)). 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. ```ruby
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:
```ruby
>> 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>

Issue #19253 has been updated by byroot (Jean Boussier). Interesting, `to_r` is what we were currently using: https://github.com/Shopify/paquito/pull/28. The only part missing is the `zone` property: ```ruby
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.
----------------------------------------
Bug #19253: `Time` objects can't be efficiently and precisely serialized without Marshal
https://bugs.ruby-lang.org/issues/19253#change-100768
* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
### 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](https://shopify.engineering/caching-without-marshal-part-one)).
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.
```ruby
>> 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: ```ruby
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>
--
https://bugs.ruby-lang.org/

Issue #19253 has been updated by byroot (Jean Boussier). Status changed from Open to Closed Ok, I suppose the `#zone` isn't that important. Thank you @mame. ---------------------------------------- Bug #19253: `Time` objects can't be efficiently and precisely serialized without Marshal https://bugs.ruby-lang.org/issues/19253#change-100770 * Author: byroot (Jean Boussier) * Status: Closed * Priority: Normal * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- ### 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](https://shopify.engineering/caching-without-marshal-part-one)). 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. ```ruby
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:
```ruby
>> 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>

Issue #19253 has been updated by Eregon (Benoit Daloze). `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) ```ruby
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.
----------------------------------------
Bug #19253: `Time` objects can't be efficiently and precisely serialized without Marshal
https://bugs.ruby-lang.org/issues/19253#change-100790
* Author: byroot (Jean Boussier)
* Status: Closed
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
### 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](https://shopify.engineering/caching-without-marshal-part-one)).
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.
```ruby
>> 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: ```ruby
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>
--
https://bugs.ruby-lang.org/
participants (3)
-
byroot (Jean Boussier)
-
Eregon (Benoit Daloze)
-
mame (Yusuke Endoh)