aep.dev icon indicating copy to clipboard operation
aep.dev copied to clipboard

re-review AEP-0142 Time and duration

Open makahmad opened this issue 2 years ago • 9 comments
trafficstars

makahmad avatar Oct 05 '23 22:10 makahmad

There's something strange here:

If there is no canonical unit, the service should use a string field with [ISO 8601][] duration values, such as P3Y6M4DT12H30M5S. The field name should end with _duration. Designators for zero values may be omitted in accordance with ISO 8601 (for example: PT12H is sufficient to represent "12 hours"), but at least one designator must be present (therefore, a zero duration is PT0S or P0D).

Services should use IDL-specific durations where applicable (such as [google.protobuf.Duration][duration] in protocol buffers) provided that the service converts values to ISO 8601 duration strings in JSON.

It seems like the implication is that an API would support google.protobuf.Duration in Protobuf and ISO 8601 durations in JSON, but these formats are not compatible. For example, there is no way to convert P1M into google.protobuf.Duration (For example, 2024-01-01 + P1M = 2024-02-01 and 2024-02-01 + P1M = 2024-03-01, meaning P1M is not, by a long shot, a fixed number of seconds.)

hudlow avatar Dec 20 '24 04:12 hudlow

(updating the title since this is more about reviewing it, and not about adopting).

It seems like the implication is that an API would support google.protobuf.Duration in Protobuf and ISO 8601 durations in JSON, but these formats are not compatible.

This does seem like a fundamental issue. The original AIPs don't mention adherence to ISO8601: https://google.aip.dev/142.

@rofrankel might have an opinion here. I would say that protobuf duration is a strict subset of ISO8601 duration - protobuf duration can only accept seconds and nanos, while ISO8601 can accept that and other durations.

Maybe the simplest solution is to add a caveat to protobuf.duration for now that explain it can only support nanos / seconds?

toumorokoshi avatar Jan 06 '25 18:01 toumorokoshi

One could possibly argue that as long as leap seconds are smeared, and as long as durations only interact with ISO 8601 dates (not civil time) then you could accept units up to and including weeks (leaving only months and years excluded). But I am inclined to argue that it would be better not to ever do unit canonicalization with an ISO 8601 duration.

One issue I have here is that I think years and months are profoundly useful durations (very often used in contracts, for example) and that the limitation of google.protobuf.Duration is a real deficiency.

As with all sticky date/time problems, we really need @jskeet to tell us what to do.

hudlow avatar Jan 06 '25 18:01 hudlow

Some thoughts:

  • Duration supports a subset of ISO 8601 duration.
  • Individual APIs need to be able to specify that they support a subset of durations (e.g. some specific API method might not support month-based durations).
  • For many use cases, seconds-based durations supported by Duration are probably sufficient.

So I could imagine us writing guidance like:

Services should use IDL-specific durations where applicable (such as [google.protobuf.Duration][duration] in protocol buffers) provided that the service converts values to ISO 8601 duration strings in JSON. If the resource or API method needs to use duration units not supported by the IDL-specific duration representation, it should use a string field instead, and document exactly which units are supported. For example, google.protobuf.Duration only supports second/nanosecond resolution, so a resource that needs to support month-based durations should use a string field.

// An ISO-8601-formatted duration string.  The supported units are ...
string duration = 100 [<some protovalidate annotation>];

rofrankel avatar Jan 06 '25 20:01 rofrankel

Somewhat drive-by comment: there's a lot of room for confusion here before google.protobuf.Duration and ISO-8601 duration represent very different things. I would personally lean on "period" as the term for an ISO-8601 duration instead.

Fundamentally, google.protobuf.Duration is more about "machine time" where ISO-8601 is more about "civil time". I think it's relatively rare for an API to need to support both, and API producers should be really clear about which they mean. In particular, civil time periods should usually be used to represent differences between civils times - so it's reasonable to say "schedule X for one day later than Y" where Y has a civil time associated with it. That may not be the same as saying "schedule X for 24 * 60 * 60 seconds later than Y" due to time zone offset discontinuities... "1 day after 5pm" should, in most use cases, still be 5pm, whereas "24 * 60 * 60 seconds later than an instant which was observed to be 5pm in a particular time zone" may not be 5pm the next day in that same time zone.

Life is generally simpler for software engineers when using machine time, but simpler for end users when using civil time. Alternatively, think of it as "machine-derived values" (e.g. transaction timestamps) vs "user-derived values" (e.g. meeting times).

I hope that provides more help than confusion...

jskeet avatar Jan 07 '25 09:01 jskeet

@jskeet thanks for popping in! A few observations that seem to me to make it a little more complex than civil time vs machine time:

  1. ISO 8601 / RFC 3339 date-time values don't represent civil time since they don't have time zones, just fixed offsets?
  2. <ISO 8601 date-time> + <1 month> seems like a useful construct even if it doesn't fully consider civil time (i.e., time zones).
  3. google.protobuf.Timestamp has a well-defined mapping to and from RFC 3339[^leap-seconds].
  4. Ergo, <google.protobuf.Timestamp> ± <ISO 8601 duration> is well defined?

But I agree there are some complicating factors, like "duration" being a questionable name (though RFC 3339 already uses the term "period" for something else) and the fact that this is a bit of a middle ground between the rationality of fixed-length durations and duration values which fully-consider civil time[^civil-time-duration], and therefore may be more suitable to representing legal contracts.

[^leap-seconds]: This mapping doesn't seem to consider leap seconds, and I assume the intent is that the syntax of RFC 3339 is used, but leap seconds are implicitly smeared such that a valid RFC 3339 value such as 2016-12-31T23:59:60Z is not mappable. [^civil-time-duration]: I assume ISO 8601 does not define <civil date-time value> ± <ISO 8601 duration> though I suppose this could be well defined.

hudlow avatar Jan 12 '25 18:01 hudlow

@rofrankel wouldn't it also be worthwhile to define a proto type for ISO 8601 durations?

hudlow avatar Jan 12 '25 18:01 hudlow

I hope that provides more help than confusion...

Your input is always helpful @jskeet! Thanks for taking the time.

ISO 8601 / RFC 3339 date-time values don't represent civil time since they don't have time zones, just fixed offsets? <ISO 8601 date-time> + <1 month> seems like a useful construct even if it doesn't fully consider civil time (i.e., time zones).

My interpretation of Jon's message was more about how it's in the realm of a "civil time", in the sense that it's a unit of time that humans care about more than machines (which may care about a duration for timeouts, an example).

I imagine civil time is not a scientifically precise definition - so perhaps we could define it to also refer to ISO 8601 times without a time zone?

I agree with the sentiment of the comment though: ISO 8601 durations can represent a unit of time that is different from that of a simple proto duration. And therefore it's probably worth separating the definition. perhaps "duration" and "civil duration" or something along those lines.

toumorokoshi avatar Jan 13 '25 22:01 toumorokoshi

Please don't proceed here without reading at least the introduction of the ECMAScript Temporal proposal, which already incorporates lots of research and decisions in exactly this space. Java separates durations into distinct Period (calendar units) and Duration (fixed time units) concepts, but that is troublesome for real-world needs like P1MT1H (one month plus one hour). Temporal also incorporates RFC 9557 (extending RFC 3339 to include time zone/calendar/etc.), and defines distinct arithmetic for instants vs. zoned date-times vs. plain (zoneless) date-times.

gibson042 avatar Jan 14 '25 15:01 gibson042