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

LocalDateTime arithmetic #66

Closed
hfhbd opened this issue Nov 2, 2020 · 1 comment · Fixed by #126
Closed

LocalDateTime arithmetic #66

hfhbd opened this issue Nov 2, 2020 · 1 comment · Fixed by #126

Comments

@hfhbd
Copy link
Contributor

hfhbd commented Nov 2, 2020

Currently, the ReadMe does not cover LocalDateTime arithmetic. Either make this arithmetic clear by adding this to the Instant arithmetic, with needs a timeZone.

val localDateTime: LocalDateTime = ...
val timeZone: TimeZone = ...
val futureLocalDateTime = (localDateTime.toInstant(timeZone) + 7.days).toLocalDateTime(timeZone)

Or add LocalDateTime arithmetic without a TimeZone, which can result into an overflow, like the java.time API.

val localDateTime: LocalDateTime = ...
val futureLocalDateTime = localDateTime + 7.days
// java.time overflow arithmetic
public LocalDateTime plusHours(long hours) {
    return plusWithOverflow(date, hours, 0, 0, 0, 1);
}

I can submit a PR with LocalDateTime arithmetic, if wanted.

@dkhalanskyjb
Copy link
Collaborator

dkhalanskyjb commented Jun 3, 2021

The problem with LocalDateTime arithmetic is that it is not well-defined. What does it mean to add two hours to a LocalDateTime? There could be two possible interpretations:

  • Change the hour, adding 2 to it, possibly getting past midnight and starting a new day.
  • Find out the time that the clock will show two hours after it has shown the source LocalDateTime.

Somewhat surprisingly, these two approaches give different results. The first one is easier to implement, but it is also often wrong. The reason for that is daylight saving time transitions: if clocks were moved an hour back "during" the addition, then adding two hours to the current time actually represents waiting for three hours.

It's easy to forget to handle daylight saving time, and the resulting errors are very annoying, as they won't show up in testing and will most likely cause problems in production. As LocalDateTime arithmetic that ignores DST is of limited use and encourages errors, we don't provide it.

val futureLocalDateTime = (localDateTime.toInstant(timeZone) + 7.days).toLocalDateTime(timeZone)

This may be either correct or not correct, depending on what you wish to do here, and I'd guess that it's incorrect. In fact, it says to return the wall clock readings after 7 * 24 hours. If localDateTime.hour == 14, futureLocalDateTime.hour may be 13, 14, 15, or, for fancy time zones, even something else. If you want to advance seven days without affecting time-of-day, a solution would be

val futureLocalDateTime = localDateTime.toInstant(timeZone)
    .plus(7, DateTimeUnit.DAY, timeZone)
    .toLocalDateTime(timeZone)

This doesn't still guarantee that futureLocalDateTime.hour == localDateTime.hour, as some date+time combinations don't exist in some time zones due to transition gaps (if the clock was moved forward from 13:00 to 14:00, then 13:35 doesn't exist that day). However, it is often preferable, as obtaining a non-existent LocalDateTime may be unacceptable in some cases. If you do really want to ignore transition gaps, you can do

val futureLocalDateTime = localDateTime.date.plus(DatePeriod(days = 7))
    .atTime(localDateTime.hour, localDateTime.minute, localDateTime.second, localDateTime.nanosecond)

This will probably be simpler when we implement #57:

// doesn't actually work now, subject to change
val futureLocalDateTime = localDateTime.date.plus(DatePeriod(days = 7))
    .atTime(localDateTime.time)

The bottom line: adding time-based periods to a LocalDateTime is intentionally cumbersome, as this is rarely what one really wants.

EDIT: fixed some inaccuracies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants