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

Statically distinguish date-only and date+time units and periods #7

Merged
merged 10 commits into from
Jul 20, 2020

Conversation

ilya-g
Copy link
Member

@ilya-g ilya-g commented Apr 30, 2020

In this PR we're removing CalendarPeriod and introducing DateTimePeriod instead with its subclass DatePeriod.

Also, CalendarUnit enum with the fixed set of values is replaced with DateTimeUnit sealed hierarchy with several predefined units declared in its companion and an operation to produce derived units which are multiples of the basic units.

This allows us to avoid IllegalArgumentException entirely by prohibiting to use time-based units in LocalDate operations.

@ilya-g ilya-g force-pushed the master branch 2 times, most recently from eb9fdee to a1cc52d Compare May 1, 2020 00:00
@ilya-g ilya-g force-pushed the date-calc branch 2 times, most recently from 163b762 to 63c8395 Compare July 4, 2020 00:45
@ilya-g ilya-g changed the title Add Instant.minus(Instant, TimeZone) operation Statically distinguish date-only and date+time units and periods Jul 11, 2020
... for consistency with LocalDate operations.
@ilya-g ilya-g requested review from dkhalanskyjb and kluever and removed request for dkhalanskyjb and kluever July 16, 2020 03:38
@ilya-g
Copy link
Member Author

ilya-g commented Jul 16, 2020

Hi, @kluever, you wanted to review the new approach to date/time units.

Sorry, I cannot request a review from more than one reviewer for this PR for some reason, so I'm just notifying you with this message.

public fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate =
plus(unit.calendarScale, unit.calendarUnit)
public fun LocalDate.plus(value: Int, unit: DateTimeUnit.DateBased): LocalDate =
plus(value * unit.calendarScale, unit.calendarUnit)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, too, safeMultiply or the like is needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be used later, after commonizing that function.

* @throws DateTimeArithmeticException if this value or the result is too large to fit in [LocalDateTime].
*/
public fun Instant.plus(value: Int, unit: DateTimeUnit, zone: TimeZone): Instant =
plus(value * unit.calendarScale, unit.calendarUnit, zone)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to provide an example of how this method could throw (assuming safeAdd) even when the result and the intermediate value fits in LocalDateTime:

Clock.System.now().plus(Long.MAX_VALUE, DateTimeUnit.TimeBased(2), TimeZone.UTC)

In effect, we're asking the system to add (2^63 - 1) * 2 nanoseconds, which is less than 600 years: https://www.wolframalpha.com/input/?i=%282%5E63+-+1%29+*+2+nanoseconds+in+years. The result would fit in a LocalDateTime here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can specialize plus implementation later for different platforms for better handling this situation.

try {
toZonedLocalDateTimeFailing(zone).until(other.toZonedLocalDateTimeFailing(zone), unit)
// TODO: inline 'until' here and simplify operation for time-based units
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify how?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid converting them to different offsets and then bringing back to the same offset.
Alternatively, ensure that they can be both converted to localdatetime, then calculate difference with Instant components.

MILLISECOND,
MICROSECOND,
NANOSECOND
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think splitting CalendarUnit into a similar date/time hierarchy of sealed classes here could simplify some of our implementation code, but I don't know whether this approach has some other downsides, performance etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to migrate our implementation code to DateTimeUnit. CalendarUnit is left mostly for conversion to ChronoUnit when interfacing with external implementation.

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 this pull request may close these issues.

2 participants