Skip to content

Consider deprecating DateTimePeriod.plus and DatePeriod.plus #381

@dkhalanskyjb

Description

@dkhalanskyjb

The purpose of DatePeriod and DateTimePeriod is to define the duration between two dates or instants, respectively. Intuitively, it makes sense to define addition of two periods: if there's time p1 between moments A and B and time p2 between moments B and C, then p1 + p2 could be the amount of time between A and C. Unfortunately, it doesn't work this way.

val date1 = LocalDate(2024, 4, 12)
val date2 = LocalDate(2024, 9, 4)
val date3 = LocalDate(2024, 12, 27)
assertEquals(LocalDate(2024, 12, 27), (date1 + (date2 - date1)) + (date3 - date2)) // without using period addition
assertEquals(LocalDate(2024, 12, 28), date1 + ((date2 - date1) + (date3 - date2))) // using period addition

assertEquals(DatePeriod(months = 4, days = 23), date2 - date1)
assertEquals(DatePeriod(months = 3, days = 23), date3 - date2)
assertEquals(DatePeriod(months = 3, days = 23), date3 - date2)
assertEquals(DatePeriod(months = 8, days = 15), date3 - date1)
assertEquals(DatePeriod(months = 7, days = 46), (date3 - date2) + (date2 - date1))

If we don't add periods together, we get what we expected: x + (y - x) becomes y, so in the end, we get back to date3.

The reason the result is different when we add periods together first lies in the order in which addition of periods happens. In the normal case, we get

date1.plus(4, DateTimeUnit.MONTH).plus(23, DateTimeUnit.DAY).plus(3, DateTimeUnit.MONTH).plus(23, DateTimeUnit.DAY)

In the case of adding periods together first, instead, we get

date1.plus(4, DateTimeUnit.MONTH).plus(3, DateTimeUnit.MONTH).plus(23, DateTimeUnit.DAY).plus(23, DateTimeUnit.DAY)

But waiting for three months and then for 23 days is not the same as waiting for 23 days and then waiting for three months, because the lengths of months are different.

With DateTimePeriod, the situation is even worse, because the time component is also added, giving us additional problems like the difference between waiting for 24 hours and one whole day (which isn't always the same, because clocks could shift due to DST transitions).

With all of this in mind, it's unclear what use cases adding periods together correctly achieves and even what precise semantics it has. If we learn of use cases where plus always does what's expected, we could try to limit plus to just these cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking changeThis could break existing code

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions