-
Notifications
You must be signed in to change notification settings - Fork 110
Orthogonal DateTimePeriod implementation #80
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
Changes from all commits
5947748
3caca4c
d7a8141
512e021
d4c99c0
eb0f283
4393361
54930c6
bf1195d
70d4db8
3214ee3
5d7fb04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,12 +12,45 @@ import kotlin.time.* | |
|
||
class DateTimePeriodTest { | ||
|
||
@Test | ||
fun normalization() { | ||
assertPeriodComponents(DateTimePeriod(years = 1) as DatePeriod, years = 1) | ||
assertPeriodComponents(DateTimePeriod(years = 1, months = 1) as DatePeriod, years = 1, months = 1) | ||
assertPeriodComponents(DateTimePeriod(years = 1, months = -1) as DatePeriod, months = 11) | ||
assertPeriodComponents(DateTimePeriod(years = -1, months = 1) as DatePeriod, months = -11) | ||
assertPeriodComponents(DateTimePeriod(years = -1, months = -1) as DatePeriod, years = -1, months = -1) | ||
assertPeriodComponents(DateTimePeriod(months = 11) as DatePeriod, months = 11) | ||
assertPeriodComponents(DateTimePeriod(months = 14) as DatePeriod, years = 1, months = 2) | ||
assertPeriodComponents(DateTimePeriod(months = -14) as DatePeriod, years = -1, months = -2) | ||
assertPeriodComponents(DateTimePeriod(months = 10, days = 5) as DatePeriod, months = 10, days = 5) | ||
assertPeriodComponents(DateTimePeriod(years = 1, days = 40) as DatePeriod, years = 1, days = 40) | ||
assertPeriodComponents(DateTimePeriod(years = 1, days = -40) as DatePeriod, years = 1, days = -40) | ||
assertPeriodComponents(DateTimePeriod(days = 5) as DatePeriod, days = 5) | ||
|
||
assertPeriodComponents(DateTimePeriod(hours = 3), hours = 3) | ||
assertPeriodComponents(DateTimePeriod(hours = 1, minutes = 120), hours = 3) | ||
assertPeriodComponents(DateTimePeriod(hours = 1, minutes = 119, seconds = 60), hours = 3) | ||
assertPeriodComponents(DateTimePeriod(hours = 1, minutes = 119, seconds = 59, nanoseconds = 1_000_000_000), hours = 3) | ||
assertPeriodComponents(DateTimePeriod(hours = 1, minutes = 121, seconds = -59, nanoseconds = -1_000_000_000), hours = 3) | ||
assertPeriodComponents(DateTimePeriod()) | ||
assertPeriodComponents(DatePeriod()) | ||
|
||
assertPeriodComponents(DateTimePeriod(days = 1, hours = -1), days = 1, hours = -1) | ||
assertPeriodComponents(DateTimePeriod(days = -1, hours = -1), days = -1, hours = -1) | ||
|
||
assertPeriodComponents(DateTimePeriod(years = -1, months = -2, days = -3, hours = -4, minutes = -5, seconds = 0, nanoseconds = 500_000_000), | ||
years = -1, months = -2, days = -3, hours = -4, minutes = -4, seconds = -59, nanoseconds = -500_000_000) | ||
|
||
assertPeriodComponents(DateTimePeriod(nanoseconds = 999_999_999_999_999L), hours = 277, minutes = 46, seconds = 39, nanoseconds = 999_999_999) | ||
assertPeriodComponents(DateTimePeriod(nanoseconds = -999_999_999_999_999L), hours = -277, minutes = -46, seconds = -39, nanoseconds = -999_999_999) | ||
} | ||
|
||
@Test | ||
fun toStringConversion() { | ||
assertEquals("P1Y", DateTimePeriod(years = 1).toString()) | ||
assertEquals("P1Y1M", DatePeriod(years = 1, months = 1).toString()) | ||
assertEquals("P11M", DateTimePeriod(months = 11).toString()) | ||
assertEquals("P14M", DateTimePeriod(months = 14).toString()) // TODO: normalize or not | ||
assertEquals("P1Y2M", DateTimePeriod(months = 14).toString()) | ||
assertEquals("P10M5D", DateTimePeriod(months = 10, days = 5).toString()) | ||
assertEquals("P1Y40D", DateTimePeriod(years = 1, days = 40).toString()) | ||
|
||
|
@@ -29,8 +62,74 @@ class DateTimePeriodTest { | |
assertEquals("-P1DT1H", DateTimePeriod(days = -1, hours = -1).toString()) | ||
assertEquals("-P1M", DateTimePeriod(months = -1).toString()) | ||
|
||
assertEquals("P-1Y-2M-3DT-4H-5M0.500000000S", | ||
assertEquals("-P1Y2M3DT4H4M59.500000000S", | ||
DateTimePeriod(years = -1, months = -2, days = -3, hours = -4, minutes = -5, seconds = 0, nanoseconds = 500_000_000).toString()) | ||
|
||
assertEquals("PT277H46M39.999999999S", DateTimePeriod(nanoseconds = 999_999_999_999_999L).toString()) | ||
assertEquals("PT0.999999999S", DateTimePeriod(seconds = 1, nanoseconds = -1L).toString()) | ||
assertEquals("-PT0.000000001S", DateTimePeriod(nanoseconds = -1L).toString()) | ||
assertEquals("P1DT-0.000000001S", DateTimePeriod(days = 1, nanoseconds = -1L).toString()) | ||
assertEquals("-PT0.999999999S", DateTimePeriod(seconds = -1, nanoseconds = 1L).toString()) | ||
assertEquals("P1DT-0.999999999S", DateTimePeriod(days = 1, seconds = -1, nanoseconds = 1L).toString()) | ||
} | ||
|
||
@Test | ||
fun parseIsoString() { | ||
assertEquals(DateTimePeriod(years = 1), DateTimePeriod.parse("P1Y")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to assert the expected property values rather than equality as a whole in this test. Because otherwise it's unclear what values these properties would get as a result of parsing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You raise a good point, though I think that the values of properties and the results of parsing are separate concerns, so I added another test to check the normalization. |
||
assertEquals(DatePeriod(years = 1, months = 1), DateTimePeriod.parse("P1Y1M")) | ||
assertEquals(DateTimePeriod(months = 11), DateTimePeriod.parse("P11M")) | ||
assertEquals(DateTimePeriod(months = 10, days = 5), DateTimePeriod.parse("P10M5D")) | ||
assertEquals(DateTimePeriod(years = 1, days = 40), DateTimePeriod.parse("P1Y40D")) | ||
|
||
assertEquals(DateTimePeriod(months = 14), DateTimePeriod.parse("P14M")) | ||
assertPeriodComponents(DateTimePeriod.parse("P14M") as DatePeriod, years = 1, months = 2) | ||
|
||
assertEquals(DateTimePeriod(hours = 1), DateTimePeriod.parse("PT1H")) | ||
assertEquals(DateTimePeriod(), DateTimePeriod.parse("P0D")) | ||
assertEquals(DatePeriod(), DateTimePeriod.parse("P0D")) | ||
|
||
assertEquals(DateTimePeriod(days = 1, hours = -1), DateTimePeriod.parse("P1DT-1H")) | ||
assertEquals(DateTimePeriod(days = -1, hours = -1), DateTimePeriod.parse("-P1DT1H")) | ||
assertEquals(DateTimePeriod(months = -1), DateTimePeriod.parse("-P1M")) | ||
|
||
assertEquals(DateTimePeriod(years = -1, months = -2, days = -3, hours = -4, minutes = -5, seconds = 0, nanoseconds = 500_000_000), | ||
DateTimePeriod.parse("P-1Y-2M-3DT-4H-5M0.500000000S")) | ||
assertPeriodComponents(DateTimePeriod.parse("P-1Y-2M-3DT-4H-5M0.500000000S"), | ||
years = -1, months = -2, days = -3, hours = -4, minutes = -4, seconds = -59, nanoseconds = -500_000_000) | ||
|
||
assertEquals(DateTimePeriod(nanoseconds = 999_999_999_999_999L), DateTimePeriod.parse("PT277H46M39.999999999S")) | ||
assertPeriodComponents(DateTimePeriod.parse("PT277H46M39.999999999S"), | ||
hours = 277, minutes = 46, seconds = 39, nanoseconds = 999_999_999) | ||
|
||
assertEquals(DateTimePeriod(nanoseconds = 999_999_999), DateTimePeriod.parse("PT0.999999999S")) | ||
assertEquals(DateTimePeriod(nanoseconds = -1), DateTimePeriod.parse("-PT0.000000001S")) | ||
assertEquals(DateTimePeriod(days = 1, nanoseconds = -1), DateTimePeriod.parse("P1DT-0.000000001S")) | ||
assertEquals(DateTimePeriod(nanoseconds = -999_999_999), DateTimePeriod.parse("-PT0.999999999S")) | ||
assertEquals(DateTimePeriod(days = 1, nanoseconds = -999_999_999), DateTimePeriod.parse("P1DT-0.999999999S")) | ||
assertPeriodComponents(DateTimePeriod.parse("P1DT-0.999999999S"), days = 1, nanoseconds = -999_999_999) | ||
|
||
// overflow of `Int.MAX_VALUE` months | ||
assertFailsWith<IllegalArgumentException> { DateTimePeriod.parse("P2000000000Y") } | ||
assertFailsWith<IllegalArgumentException> { DateTimePeriod.parse("P1Y2147483640M") } | ||
|
||
// too large a number in a field | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P3000000000Y") } | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P3000000000M") } | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P3000000000D") } | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P3000000000H") } | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P3000000000M") } | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P3000000000S") } | ||
|
||
// wrong order of signifiers | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P1Y2D3M") } | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P0DT1M2H") } | ||
|
||
// loss of precision in fractional seconds | ||
assertFailsWith<DateTimeFormatException> { DateTimePeriod.parse("P0.000000000001S") } | ||
|
||
// non-zero time components when parsing DatePeriod | ||
assertFailsWith<IllegalArgumentException> { DatePeriod.parse("P1DT1H") } | ||
DatePeriod.parse("P1DT0H") | ||
} | ||
|
||
@Test | ||
|
@@ -69,4 +168,16 @@ class DateTimePeriodTest { | |
assertEquals(period, duration.toDateTimePeriod()) | ||
} | ||
} | ||
} | ||
|
||
private fun assertPeriodComponents(period: DateTimePeriod, | ||
years: Int = 0, months: Int = 0, days: Int = 0, | ||
hours: Int = 0, minutes: Int = 0, seconds: Int = 0, nanoseconds: Int = 0) { | ||
assertEquals(years, period.years) | ||
assertEquals(months, period.months) | ||
assertEquals(days, period.days) | ||
assertEquals(hours, period.hours) | ||
assertEquals(minutes, period.minutes) | ||
assertEquals(seconds, period.seconds) | ||
assertEquals(nanoseconds, period.nanoseconds) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use this assertion function in tests for
parse
as well?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but, in my opinion, it would make those tests less clear. For example,
clearly implies that
DateTimePeriod(months = m) == DateTimePeriod.parse("P${m}M")
, which is a property that we want. Seeing the result in terms of values of class properties would obscure this. Moreover, the values of class properties are now being tested withnormalization()
.What benefits do you see in using this function for tests of parsing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By mixing parsing and normalized equality this test makes an impression that the resulting properties of the parsed
DateTimePeriod
would be the same as those passed to the constructor-like function of expected value. This is misleading.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some unfathomable reason, I didn't see a notification about your response. I am sorry for the delay.
I still disagree and consider these concerns orthogonal: we want parsing to behave the same way construction does, and we want construction to produce specific values of properties. Parsing is not semantically connected to the values of properties, and mixing these rubs me the wrong way. However, I don't consider this issue important enough to die on this hill, so I added a commit to accommodate your preferences, though I would be glad to revert it were you to change your mind about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a compromise, we could leave the equality assertions in the parsing test, but add
assertPeriodComponents
for those values that cause normalization to happen. This would be done mostly for explanatory reasons.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added checks for actual values of properties to places where normalization does affect them.