question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Normalize components in DateTimePeriod/DatePeriod

See original GitHub issue

Currently we provide DateTimePeriod type as a bag of several independent components: years, months, days, hours, minutes, seconds, and nanosecodns. This approach leads to several problems:

  • when nanoseconds value exceeds the range [0…10^9), the ISO representation is rendered incorrectly (see #79)
  • there can be several non-equal values of DateTimePeriod that produce the same ISO string representation. Since we intend to use that string representation as the default serialized form for textual formats, it could lead to a situation when deserialized value would be not equal to what was serialized before.

To avoid these problems we propose to do DateTimePeriod component normalization at construction time in the following way:

  • all time components, e.g. hoursnanoseconds are normalized to the single long total value of nanoseconds;
  • days component is stored as is;
  • years and months components are stored together as the total number of months.

Note that this normalization only affects DateTimePeriod storage and internal representation. There shall still remain component properties that will derive values from the normalized internal state. For example, hours property will return the normalized number of nanoseconds divided by 3600 * 10^9, and months will return totalMonths % 12.

This change will have the following consequences:

  • seconds and nanoseconds properties change their type from Long to Int. We may leave a secondary constructor with the long nanoseconds parameter for convenience, though.
  • it may be impossible to fit the difference between extreme dates, such as platform specific min and max dates, as an Int total number of months.
  • the time part of DateTimePeriod will be limited to the ±292 year range. This usually isn’t a practical concern because the time part doesn’t exceed 24H in most cases.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
dkhalanskyjbcommented, Jan 12, 2021

Hmm, I see your point and generally agree with it. Notably, you aren’t the first to approach us with this concern: https://github.com/Kotlin/kotlinx-datetime/issues/89 The angle there is different, but the root cause is the same. The way you put this did make me rethink that issue as well and better understand the concern. Still, it’s not clear what would be the right solution for this.

Indeed, complex, ad-hoc rules governing the data types are usually a sign of a design error. However, it is somewhat deliberate in this case.

or it can mean to turn the wall clock to a reading that equals to the conceptual distance represented by the amount (that’s the timer til sale).

My point was, the timer is already this mix of conceptual/elapsed. Let’s say take a timer that always operates with the wall clock time, which means it has simpler semantics. Then the following is possible: if it is 02 hours, 00 minutes, and 30 seconds until something happens, the timer shows as much, which I observe, then, after one minute passes, the timer would claim that 02 hours, 59 minutes, and 30 seconds are left, utterly confusing me, as, being an end-user in this case, I would not expect that at all. Would you not think it was a bug?

In general, from my perspective, it would be very counterintuitive if, for some moments A, A’, and B, such that A < A’ < B, it happened that A.periodUntil(B, tz) < A'.periodUntil(B, tz) (in terms of the componentwise partial order). This unfortunate disconnect between how countdowns of dates and times are handled does seem to me like a part of the subject area and a big part of why DST transitions are confusing. The asymmetry stems from the fact that DST transitions never retract or advance whole days, only hours and smaller units. Hence, the current implementation preserves this weaker form of monotonicity of periodUntil.

ISO 8601 focuses on information interchange and really doesn’t specify the interpration of durations, it just covers the representation format. Meaning is specific to the application context.

The standard does build up from the definition of seconds by the International System of Units, so I don’t see the room for ambiguity. One second period is defined always to represent one SI second and never, for example, 3601 SI seconds.

But there’s no other way to represent conceptual units smaller than days.

You are right, there is none, and yes, this is unpleasantly asymmetric. Yet, from the end-user perspective, a conceptual minute would be defined like this: “The amount of time it would take for the wall clock to advance by exactly one minute, except if the result would be in a time shift gap, in which case it is the amount of time it would take for the wall clock to advance by the number of minutes in the gap + 1”. Is this a useful concept at all? “Same time, next day” is arguably clearer in meaning and more applicable than “same sub-minute part, next minute.” Do you have any particular use case in mind for this? If so, it would greatly strengthen the argument that we may need to implement such a concept.

To summarize, the solution you’re suggesting–making DateTimePeriod operate on conceptual time units–would defy at least some expectations and become slightly incompatible with ISO-8601. Another solution would be to do this the way Java does it and separate what we call DateTimePeriod into what they call Duration and Period. We don’t want to go down that path. While also conceptually more orthogonal, it is also confusing to the programmers, judging by the number of questions these data types attracted.

Thank you for raising this! Your framing is interesting and thought-provoking. What’s clear for now is contention about us mixing the operations that operate on the wall time (always only out of necessity) with those that don’t, muddying the API semantics with the separation between time-based units and date-based units. It’s not clear for now what can be done about it, though.

0reactions
straight-shootacommented, Jan 12, 2021

The timer example is indeed a really good case for mixing concepts 🤔 When the amount is larger and expressed in days for conciseness, a potential difference introduced by time zone offset change doesn’t have a huge impact on the overall amount and probably would correlate to the common understanding of a day. But when it’s only a couple of hours, the difference between conceptual and elapsed time is significant and the latter is definitely more intuitive.

So it seems having normalized months, days, and normalized (nano-)seconds fields as suggested is probably the best solution then. It’s not totally clean, but practical. At least I don’t have a better option either 😄 As long as the different interpretations as conceptual/elapsed are documented, it’s probably fine.

Read more comments on GitHub >

github_iconTop Results From Across the Web

DatePeriod - Manual
Represents a date period. A date period allows iteration over a set of dates and times, recurring at regular intervals, over a given...
Read more >
Period (Java Platform SE 8 )
A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'. This class models a...
Read more >
DateTimePeriod
If all the time components are zero, returns a DatePeriod. ... The passed numbers are not stored as is but are normalized instead...
Read more >
Why does java.time.Period#normalized() not ...
This is because a period of years or months is always the same amount of time (the same period) for any given date....
Read more >
pandas.Period — pandas 1.5.2 documentation
Get minute of the hour component of the Period. month. Return the month this Period falls on. quarter.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found