All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonTest.io.islandtime.DateTest.kt Maven / Gradle / Ivy

The newest version!
package io.islandtime

import io.islandtime.measures.*
import io.islandtime.parser.*
import io.islandtime.test.AbstractIslandTimeTest
import kotlin.test.*

class DateTest : AbstractIslandTimeTest() {
    @Test
    fun `throws an exception when constructed with an invalid year`() {
        listOf(
            { Date(-1_000_000_000, Month.JANUARY, 1) },
            { Date(1_000_000_000, Month.DECEMBER, 31) },
            { Date(Int.MIN_VALUE, Month.JANUARY, 1) },
            { Date(Int.MAX_VALUE, Month.DECEMBER, 31) }
        ).forEach {
            assertFailsWith { it() }
        }
    }

    @Test
    fun `throws an exception when constructed with an invalid day`() {
        assertFailsWith { Date(2000, Month.JANUARY, 32) }
        assertFailsWith { Date(2001, Month.FEBRUARY, 29) }
        assertFailsWith { Date(2001, Month.FEBRUARY, 0) }
    }

    @Test
    fun `throws an exception when creating from a day of year that's impossible`() {
        assertFailsWith { Date(2019, -1) }
        assertFailsWith { Date(2019, 0) }
        assertFailsWith { Date(2019, 367) }
        assertFailsWith { Date(2019, 366) }
    }

    @Test
    fun `dates can be constructed from year and day of year`() {
        assertEquals(Date(2019, Month.DECEMBER, 1), Date(2019, 335))
        assertEquals(Date(2020, Month.DECEMBER, 1), Date(2020, 336))
    }

