├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── docs.yml │ ├── main.yml │ ├── publish.yml │ └── update_gradle_wrapper.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots └── logo.png ├── settings.gradle └── src ├── main └── kotlin │ └── javatimefun │ ├── ZoneIds.kt │ ├── calendar │ └── extensions │ │ └── CalendarMutatingExtensions.kt │ ├── date │ └── extensions │ │ └── DateMutatingExtensions.kt │ ├── localdate │ ├── LocalDateFactory.kt │ ├── LocalDates.kt │ └── extensions │ │ ├── LocalDateAttributeExtensions.kt │ │ ├── LocalDateComparisonExtensions.kt │ │ ├── LocalDateMutatingExtensions.kt │ │ ├── LocalDateParsingExtensions.kt │ │ └── LocalDatePrintExtensions.kt │ ├── localdatetime │ ├── LocalDateTimeFactory.kt │ ├── LocalDateTimes.kt │ └── extensions │ │ ├── LocalDateTimeAttributeExtensions.kt │ │ ├── LocalDateTimeComparisonExtensions.kt │ │ ├── LocalDateTimeMutatingExtensions.kt │ │ ├── LocalDateTimeParsingExtensions.kt │ │ └── LocalDateTimePrintExtensions.kt │ ├── localtime │ ├── LocalTimeUtil.kt │ ├── LocalTimes.kt │ └── extensions │ │ ├── LocalTimeAttributeExtensions.kt │ │ ├── LocalTimeComparisonExtensions.kt │ │ ├── LocalTimeParsingExtensions.kt │ │ └── LocalTimePrintExtensions.kt │ └── zoneddatetime │ ├── ZonedDateTimeFactory.kt │ ├── ZonedDateTimes.kt │ └── extensions │ ├── ZonedDateTimeAttributeExtensions.kt │ ├── ZonedDateTimeComparisonExtensions.kt │ ├── ZonedDateTimeMutatingExtensions.kt │ ├── ZonedDateTimeParsingExtensions.kt │ └── ZonedDateTimePrintExtensions.kt └── test └── kotlin ├── localdate ├── LocalDateComparisonExtensionsTest.kt └── LocalDateFactoryTest.kt ├── localdatetime ├── LocalDateTimeComparisonExtensionsTest.kt ├── LocalDateTimeFactoryTest.kt └── LocalDateTimeParsingExtensionsTest.kt ├── localtime ├── LocalTimeComparisonExtensionsTest.kt └── LocalTimeUtilTest.kt └── zoneddatetime ├── ZonedDateTimeAttributeExtensionsTest.kt ├── ZonedDateTimeComparisonExtensionsTest.kt ├── ZonedDateTimeMutatingExtensionsTest.kt ├── ZonedDateTimeParsingExtensionsTest.kt └── ZonedDateTimeUtilTest.kt /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: gradle 8 | directory: "/" 9 | schedule: 10 | interval: daily 11 | open-pull-requests-limit: 10 12 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuration-options 2 | changelog: 3 | exclude: 4 | labels: 5 | - ignore-for-release 6 | categories: 7 | - title: ✨ Enhancements 8 | labels: 9 | - '*' 10 | exclude: 11 | labels: 12 | - dependencies 13 | - title: 🔨 Dependencies 14 | labels: 15 | - dependencies -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | persist-credentials: false 14 | 15 | - name: Set up JDK 21 16 | uses: actions/setup-java@v4 17 | with: 18 | java-version: 21 19 | distribution: 'adopt' 20 | 21 | - name: Build documentation 22 | run: ./gradlew dokkaHtml 23 | 24 | - name: Publish documentation 25 | uses: JamesIves/github-pages-deploy-action@releases/v4 26 | with: 27 | BRANCH: gh-pages 28 | FOLDER: build/dokka/html 29 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Kotlin CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | jobs: 13 | test: 14 | name: Run Unit Tests 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up JDK 21 22 | uses: actions/setup-java@v4 23 | with: 24 | java-version: 21 25 | distribution: 'adopt' 26 | 27 | - name: Make Gradle executable 28 | run: chmod +x ./gradlew 29 | 30 | - name: Unit tests 31 | run: bash ./gradlew test --stacktrace 32 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Maven 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - release 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Set up JDK 21 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '21' 18 | distribution: 'adopt' 19 | 20 | - name: Publish 21 | run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -no-daemon --no-parallel --stacktrace -DSONATYPE_USERNAME="$SONATYPE_USERNAME" -DSONATYPE_PASSWORD="$SONATYPE_PASSWORD" -DGPG_PRIVATE_PASSWORD="$GPG_PRIVATE_PASSWORD" -DGPG_PRIVATE_KEY="$GPG_PRIVATE_KEY" 22 | env: 23 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_TOKEN_USERNAME }} 24 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_TOKEN_PASSWORD }} 25 | SONATYPE_REPOSITORY_ID: ${{ needs.create_staging_repository.outputs.repository_id }} 26 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 27 | GPG_PRIVATE_PASSWORD: ${{ secrets.GPG_PRIVATE_PASSWORD }} 28 | ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_TOKEN_USERNAME }} 29 | ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_TOKEN_PASSWORD }} 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/update_gradle_wrapper.yml: -------------------------------------------------------------------------------- 1 | name: Update Gradle Wrapper 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | 7 | jobs: 8 | update-gradle-wrapper: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Update Gradle Wrapper 15 | uses: gradle-update/update-gradle-wrapper-action@v2 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | *.iml 3 | .idea 4 | .gradle 5 | .kotlin 6 | app/build 7 | build 8 | 9 | # Mac 10 | .DS_Store 11 | 12 | # Compiled class file 13 | *.class 14 | 15 | # Log file 16 | *.log 17 | 18 | # BlueJ files 19 | *.ctxt 20 | 21 | # Mobile Tools for Java (J2ME) 22 | .mtj.tmp/ 23 | 24 | # Package Files # 25 | *.jar 26 | *.war 27 | *.nar 28 | *.ear 29 | *.zip 30 | *.tar.gz 31 | *.rar 32 | 33 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 34 | hs_err_pid* 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Sami Eljabali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Java Time Kotlin extension functions.
7 |16 | * 17 | * @param this String representation of LocalDateTime. 18 | * @param format String representing format that should solely be used when parsing the date. 19 | * @return LocalDateTime? Null means couldn't parse, else parsed LocalDateTime. 20 | */ 21 | fun String.toLocalDateTime(format: String? = null): LocalDateTime? = 22 | parseLocalDateTimeOrNull(this, format) ?: parseLocalDateTimeOrNull(this, flexibleIso8601Format) 23 | 24 | private fun parseLocalDateTimeOrNull(dateText: String, format: String?): LocalDateTime? = 25 | if (format.isNullOrEmpty()) 26 | try { 27 | LocalDateTime.parse(dateText) 28 | } catch (e: DateTimeParseException) { 29 | null 30 | } catch (e: IllegalArgumentException) { 31 | null 32 | } 33 | else { 34 | try { 35 | LocalDateTime.parse(dateText, DateTimeFormatter.ofPattern(format)) 36 | } catch (e: DateTimeParseException) { 37 | null 38 | } catch (e: IllegalArgumentException) { 39 | null 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/extensions/LocalDateTimePrintExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime.extensions 2 | 3 | import java.time.LocalDateTime 4 | import java.time.format.DateTimeFormatterBuilder 5 | import java.util.Locale 6 | 7 | fun LocalDateTime.print(format: String, locale: Locale = Locale.US): String = 8 | this.format(DateTimeFormatterBuilder().appendPattern(format).toFormatter(locale)) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localtime/LocalTimeUtil.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localtime 2 | 3 | import javatimefun.localdatetime.LocalDateTimeFactory 4 | import java.time.LocalTime 5 | import java.time.ZoneId 6 | 7 | /** 8 | * Contains helper functions that only serve to create new LocalTimes. 9 | * Creation methods do not include parsing methods. 10 | */ 11 | object LocalTimeUtil { 12 | 13 | /** 14 | * @param hourIn24 Hour of the day with range 0-23. 15 | * @param minute Minute of the hour with range 0-59. 16 | * @param second Second of the minute with range 0-59. 17 | * @param nanoOfSecond Nano-of-second to represent, from 0 to 999,999,999. 18 | * @return LocalTime. 19 | */ 20 | fun new( 21 | hourIn24: Int, 22 | minute: Int = 0, 23 | second: Int = 0, 24 | nanoOfSecond: Int = 0, 25 | ): LocalTime = 26 | LocalTime.of(hourIn24, minute, second, nanoOfSecond) 27 | 28 | fun new(epochMilliseconds: Long, zoneId: ZoneId = ZoneId.systemDefault()): LocalTime = 29 | LocalDateTimeFactory.new(epochMilliseconds, zoneId).toLocalTime() 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localtime/LocalTimes.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localtime 2 | 3 | import java.time.LocalTime 4 | 5 | object LocalTimes { 6 | fun startOfDay(): LocalTime = LocalTime.MIN 7 | fun noon(): LocalTime = LocalTime.NOON 8 | fun endOfDay(): LocalTime = LocalTime.MAX 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localtime/extensions/LocalTimeAttributeExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localtime.extensions 2 | 3 | import java.time.LocalTime 4 | 5 | fun LocalTime.isAtStartOfDay(): Boolean = this == LocalTime.MIN 6 | 7 | fun LocalTime.isAtEndOfDay(): Boolean = this == LocalTime.MAX 8 | 9 | fun LocalTime.isInAm(): Boolean = this.isBeforeTime(LocalTime.NOON) 10 | 11 | fun LocalTime.isInPm(): Boolean = this.isAfterEqualTime(LocalTime.NOON) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localtime/extensions/LocalTimeComparisonExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localtime.extensions 2 | 3 | import java.time.Duration 4 | import java.time.LocalTime 5 | 6 | // region Time Comparisons 7 | fun LocalTime.compareTime(toDate: LocalTime): Int = 8 | when { 9 | isEqualTime(toDate) -> 0 10 | isBeforeTime(toDate) -> -1 11 | else -> 1 12 | } 13 | 14 | fun LocalTime.isEqualTime(b: LocalTime): Boolean = this == b 15 | 16 | fun LocalTime.isBeforeTime(b: LocalTime): Boolean = this.isBefore(b) 17 | 18 | fun LocalTime.isBeforeEqualTime(b: LocalTime): Boolean = compareTime(b) <= 0 19 | 20 | fun LocalTime.isAfterTime(b: LocalTime): Boolean = compareTime(b) > 0 21 | 22 | fun LocalTime.isAfterEqualTime(b: LocalTime): Boolean = compareTime(b) >= 0 23 | // endregion 24 | 25 | fun LocalTime.getMilliSecondDifference(localTimeB: LocalTime): Int = 26 | Duration.between(this, localTimeB).toMillis().toInt() 27 | 28 | fun LocalTime.getSecondDifference(localTimeB: LocalTime): Int = 29 | Duration.between(this, localTimeB).seconds.toInt() 30 | 31 | fun LocalTime.getMinuteDifference(localTimeB: LocalTime): Int = 32 | Duration.between(this, localTimeB).toMinutes().toInt() 33 | 34 | fun LocalTime.getHourDifference(localTimeB: LocalTime): Int = 35 | Duration.between(this, localTimeB).toHours().toInt() -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localtime/extensions/LocalTimeParsingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localtime.extensions 2 | 3 | import java.time.LocalTime 4 | import java.time.format.DateTimeFormatter 5 | import java.time.format.DateTimeParseException 6 | 7 | fun String.toLocalTime(format: String? = null): LocalTime? = 8 | if (format.isNullOrEmpty()) { 9 | try { 10 | LocalTime.parse(this) 11 | } catch (e: DateTimeParseException) { 12 | null 13 | } catch (e: IllegalArgumentException) { 14 | null 15 | } 16 | } else { 17 | try { 18 | LocalTime.parse(this, DateTimeFormatter.ofPattern(format)) 19 | } catch (e: DateTimeParseException) { 20 | null 21 | } catch (e: IllegalArgumentException) { 22 | null 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localtime/extensions/LocalTimePrintExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localtime.extensions 2 | 3 | import java.time.LocalTime 4 | import java.time.format.DateTimeFormatterBuilder 5 | import java.util.Locale 6 | 7 | fun LocalTime.print(format: String, locale: Locale = Locale.US): String = 8 | this.format(DateTimeFormatterBuilder().appendPattern(format).toFormatter(locale)) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/ZonedDateTimeFactory.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime 2 | 3 | import java.time.Instant 4 | import java.time.ZoneId 5 | import java.time.ZonedDateTime 6 | 7 | /** 8 | * Contains helper functions that only serve to create new ZonedDateTimes. 9 | * Creation methods do not include parsing methods. 10 | */ 11 | object ZonedDateTimeFactory { 12 | 13 | /** 14 | * @param year Year, ie, 2020. 15 | * @param month Month with range 1-12, i.e. 1 for January. 16 | * @param day Day of the month with range 1-31. 17 | * @param hourIn24 Hour of the day with range 0-23. 18 | * @param minute Minute of the hour with range 0-59. 19 | * @param second Second of the minute with range 0-59. 20 | * @param nano Nano-of-second to represent, from 0 to 999,999,999. 21 | * @param zoneId Defaulted to time zone of the device. 22 | * @return ZonedDateTime. 23 | */ 24 | fun new( 25 | year: Int, 26 | month: Int, 27 | day: Int, 28 | hourIn24: Int = 0, 29 | minute: Int = 0, 30 | second: Int = 0, 31 | nano: Int = 0, 32 | zoneId: ZoneId = ZoneId.systemDefault() 33 | ): ZonedDateTime 34 | = ZonedDateTime.of( 35 | year, 36 | month, 37 | day, 38 | hourIn24, 39 | minute, 40 | second, 41 | nano, 42 | zoneId 43 | ) 44 | 45 | /** 46 | * @param epochMilliseconds Epoch time, aka Unix time, are seconds elapsed since January 1st 1970 at 00:00:00 UTC. 47 | * @param zoneId Defaulted to time zone of the device. 48 | * @return ZonedDateTime. 49 | */ 50 | fun new( 51 | epochMilliseconds: Long, 52 | zoneId: ZoneId = ZoneId.systemDefault() 53 | ): ZonedDateTime = 54 | ZonedDateTime.ofInstant( 55 | Instant.ofEpochMilli(epochMilliseconds), 56 | zoneId 57 | ) 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/ZonedDateTimes.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime 2 | 3 | import javatimefun.zoneddatetime.extensions.atEndOfDay 4 | import javatimefun.zoneddatetime.extensions.atStartOfDay 5 | import javatimefun.zoneddatetime.extensions.getLast 6 | import javatimefun.zoneddatetime.extensions.getNext 7 | import java.time.DayOfWeek.FRIDAY 8 | import java.time.DayOfWeek.MONDAY 9 | import java.time.DayOfWeek.SATURDAY 10 | import java.time.DayOfWeek.SUNDAY 11 | import java.time.DayOfWeek.THURSDAY 12 | import java.time.DayOfWeek.TUESDAY 13 | import java.time.DayOfWeek.WEDNESDAY 14 | import java.time.ZoneId 15 | import java.time.ZonedDateTime 16 | 17 | object ZonedDateTimes { 18 | fun now(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = ZonedDateTime.now(zoneId) 19 | fun today(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = now(zoneId).atStartOfDay() 20 | fun yesterday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).minusDays(1) 21 | fun tomorrow(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).plusDays(1) 22 | 23 | fun lastMonday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(MONDAY) 24 | fun lastTuesday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(TUESDAY) 25 | fun lastWednesday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(WEDNESDAY) 26 | fun lastThursday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(THURSDAY) 27 | fun lastFriday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(FRIDAY) 28 | fun lastSaturday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(SATURDAY) 29 | fun lastSunday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getLast(SUNDAY) 30 | 31 | fun nextMonday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(MONDAY) 32 | fun nextTuesday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(TUESDAY) 33 | fun nextWednesday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(WEDNESDAY) 34 | fun nextThursday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(THURSDAY) 35 | fun nextFriday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(FRIDAY) 36 | fun nextSaturday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(SATURDAY) 37 | fun nextSunday(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = today(zoneId).getNext(SUNDAY) 38 | 39 | fun startOfYear(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = 40 | ZonedDateTimeFactory.new(today(zoneId).year, 1, 1) 41 | fun endOfYear(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = 42 | ZonedDateTimeFactory.new(today(zoneId).year, 12, 31) 43 | .atEndOfDay() 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/extensions/ZonedDateTimeAttributeExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime.extensions 2 | 3 | import java.time.Year 4 | import java.time.ZonedDateTime 5 | 6 | /** 7 | * Works off of ZonedDateTime context. 8 | * @return True if context's year is a leap year. 9 | */ 10 | fun ZonedDateTime.isInLeapYear(): Boolean = Year.of(year).isLeap 11 | 12 | /** 13 | * Works off of ZonedDateTime context. 14 | * @return True if zoned date time's is at very start of day. 15 | */ 16 | fun ZonedDateTime.isAtStartOfDay(): Boolean = this.isEqualTime(this.atStartOfDay()) 17 | 18 | /** 19 | * Works off of ZonedDateTime context. 20 | * @return True if zoned date time's is at very last moment of day. 21 | */ 22 | fun ZonedDateTime.isAtEndOfDay(): Boolean = this.isEqualTime(this.atEndOfDay()) 23 | 24 | /** 25 | * Works off of ZonedDateTime context. 26 | * @return Month value of ZonedDateTime base 0, with range 0-11, where 0 is January. 27 | */ 28 | fun ZonedDateTime.getMonthBaseZero(): Int = this.monthValue - 1 29 | 30 | /** 31 | * Works off of ZonedDateTime context. 32 | * @return Number of days found in the particular month of ZonedDateTime, with range 28-31. 33 | */ 34 | fun ZonedDateTime.getDaysInMonth(): Int = this.month.length(isInLeapYear()) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/extensions/ZonedDateTimeComparisonExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime.extensions 2 | 3 | import java.time.Duration 4 | import java.time.ZonedDateTime 5 | import java.time.temporal.ChronoUnit 6 | 7 | // region Year Comparisons 8 | /** 9 | * Works off of ZonedDateTime context. 10 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's year to. 11 | * @return Int, where 0 means they're the same, -1 context's is less then, 1 context's is more than param's. 12 | */ 13 | fun ZonedDateTime.compareYear(zonedDateTimeB: ZonedDateTime): Int = 14 | this.year.compareTo(zonedDateTimeB.withTimeZoneOf(this).year) 15 | 16 | /** 17 | * Works off of ZonedDateTime context. 18 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's year to. 19 | * @return True if context's is less than param's year, false otherwise. 20 | */ 21 | fun ZonedDateTime.isBeforeYear(zonedDateTimeB: ZonedDateTime): Boolean { 22 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 23 | return this.year < otherZonedDateTime.year 24 | } 25 | 26 | /** 27 | * Works off of ZonedDateTime context. 28 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's year to. 29 | * @return True if context's is less than equal param's year, false otherwise. 30 | */ 31 | fun ZonedDateTime.isBeforeEqualYear(zonedDateTimeB: ZonedDateTime): Boolean { 32 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 33 | return this.year <= otherZonedDateTime.year 34 | } 35 | 36 | /** 37 | * Works off of ZonedDateTime context. 38 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's year to. 39 | * @return True if context's equals param's year, false otherwise. 40 | */ 41 | fun ZonedDateTime.isEqualYear(zonedDateTimeB: ZonedDateTime): Boolean { 42 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 43 | return this.year == otherZonedDateTime.year 44 | } 45 | 46 | /** 47 | * Works off of ZonedDateTime context. 48 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's year to. 49 | * @return True if context's after or equals param's year, false otherwise. 50 | */ 51 | fun ZonedDateTime.isAfterEqualYear(zonedDateTimeB: ZonedDateTime): Boolean { 52 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 53 | return this.year >= otherZonedDateTime.year 54 | } 55 | 56 | /** 57 | * Works off of ZonedDateTime context. 58 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's year to. 59 | * @return True if context's after param's year, false otherwise. 60 | */ 61 | fun ZonedDateTime.isAfterYear(zonedDateTimeB: ZonedDateTime): Boolean { 62 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 63 | return this.year > otherZonedDateTime.year 64 | } 65 | // endregion 66 | 67 | // region Month Comparisons 68 | /** 69 | * Works off of ZonedDateTime context. 70 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's month to. 71 | * @return 0 when they're matching months, -1 when context's is less than, 1 when context's is more than param's. 72 | */ 73 | fun ZonedDateTime.compareMonth(zonedDateTimeB: ZonedDateTime): Int = 74 | when { 75 | isBeforeMonth(zonedDateTimeB) -> -1 76 | isEqualMonth(zonedDateTimeB) -> 0 77 | else -> 1 78 | } 79 | 80 | /** 81 | * Works off of ZonedDateTime context. 82 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's month to. 83 | * @return True if context's before param's month, false otherwise. 84 | */ 85 | fun ZonedDateTime.isBeforeMonth(zonedDateTimeB: ZonedDateTime): Boolean { 86 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 87 | if (this.year > otherZonedDateTime.year) return false 88 | if (this.year < otherZonedDateTime.year) return true 89 | return this.month < otherZonedDateTime.month 90 | } 91 | 92 | /** 93 | * Works off of ZonedDateTime context. 94 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's month to. 95 | * @return True if context's before or equals param's month, false otherwise. 96 | */ 97 | fun ZonedDateTime.isBeforeEqualMonth(zonedDateTimeB: ZonedDateTime): Boolean { 98 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 99 | if (this.year > otherZonedDateTime.year) return false 100 | if (this.year < otherZonedDateTime.year) return true 101 | return this.month <= otherZonedDateTime.month 102 | } 103 | 104 | /** 105 | * Works off of ZonedDateTime context. 106 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's month to. 107 | * @return True if context's equals param's month, false otherwise. 108 | */ 109 | fun ZonedDateTime.isEqualMonth(zonedDateTimeB: ZonedDateTime): Boolean { 110 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 111 | return this.year == otherZonedDateTime.year && this.month == otherZonedDateTime.month 112 | } 113 | 114 | /** 115 | * Works off of ZonedDateTime context. 116 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's month to. 117 | * @return True if context's after or equals param's month, false otherwise. 118 | */ 119 | fun ZonedDateTime.isAfterEqualMonth(zonedDateTimeB: ZonedDateTime): Boolean { 120 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 121 | if (this.year > otherZonedDateTime.year) return true 122 | if (this.year < otherZonedDateTime.year) return false 123 | return this.month >= otherZonedDateTime.month 124 | } 125 | 126 | /** 127 | * Works off of ZonedDateTime context. 128 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's month to. 129 | * @return True if context's after param's month, false otherwise. 130 | */ 131 | fun ZonedDateTime.isAfterMonth(zonedDateTimeB: ZonedDateTime): Boolean { 132 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 133 | if (this.year > otherZonedDateTime.year) return true 134 | if (this.year < otherZonedDateTime.year) return false 135 | return this.month > otherZonedDateTime.month 136 | } 137 | // endregion 138 | 139 | // region Day Comparisons 140 | /** 141 | * Works off of ZonedDateTime context. 142 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day to. 143 | * @return 0 when they're matching days, -1 when context's is less than, 1 when context's is more than param's. 144 | */ 145 | fun ZonedDateTime.compareDay(zonedDateTimeB: ZonedDateTime): Int { 146 | val dayDifference = this.getDayDifference(zonedDateTimeB) 147 | return when { 148 | dayDifference > 0 -> -1 149 | dayDifference < 0 -> 1 150 | else -> 0 151 | } 152 | } 153 | 154 | /** 155 | * Works off of ZonedDateTime context. 156 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day to. 157 | * @return True if context's equals param's day, false otherwise. 158 | */ 159 | fun ZonedDateTime.isEqualDay(zonedDateTimeB: ZonedDateTime): Boolean = 160 | this.compareDay(zonedDateTimeB) == 0 161 | 162 | /** 163 | * Works off of ZonedDateTime context. 164 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day to. 165 | * @return True if context is before param's day, false otherwise. 166 | */ 167 | fun ZonedDateTime.isBeforeDay(zonedDateTimeB: ZonedDateTime): Boolean = 168 | this.compareDay(zonedDateTimeB) < 0 169 | 170 | /** 171 | * Works off of ZonedDateTime context. 172 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day to. 173 | * @return True if context is before or equal param's day, false otherwise. 174 | */ 175 | fun ZonedDateTime.isBeforeEqualDay(zonedDateTimeB: ZonedDateTime): Boolean = 176 | this.compareDay(zonedDateTimeB) <= 0 177 | 178 | /** 179 | * Works off of ZonedDateTime context. 180 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day to. 181 | * @return True if context is after param's day, false otherwise. 182 | */ 183 | fun ZonedDateTime.isAfterDay(zonedDateTimeB: ZonedDateTime): Boolean = 184 | this.compareDay(zonedDateTimeB) > 0 185 | 186 | /** 187 | * Works off of ZonedDateTime context. 188 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day to. 189 | * @return True if context is after or equal param's day, false otherwise. 190 | */ 191 | fun ZonedDateTime.isAfterEqualDay(zonedDateTimeB: ZonedDateTime): Boolean = 192 | this.compareDay(zonedDateTimeB) >= 0 193 | // endregion 194 | 195 | // region Time Comparisons 196 | /** 197 | * Works off of ZonedDateTime context. 198 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day & time to. 199 | * @return 0 when they're matching day & time, -1 when context's is less than, 1 when context's is more than param's. 200 | */ 201 | fun ZonedDateTime.compareTime(zonedDateTimeB: ZonedDateTime): Int = 202 | when { 203 | this.isEqualTime(zonedDateTimeB) -> 0 204 | this.isBeforeTime(zonedDateTimeB) -> -1 205 | else -> 1 206 | } 207 | 208 | /** 209 | * Works off of ZonedDateTime context. 210 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day & time to. 211 | * @return True if context's equals param's day & time, false otherwise. 212 | */ 213 | fun ZonedDateTime.isEqualTime(zonedDateTimeB: ZonedDateTime): Boolean = this.isEqual(zonedDateTimeB) 214 | 215 | /** 216 | * Works off of ZonedDateTime context. 217 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day & time to. 218 | * @return True if context is before param's day & time, false otherwise. 219 | */ 220 | fun ZonedDateTime.isBeforeTime(zonedDateTimeB: ZonedDateTime): Boolean = 221 | this.isBefore(zonedDateTimeB) 222 | 223 | /** 224 | * Works off of ZonedDateTime context. 225 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day & time to. 226 | * @return True if context is before or equal param's day & time, false otherwise. 227 | */ 228 | fun ZonedDateTime.isBeforeEqualTime(zonedDateTimeB: ZonedDateTime): Boolean = 229 | this.compareTime(zonedDateTimeB) <= 0 230 | 231 | /** 232 | * Works off of ZonedDateTime context. 233 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day & time to. 234 | * @return True if context is after param's day & time, false otherwise. 235 | */ 236 | fun ZonedDateTime.isAfterTime(zonedDateTimeB: ZonedDateTime): Boolean = 237 | this.compareTime(zonedDateTimeB) > 0 238 | 239 | /** 240 | * Works off of ZonedDateTime context. 241 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day & time to. 242 | * @return True if context is after or equal param's day & time, false otherwise. 243 | */ 244 | fun ZonedDateTime.isAfterEqualTime(zonedDateTimeB: ZonedDateTime): Boolean = 245 | this.compareTime(zonedDateTimeB) >= 0 246 | // endregion 247 | 248 | /** 249 | * Works off of ZonedDateTime context. 250 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's second difference from. 251 | * @return Seconds away from param be it positive or negative. 252 | */ 253 | fun ZonedDateTime.getSecondDifference(zonedDateTimeB: ZonedDateTime): Long { 254 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 255 | return Duration.between(this, otherZonedDateTime).toSeconds() 256 | } 257 | 258 | /** 259 | * Works off of ZonedDateTime context. 260 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's minute difference from. 261 | * @return Minutes away from param be it positive or negative. 262 | */ 263 | fun ZonedDateTime.getMinuteDifference(zonedDateTimeB: ZonedDateTime): Long { 264 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 265 | return Duration.between(this, otherZonedDateTime).toMinutes() 266 | } 267 | 268 | /** 269 | * Works off of ZonedDateTime context. 270 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's hour difference from. 271 | * @return Hours away from param be it positive or negative. 272 | */ 273 | fun ZonedDateTime.getHourDifference(zonedDateTimeB: ZonedDateTime): Long { 274 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 275 | return Duration.between(this, otherZonedDateTime).toHours() 276 | } 277 | 278 | /** 279 | * Works off of ZonedDateTime context. 280 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's day difference from. 281 | * @return Days away from param be it positive or negative. 282 | */ 283 | fun ZonedDateTime.getDayDifference(zonedDateTimeB: ZonedDateTime): Long { 284 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 285 | return Duration.between(this.atStartOfDay(), otherZonedDateTime.atStartOfDay()).toDays() 286 | } 287 | 288 | /** 289 | * Works off of ZonedDateTime context. 290 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's months difference from. 291 | * @return Months away from param be it positive or negative. 292 | */ 293 | fun ZonedDateTime.getMonthDifference(zonedDateTimeB: ZonedDateTime): Long { 294 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 295 | return ChronoUnit.MONTHS.between(this, otherZonedDateTime) 296 | } 297 | 298 | /** 299 | * Works off of ZonedDateTime context. 300 | * @param zonedDateTimeB ZonedDateTime of which we want to compare context's years difference from. 301 | * @return Years away from param be it positive or negative. 302 | */ 303 | fun ZonedDateTime.getYearDifference(zonedDateTimeB: ZonedDateTime): Long { 304 | val otherZonedDateTime = zonedDateTimeB.withTimeZoneOf(this) 305 | return ChronoUnit.YEARS.between(this, otherZonedDateTime) 306 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/extensions/ZonedDateTimeMutatingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime.extensions 2 | 3 | import java.time.DayOfWeek 4 | import java.time.LocalTime 5 | import java.time.ZonedDateTime 6 | 7 | /** 8 | * Works off of ZonedDateTime context. 9 | * @return ZonedDateTime with its LocalTime set to the start of day. 10 | */ 11 | fun ZonedDateTime.atStartOfDay(): ZonedDateTime = this.withLocalTime(LocalTime.MIN) 12 | 13 | /** 14 | * Works off of ZonedDateTime context. 15 | * @return ZonedDateTime with its LocalTime set to the very last moment of the day. 16 | */ 17 | fun ZonedDateTime.atEndOfDay(): ZonedDateTime = this.withLocalTime(LocalTime.MAX) 18 | 19 | /** 20 | * Works off of ZonedDateTime context. 21 | * @param localTime LocalTime of which we want to copy its time to our contextual ZonedDateTime 22 | * @return ZonedDateTime with its LocalTime updated to use param's. 23 | */ 24 | fun ZonedDateTime.withLocalTime(localTime: LocalTime): ZonedDateTime = 25 | this.withHour(localTime.hour) 26 | .withMinute(localTime.minute) 27 | .withSecond(localTime.second) 28 | .withNano(localTime.nano) 29 | 30 | /** 31 | * Works off of ZonedDateTime context. 32 | * @param zonedDateTime ZonedDateTime of whose time zone we want to convert context's time zone to. 33 | * @return ZonedDateTime updated to new time zone if varying in time zone with param's. 34 | */ 35 | fun ZonedDateTime.withTimeZoneOf(zonedDateTime: ZonedDateTime): ZonedDateTime { 36 | if (this.zone != zonedDateTime.zone) { 37 | return this.withZoneSameInstant(zonedDateTime.zone) 38 | } 39 | return this 40 | } 41 | 42 | /** 43 | * Works off of ZonedDateTime context. 44 | * @param dayOfWeek Monday, Tues, etc, which we want to find the last instance of when going back in time. 45 | * @param countingInThisDay When true, & context's DayOfWeek matches param, then return context, else go back a week. 46 | * @return ZonedDateTime of last DayOfWeek. 47 | */ 48 | fun ZonedDateTime.getLast(dayOfWeek: DayOfWeek, countingInThisDay: Boolean = false): ZonedDateTime { 49 | if (countingInThisDay && this.dayOfWeek == dayOfWeek) { 50 | return this 51 | } 52 | var mostRecentDay = this 53 | if (mostRecentDay.dayOfWeek == dayOfWeek) { 54 | mostRecentDay = mostRecentDay.minusDays(1) 55 | } 56 | while (mostRecentDay.dayOfWeek != dayOfWeek) { 57 | mostRecentDay = mostRecentDay.minusDays(1) 58 | } 59 | return mostRecentDay 60 | } 61 | 62 | /** 63 | * Works off of ZonedDateTime context. 64 | * @param dayOfWeek Monday, Tues, etc, which we want to find the next instance of when going forward in time. 65 | * @param countingInThisDay When true, & context's DayOfWeek matches param, then return context, else go next week. 66 | * @return ZonedDateTime of next DayOfWeek. 67 | */ 68 | fun ZonedDateTime.getNext(dayOfWeek: DayOfWeek, countingInThisDay: Boolean = false): ZonedDateTime { 69 | if (countingInThisDay && this.dayOfWeek == dayOfWeek) { 70 | return this 71 | } 72 | var nextZonedDate = this 73 | if (nextZonedDate.dayOfWeek == dayOfWeek) { 74 | nextZonedDate = nextZonedDate.plusDays(1) 75 | } 76 | while (nextZonedDate.dayOfWeek != dayOfWeek) { 77 | nextZonedDate = nextZonedDate.plusDays(1) 78 | } 79 | return nextZonedDate 80 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/extensions/ZonedDateTimeParsingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime.extensions 2 | 3 | import java.time.ZonedDateTime 4 | import java.time.format.DateTimeFormatter 5 | import java.time.format.DateTimeParseException 6 | 7 | /** 8 | * Works off of String representations of ZonedDateTime: 9 | * When a format is present however, it'll try parsing using that format alone, & return null if it fails. 10 | * 11 | * @param this String representation of ZonedDateTime. 12 | * @param format String representing format that should solely be used when parsing the date. 13 | * @return ZonedDateTime? Null means couldn't parse, else parsed ZonedDateTime. 14 | */ 15 | fun String.toZonedDateTime(format: String? = null): ZonedDateTime? = 16 | parseZonedDateTimeOrNull(this, format) 17 | 18 | private fun parseZonedDateTimeOrNull(dateText: String, format: String?): ZonedDateTime? = 19 | if (format.isNullOrEmpty()) { 20 | try { 21 | ZonedDateTime.parse(dateText) 22 | } catch (e: DateTimeParseException) { 23 | null 24 | } catch (e: IllegalArgumentException) { 25 | null 26 | } 27 | } else { 28 | try { 29 | ZonedDateTime.parse(dateText, DateTimeFormatter.ofPattern(format)) 30 | } catch (e: DateTimeParseException) { 31 | null 32 | } catch (e: IllegalArgumentException) { 33 | null 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/zoneddatetime/extensions/ZonedDateTimePrintExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.zoneddatetime.extensions 2 | 3 | import java.time.ZonedDateTime 4 | import java.time.format.DateTimeFormatterBuilder 5 | import java.util.Locale 6 | 7 | fun ZonedDateTime.print(format: String, locale: Locale = Locale.US): String = 8 | this.format(DateTimeFormatterBuilder().appendPattern(format).toFormatter(locale)) -------------------------------------------------------------------------------- /src/test/kotlin/localdate/LocalDateComparisonExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package localdate 2 | 3 | import javatimefun.localdate.LocalDateFactory 4 | import javatimefun.localdate.extensions.getDayDifference 5 | import javatimefun.localdate.extensions.getHourDifference 6 | import javatimefun.localdate.extensions.getMinuteDifference 7 | import javatimefun.localdate.extensions.getMonthDifference 8 | import javatimefun.localdate.extensions.getSecondDifference 9 | import javatimefun.localdate.extensions.getYearDifference 10 | import org.junit.jupiter.api.Assertions.assertEquals 11 | import org.junit.jupiter.api.Test 12 | 13 | class LocalDateComparisonExtensionsTest { 14 | 15 | @Test 16 | fun `given 2 dates 0 days apart, then should see such differences in comparing`() { 17 | // given 18 | // 2021-06-08 19 | val dateA = LocalDateFactory.new( 20 | year = 2021, 21 | month = 6, 22 | day = 8, 23 | ) 24 | 25 | // 2021-06-08 26 | val dateB = LocalDateFactory.new( 27 | year = 2021, 28 | month = 6, 29 | day = 8, 30 | ) 31 | 32 | // when 33 | val actualYearsDifference = dateA.getYearDifference(dateB) 34 | val actualMonthDifference = dateA.getMonthDifference(dateB) 35 | val actualDayDifference = dateA.getDayDifference(dateB) 36 | val actualHourDifference = dateA.getHourDifference(dateB) 37 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 38 | val actualSecondDifference = dateA.getSecondDifference(dateB) 39 | 40 | // then 41 | assertEquals(0, actualYearsDifference) 42 | assertEquals(0, actualMonthDifference) 43 | assertEquals(0, actualDayDifference) 44 | assertEquals(0, actualHourDifference) 45 | assertEquals(0, actualMinuteDifference) 46 | assertEquals(0, actualSecondDifference) 47 | } 48 | 49 | @Test 50 | fun `given 2 dateTimes 3 years apart, then should see such differences in comparing`() { 51 | // given 52 | // 2021-06-08 3:30 PM 53 | val dateA = LocalDateFactory.new( 54 | year = 2021, 55 | month = 6, 56 | day = 8, 57 | ) 58 | 59 | // 2024-06-08 3:30 PM 60 | val dateB = LocalDateFactory.new( 61 | year = 2024, 62 | month = 6, 63 | day = 8, 64 | ) 65 | 66 | // when 67 | val actualYearsDifference = dateA.getYearDifference(dateB) 68 | val actualMonthDifference = dateA.getMonthDifference(dateB) 69 | val actualDayDifference = dateA.getDayDifference(dateB) 70 | val actualHourDifference = dateA.getHourDifference(dateB) 71 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 72 | val actualSecondDifference = dateA.getSecondDifference(dateB) 73 | 74 | // then 75 | assertEquals(3, actualYearsDifference) 76 | assertEquals(36, actualMonthDifference) 77 | assertEquals(1096, actualDayDifference) 78 | assertEquals(26304, actualHourDifference) 79 | assertEquals(1578240, actualMinuteDifference) 80 | assertEquals(94694400, actualSecondDifference) 81 | } 82 | } -------------------------------------------------------------------------------- /src/test/kotlin/localdate/LocalDateFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package localdate 2 | 3 | import javatimefun.calendar.extensions.toLocalDate 4 | import javatimefun.date.extensions.toLocalDate 5 | import javatimefun.localdate.extensions.getMonthBaseZero 6 | import org.junit.jupiter.api.Assertions.assertEquals 7 | import org.junit.jupiter.api.Test 8 | import java.util.Date 9 | import java.util.Calendar 10 | import java.util.GregorianCalendar 11 | import java.util.TimeZone 12 | 13 | class LocalDateFactoryTest { 14 | 15 | @Test 16 | fun `given date epoch millisecond, when converted to localDateTime, then should match attributes`() { 17 | // given 18 | val epoch = 1325134800000 19 | 20 | // when 21 | val localDate = javatimefun.localdate.LocalDateFactory.new(epoch) 22 | 23 | // then 24 | assertEquals(2011, localDate.year) 25 | assertEquals(12, localDate.monthValue) 26 | assertEquals(29, localDate.dayOfMonth) 27 | } 28 | 29 | @Test 30 | fun `given date epoch millisecond of Date, when converted to localDateTime, then should match attributes`() { 31 | // given 32 | val epoch = 1325134800000 33 | 34 | // when 35 | val date = Date(epoch) 36 | val localDate = date.toLocalDate() 37 | 38 | // then 39 | assertEquals(2011, localDate.year) 40 | assertEquals(12, localDate.monthValue) 41 | assertEquals(29, localDate.dayOfMonth) 42 | } 43 | 44 | @Test 45 | fun `given date epoch millisecond of Date & UTC timezone, when converted to localDateTime, then should match attributes`() { 46 | // given 47 | val epoch = 1325134800000 48 | 49 | // when 50 | val date = Date(epoch) 51 | val calendar = GregorianCalendar().apply { 52 | timeZone = TimeZone.getTimeZone("UTC") 53 | time = date 54 | } 55 | val localDate = calendar.toLocalDate() 56 | 57 | // then 58 | assertEquals(calendar[Calendar.YEAR], localDate.year) 59 | assertEquals(calendar[Calendar.MONTH], localDate.getMonthBaseZero()) 60 | assertEquals(calendar[Calendar.DAY_OF_MONTH], localDate.dayOfMonth) 61 | assertEquals(2011, localDate.year) 62 | assertEquals(12, localDate.monthValue) 63 | assertEquals(29, localDate.dayOfMonth) 64 | } 65 | } -------------------------------------------------------------------------------- /src/test/kotlin/localdatetime/LocalDateTimeComparisonExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package localdatetime 2 | 3 | import javatimefun.localdatetime.LocalDateTimeFactory 4 | import javatimefun.localdatetime.extensions.getDayDifference 5 | import javatimefun.localdatetime.extensions.getHourDifference 6 | import javatimefun.localdatetime.extensions.getMinuteDifference 7 | import javatimefun.localdatetime.extensions.getSecondDifference 8 | import javatimefun.localdatetime.extensions.getYearDifference 9 | import org.junit.jupiter.api.Assertions.assertEquals 10 | import org.junit.jupiter.api.Test 11 | 12 | class LocalDateTimeComparisonExtensionsTest { 13 | 14 | @Test 15 | fun `given 2 dateTimes 1hr and 10m apart, then should see such differences in comparing`() { 16 | // given 17 | // 2021-06-08 3:30 PM 18 | val dateA = LocalDateTimeFactory.new( 19 | year = 2021, 20 | month = 6, 21 | day = 8, 22 | hourIn24 = 15, 23 | minute = 30, 24 | second = 0, 25 | nano = 0 26 | ) 27 | 28 | // 2021-06-08 4:40 PM 29 | val dateB = LocalDateTimeFactory.new( 30 | year = 2021, 31 | month = 6, 32 | day = 8, 33 | hourIn24 = 16, 34 | minute = 40, 35 | second = 0, 36 | nano = 0 37 | ) 38 | 39 | // when 40 | val actualYearsDifference = dateA.getYearDifference(dateB) 41 | val actualDayDifference = dateA.getDayDifference(dateB) 42 | val actualHourDifference = dateA.getHourDifference(dateB) 43 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 44 | val actualSecondDifference = dateA.getSecondDifference(dateB) 45 | 46 | // then 47 | assertEquals(0, actualYearsDifference) 48 | assertEquals(0, actualDayDifference) 49 | assertEquals(1, actualHourDifference) 50 | assertEquals(70, actualMinuteDifference) 51 | assertEquals(4200, actualSecondDifference) 52 | } 53 | 54 | @Test 55 | fun `given 2 dateTimes 3 years apart, then should see such differences in comparing`() { 56 | // given 57 | // 2021-06-08 3:30 PM 58 | val dateA = LocalDateTimeFactory.new( 59 | year = 2021, 60 | month = 6, 61 | day = 8, 62 | hourIn24 = 15, 63 | minute = 30, 64 | second = 0, 65 | nano = 0 66 | ) 67 | 68 | // 2024-06-08 3:30 PM 69 | val dateB = LocalDateTimeFactory.new( 70 | year = 2024, 71 | month = 6, 72 | day = 8, 73 | hourIn24 = 15, 74 | minute = 30, 75 | second = 0, 76 | nano = 0 77 | ) 78 | 79 | // when 80 | val actualYearsDifference = dateA.getYearDifference(dateB) 81 | val actualDayDifference = dateA.getDayDifference(dateB) 82 | val actualHourDifference = dateA.getHourDifference(dateB) 83 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 84 | val actualSecondDifference = dateA.getSecondDifference(dateB) 85 | 86 | // then 87 | assertEquals(3, actualYearsDifference) 88 | assertEquals(1096, actualDayDifference) 89 | assertEquals(26304, actualHourDifference) 90 | assertEquals(1578240, actualMinuteDifference) 91 | assertEquals(94694400, actualSecondDifference) 92 | } 93 | } -------------------------------------------------------------------------------- /src/test/kotlin/localdatetime/LocalDateTimeFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package localdatetime 2 | 3 | import javatimefun.ZoneIds 4 | import javatimefun.calendar.extensions.toLocalDateTime 5 | import javatimefun.date.extensions.toLocalDateTime 6 | import javatimefun.localdatetime.LocalDateTimeFactory 7 | import javatimefun.localdatetime.extensions.getMonthBaseZero 8 | import org.junit.jupiter.api.Assertions.assertEquals 9 | import org.junit.jupiter.api.Test 10 | import java.util.Calendar 11 | import java.util.Date 12 | import java.util.GregorianCalendar 13 | import java.util.TimeZone 14 | 15 | class LocalDateTimeFactoryTest { 16 | 17 | @Test 18 | fun `given date epoch millisecond, when converted to localDateTime, then should match attributes`() { 19 | // given 20 | val epoch = 1325134800000 21 | 22 | // when 23 | val localDateTime = LocalDateTimeFactory.new(epoch, ZoneIds.UTC) 24 | 25 | // then 26 | assertEquals(2011, localDateTime.year) 27 | assertEquals(12, localDateTime.monthValue) 28 | assertEquals(29, localDateTime.dayOfMonth) 29 | assertEquals(5, localDateTime.hour) 30 | assertEquals(0, localDateTime.minute) 31 | assertEquals(0, localDateTime.second) 32 | } 33 | 34 | @Test 35 | fun `given date epoch millisecond of Date, when converted to localDateTime, then should match attributes`() { 36 | // given 37 | val epoch = 1325134800000 38 | 39 | // when 40 | val date = Date(epoch) 41 | val localDateTime = date.toLocalDateTime(ZoneIds.UTC) 42 | 43 | // then 44 | assertEquals(2011, localDateTime.year) 45 | assertEquals(12, localDateTime.monthValue) 46 | assertEquals(29, localDateTime.dayOfMonth) 47 | assertEquals(5, localDateTime.hour) 48 | assertEquals(0, localDateTime.minute) 49 | assertEquals(0, localDateTime.second) 50 | } 51 | 52 | @Test 53 | fun `given date epoch millisecond of Date & UTC timezone, when converted to localDateTime, then should match attributes`() { 54 | // given 55 | val epoch = 1325134800000 56 | 57 | // when 58 | val date = Date(epoch) 59 | val calendar = GregorianCalendar().apply { 60 | timeZone = TimeZone.getTimeZone("UTC") 61 | time = date 62 | } 63 | val localDateTime = calendar.toLocalDateTime() 64 | 65 | // then 66 | assertEquals(2011, localDateTime.year) 67 | assertEquals(12, localDateTime.monthValue) 68 | assertEquals(29, localDateTime.dayOfMonth) 69 | assertEquals(5, localDateTime.hour) 70 | assertEquals(0, localDateTime.minute) 71 | assertEquals(0, localDateTime.second) 72 | assertEquals(calendar[Calendar.YEAR], localDateTime.year) 73 | assertEquals(calendar[Calendar.MONTH], localDateTime.getMonthBaseZero()) 74 | assertEquals(calendar[Calendar.DAY_OF_MONTH], localDateTime.dayOfMonth) 75 | assertEquals(calendar[Calendar.HOUR], localDateTime.hour) 76 | assertEquals(calendar[Calendar.MINUTE], localDateTime.minute) 77 | assertEquals(calendar[Calendar.SECOND], localDateTime.second) 78 | assertEquals(calendar[Calendar.MILLISECOND], localDateTime.toLocalTime().nano / 1000000) 79 | } 80 | } -------------------------------------------------------------------------------- /src/test/kotlin/localdatetime/LocalDateTimeParsingExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package localdatetime 2 | 3 | import javatimefun.localdatetime.extensions.print 4 | import javatimefun.localdatetime.extensions.toLocalDateTime 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | import java.lang.RuntimeException 8 | import java.time.LocalDateTime 9 | 10 | class LocalDateTimeParsingExtensionsTest { 11 | 12 | companion object { 13 | private const val ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'" 14 | private const val ISO_8601_S = "yyyy-MM-dd'T'HH:mm:ss.S'Z'" 15 | private const val ISO_8601_SS = "yyyy-MM-dd'T'HH:mm:ss.SS'Z'" 16 | private const val ISO_8601_SSS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" 17 | private const val ISO_8601_4S = "yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'" 18 | private const val ISO_8601_5S = "yyyy-MM-dd'T'HH:mm:ss.SSSSS'Z'" 19 | private const val ISO_8601_6S = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'" 20 | } 21 | 22 | @Test 23 | fun `given date in ISO_8601, when parsed without format & converted to date, then should match when printed back to text`() { 24 | // given 25 | val dateInText = "2023-07-26T12:34:56" 26 | val dateInTextWithZ = "${dateInText}Z" 27 | 28 | // when 29 | val dateParsed: LocalDateTime = dateInTextWithZ.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 30 | val dateParsedWithZ: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 31 | 32 | // then 33 | assertEquals(dateInTextWithZ, dateParsed.print(ISO_8601)) 34 | assertEquals(dateInTextWithZ, dateParsedWithZ.print(ISO_8601)) 35 | assertEquals("2023-07-26T12:34:56.000000Z", dateParsed.print(ISO_8601_6S)) 36 | assertEquals("2023-07-26T12:34:56.000000Z", dateParsedWithZ.print(ISO_8601_6S)) 37 | } 38 | 39 | @Test 40 | fun `given date in ISO_8601_S, when parsed without format & converted to date, then should match when printed back to text`() { 41 | // given 42 | val dateInText = "2023-07-26T12:34:56.1Z" 43 | 44 | // when 45 | val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 46 | 47 | // then 48 | assertEquals(dateInText, dateParsed.print(ISO_8601_S)) 49 | assertEquals("2023-07-26T12:34:56.100000Z", dateParsed.print(ISO_8601_6S)) 50 | } 51 | 52 | @Test 53 | fun `given date in ISO_8601_SS, when parsed without format & converted to date, then should match when printed back to text`() { 54 | // given 55 | val dateInText = "2023-07-26T12:34:56.12Z" 56 | 57 | // when 58 | val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 59 | 60 | // then 61 | assertEquals(dateInText, dateParsed.print(ISO_8601_SS)) 62 | assertEquals("2023-07-26T12:34:56.120000Z", dateParsed.print(ISO_8601_6S)) 63 | } 64 | 65 | @Test 66 | fun `given date in ISO_8601_SSS, when parsed without format & converted to date, then should match when printed back to text`() { 67 | // given 68 | val dateInText = "2023-07-26T12:34:56.123Z" 69 | 70 | // when 71 | val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 72 | 73 | // then 74 | assertEquals(dateInText, dateParsed.print(ISO_8601_SSS)) 75 | assertEquals("2023-07-26T12:34:56.123000Z", dateParsed.print(ISO_8601_6S)) 76 | } 77 | 78 | @Test 79 | fun `given date in ISO_8601_4S, when parsed without format & converted to date, then should match when printed back to text`() { 80 | // given 81 | val dateInText = "2023-07-26T12:34:56.1234Z" 82 | 83 | // when 84 | val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 85 | 86 | // then 87 | assertEquals(dateInText, dateParsed.print(ISO_8601_4S)) 88 | assertEquals("2023-07-26T12:34:56.123400Z", dateParsed.print(ISO_8601_6S)) 89 | } 90 | 91 | @Test 92 | fun `given date in ISO_8601_5S, when parsed without format & converted to date, then should be null`() { 93 | // given 94 | val dateInText = "2023-07-26T12:34:56.12345" 95 | val dateInTextWithZ = "${dateInText}Z" 96 | 97 | // when 98 | val dateParsed: LocalDateTime = dateInTextWithZ.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 99 | val dateParsedWithZ: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 100 | 101 | // then 102 | assertEquals(dateInTextWithZ, dateParsed.print(ISO_8601_5S)) 103 | assertEquals(dateInTextWithZ, dateParsedWithZ.print(ISO_8601_5S)) 104 | assertEquals("2023-07-26T12:34:56.123450Z", dateParsed.print(ISO_8601_6S)) 105 | assertEquals("2023-07-26T12:34:56.123450Z", dateParsedWithZ.print(ISO_8601_6S)) 106 | } 107 | 108 | @Test 109 | fun `given date in ISO_8601_6S, when parsed without format & converted to date, then should match when printed back to text`() { 110 | // given 111 | val dateInText = "2023-07-26T12:34:56.123456Z" 112 | 113 | // when 114 | val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse") 115 | 116 | // then 117 | assertEquals(dateInText, dateParsed.print(ISO_8601_6S)) 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /src/test/kotlin/localtime/LocalTimeComparisonExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package localtime 2 | 3 | import javatimefun.localtime.LocalTimeUtil 4 | import javatimefun.localtime.extensions.getHourDifference 5 | import javatimefun.localtime.extensions.getMilliSecondDifference 6 | import javatimefun.localtime.extensions.getMinuteDifference 7 | import javatimefun.localtime.extensions.getSecondDifference 8 | import org.junit.jupiter.api.Assertions.assertEquals 9 | import org.junit.jupiter.api.Test 10 | 11 | class LocalTimeComparisonExtensionsTest { 12 | 13 | @Test 14 | fun `given 2 times 1hr and 10m apart, then should see such differences in comparing`() { 15 | // given 16 | // 3:30 PM 17 | val dateA = LocalTimeUtil.new( 18 | hourIn24 = 15, 19 | minute = 30, 20 | second = 0, 21 | ) 22 | 23 | // 4:40 PM 24 | val dateB = LocalTimeUtil.new( 25 | hourIn24 = 16, 26 | minute = 40, 27 | second = 0, 28 | ) 29 | 30 | // when 31 | val actualHourDifference = dateA.getHourDifference(dateB) 32 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 33 | val actualSecondDifference = dateA.getSecondDifference(dateB) 34 | val actualMilliDifference = dateA.getMilliSecondDifference(dateB) 35 | 36 | // then 37 | assertEquals(1, actualHourDifference) 38 | assertEquals(70, actualMinuteDifference) 39 | assertEquals(4200, actualSecondDifference) 40 | assertEquals(4200000, actualMilliDifference) 41 | } 42 | 43 | @Test 44 | fun `given 2 times and no time apart, then should see such differences in comparing`() { 45 | // given 46 | // 3:30 PM 47 | val dateA = LocalTimeUtil.new( 48 | hourIn24 = 15, 49 | minute = 30, 50 | second = 0, 51 | ) 52 | 53 | // 3:30 PM 54 | val dateB = LocalTimeUtil.new( 55 | hourIn24 = 15, 56 | minute = 30, 57 | second = 0, 58 | ) 59 | 60 | // when 61 | val actualHourDifference = dateA.getHourDifference(dateB) 62 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 63 | val actualSecondDifference = dateA.getSecondDifference(dateB) 64 | val actualMilliSecondDifference = dateA.getMilliSecondDifference(dateB) 65 | 66 | // then 67 | assertEquals(0, actualHourDifference) 68 | assertEquals(0, actualMinuteDifference) 69 | assertEquals(0, actualSecondDifference) 70 | assertEquals(0, actualMilliSecondDifference) 71 | } 72 | } -------------------------------------------------------------------------------- /src/test/kotlin/localtime/LocalTimeUtilTest.kt: -------------------------------------------------------------------------------- 1 | package localtime 2 | 3 | import javatimefun.ZoneIds 4 | import javatimefun.calendar.extensions.toLocalTime 5 | import javatimefun.date.extensions.toLocalTime 6 | import javatimefun.localtime.LocalTimeUtil 7 | import org.junit.jupiter.api.Assertions.assertEquals 8 | import org.junit.jupiter.api.Test 9 | import java.util.Date 10 | import java.util.Calendar 11 | import java.util.GregorianCalendar 12 | import java.util.TimeZone 13 | 14 | class LocalTimeUtilTest { 15 | 16 | @Test 17 | fun `given date epoch millisecond, when converted to localDateTime, then should match attributes`() { 18 | // given 19 | val epoch = 1325134800000 20 | 21 | // when 22 | val localTime = LocalTimeUtil.new(epoch, ZoneIds.UTC) 23 | 24 | // then 25 | assertEquals(5, localTime.hour) 26 | assertEquals(0, localTime.minute) 27 | assertEquals(0, localTime.second) 28 | } 29 | 30 | @Test 31 | fun `given date epoch millisecond of Date, when converted to localDateTime, then should match attributes`() { 32 | // given 33 | val epoch = 1325134800000 34 | 35 | // when 36 | val date = Date(epoch) 37 | val localTime = date.toLocalTime(ZoneIds.UTC) 38 | 39 | // then 40 | assertEquals(5, localTime.hour) 41 | assertEquals(0, localTime.minute) 42 | assertEquals(0, localTime.second) 43 | } 44 | 45 | @Test 46 | fun `given date epoch millisecond of Date & UTC timezone, when converted to localDateTime, then should match attributes`() { 47 | // given 48 | val epoch = 1325134800000 49 | 50 | // when 51 | val date = Date(epoch) 52 | val calendar = GregorianCalendar().apply { 53 | timeZone = TimeZone.getTimeZone("UTC") 54 | time = date 55 | } 56 | val localTime = calendar.toLocalTime() 57 | 58 | // then 59 | assertEquals(5, localTime.hour) 60 | assertEquals(0, localTime.minute) 61 | assertEquals(0, localTime.second) 62 | assertEquals(calendar[Calendar.HOUR], localTime.hour) 63 | assertEquals(calendar[Calendar.MINUTE], localTime.minute) 64 | assertEquals(calendar[Calendar.SECOND], localTime.second) 65 | assertEquals(calendar[Calendar.MILLISECOND], localTime.nano / 1000000) 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/test/kotlin/zoneddatetime/ZonedDateTimeAttributeExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package zoneddatetime 2 | 3 | import javatimefun.zoneddatetime.ZonedDateTimeFactory 4 | import org.junit.jupiter.api.Assertions 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Assertions.assertTrue 7 | import org.junit.jupiter.api.Test 8 | import javatimefun.zoneddatetime.extensions.* 9 | 10 | class ZonedDateTimeAttributeExtensionsTest { 11 | 12 | @Test 13 | fun `given leap years, then should properly mark them as such`() { 14 | // given 15 | val leapYearList = arrayOf(1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032) 16 | var areAllLeapYears = true 17 | 18 | // when 19 | leapYearList.forEach { year -> 20 | val date = ZonedDateTimeFactory.new(year, 1, 1) 21 | areAllLeapYears = areAllLeapYears and date.isInLeapYear() 22 | } 23 | 24 | // then 25 | Assertions.assertTrue(areAllLeapYears) 26 | } 27 | 28 | @Test 29 | fun `given non-leap years, then should properly mark them as such`() { 30 | // given 31 | val nonLeapYearList = 32 | arrayOf(1981, 1983, 1989, 1991, 1995, 2001, 2005, 2009, 2013, 2017, 2021, 2022, 2029, 2031) 33 | var areAnyLeapYears = false 34 | 35 | // when 36 | nonLeapYearList.forEach { year -> 37 | val date = ZonedDateTimeFactory.new(year, 1, 1) 38 | areAnyLeapYears = areAnyLeapYears or date.isInLeapYear() 39 | } 40 | 41 | // then 42 | Assertions.assertFalse(areAnyLeapYears) 43 | } 44 | 45 | @Test 46 | fun `given date A, then should properly describe its attributes`() { 47 | // given 48 | val dateA = ZonedDateTimeFactory.new(2020, 6, 20) 49 | 50 | // when 51 | val monthBase0 = dateA.getMonthBaseZero() 52 | val getDaysInMonth = dateA.getDaysInMonth() 53 | 54 | // then 55 | assertEquals(monthBase0, 5) 56 | assertEquals(getDaysInMonth, 30) 57 | } 58 | 59 | @Test 60 | fun `given date A at start of day, then should properly describe it as such`() { 61 | // given 62 | val dateA = ZonedDateTimeFactory.new( 63 | 2020, 64 | 6, 65 | 20, 66 | 0, 67 | 0, 68 | 0, 69 | 0, 70 | ) 71 | 72 | // when 73 | val startOfDay = dateA.isAtStartOfDay() 74 | 75 | // then 76 | assertTrue(startOfDay) 77 | } 78 | 79 | @Test 80 | fun `given date A at end of day, then should properly describe it as such`() { 81 | // given 82 | val dateA = ZonedDateTimeFactory.new( 83 | 2020, 84 | 6, 85 | 20, 86 | 23, 87 | 59, 88 | 59, 89 | 999_999_999, 90 | ) 91 | 92 | // when 93 | val startOfDay = dateA.isAtEndOfDay() 94 | 95 | // then 96 | assertTrue(startOfDay) 97 | } 98 | } -------------------------------------------------------------------------------- /src/test/kotlin/zoneddatetime/ZonedDateTimeComparisonExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package zoneddatetime 2 | 3 | import javatimefun.zoneddatetime.ZonedDateTimeFactory 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Assertions.assertTrue 6 | import org.junit.jupiter.api.Assertions.assertFalse 7 | import org.junit.jupiter.api.Test 8 | import javatimefun.zoneddatetime.extensions.* 9 | 10 | class ZonedDateTimeComparisonExtensionsTest { 11 | 12 | @Test 13 | fun `given date A & B, when A is before B, then should properly have compare results as such`() { 14 | // given 15 | val dateA = ZonedDateTimeFactory.new(2020, 3, 20) 16 | val dateB = ZonedDateTimeFactory.new(2020, 3, 25) 17 | 18 | // when 19 | val isDateABeforeDayB = 20 | dateA.isBeforeDay(dateB) && dateA.isBeforeEqualDay(dateB) && dateA.compareDay(dateB) == -1 21 | val isDateBAfterDayA = 22 | dateB.isAfterDay(dateA) && dateB.isAfterEqualDay(dateA) && dateB.compareDay(dateA) == 1 23 | 24 | val isDateABeforeDayTimeB = 25 | dateA.isBeforeTime(dateB) && dateA.isBeforeEqualTime(dateB) && dateA.compareTime(dateB) == -1 26 | val isDateBAfterDayTimeA = 27 | dateB.isAfterTime(dateA) && dateB.isAfterEqualTime(dateA) && dateB.compareTime(dateA) == 1 28 | 29 | val isDateANotEqualDayDateB = !dateA.isEqualDay(dateB) && !dateB.isEqualDay(dateA) 30 | val isDateANotEqualDayTimeDateB = !dateA.isEqualTime(dateB) && !dateB.isEqualTime(dateA) 31 | 32 | // then 33 | assertTrue(isDateABeforeDayB) 34 | assertTrue(isDateBAfterDayA) 35 | assertTrue(isDateABeforeDayTimeB) 36 | assertTrue(isDateBAfterDayTimeA) 37 | assertTrue(isDateANotEqualDayDateB) 38 | assertTrue(isDateANotEqualDayTimeDateB) 39 | } 40 | 41 | @Test 42 | fun `given date A & B, when A is before B, then should properly have day & month compare results as such`() { 43 | // given 44 | val dateA = ZonedDateTimeFactory.new(2020, 3, 20) 45 | val dateB = ZonedDateTimeFactory.new(2020, 3, 25) 46 | 47 | // when 48 | val monthDifferenceOfAAndB = dateA.getMonthDifference(dateB) 49 | val monthDifferenceOfAAndBAreTheSame = dateA.getMonthDifference(dateB) == dateB.getMonthDifference(dateA) 50 | 51 | val dateAAndBAreInSameYear = dateA.isEqualYear(dateB) and dateB.isEqualYear(dateA) 52 | val dateAAndBAreInSameMonth = dateA.isEqualMonth(dateB) and dateB.isEqualMonth(dateA) 53 | 54 | val dateAAndBDayDifference = dateA.getDayDifference(dateB) 55 | 56 | // then 57 | assertEquals(0, monthDifferenceOfAAndB) 58 | assertTrue(monthDifferenceOfAAndBAreTheSame) 59 | 60 | assertTrue(dateAAndBAreInSameYear) 61 | assertTrue(dateAAndBAreInSameMonth) 62 | 63 | assertEquals(dateAAndBDayDifference, 5) 64 | } 65 | 66 | @Test 67 | fun `given date A & B, when A is same day as B but not same time, then should properly have matching day comparisons`() { 68 | // given 69 | val dateA = ZonedDateTimeFactory.new(2020, 3, 20) 70 | val dateB = ZonedDateTimeFactory.new(2020, 3, 20, 12, 12, 0, 0) 71 | 72 | // when 73 | val isDateABeforeDayB = dateA.isBeforeDay(dateB) && dateA.compareDay(dateB) == -1 74 | val isDateAEqualDayB = 75 | dateA.isBeforeEqualDay(dateB) && dateA.isEqualDay(dateB) && dateA.compareDay(dateB) == 0 76 | val isDateBAfterDayA = dateB.isAfterDay(dateA) && dateB.compareDay(dateA) == 1 77 | val isDateBAfterEqualDayA = dateB.isAfterEqualDay(dateA) 78 | val isDateAEqualDayDateB = dateA.isEqualDay(dateB) && dateB.isEqualDay(dateA) 79 | 80 | // then 81 | assertFalse(isDateABeforeDayB) 82 | assertTrue(isDateAEqualDayB) 83 | assertTrue(isDateBAfterEqualDayA) 84 | assertFalse(isDateBAfterDayA) 85 | assertTrue(isDateAEqualDayDateB) 86 | } 87 | 88 | @Test 89 | fun `given date A & B, when A is same day as B but not same time, then should properly have matching time comparisons`() { 90 | // given 91 | val dateA = ZonedDateTimeFactory.new(2020, 3, 20) 92 | val dateB = ZonedDateTimeFactory.new(2020, 3, 20, 12, 12, 0, 0) 93 | 94 | // when 95 | val isDateABeforeDayTimeB = 96 | dateA.isBeforeTime(dateB) && dateA.isBeforeEqualTime(dateB) && dateA.compareTime(dateB) == -1 97 | val isDateBAfterDayTimeA = 98 | dateB.isAfterTime(dateA) && dateB.isAfterEqualTime(dateA) && dateB.compareTime(dateA) == 1 99 | val isDateAEqualTimeDateB = dateA.isEqualTime(dateB) && dateB.isEqualTime(dateA) 100 | 101 | // then 102 | assertTrue(isDateABeforeDayTimeB) 103 | assertTrue(isDateBAfterDayTimeA) 104 | assertFalse(isDateAEqualTimeDateB) 105 | } 106 | 107 | @Test 108 | fun `given 2 dateTimes 1hr and 10m apart, then should see such differences in comparing`() { 109 | // given 110 | // 2021-06-08 3:30 PM 111 | val dateA = ZonedDateTimeFactory.new( 112 | year = 2021, 113 | month = 6, 114 | day = 8, 115 | hourIn24 = 15, 116 | minute = 30, 117 | second = 0, 118 | nano = 0 119 | ) 120 | 121 | // 2021-06-08 4:40 PM 122 | val dateB = ZonedDateTimeFactory.new( 123 | year = 2021, 124 | month = 6, 125 | day = 8, 126 | hourIn24 = 16, 127 | minute = 40, 128 | second = 0, 129 | nano = 0 130 | ) 131 | 132 | // when 133 | val actualYearsDifference = dateA.getYearDifference(dateB) 134 | val actualDayDifference = dateA.getDayDifference(dateB) 135 | val actualHourDifference = dateA.getHourDifference(dateB) 136 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 137 | val actualSecondDifference = dateA.getSecondDifference(dateB) 138 | 139 | // then 140 | assertEquals(0, actualYearsDifference) 141 | assertEquals(0, actualDayDifference) 142 | assertEquals(1, actualHourDifference) 143 | assertEquals(70, actualMinuteDifference) 144 | assertEquals(4200, actualSecondDifference) 145 | } 146 | 147 | @Test 148 | fun `given 2 dateTimes 3 years apart, then should see such differences in comparing`() { 149 | // given 150 | // 2021-06-08 3:30 PM 151 | val dateA = ZonedDateTimeFactory.new( 152 | year = 2021, 153 | month = 6, 154 | day = 8, 155 | hourIn24 = 15, 156 | minute = 30, 157 | second = 0, 158 | nano = 0 159 | ) 160 | 161 | // 2024-06-08 3:30 PM 162 | val dateB = ZonedDateTimeFactory.new( 163 | year = 2024, 164 | month = 6, 165 | day = 8, 166 | hourIn24 = 15, 167 | minute = 30, 168 | second = 0, 169 | nano = 0 170 | ) 171 | 172 | // when 173 | val actualYearsDifference = dateA.getYearDifference(dateB) 174 | val actualDayDifference = dateA.getDayDifference(dateB) 175 | val actualHourDifference = dateA.getHourDifference(dateB) 176 | val actualMinuteDifference = dateA.getMinuteDifference(dateB) 177 | val actualSecondDifference = dateA.getSecondDifference(dateB) 178 | 179 | // then 180 | assertEquals(3, actualYearsDifference) 181 | assertEquals(1096, actualDayDifference) 182 | assertEquals(26304, actualHourDifference) 183 | assertEquals(1578240, actualMinuteDifference) 184 | assertEquals(94694400, actualSecondDifference) 185 | } 186 | } -------------------------------------------------------------------------------- /src/test/kotlin/zoneddatetime/ZonedDateTimeMutatingExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package zoneddatetime 2 | 3 | import javatimefun.localtime.extensions.toLocalTime 4 | import javatimefun.localtime.extensions.print 5 | import javatimefun.zoneddatetime.ZonedDateTimeFactory 6 | import org.junit.jupiter.api.Assertions 7 | import org.junit.jupiter.api.Assertions.assertTrue 8 | import org.junit.jupiter.api.Test 9 | import javatimefun.zoneddatetime.extensions.getLast 10 | import javatimefun.zoneddatetime.extensions.getNext 11 | import javatimefun.zoneddatetime.extensions.isEqualDay 12 | import javatimefun.zoneddatetime.extensions.withLocalTime 13 | import java.time.DayOfWeek 14 | import java.time.LocalTime 15 | 16 | class ZonedDateTimeMutatingExtensionsTest { 17 | 18 | companion object { 19 | private const val HH_MM_SS_AM = "hh:mm:ss a" 20 | } 21 | 22 | @Test 23 | fun `given date is on a Monday, when looking for last Monday inclusive, then should return same date`() { 24 | // given 25 | val dateA = ZonedDateTimeFactory.new(2021, 6, 7) 26 | 27 | // when 28 | val resultLastMondayInclusive = dateA.getLast(DayOfWeek.MONDAY, countingInThisDay = true) 29 | 30 | // then 31 | assertTrue(dateA.isEqualDay(resultLastMondayInclusive)) 32 | } 33 | 34 | @Test 35 | fun `given date is on a Monday, when looking for last Monday exclusive, then should return date minus 1 week`() { 36 | // given 37 | val dateA = ZonedDateTimeFactory.new(2021, 6, 14) 38 | val expectedDate = ZonedDateTimeFactory.new(2021, 6, 7) 39 | 40 | // when 41 | val resultLastMondayExclusive = dateA.getLast(DayOfWeek.MONDAY) 42 | 43 | // then 44 | assertTrue(expectedDate.isEqualDay(resultLastMondayExclusive)) 45 | } 46 | 47 | @Test 48 | fun `given date is on a Monday, when looking for next Monday inclusive, then should return same date`() { 49 | // given 50 | val dateA = ZonedDateTimeFactory.new(2021, 6, 7) 51 | 52 | // when 53 | val resultNextMondayInclusive = dateA.getNext(DayOfWeek.MONDAY, countingInThisDay = true) 54 | 55 | // then 56 | assertTrue(dateA.isEqualDay(resultNextMondayInclusive)) 57 | } 58 | 59 | @Test 60 | fun `given date is on a Monday, when looking for next Monday exclusive, then should return date plus 1 week`() { 61 | // given 62 | val dateA = ZonedDateTimeFactory.new(2021, 6, 7) 63 | val expectedDate = ZonedDateTimeFactory.new(2021, 6, 14) 64 | 65 | // when 66 | val resultNextMondayExclusive = dateA.getNext(DayOfWeek.MONDAY) 67 | 68 | // then 69 | assertTrue(expectedDate.isEqualDay(resultNextMondayExclusive)) 70 | } 71 | 72 | @Test 73 | fun `given date A, when adjusted time, then should properly apply time`() { 74 | // given 75 | var dateA = ZonedDateTimeFactory.new(2020, 3, 20) 76 | val timeText = "07:35:11 AM" 77 | val time: LocalTime = timeText.toLocalTime(HH_MM_SS_AM) ?: throw RuntimeException("Failed to parse") 78 | 79 | // when 80 | dateA = dateA.withLocalTime(time) 81 | val resultTime = dateA.toLocalTime() 82 | val resultText = resultTime.print(HH_MM_SS_AM) 83 | 84 | // then 85 | Assertions.assertEquals(time, resultTime) 86 | Assertions.assertEquals(timeText, resultText) 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/test/kotlin/zoneddatetime/ZonedDateTimeParsingExtensionsTest.kt: -------------------------------------------------------------------------------- 1 | package zoneddatetime 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import javatimefun.zoneddatetime.extensions.toZonedDateTime 6 | import javatimefun.zoneddatetime.extensions.print 7 | import java.lang.RuntimeException 8 | import java.time.ZonedDateTime 9 | 10 | class ZonedDateTimeParsingExtensionsTest { 11 | 12 | companion object { 13 | private const val YYYY_MM_DD_DASH_T_HH_MM_SS_XXX = "yyyy-MM-dd'T'HH:mm:ssXXX" 14 | private const val YYYY_MM_DD_DASH_T_HH_MM_SS = "yyyy-MM-dd'T'HH:mm:ss" 15 | } 16 | 17 | @Test 18 | fun `given date in ISO8601 Zulu format, when parsed & converted, then should match when printed back`() { 19 | // given 20 | val dateInText = "2024-02-17T12:34:56Z" 21 | 22 | // when 23 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 24 | 25 | // then 26 | assertEquals(dateInText, dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS_XXX)) 27 | } 28 | 29 | @Test 30 | fun `given date in ISO8601 with UTC offset, when parsed & converted, then should match when printed back`() { 31 | // given 32 | val dateInText = "2024-02-17T12:34:56+00:00" 33 | 34 | // when 35 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 36 | 37 | // then 38 | assertEquals("2024-02-17T12:34:56", dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS)) 39 | } 40 | 41 | @Test 42 | fun `given date in ISO8601 with negative offset, when parsed & converted, then should match when printed back`() { 43 | // given 44 | val dateInText = "2024-02-17T12:34:56-08:00" 45 | 46 | // when 47 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 48 | 49 | // then 50 | assertEquals(dateInText, dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS_XXX)) 51 | } 52 | 53 | @Test 54 | fun `given date with milliseconds precision, when parsed & converted, then should match when printed back`() { 55 | // given 56 | val dateInText = "2024-02-17T12:34:56.123Z" 57 | 58 | // when 59 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 60 | 61 | // then 62 | assertEquals(dateInText, dateParsed.print("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")) 63 | } 64 | 65 | @Test 66 | fun `given date with microseconds precision, when parsed & converted, then should match when printed back`() { 67 | // given 68 | val dateInText = "2024-02-17T12:34:56.123456Z" 69 | 70 | // when 71 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 72 | 73 | // then 74 | assertEquals(dateInText, dateParsed.print("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX")) 75 | } 76 | 77 | @Test 78 | fun `given date at midnight UTC, when parsed & converted, then should match when printed back`() { 79 | // given 80 | val dateInText = "2024-02-17T00:00:00Z" 81 | 82 | // when 83 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 84 | 85 | // then 86 | assertEquals(dateInText, dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS_XXX)) 87 | } 88 | 89 | @Test 90 | fun `given date at end of day UTC, when parsed & converted, then should match when printed back`() { 91 | // given 92 | val dateInText = "2024-02-17T23:59:59.999999Z" 93 | 94 | // when 95 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 96 | 97 | // then 98 | assertEquals(dateInText, dateParsed.print("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX")) 99 | } 100 | 101 | @Test 102 | fun `given date at end of day UTC #2, when parsed & converted, then should match when printed back`() { 103 | // given 104 | val dateInText = "2024-02-17T23:59:59.999999Z" 105 | 106 | // when 107 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 108 | 109 | // then 110 | assertEquals("2024-02-17T23:59:59.999999", dateParsed.print("yyyy-MM-dd'T'HH:mm:ss.SSSSSS")) 111 | } 112 | 113 | @Test 114 | fun `given leap year date, when parsed & converted, then should match when printed back`() { 115 | // given 116 | val dateInText = "2024-02-29T12:34:56Z" 117 | 118 | // when 119 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 120 | 121 | // then 122 | assertEquals(dateInText, dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS_XXX)) 123 | } 124 | 125 | @Test 126 | fun `given date with max timezone offset, when parsed & converted, then should match when printed back`() { 127 | // given 128 | val dateInText = "2024-12-31T23:59:59+14:00" 129 | 130 | // when 131 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 132 | 133 | // then 134 | assertEquals(dateInText, dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS_XXX)) 135 | } 136 | 137 | @Test 138 | fun `given date with min timezone offset, when parsed & converted, then should match when printed back`() { 139 | // given 140 | val dateInText = "2024-12-31T23:59:59-12:00" 141 | 142 | // when 143 | val dateParsed: ZonedDateTime = dateInText.toZonedDateTime() ?: throw RuntimeException("Failed to parse") 144 | 145 | // then 146 | assertEquals(dateInText, dateParsed.print(YYYY_MM_DD_DASH_T_HH_MM_SS_XXX)) 147 | } 148 | 149 | } -------------------------------------------------------------------------------- /src/test/kotlin/zoneddatetime/ZonedDateTimeUtilTest.kt: -------------------------------------------------------------------------------- 1 | package zoneddatetime 2 | 3 | import javatimefun.ZoneIds 4 | import javatimefun.calendar.extensions.toZonedDateTime 5 | import javatimefun.zoneddatetime.ZonedDateTimeFactory 6 | import org.junit.jupiter.api.Assertions.assertEquals 7 | import org.junit.jupiter.api.Test 8 | import javatimefun.zoneddatetime.extensions.getMonthBaseZero 9 | import java.time.ZonedDateTime 10 | import java.util.Calendar 11 | import java.util.Calendar.HOUR 12 | import java.util.Calendar.DAY_OF_MONTH 13 | import java.util.Calendar.MILLISECOND 14 | import java.util.Calendar.MINUTE 15 | import java.util.Calendar.MONTH 16 | import java.util.Calendar.SECOND 17 | import java.util.Calendar.YEAR 18 | import java.util.Date 19 | import java.util.GregorianCalendar 20 | import java.util.TimeZone 21 | 22 | class ZonedDateTimeTest { 23 | 24 | @Test 25 | fun `given date day month & year, when converted to date, then should match attributes`() { 26 | // given 27 | val day = 7 28 | val month = 6 29 | val year = 2021 30 | 31 | // when 32 | val dateFormed: ZonedDateTime = ZonedDateTimeFactory.new( 33 | year = year, 34 | month = month, 35 | day = day 36 | ) 37 | 38 | // then 39 | assertEquals(day, dateFormed.dayOfMonth) 40 | assertEquals(month, dateFormed.month.value) 41 | assertEquals(year, dateFormed.year) 42 | } 43 | 44 | @Test 45 | fun `given date day month year & time, when converted to date, then should match attributes`() { 46 | // given 47 | val day = 7 48 | val month = 6 49 | val year = 2021 50 | val hour = 7 51 | val minute = 35 52 | val second = 45 53 | 54 | // when 55 | val dateFormed: ZonedDateTime = ZonedDateTimeFactory.new( 56 | year = year, 57 | month = month, 58 | day = day, 59 | hourIn24 = hour, 60 | minute = minute, 61 | second = second 62 | ) 63 | 64 | // then 65 | assertEquals(day, dateFormed.dayOfMonth) 66 | assertEquals(month, dateFormed.month.value) 67 | assertEquals(year, dateFormed.year) 68 | assertEquals(hour, dateFormed.hour) 69 | assertEquals(minute, dateFormed.minute) 70 | assertEquals(second, dateFormed.second) 71 | assertEquals(0, dateFormed.nano) 72 | } 73 | 74 | @Test 75 | fun `given date day month year & time in AM, when converted to date, then should match attributes`() { 76 | // given 77 | val day = 7 78 | val month = 6 79 | val year = 2021 80 | val hour = 7 81 | val minute = 35 82 | val second = 45 83 | 84 | // when 85 | val dateFormed: ZonedDateTime = ZonedDateTimeFactory.new( 86 | year = year, 87 | month = month, 88 | day = day, 89 | hourIn24 = hour, 90 | minute = minute, 91 | second = second 92 | ) 93 | 94 | // then 95 | assertEquals(day, dateFormed.dayOfMonth) 96 | assertEquals(month, dateFormed.month.value) 97 | assertEquals(year, dateFormed.year) 98 | assertEquals(hour, dateFormed.hour) 99 | assertEquals(minute, dateFormed.minute) 100 | assertEquals(second, dateFormed.second) 101 | assertEquals(0, dateFormed.nano) 102 | } 103 | 104 | @Test 105 | fun `given date day month year & time in PM, when converted to date, then should match attributes`() { 106 | // given 107 | val day = 7 108 | val month = 6 109 | val year = 2021 110 | val hour = 19 111 | val minute = 35 112 | val second = 45 113 | 114 | // when 115 | val dateFormed: ZonedDateTime = ZonedDateTimeFactory.new( 116 | year = year, 117 | month = month, 118 | day = day, 119 | hourIn24 = hour, 120 | minute = minute, 121 | second = second, 122 | zoneId = ZoneIds.UTC 123 | ) 124 | 125 | // then 126 | assertEquals(day, dateFormed.dayOfMonth) 127 | assertEquals(month, dateFormed.month.value) 128 | assertEquals(year, dateFormed.year) 129 | assertEquals(hour, dateFormed.hour) 130 | assertEquals(minute, dateFormed.minute) 131 | assertEquals(second, dateFormed.second) 132 | assertEquals(0, dateFormed.nano) 133 | } 134 | 135 | @Test 136 | fun `given date day month year & time with calendar, when converted to zonedDateTime, then should match attributes`() { 137 | // given 138 | val day = 7 139 | val month = 6 140 | val year = 2021 141 | val hour = 7 142 | val minute = 35 143 | val second = 45 144 | val timeZoneId = "GMT" 145 | 146 | // when 147 | val calendar = Calendar.getInstance().apply { 148 | timeZone = TimeZone.getTimeZone(timeZoneId) 149 | set(year, month, day, hour, minute, second) 150 | } 151 | val zonedDateTime = calendar.toZonedDateTime() 152 | 153 | // then 154 | assertEquals(calendar.timeZone.id, zonedDateTime.zone.id) 155 | assertEquals(calendar[YEAR], zonedDateTime.year) 156 | assertEquals(calendar[MONTH], zonedDateTime.getMonthBaseZero()) 157 | assertEquals(calendar[DAY_OF_MONTH], zonedDateTime.dayOfMonth) 158 | assertEquals(calendar[HOUR], zonedDateTime.hour) 159 | assertEquals(calendar[MINUTE], zonedDateTime.minute) 160 | assertEquals(calendar[SECOND], zonedDateTime.second) 161 | assertEquals(calendar[MILLISECOND], zonedDateTime.toLocalTime().nano/1000000) 162 | } 163 | 164 | @Test 165 | fun `given date epoch millisecond of Date, when converted to zonedDateTime, then should match attributes`() { 166 | // given 167 | val epoch = 1325134800000 168 | 169 | // when 170 | val date = Date(epoch) 171 | val zonedDateTime = ZonedDateTimeFactory.new(epoch) 172 | 173 | // then 174 | assertEquals(date.time, zonedDateTime.toInstant().toEpochMilli()) 175 | } 176 | 177 | @Test 178 | fun `given date epoch millisecond of Date & UTC timezone, when converted to zonedDateTime, then should match attributes`() { 179 | // given 180 | val epoch = 1325134800000 181 | val defaultTimeZoneId = "UTC" 182 | 183 | // when 184 | val date = Date(epoch) 185 | val zonedDateTime = ZonedDateTimeFactory.new(epoch, ZoneIds.UTC) 186 | val calendar = GregorianCalendar().apply { 187 | timeZone = TimeZone.getTimeZone(defaultTimeZoneId) 188 | time = date 189 | } 190 | 191 | // then 192 | assertEquals(date.time, zonedDateTime.toInstant().toEpochMilli()) 193 | assertEquals(calendar[YEAR], zonedDateTime.year) 194 | assertEquals(calendar[MONTH], zonedDateTime.getMonthBaseZero()) 195 | assertEquals(calendar[DAY_OF_MONTH], zonedDateTime.dayOfMonth) 196 | assertEquals(calendar[HOUR], zonedDateTime.hour) 197 | assertEquals(calendar[MINUTE], zonedDateTime.minute) 198 | assertEquals(calendar[SECOND], zonedDateTime.second) 199 | assertEquals(calendar[MILLISECOND], zonedDateTime.toLocalTime().nano/1000000) 200 | } 201 | 202 | } --------------------------------------------------------------------------------