-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable construction of dates from week date representation (#106)
* Enable construction of dates from week date representation * Fix validation on week date construction * Fix compile error
- Loading branch information
Showing
9 changed files
with
274 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 0 additions & 14 deletions
14
core/src/commonMain/kotlin/io/islandtime/DateProperties.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
@file:JvmMultifileClass | ||
@file:JvmName("DateTimesKt") | ||
|
||
package io.islandtime | ||
|
||
import io.islandtime.calendar.WeekSettings | ||
import io.islandtime.internal.lastWeekOfWeekBasedYear | ||
import io.islandtime.measures.days | ||
import io.islandtime.measures.weeks | ||
import kotlin.jvm.JvmMultifileClass | ||
import kotlin.jvm.JvmName | ||
|
||
/** | ||
* Converts this date to an ISO week date representation. | ||
*/ | ||
inline fun <T> Date.toWeekDate(action: (year: Int, week: Int, day: Int) -> T): T { | ||
return action(weekBasedYear, weekOfWeekBasedYear, dayOfWeek.number) | ||
} | ||
|
||
/** | ||
* Converts this date to a week date representation using the week definition in [settings]. | ||
*/ | ||
inline fun <T> Date.toWeekDate(settings: WeekSettings, action: (year: Int, week: Int, day: Int) -> T): T { | ||
return action(weekBasedYear(settings), weekOfWeekBasedYear(settings), dayOfWeek.number(settings)) | ||
} | ||
|
||
/** | ||
* Create a [Date] from an ISO week date. | ||
* @param year the week-based year | ||
* @param week the week number of the week-based year | ||
* @param day the ISO day of week number, 1 (Monday) to 7 (Sunday) | ||
* @throws DateTimeException if the year, week, or day is invalid | ||
*/ | ||
fun Date.Companion.fromWeekDate(year: Int, week: Int, day: Int): Date { | ||
checkValidDayOfWeek(day) | ||
checkValidYear(year) | ||
checkValidWeekOfWeekBasedYear(week, year) | ||
// TODO: The day number may exceed the max near the end of the year, but is it even worth checking? | ||
|
||
val jan4 = Date(year, Month.JANUARY, 4) | ||
val dayOfYear = (week * 7 + day) - (jan4.dayOfWeek.number + 3) | ||
|
||
return if (dayOfYear < 1) { | ||
Date(year = year - 1, dayOfYear = dayOfYear + lastDayOfYear(year - 1)) | ||
} else { | ||
val lastDay = lastDayOfYear(year) | ||
|
||
if (dayOfYear > lastDay) { | ||
Date(year = year + 1, dayOfYear = dayOfYear - lastDay) | ||
} else { | ||
Date(year, dayOfYear) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Create a [Date] from a week date representation using the week definition in [settings]. | ||
* @param year the week-based year | ||
* @param week the week number of the week-based year | ||
* @param day the day of week number, 1-7 | ||
* @param settings the week definition to use when interpreting the [year], [week], and [day] | ||
*/ | ||
fun Date.Companion.fromWeekDate(year: Int, week: Int, day: Int, settings: WeekSettings): Date { | ||
checkValidDayOfWeek(day) | ||
|
||
// Week dates around Date.MIN and Date.MAX can fail here if the week year exceeds the supported range, but no easy | ||
// way to work around that. | ||
checkValidYear(year) | ||
|
||
// TODO: This allows the week number to be invalid for the year, but is it worth checking? The day number may also | ||
// exceed the max near the end of the year. | ||
checkValidWeekOfWeekBasedYear(week) | ||
|
||
val date = Date(year, Month.JANUARY, day = settings.minimumDaysInFirstWeek) | ||
val weeksToAdd = (week - date.weekOfYear(settings)).weeks | ||
val daysToAdd = weeksToAdd + (day - date.dayOfWeek.number(settings)).days | ||
return date + daysToAdd | ||
} | ||
|
||
private fun checkValidWeekOfWeekBasedYear(week: Int): Int { | ||
if (week !in 1..53) { | ||
throw DateTimeException("The week '$week' is outside the supported range of 1-53") | ||
} | ||
return week | ||
} | ||
|
||
private fun checkValidWeekOfWeekBasedYear(week: Int, year: Int): Int { | ||
checkValidWeekOfWeekBasedYear(week) | ||
|
||
if (week > lastWeekOfWeekBasedYear(year)) { | ||
throw DateTimeException("Week 53 doesn't exist in $year") | ||
} | ||
|
||
return week | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package io.islandtime | ||
|
||
import io.islandtime.calendar.WeekSettings.Companion.SUNDAY_START | ||
import io.islandtime.test.AbstractIslandTimeTest | ||
import io.islandtime.test.TestData | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertFailsWith | ||
|
||
class WeekDateTest : AbstractIslandTimeTest() { | ||
@Test | ||
fun `Date_toWeekDate() converts to ISO week date`() { | ||
TestData.isoWeekDates.forEach { (date, weekDate) -> | ||
assertEquals(weekDate, date.toWeekDate(::Triple), date.toString()) | ||
} | ||
} | ||
|
||
@Test | ||
fun `Date_toWeekDate() converts to week date with Sunday start week definition`() { | ||
TestData.sundayStartWeekDates.forEach { (date, weekDate) -> | ||
assertEquals(weekDate, date.toWeekDate(SUNDAY_START, ::Triple), date.toString()) | ||
} | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate() throws an exception when year is out of range`() { | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(Year.MIN_VALUE - 1, 52, 1) } | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(Year.MAX_VALUE + 1, 1, 1) } | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate(settings) throws an exception when year is out of range`() { | ||
assertFailsWith<DateTimeException> { | ||
Date.fromWeekDate(Year.MIN_VALUE - 1, 52, 1, SUNDAY_START) | ||
} | ||
assertFailsWith<DateTimeException> { | ||
Date.fromWeekDate(Year.MAX_VALUE + 1, 1, 1, SUNDAY_START) | ||
} | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate() throws an exception when week is out of range`() { | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2000, 0, 1) } | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2010, 53, 1) } | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2010, 54, 1) } | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate(settings) throws an exception when week is out of range`() { | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2000, 0, 1, SUNDAY_START) } | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2010, 54, 1, SUNDAY_START) } | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate() throws an exception when day is out of range`() { | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2000, 23, 0) } | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2010, 35, 8) } | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate(settings) throws an exception when day is out of range`() { | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2000, 23, 0, SUNDAY_START) } | ||
assertFailsWith<DateTimeException> { Date.fromWeekDate(2010, 35, 8, SUNDAY_START) } | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate() creates a Date from an ISO week date`() { | ||
TestData.isoWeekDates.forEach { (date, weekDate) -> | ||
val (year, week, day) = weekDate | ||
assertEquals(date, Date.fromWeekDate(year, week, day), date.toString()) | ||
} | ||
} | ||
|
||
@Test | ||
fun `Date_fromWeekDate() creates a Date from a Sunday start week date`() { | ||
TestData.sundayStartWeekDates.filter { it.first != Date.MAX }.forEach { (date, weekDate) -> | ||
val (year, week, day) = weekDate | ||
assertEquals(date, Date.fromWeekDate(year, week, day, SUNDAY_START), date.toString()) | ||
} | ||
} | ||
} |
Oops, something went wrong.