aep.dev
aep.dev copied to clipboard
re-review AEP-0142 Time and duration
There's something strange here:
If there is no canonical unit, the service should use a
stringfield with [ISO 8601][] duration values, such asP3Y6M4DT12H30M5S. The field name should end with_duration. Designators for zero values may be omitted in accordance with ISO 8601 (for example:PT12His sufficient to represent "12 hours"), but at least one designator must be present (therefore, a zero duration isPT0SorP0D).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.)
(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?
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.
Some thoughts:
Durationsupports 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
Durationare 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.Durationonly 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>];
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 thanks for popping in! A few observations that seem to me to make it a little more complex than civil time vs machine 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).google.protobuf.Timestamphas a well-defined mapping to and from RFC 3339[^leap-seconds].- 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.
@rofrankel wouldn't it also be worthwhile to define a proto type for ISO 8601 durations?
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.
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.