66package kotlinx.datetime.test
77
88import kotlinx.datetime.*
9+ import kotlinx.datetime.internal.*
910import kotlin.random.*
1011import kotlin.test.*
1112
@@ -36,8 +37,9 @@ class LocalDateTest {
3637
3738 @Test
3839 fun parseIsoString () {
39- fun checkParsedComponents (value : String , year : Int , month : Int , day : Int , dayOfWeek : Int , dayOfYear : Int ) {
40+ fun checkParsedComponents (value : String , year : Int , month : Int , day : Int , dayOfWeek : Int? = null , dayOfYear : Int? = null ) {
4041 checkComponents(LocalDate .parse(value), year, month, day, dayOfWeek, dayOfYear)
42+ assertEquals(value, LocalDate (year, month, day).toString())
4143 }
4244 checkParsedComponents(" 2019-10-01" , 2019 , 10 , 1 , 2 , 274 )
4345 checkParsedComponents(" 2016-02-29" , 2016 , 2 , 29 , 1 , 60 )
@@ -49,6 +51,17 @@ class LocalDateTest {
4951 assertInvalidFormat { LocalDate .parse(" 2017-10--01" ) }
5052 // this date is currently larger than the largest representable one any of the platforms:
5153 assertInvalidFormat { LocalDate .parse(" +1000000000-10-01" ) }
54+ // threetenbp
55+ checkParsedComponents(" 2008-07-05" , 2008 , 7 , 5 )
56+ checkParsedComponents(" 2007-12-31" , 2007 , 12 , 31 )
57+ checkParsedComponents(" 0999-12-31" , 999 , 12 , 31 )
58+ checkParsedComponents(" -0001-01-02" , - 1 , 1 , 2 )
59+ checkParsedComponents(" 9999-12-31" , 9999 , 12 , 31 )
60+ checkParsedComponents(" -9999-12-31" , - 9999 , 12 , 31 )
61+ checkParsedComponents(" +10000-01-01" , 10000 , 1 , 1 )
62+ checkParsedComponents(" -10000-01-01" , - 10000 , 1 , 1 )
63+ checkParsedComponents(" +123456-01-01" , 123456 , 1 , 1 )
64+ checkParsedComponents(" -123456-01-01" , - 123456 , 1 , 1 )
5265 }
5366
5467 @Test
@@ -221,9 +234,61 @@ class LocalDateTest {
221234 assertEquals(Int .MIN_VALUE , LocalDate .MAX .until(LocalDate .MIN , DateTimeUnit .DAY ))
222235 }
223236 }
224- }
225-
237+ @Test
238+ fun fromEpochDays () {
239+ /* * This test uses [LocalDate.next] and [LocalDate.previous] and not [LocalDate.plus] because, on Native,
240+ * [LocalDate.plus] is implemented via [LocalDate.toEpochDays]/[LocalDate.fromEpochDays], and so it's better to
241+ * test those independently. */
242+ if (LocalDate .fromEpochDays(0 ).daysUntil(LocalDate .MIN ) > Int .MIN_VALUE ) {
243+ assertEquals(LocalDate .MIN , LocalDate .fromEpochDays(LocalDate .MIN .toEpochDays()))
244+ assertFailsWith<IllegalArgumentException > { LocalDate .fromEpochDays(LocalDate .MIN .toEpochDays() - 1 ) }
245+ assertFailsWith<IllegalArgumentException > { LocalDate .fromEpochDays(Int .MIN_VALUE ) }
246+ }
247+ if (LocalDate .fromEpochDays(0 ).daysUntil(LocalDate .MAX ) < Int .MAX_VALUE ) {
248+ assertEquals(LocalDate .MAX , LocalDate .fromEpochDays(LocalDate .MAX .toEpochDays()))
249+ assertFailsWith<IllegalArgumentException > { LocalDate .fromEpochDays(LocalDate .MAX .toEpochDays() + 1 ) }
250+ assertFailsWith<IllegalArgumentException > { LocalDate .fromEpochDays(Int .MAX_VALUE ) }
251+ }
252+ val eraBeginning = - 678941 - 40587
253+ assertEquals(LocalDate (1970 , 1 , 1 ), LocalDate .fromEpochDays(0 ))
254+ assertEquals(LocalDate (0 , 1 , 1 ), LocalDate .fromEpochDays(eraBeginning))
255+ assertEquals(LocalDate (- 1 , 12 , 31 ), LocalDate .fromEpochDays(eraBeginning - 1 ))
256+ var test = LocalDate (0 , 1 , 1 )
257+ for (i in eraBeginning.. 699999 ) {
258+ assertEquals(test, LocalDate .fromEpochDays(i))
259+ test = test.next
260+ }
261+ test = LocalDate (0 , 1 , 1 )
262+ for (i in eraBeginning downTo - 2000000 + 1 ) {
263+ assertEquals(test, LocalDate .fromEpochDays(i))
264+ test = test.previous
265+ }
266+ }
226267
268+ // threetenbp
269+ @Test
270+ fun toEpochDays () {
271+ /* * This test uses [LocalDate.next] and [LocalDate.previous] and not [LocalDate.plus] because, on Native,
272+ * [LocalDate.plus] is implemented via [LocalDate.toEpochDays]/[LocalDate.fromEpochDays], and so it's better to
273+ * test those independently. */
274+ val startOfEra = - 678941 - 40587
275+ var date = LocalDate (0 , 1 , 1 )
276+ for (i in startOfEra.. 699999 ) {
277+ assertEquals(i, date.toEpochDays())
278+ date = date.next
279+ }
280+ date = LocalDate (0 , 1 , 1 )
281+ for (i in startOfEra downTo - 2000000 + 1 ) {
282+ assertEquals(i, date.toEpochDays())
283+ date = date.previous
284+ }
285+ assertEquals(- 40587 , LocalDate (1858 , 11 , 17 ).toEpochDays())
286+ assertEquals(- 678575 - 40587 , LocalDate (1 , 1 , 1 ).toEpochDays())
287+ assertEquals(49987 - 40587 , LocalDate (1995 , 9 , 27 ).toEpochDays())
288+ assertEquals(0 , LocalDate (1970 , 1 , 1 ).toEpochDays())
289+ assertEquals(- 678942 - 40587 , LocalDate (- 1 , 12 , 31 ).toEpochDays())
290+ }
291+ }
227292
228293fun checkInvalidDate (constructor : (year: Int , month: Int , day: Int ) -> LocalDate ) {
229294 assertFailsWith<IllegalArgumentException > { constructor (2007 , 2 , 29 ) }
@@ -236,3 +301,22 @@ fun checkInvalidDate(constructor: (year: Int, month: Int, day: Int) -> LocalDate
236301 assertFailsWith<IllegalArgumentException > { constructor (2007 , 0 , 1 ) }
237302 assertFailsWith<IllegalArgumentException > { constructor (2007 , 13 , 1 ) }
238303}
304+
305+ private val LocalDate .next: LocalDate get() =
306+ if (dayOfMonth != monthNumber.monthLength(isLeapYear(year))) {
307+ LocalDate (year, monthNumber, dayOfMonth + 1 )
308+ } else if (monthNumber != 12 ) {
309+ LocalDate (year, monthNumber + 1 , 1 )
310+ } else {
311+ LocalDate (year + 1 , 1 , 1 )
312+ }
313+
314+ private val LocalDate .previous: LocalDate get() =
315+ if (dayOfMonth != 1 ) {
316+ LocalDate (year, monthNumber, dayOfMonth - 1 )
317+ } else if (monthNumber != 1 ) {
318+ val newMonthNumber = monthNumber - 1
319+ LocalDate (year, newMonthNumber, newMonthNumber.monthLength(isLeapYear(year)))
320+ } else {
321+ LocalDate (year - 1 , 12 , 31 )
322+ }
0 commit comments