    @Test
    fun `copy() creates a new Date, replacing any combination of values`() {
        assertEquals(
            Date(2017, Month.NOVEMBER, 19),
            Date(2018, Month.NOVEMBER, 19).copy(year = 2017)
        )

        assertEquals(
            Date(2018, Month.DECEMBER, 19),
            Date(2018, Month.NOVEMBER, 19).copy(month = Month.DECEMBER)
        )

        assertEquals(
            Date(2018, Month.NOVEMBER, 3),
            Date(2018, Month.NOVEMBER, 19).copy(dayOfMonth = 3)
        )

        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2018, Month.NOVEMBER, 19).copy(dayOfYear = 365)
        )
    }

    @Test
    fun `dayOfWeek returns the expected day`() {
        assertEquals(
            DayOfWeek.THURSDAY, Date(1970, Month.JANUARY, 1).dayOfWeek
        )
        assertEquals(
            DayOfWeek.FRIDAY, Date(1970, Month.JANUARY, 2).dayOfWeek
        )
        assertEquals(
            DayOfWeek.WEDNESDAY, Date(1969, Month.DECEMBER, 31).dayOfWeek
        )
        assertEquals(
            DayOfWeek.SATURDAY, Date(2019, Month.JULY, 27).dayOfWeek
        )
    }

    @Test
    fun `dayOfMonth returns the expected day`() {
        assertEquals(1, Date(2019, Month.JANUARY, 1).dayOfMonth)
    }

    @Test
    fun `dayOfYear works correctly in common years`() {
        assertEquals(1, Date(2019, Month.JANUARY, 1).dayOfYear)
        assertEquals(365, Date(2019, Month.DECEMBER, 31).dayOfYear)
        assertEquals(59, Date(2019, Month.FEBRUARY, 28).dayOfYear)
    }

    @Test
    fun `dayOfYear works correctly in leap years`() {
        assertEquals(1, Date(2020, Month.JANUARY, 1).dayOfYear)
        assertEquals(60, Date(2020, Month.FEBRUARY, 29).dayOfYear)
        assertEquals(366, Date(2020, Month.DECEMBER, 31).dayOfYear)
    }

    @Test
    fun `can be broken down into components`() {
        val (year, month, day) = Date(2019, Month.AUGUST, 4)
        assertEquals(2019, year)
        assertEquals(Month.AUGUST, month)
        assertEquals(4, day)
    }

    @Test
    fun `date equality`() {
        val testDate = Date(2019, Month.JUNE, 21)
        assertTrue { testDate == testDate }
        assertTrue { testDate == Date(2019, Month.JUNE, 21) }
    }

    @Test
    fun `date inequality`() {
        assertTrue { Date(2018, Month.JUNE, 21) != Date(2019, Month.JUNE, 21) }
        assertTrue { Date(2019, Month.JULY, 21) != Date(2019, Month.JUNE, 21) }
        assertTrue { Date(2019, Month.JUNE, 22) != Date(2019, Month.JUNE, 21) }
    }

    @Test
    fun `date less than`() {
        assertTrue { Date(2019, Month.JUNE, 20) < Date(2019, Month.JUNE, 21) }
        assertTrue { Date(2019, Month.JUNE, 21) < Date(2019, Month.JULY, 21) }
    }

    @Test
    fun `date greater than`() {
        assertTrue { Date(2019, Month.JULY, 21) > Date(2019, Month.JUNE, 21) }
        assertTrue { Date(2020, Month.JUNE, 21) > Date(2019, Month.JUNE, 21) }
    }

    @Test
    fun `add period of zero`() {
        assertEquals(
            Date(2016, Month.FEBRUARY, 29),
            Date(2016, Month.FEBRUARY, 29) + Period.ZERO
        )
    }

    @Test
    fun `adding a period first adds years, then months, then days`() {
        assertEquals(
            Date(2017, Month.MARCH, 29),
            Date(2016, Month.FEBRUARY, 29) + periodOf(1.years, 1.months, 1.days)
        )

        assertEquals(
            Date(2015, Month.JANUARY, 27),
            Date(2016, Month.FEBRUARY, 29) + periodOf((-1).years, (-1).months, (-1).days)
        )
    }

    @Test
    fun `subtract period of zero`() {
        assertEquals(
            Date(2016, Month.FEBRUARY, 29),
            Date(2016, Month.FEBRUARY, 29) - Period.ZERO
        )
    }

    @Test
    fun `subtracting a period first subtracts years, then months, then days`() {
        assertEquals(
            Date(2017, Month.MARCH, 29),
            Date(2016, Month.FEBRUARY, 29) - periodOf((-1).years, (-1).months, (-1).days)
        )

        assertEquals(
            Date(2015, Month.JANUARY, 27),
            Date(2016, Month.FEBRUARY, 29) - periodOf(1.years, 1.months, 1.days)
        )
    }

    @Test
    fun `add zero days`() {
        val date = Date(2019, Month.JANUARY, 1)
        assertEquals(date, date + 0.days)
        assertEquals(date, date + 0L.days)
    }

    @Test
    fun `add positive days`() {
        assertEquals(
            Date(2019, Month.JANUARY, 1),
            Date(2018, Month.DECEMBER, 31) + 1.days
        )

        assertEquals(
            Date(2020, Month.MARCH, 1),
            Date(2018, Month.DECEMBER, 31) + 426.days
        )

        assertEquals(
            Date(1970, Month.MARCH, 1),
            Date(1969, Month.MARCH, 1) + 365.days
        )

        assertEquals(
            Date(1970, Month.MARCH, 1),
            Date(1969, Month.MARCH, 1) + 365L.days
        )
    }

    @Test
    fun `add negative days`() {
        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2019, Month.JANUARY, 1) + (-1).days
        )

        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2020, Month.MARCH, 1) + -(426.days)
        )

        assertEquals(
            Date(1969, Month.MARCH, 1),
            Date(1970, Month.MARCH, 1) + (-365).days
        )
    }

    @Test
    fun `subtract zero days`() {
        val date = Date(2019, Month.JANUARY, 1)
        assertEquals(date, date - 0.days)
        assertEquals(date, date - 0L.days)
    }

    @Test
    fun `throws an exception when adding or subtracting days causes overflow`() {
        listOf(
            { Date.MAX + Long.MAX_VALUE.days },
            { Date.MIN + Long.MIN_VALUE.days },
            { Date(9999, Month.JANUARY, 28) + Long.MAX_VALUE.days },
            { Date(1, Month.JANUARY, 28) + Long.MIN_VALUE.days },
            { Date.MAX - Long.MIN_VALUE.days },
            { Date.MIN - Long.MAX_VALUE.days },
            { Date(1, Month.JANUARY, 28) - Long.MAX_VALUE.days },
            { Date(9999, Month.JANUARY, 28) - Long.MIN_VALUE.days }
        ).forEach {
            assertFailsWith { it() }
        }
    }

    @Test
    fun `throws an exception when adding or subtracting days puts the date out of range`() {
        listOf(
            { Date.MAX + 1.days },
            { Date.MIN - 1.days },
            { Date(Year.MAX_VALUE, Month.JANUARY, 28) + Int.MAX_VALUE.days },
            { Date(Year.MIN_VALUE, Month.JANUARY, 28) + Int.MIN_VALUE.days },
            { Date(Year.MIN_VALUE, Month.JANUARY, 28) - Int.MAX_VALUE.days },
            { Date(Year.MAX_VALUE, Month.JANUARY, 28) - Int.MIN_VALUE.days },
            { Date(9999, Month.JANUARY, 28) - Long.MAX_VALUE.days },
            { Date(1, Month.JANUARY, 28) - Long.MIN_VALUE.days }
        ).forEach {
            assertFailsWith { it() }
        }
    }

    @Test
    fun `subtract positive days`() {
        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2019, Month.JANUARY, 1) - 1.days
        )

        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2020, Month.MARCH, 1) - 426.days
        )

        assertEquals(
            Date(1969, Month.MARCH, 1),
            Date(1970, Month.MARCH, 1) - 365.days
        )
    }

    @Test
    fun `subtract negative days`() {
        assertEquals(
            Date(2019, Month.JANUARY, 1),
            Date(2018, Month.DECEMBER, 31) - (-1).days
        )

        assertEquals(
            Date(2020, Month.MARCH, 1),
            Date(2018, Month.DECEMBER, 31) - -(426.days)
        )

        assertEquals(
            Date(1970, Month.MARCH, 1),
            Date(1969, Month.MARCH, 1) - (-365).days
        )
    }

    @Test
    fun `adding zero weeks has no effect`() {
        assertEquals(
            Date(1930, Month.JANUARY, 28),
            Date(1930, Month.JANUARY, 28) + 0.weeks
        )

        assertEquals(
            Date(2030, Month.JANUARY, 28),
            Date(2030, Month.JANUARY, 28) + 0L.weeks
        )
    }

    @Test
    fun `subtracting zero weeks has no effect`() {
        assertEquals(
            Date(1930, Month.JANUARY, 28),
            Date(1930, Month.JANUARY, 28) - 0.weeks
        )

        assertEquals(
            Date(2030, Month.JANUARY, 28),
            Date(2030, Month.JANUARY, 28) - 0L.weeks
        )
    }

    @Test
    fun `throws an exception when adding or subtracting weeks causes overflow`() {
        assertFailsWith { Date(2019, Month.NOVEMBER, 10) + Long.MAX_VALUE.weeks }
        assertFailsWith { Date(2019, Month.NOVEMBER, 10) + Long.MIN_VALUE.weeks }
        assertFailsWith { Date(2019, Month.NOVEMBER, 10) - Long.MAX_VALUE.weeks }
        assertFailsWith { Date(2019, Month.NOVEMBER, 10) - Long.MIN_VALUE.weeks }
    }

    @Test
    fun `throws an exception when adding or subtracting weeks puts the date out of range`() {
        assertFailsWith { Date.MAX + 1.weeks }
        assertFailsWith { Date.MIN - 1.weeks }
        assertFailsWith { Date(999_991_000, Month.JANUARY, 28) + Int.MAX_VALUE.weeks }
        assertFailsWith { Date(-999_000_000, Month.JANUARY, 28) + Int.MIN_VALUE.weeks }
        assertFailsWith { Date(-999_000_000, Month.JANUARY, 28) - Int.MAX_VALUE.weeks }
        assertFailsWith { Date(999_000_000, Month.JANUARY, 28) - Int.MIN_VALUE.weeks }
    }

    @Test
    fun `add positive weeks`() {
        assertEquals(
            Date(2019, Month.FEBRUARY, 4),
            Date(2019, Month.JANUARY, 28) + 1.weeks
        )

        assertEquals(
            Date(2019, Month.FEBRUARY, 4),
            Date(2019, Month.JANUARY, 28) + 1L.weeks
        )
    }

    @Test
    fun `add negative weeks`() {
        assertEquals(
            Date(2019, Month.JANUARY, 21),
            Date(2019, Month.JANUARY, 28) + (-1).weeks
        )

        assertEquals(
            Date(2019, Month.JANUARY, 21),
            Date(2019, Month.JANUARY, 28) + (-1L).weeks
        )
    }

    @Test
    fun `subtract positive weeks`() {
        assertEquals(
            Date(2019, Month.JANUARY, 21),
            Date(2019, Month.JANUARY, 28) - 1.weeks
        )

        assertEquals(
            Date(2019, Month.JANUARY, 21),
            Date(2019, Month.JANUARY, 28) - 1L.weeks
        )
    }

    @Test
    fun `subtract negative weeks`() {
        assertEquals(
            Date(2019, Month.FEBRUARY, 4),
            Date(2019, Month.JANUARY, 28) - (-1).weeks
        )

        assertEquals(
            Date(2019, Month.FEBRUARY, 4),
            Date(2019, Month.JANUARY, 28) - (-1L).weeks
        )
    }

    @Test
    fun `throws an exception when adding or subtracting months puts the date out of range`() {
        assertFailsWith { Date.MAX + 1.months }
        assertFailsWith { Date.MIN - 1.months }
        assertFailsWith { Date(Year.MAX_VALUE, Month.JANUARY, 28) + Int.MAX_VALUE.months }
        assertFailsWith { Date(Year.MIN_VALUE, Month.JANUARY, 28) + Int.MIN_VALUE.months }
        assertFailsWith { Date(Year.MAX_VALUE, Month.JANUARY, 28) + Long.MAX_VALUE.months }
        assertFailsWith { Date(Year.MIN_VALUE, Month.JANUARY, 28) + Long.MIN_VALUE.months }
        assertFailsWith { Date(Year.MIN_VALUE, Month.JANUARY, 28) - Int.MAX_VALUE.months }
        assertFailsWith { Date(Year.MAX_VALUE, Month.JANUARY, 28) - Int.MIN_VALUE.months }
        assertFailsWith { Date(Year.MAX_VALUE, Month.JANUARY, 28) - Long.MAX_VALUE.months }
        assertFailsWith { Date(Year.MIN_VALUE, Month.JANUARY, 28) - Long.MIN_VALUE.months }
    }

    @Test
    fun `add zero months`() {
        val date = Date(2019, Month.JANUARY, 28)
        assertEquals(date, date + 0.months)
        assertEquals(date, date + 0L.months)
    }

    @Test
    fun `add positive months`() {
        assertEquals(
            Date(2019, Month.FEBRUARY, 28),
            Date(2019, Month.JANUARY, 28) + 1.months
        )

        assertEquals(
            Date(2019, Month.FEBRUARY, 28),
            Date(2019, Month.JANUARY, 31) + 1.months
        )

        assertEquals(
            Date(2020, Month.FEBRUARY, 29),
            Date(2019, Month.JANUARY, 31) + 13.months
        )

        assertEquals(
            Date(1904, Month.FEBRUARY, 29),
            Date(1903, Month.JANUARY, 31) + 13.months
        )
    }

    @Test
    fun `add negative months`() {
        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2019, Month.JANUARY, 31) + (-1).months
        )

        assertEquals(
            Date(2019, Month.FEBRUARY, 28),
            Date(2019, Month.MARCH, 31) + -(1.months)
        )

        assertEquals(
            Date(2017, Month.NOVEMBER, 30),
            Date(2019, Month.JANUARY, 31) + (-14).months
        )

        assertEquals(
            Date(1910, Month.NOVEMBER, 30),
            Date(1912, Month.JANUARY, 31) + (-14).months
        )
    }

    @Test
    fun `subtract zero months`() {
        val date = Date(2019, Month.JANUARY, 28)
        assertEquals(date, date - 0.months)
        assertEquals(date, date - 0L.months)
    }

    @Test
    fun `subtract positive months`() {
        assertEquals(
            Date(2018, Month.DECEMBER, 31),
            Date(2019, Month.JANUARY, 31) - 1.months
        )

        assertEquals(
            Date(2019, Month.FEBRUARY, 28),
            Date(2019, Month.MARCH, 31) - 1.months
        )

        assertEquals(
            Date(2017, Month.NOVEMBER, 30),
            Date(2019, Month.JANUARY, 31) - 14.months
        )
    }

    @Test
    fun `subtract negative months`() {
        assertEquals(
            Date(2019, Month.FEBRUARY, 28),
            Date(2019, Month.JANUARY, 28) - (-1).months
        )

        assertEquals(
            Date(2019, Month.FEBRUARY, 28),
            Date(2019, Month.JANUARY, 31) - -(1.months)
        )

        assertEquals(
            Date(2020, Month.FEBRUARY, 29),
            Date(2019, Month.JANUARY, 31) - (-13).months
        )
    }

    @Test
    fun `throws an exception when adding or subtracting years puts the date out of range`() {
        assertFailsWith { Date.MAX + 1.years }
        assertFailsWith { Date.MIN - 1.years }
        assertFailsWith { Date(9999, Month.JANUARY, 28) + Int.MAX_VALUE.years }
        assertFailsWith { Date(1, Month.JANUARY, 28) + Int.MIN_VALUE.years }
        assertFailsWith { Date(9999, Month.JANUARY, 28) + Long.MAX_VALUE.years }
        assertFailsWith { Date(1, Month.JANUARY, 28) + Long.MIN_VALUE.years }
        assertFailsWith { Date(9999, Month.JANUARY, 28) - Int.MAX_VALUE.years }
        assertFailsWith { Date(1, Month.JANUARY, 28) - Int.MIN_VALUE.years }
        assertFailsWith { Date(9999, Month.JANUARY, 28) - Long.MAX_VALUE.years }
        assertFailsWith { Date(1, Month.JANUARY, 28) - Long.MIN_VALUE.years }
    }

    @Test
    fun `add zero years`() {
        val date = Date(2019, Month.JANUARY, 28)
        assertEquals(date, date + 0.years)
        assertEquals(date, date + 0L.years)
    }

    @Test
    fun `add positive years`() {
        assertEquals(
            Date(2020, Month.JANUARY, 31),
            Date(2019, Month.JANUARY, 31) + 1.years
        )

        assertEquals(
            Date(2021, Month.FEBRUARY, 28),
            Date(2020, Month.FEBRUARY, 29) + 1.years
        )

        assertEquals(
            Date(2024, Month.FEBRUARY, 29),
            Date(2020, Month.FEBRUARY, 29) + 4.years
        )
    }

    @Test
    fun `add negative years`() {
        assertEquals(
            Date(2018, Month.JANUARY, 31),
            Date(2019, Month.JANUARY, 31) + (-1).years
        )

        assertEquals(
            Date(2020, Month.FEBRUARY, 28),
            Date(2021, Month.FEBRUARY, 28) + -(1.years)
        )

        assertEquals(
            Date(2016, Month.FEBRUARY, 29),
            Date(2020, Month.FEBRUARY, 29) + (-4).years
        )
    }

    @Test
    fun `subtract zero years`() {
        val date = Date(2019, Month.JANUARY, 28)
        assertEquals(date, date - 0.years)
        assertEquals(date, date - 0L.years)
    }

    @Test
    fun `subtract positive years`() {
        assertEquals(
            Date(2018, Month.JANUARY, 31),
            Date(2019, Month.JANUARY, 31) - 1.years
        )

        assertEquals(
            Date(2020, Month.FEBRUARY, 28),
            Date(2021, Month.FEBRUARY, 28) - 1.years
        )

        assertEquals(
            Date(2016, Month.FEBRUARY, 29),
            Date(2020, Month.FEBRUARY, 29) - 4.years
        )
    }

    @Test
    fun `subtract negative years`() {
        assertEquals(
            Date(2020, Month.JANUARY, 31),
            Date(2019, Month.JANUARY, 31) - (-1).years
        )

        assertEquals(
            Date(2021, Month.FEBRUARY, 28),
            Date(2020, Month.FEBRUARY, 29) - -(1.years)
        )

        assertEquals(
            Date(2024, Month.FEBRUARY, 29),
            Date(2020, Month.FEBRUARY, 29) - (-4).years
        )
    }

    @Test
    fun `daysSinceUnixEpoch property works correctly`() {
        assertEquals(0L.days, Date(1970, Month.JANUARY, 1).daysSinceUnixEpoch)
        assertEquals(1L.days, Date(1970, Month.JANUARY, 2).daysSinceUnixEpoch)
        assertEquals((-1L).days, Date(1969, Month.DECEMBER, 31).daysSinceUnixEpoch)
        assertEquals(18_105L.days, Date(2019, Month.JULY, 28).daysSinceUnixEpoch)
        assertEquals((-4_472L).days, Date(1957, Month.OCTOBER, 4).daysSinceUnixEpoch)
    }

    @Test
    fun `toString() returns an ISO-8601 extended calendar date`() {
        assertEquals("2019-08-01", Date(2019, Month.AUGUST, 1).toString())
        assertEquals("0001-10-10", Date(1, Month.OCTOBER, 10).toString())
    }

    @Test
    fun `String_toDate() throws an exception when given an empty string`() {
        assertFailsWith { "".toDate() }
    }

    @Test
    fun `String_toDate() throws an exception when the format is not an ISO-8601 extended date`() {
        listOf(
            "1",
            "--",
            "2010",
            "2010-",
            "2010--",
            "2010-10-",
            "2010-10-2",
            "999-10-20",
            "2010/10/20",
            "2010-10-20-",
            "2010-10-20 "
        ).forEach {
            assertFailsWith { it.toDate() }
        }
    }

    @Test
    fun `String_toDate() throws an exception when unexpected characters exist before a valid string`() {
        assertFailsWith { " 2010-02-20".toDate() }
        assertFailsWith { "T2010-10-20".toDate() }
    }

    @Test
    fun `String_toDate() throws an exception when unexpected characters exist after a valid string`() {
        assertFailsWith { "2010-2-20 ".toDate() }
        assertFailsWith { "2010-10-200".toDate() }
        assertFailsWith { "2010-10-20T".toDate() }
    }

    @Test
    fun `String_toDate() throws an exception when the parser cannot supply the required fields`() {
        assertFailsWith { "05:06".toDate(DateTimeParsers.Iso.TIME) }
        assertFailsWith { "2010".toDate(DateTimeParsers.Iso.YEAR) }
        assertFailsWith { "2010-05".toDate(DateTimeParsers.Iso.YEAR_MONTH) }
    }

    @Test
    fun `String_toDate() throws an exception when fields overflow`() {
        val customParser = dateTimeParser {
            year()
            +' '
            monthNumber()
            +' '
            dayOfYear()
        }

        assertFailsWith { "10000000001 5 4".toDate(customParser) }
    }

    @Test
    fun `String_toDate() parses valid ISO-8601 extended date strings by default`() {
        listOf(
            "2000-02-29" to Date(2000, Month.FEBRUARY, 29),
            "+999999999-12-31" to Date(999_999_999, Month.DECEMBER, 31),
            "-999999999-01-01" to Date(-999_999_999, Month.JANUARY, 1)
        ).forEach { (string, result) ->
            assertEquals(result, string.toDate())
        }
    }

    @Test
    fun `String_toDate() parses valid strings with explicit parser`() {
        listOf(
            "2000-02-29",
            "20000229",
            "2000-060",
            "+2000-060",
            "2000060"
        ).forEach {
            assertEquals(Date(2000, Month.FEBRUARY, 29), it.toDate(DateTimeParsers.Iso.DATE))
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy