Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provide a more convenient way to (de)serialize an integer number of units since the Unix epoch #101

Closed
BurntSushi opened this issue Aug 15, 2024 · 0 comments · Fixed by #119
Labels
enhancement New feature or request

Comments

@BurntSushi
Copy link
Owner

See #100 for a use case and how to do it with Jiff at time of writing. That is, this issue isn't about adding anything you can't already do with Jiff today, but as the answer I posted in #100 shows, coming up with your own solution to this is a little annoying.

Chrono provides a serde sub-module that handles use cases like this. For example: https://docs.rs/chrono/latest/chrono/serde/ts_seconds_option/index.html

I do like the simplicity of Chrono's approach here. In particular, it avoids defining a new wrapper type for each option, which would be a lot more involved. Instead, you just use it like serde(with = "chrono::serde::ts_seconds_option"). I'm not a huge fan of the naming, but it isn't bad.

So probably we copy Chrono's approach here? I'm struggling to see something better.

It'd be nice to make it "automatic," but we can't really, because I think timestamps in seconds and milliseconds are pretty common and it's impossible to disambiguate between them.

@BurntSushi BurntSushi added the enhancement New feature or request label Aug 15, 2024
BurntSushi added a commit that referenced this issue Aug 28, 2024
This adds a new `jiff::fmt::serde` sub-module that contains helper
sub-modules that work with Serde's `with` attribute. For example:

```rust
use jiff::Timestamp;

struct Record {
    #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
    timestamp: Timestamp,
}

let json = r#"{"timestamp":1517644800}"#;
let got: Record = serde_json::from_str(&json)?;
assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
assert_eq!(serde_json::to_string(&got)?, json);
```

This is inspired in part by how Chrono supports a similar use case.
It is expected that the behavior should be the same, although this
implementation does support the full gamut of integer types (including
a 128-bit integer number of nanoseconds). Moreover, the naming is
different. Chrono uses a flatter namespace, where as here, we bury
everything into sub-modules. The idea is to leave some room for future
expansion, although I'm not sure there is much else to add. I also feel
like spelling out `timestamp` instead of `ts` is a bit clearer.

The module paths are quite long, e.g.,
`jiff::fmt::serde::timestamp::second::required` and
`jiff::fmt::serde::timestamp::second::optional`. But they are
predictable. And to mitigate users needing to click around through a
deep module tree, we include the full tree in the `jiff::fmt::serde`
module documentation.

Ref #100, Closes #101
BurntSushi added a commit that referenced this issue Aug 28, 2024
This adds a new `jiff::fmt::serde` sub-module that contains helper
sub-modules that work with Serde's `with` attribute. For example:

```rust
use jiff::Timestamp;

struct Record {
    #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
    timestamp: Timestamp,
}

let json = r#"{"timestamp":1517644800}"#;
let got: Record = serde_json::from_str(&json)?;
assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
assert_eq!(serde_json::to_string(&got)?, json);
```

This is inspired in part by how Chrono supports a similar use case.
It is expected that the behavior should be the same, although this
implementation does support the full gamut of integer types (including
a 128-bit integer number of nanoseconds). Moreover, the naming is
different. Chrono uses a flatter namespace, where as here, we bury
everything into sub-modules. The idea is to leave some room for future
expansion, although I'm not sure there is much else to add. I also feel
like spelling out `timestamp` instead of `ts` is a bit clearer.

The module paths are quite long, e.g.,
`jiff::fmt::serde::timestamp::second::required` and
`jiff::fmt::serde::timestamp::second::optional`. But they are
predictable. And to mitigate users needing to click around through a
deep module tree, we include the full tree in the `jiff::fmt::serde`
module documentation.

Ref #100, Closes #101
BurntSushi added a commit that referenced this issue Aug 28, 2024
This adds a new `jiff::fmt::serde` sub-module that contains helper
sub-modules that work with Serde's `with` attribute. For example:

```rust
use jiff::Timestamp;

struct Record {
    #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
    timestamp: Timestamp,
}

let json = r#"{"timestamp":1517644800}"#;
let got: Record = serde_json::from_str(&json)?;
assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
assert_eq!(serde_json::to_string(&got)?, json);
```

This is inspired in part by how Chrono supports a similar use case.
It is expected that the behavior should be the same, although this
implementation does support the full gamut of integer types (including
a 128-bit integer number of nanoseconds). Moreover, the naming is
different. Chrono uses a flatter namespace, where as here, we bury
everything into sub-modules. The idea is to leave some room for future
expansion, although I'm not sure there is much else to add. I also feel
like spelling out `timestamp` instead of `ts` is a bit clearer.

The module paths are quite long, e.g.,
`jiff::fmt::serde::timestamp::second::required` and
`jiff::fmt::serde::timestamp::second::optional`. But they are
predictable. And to mitigate users needing to click around through a
deep module tree, we include the full tree in the `jiff::fmt::serde`
module documentation.

Ref #100, Closes #101
BurntSushi added a commit that referenced this issue Aug 28, 2024
This adds a new `jiff::fmt::serde` sub-module that contains helper
sub-modules that work with Serde's `with` attribute. For example:

```rust
use jiff::Timestamp;

struct Record {
    #[serde(with = "jiff::fmt::serde::timestamp::second::required")]
    timestamp: Timestamp,
}

let json = r#"{"timestamp":1517644800}"#;
let got: Record = serde_json::from_str(&json)?;
assert_eq!(got.timestamp, Timestamp::from_second(1517644800)?);
assert_eq!(serde_json::to_string(&got)?, json);
```

This is inspired in part by how Chrono supports a similar use case.
It is expected that the behavior should be the same, although this
implementation does support the full gamut of integer types (including
a 128-bit integer number of nanoseconds). Moreover, the naming is
different. Chrono uses a flatter namespace, where as here, we bury
everything into sub-modules. The idea is to leave some room for future
expansion, although I'm not sure there is much else to add. I also feel
like spelling out `timestamp` instead of `ts` is a bit clearer.

The module paths are quite long, e.g.,
`jiff::fmt::serde::timestamp::second::required` and
`jiff::fmt::serde::timestamp::second::optional`. But they are
predictable. And to mitigate users needing to click around through a
deep module tree, we include the full tree in the `jiff::fmt::serde`
module documentation.

Ref #100, Closes #101
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant