├── .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 | SwiftDate 3 |

4 | 5 |

Java Time Fun

6 |

Java Time Kotlin extension functions.

7 |

8 | Build Status 9 | Maven Central 10 | Kotlin 11 | Kotlin Weekly 12 |


13 | 14 | ```diff 15 | - val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd") 16 | - val date = LocalDate.parse(dateText, dateTimeFormatter) 17 | + val date = dateText.toLocalDate("yyyyMMdd") 18 | 19 | - val dateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy") 20 | - print(dateFormatter.format(date)) 21 | + print(date.print("MM/dd/yyyy")) 22 | 23 | - if (ChronoUnit.YEARS.between(dateOfBirth, LocalDate.now()) < 18) { 24 | + if (dateOfBirth.getYearDifference(LocalDates.today) < 18) { 25 | 26 | - val zoneId = ZoneId.of("America/Los_Angeles") 27 | + val zoneId = ZoneIds.AMERICA_LOS_ANGELES 28 | ``` 29 | 30 | ## Features 31 | ### Parsing 32 | _Convert strings into Java Time objects with ease_ 33 | ```kotlin 34 | val result = "01:30 AM".toLocalTime() 35 | val result = "2021-06-07".toLocalDate() 36 | val result = "06/07/2021".toLocalDate(format = "MM/dd/yyyy") 37 | val result = "2024-11-15T12:34:56.123456Z".toLocalDateTime() // handles fractional seconds that Java Time doesn't 38 | val result = "2021-10-04T10:10:00+0000".toZonedDateTime() 39 | ``` 40 | ### Creation 41 | _Create new date/time instances using factory functions_ 42 | ```kotlin 43 | val result = LocalTimeFactory.new(hour = 7, minute = 30) 44 | val result = LocalDateFactory.new(year = 2024, month = 11, day = 15) 45 | val result = LocalDateTimeFactory.new(year = 2024, month = 11, day = 15) 46 | val result = ZonedDateTimeFactory.new(year = 2024, month = 11, day = 15, zoneId = ZoneIds.AMERICA_LOS_ANGELES) 47 | ``` 48 | 49 | ### Conversion from Legacy Date Types 50 | _Easily convert legacy date objects to Java Time_ 51 | ```kotlin 52 | val result = Date().toLocalDateTime() 53 | val result = GregorianCalendar().toZonedDateTime() 54 | ``` 55 | 56 | ### Comparisons 57 | _Compare dates and times at various granularities_ 58 | ```kotlin 59 | // Year 60 | val result = dateA.compareYear(dateB) 61 | val result = dateA.isBeforeYear(dateB) 62 | 63 | // Month 64 | val result = dateA.compareMonth(dateB) 65 | val result = zonedDateA.getMonthDifference(zonedDateB) // auto-conversion to same time zone for expected results 66 | val result = dateA.isEqualMonth(dateB) 67 | 68 | // Day 69 | val result = dateA.compareDay(dateB) 70 | val result = dateA.getDayDifference(dateB) 71 | val result = dateA.isAfterEqualDay(dateB) 72 | 73 | // Time 74 | val result = dateA.compareTime(dateB) 75 | val result = dateA.getMinuteDifference(dateB) 76 | val result = dateA.isAfterEqualTime(dateB) 77 | ``` 78 | 79 | ### Formatting 80 | _Print dates and times using a custom format_ 81 | ```kotlin 82 | val date = "2021-07-06".toZonedDateTime() 83 | val result = date.print(format = "MM/dd/yyyy") 84 | ``` 85 | 86 | ### Attributes & Mutations 87 | _Query and transform date/time attributes_ 88 | ```kotlin 89 | val result = date.isAtStartOfDay() 90 | val result = date.getDaysInMonth() 91 | 92 | val result = date.atStartOfDay() 93 | val result = date.getLast(DayOfWeek.FRIDAY) 94 | val result = date.getNext(DayOfWeek.MONDAY) 95 | ``` 96 | 97 | ### Preset Dates 98 | _Quickly access commonly used dates_ 99 | ```kotlin 100 | val result = LocalDates.startOfYear() 101 | val result = LocalDateTimes.tomorrow() 102 | val result = ZonedDateTimes.nextMonday() 103 | ``` 104 | 105 | ## Installation 106 | Add the following to your module’s `build.gradle`: 107 | ```gradle 108 | repositories { 109 | mavenCentral() 110 | } 111 | 112 | dependencies { 113 | implementation("org.eljabali.sami.javatimefun:javatimefun:4.0.0") 114 | } 115 | ``` 116 | 117 |
118 | For Android 119 | 120 | In addition to the above, you need to desugar your module: 121 | - Ensure you're using [Gradle Plugin](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin) 4.0.0+. 122 | - Update module `build.gradle`: 123 | ```gradle 124 | android { 125 | defaultConfig { 126 | // Required when setting minSdkVersion to 20 or lower 127 | multiDexEnabled true 128 | } 129 | 130 | compileOptions { 131 | // Flag to enable support for the new language APIs 132 | coreLibraryDesugaringEnabled true 133 | // Sets Java compatibility to Java 8 134 | sourceCompatibility JavaVersion.VERSION_1_8 135 | targetCompatibility JavaVersion.VERSION_1_8 136 | } 137 | } 138 | 139 | dependencies { 140 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' 141 | } 142 | ``` 143 | For more information on Android desugaring click [here](https://developer.android.com/studio/write/java8-support#library-desugaring). 144 | 145 |
146 | 147 | ## Find this library useful? 😏 148 | If you like what you see, please star the repository __[as others have](https://github.com/seljabali/java-time-fun/stargazers)__! ⭐️
149 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | val kotlinVersion by extra { "2.1.20" } 2 | val junitVersion by extra { "5.8.1" } 3 | 4 | plugins { 5 | kotlin("jvm") version "2.1.20" 6 | id("org.jetbrains.dokka") version "1.9.20" 7 | id("io.github.gradle-nexus.publish-plugin") version "2.0.0" 8 | `java-library` 9 | `maven-publish` 10 | signing 11 | } 12 | 13 | group = "org.eljabali.sami.javatimefun" 14 | version = "4.0.0" 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") 22 | testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") 23 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion") 24 | } 25 | 26 | tasks.getByName("test") { 27 | useJUnitPlatform() 28 | } 29 | 30 | tasks{ 31 | register("dokkaJar") { 32 | from(dokkaHtml) 33 | dependsOn(dokkaHtml) 34 | archiveClassifier.set("javadoc") 35 | } 36 | withType { 37 | metaInf.with( 38 | copySpec { 39 | from("${project.rootDir}/LICENSE") 40 | } 41 | ) 42 | } 43 | } 44 | 45 | signing { 46 | useInMemoryPgpKeys( 47 | System.getProperty("GPG_PRIVATE_KEY"), 48 | System.getProperty("GPG_PRIVATE_PASSWORD") 49 | ) 50 | sign(publishing.publications) 51 | } 52 | 53 | publishing { 54 | publications { 55 | create("mavenJava") { 56 | pom { 57 | name.set(project.name) 58 | description.set("java.time Kotlin extension functions library.") 59 | url.set("https://github.com/seljabali/java-time-fun") 60 | licenses { 61 | license { 62 | name.set("MIT License") 63 | url.set("https://github.com/seljabali/java-time-fun/blob/main/LICENSE") 64 | } 65 | } 66 | developers { 67 | developer { 68 | id.set("seljabali") 69 | name.set("Sami Eljabali") 70 | email.set("sami@eljabali.org") 71 | url.set("sami.eljabali.org") 72 | } 73 | } 74 | scm { 75 | connection.set("scm:git:git://github.com/seljabali/java-time-fun.git") 76 | developerConnection.set("scm:git:ssh://github.com/seljabali/java-time-fun.git") 77 | url.set("https://github.com/seljabali/java-time-fun/tree/main") 78 | } 79 | } 80 | groupId = project.group as String 81 | artifactId = project.name 82 | version = project.version as String 83 | from(components["java"]) 84 | artifacts { 85 | artifact(tasks["dokkaJar"]) 86 | artifact(tasks.kotlinSourcesJar) { 87 | classifier = "sources" 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | nexusPublishing { 95 | repositories { 96 | sonatype { 97 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) 98 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) 99 | username.set(System.getProperty("SONATYPE_USERNAME")) 100 | password.set(System.getProperty("SONATYPE_PASSWORD")) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seljabali/java-time-fun/fc441cdfe16a61ed2b8a0710dacbaadd087e95d4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /screenshots/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seljabali/java-time-fun/fc441cdfe16a61ed2b8a0710dacbaadd087e95d4/screenshots/logo.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'javatimefun' 2 | 3 | -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/ZoneIds.kt: -------------------------------------------------------------------------------- 1 | package javatimefun 2 | 3 | import java.time.ZoneId 4 | 5 | object ZoneIds { 6 | val AFRICA_ABIDJAN by lazy { ZoneId.of("Africa/Abidjan") } 7 | val AFRICA_ACCRA by lazy { ZoneId.of("Africa/Accra") } 8 | val AFRICA_ADDIS_ABABA by lazy { ZoneId.of("Africa/Addis_Ababa") } 9 | val AFRICA_ALGIERS by lazy { ZoneId.of("Africa/Algiers") } 10 | val AFRICA_ASMERA by lazy { ZoneId.of("Africa/Asmera") } 11 | val AFRICA_BAMAKO by lazy { ZoneId.of("Africa/Bamako") } 12 | val AFRICA_BANJUL by lazy { ZoneId.of("Africa/Banjul") } 13 | val AFRICA_BANGUI by lazy { ZoneId.of("Africa/Bangui") } 14 | val AFRICA_BISSAU by lazy { ZoneId.of("Africa/Bissau") } 15 | val AFRICA_BLANTYRE by lazy { ZoneId.of("Africa/Blantyre") } 16 | val AFRICA_BRAZZAVILLE by lazy { ZoneId.of("Africa/Brazzaville") } 17 | val AFRICA_BUJUMBURA by lazy { ZoneId.of("Africa/Bujumbura") } 18 | val AFRICA_CAIRO by lazy { ZoneId.of("Africa/Cairo") } 19 | val AFRICA_CASABLANCA by lazy { ZoneId.of("Africa/Casablanca") } 20 | val AFRICA_CEUTA by lazy { ZoneId.of("Africa/Ceuta") } 21 | val AFRICA_CONAKRY by lazy { ZoneId.of("Africa/Conakry") } 22 | val AFRICA_DAKAR by lazy { ZoneId.of("Africa/Dakar") } 23 | val AFRICA_DAR_ES_SALAAM by lazy { ZoneId.of("Africa/Dar_es_Salaam") } 24 | val AFRICA_DJIBOUTI by lazy { ZoneId.of("Africa/Djibouti") } 25 | val AFRICA_DOUALA by lazy { ZoneId.of("Africa/Douala") } 26 | val AFRICA_EL_AAIUN by lazy { ZoneId.of("Africa/El_Aaiun") } 27 | val AFRICA_FREETOWN by lazy { ZoneId.of("Africa/Freetown") } 28 | val AFRICA_GABORONE by lazy { ZoneId.of("Africa/Gaborone") } 29 | val AFRICA_HARARE by lazy { ZoneId.of("Africa/Harare") } 30 | val AFRICA_JOHANNESBURG by lazy { ZoneId.of("Africa/Johannesburg") } 31 | val AFRICA_JUBA by lazy { ZoneId.of("Africa/Juba") } 32 | val AFRICA_KAMPALA by lazy { ZoneId.of("Africa/Kampala") } 33 | val AFRICA_KHARTOUM by lazy { ZoneId.of("Africa/Khartoum") } 34 | val AFRICA_KIGALI by lazy { ZoneId.of("Africa/Kigali") } 35 | val AFRICA_KINSHASA by lazy { ZoneId.of("Africa/Kinshasa") } 36 | val AFRICA_LAGOS by lazy { ZoneId.of("Africa/Lagos") } 37 | val AFRICA_LIBREVILLE by lazy { ZoneId.of("Africa/Libreville") } 38 | val AFRICA_LUBUMBASHI by lazy { ZoneId.of("Africa/Lubumbashi") } 39 | val AFRICA_LUSAKA by lazy { ZoneId.of("Africa/Lusaka") } 40 | val AFRICA_MALABO by lazy { ZoneId.of("Africa/Malabo") } 41 | val AFRICA_MBABANE by lazy { ZoneId.of("Africa/Mbabane") } 42 | val AFRICA_MAPUTO by lazy { ZoneId.of("Africa/Maputo") } 43 | val AFRICA_MASERU by lazy { ZoneId.of("Africa/Maseru") } 44 | val AFRICA_MOGADISHU by lazy { ZoneId.of("Africa/Mogadishu") } 45 | val AFRICA_MONROVIA by lazy { ZoneId.of("Africa/Monrovia") } 46 | val AFRICA_NAIROBI by lazy { ZoneId.of("Africa/Nairobi") } 47 | val AFRICA_NDJAMENA by lazy { ZoneId.of("Africa/Ndjamena") } 48 | val AFRICA_NIAMEY by lazy { ZoneId.of("Africa/Niamey") } 49 | val AFRICA_NOUAKCHOTT by lazy { ZoneId.of("Africa/Nouakchott") } 50 | val AFRICA_OUAGADOUGOU by lazy { ZoneId.of("Africa/Ouagadougou") } 51 | val AFRICA_PORTO_NOVO by lazy { ZoneId.of("Africa/Porto-Novo") } 52 | val AFRICA_SAO_TOME by lazy { ZoneId.of("Africa/Sao_Tome") } 53 | val AFRICA_TUNIS by lazy { ZoneId.of("Africa/Tunis") } 54 | val AFRICA_TRIPOLI by lazy { ZoneId.of("Africa/Tripoli") } 55 | val AFRICA_WINDHOEK by lazy { ZoneId.of("Africa/Windhoek") } 56 | 57 | val AMERICA_ADAK by lazy { ZoneId.of("America/Adak") } 58 | val AMERICA_ANGUILLA by lazy { ZoneId.of("America/Anguilla") } 59 | val AMERICA_ANTIGUA by lazy { ZoneId.of("America/Antigua") } 60 | val AMERICA_ARAGUAINA by lazy { ZoneId.of("America/Araguaina") } 61 | val AMERICA_ARGENTINA_Catamarca by lazy { ZoneId.of("America/Argentina/Catamarca") } 62 | val AMERICA_ARGENTINA_ComodRivadavia by lazy { ZoneId.of("America/Argentina/ComodRivadavia") } 63 | val AMERICA_ARGENTINA_Cordoba by lazy { ZoneId.of("America/Argentina/Cordoba") } 64 | val AMERICA_ARGENTINA_Jujuy by lazy { ZoneId.of("America/Argentina/Jujuy") } 65 | val AMERICA_ARGENTINA_La_Rioja by lazy { ZoneId.of("America/Argentina/La_Rioja") } 66 | val AMERICA_ARGENTINA_Mendoza by lazy { ZoneId.of("America/Argentina/Mendoza") } 67 | val AMERICA_ARGENTINA_Rio_Gallegos by lazy { ZoneId.of("America/Argentina/Rio_Gallegos") } 68 | val AMERICA_ARGENTINA_Salta by lazy { ZoneId.of("America/Argentina/Salta") } 69 | val AMERICA_ARGENTINA_San_Juan by lazy { ZoneId.of("America/Argentina/San_Juan") } 70 | val AMERICA_ARGENTINA_San_Luis by lazy { ZoneId.of("America/Argentina/San_Luis") } 71 | val AMERICA_ARGENTINA_Ushuaia by lazy { ZoneId.of("America/Argentina/Ushuaia") } 72 | val AMERICA_ARGENTINA_Tucuman by lazy { ZoneId.of("America/Argentina/Tucuman") } 73 | val AMERICA_ARUBA by lazy { ZoneId.of("America/Aruba") } 74 | val AMERICA_BAHIA by lazy { ZoneId.of("America/Bahia") } 75 | val AMERICA_BAHIA_BANDERAS by lazy { ZoneId.of("America/Bahia_Banderas") } 76 | val AMERICA_BARBADOS by lazy { ZoneId.of("America/Barbados") } 77 | val AMERICA_BELIZE by lazy { ZoneId.of("America/Belize") } 78 | val AMERICA_BELEM by lazy { ZoneId.of("America/Belem") } 79 | val AMERICA_BLANC_SABLON by lazy { ZoneId.of("America/Blanc-Sablon") } 80 | val AMERICA_BOGOTA by lazy { ZoneId.of("America/Bogota") } 81 | val AMERICA_BOA_VISTA by lazy { ZoneId.of("America/Boa_Vista") } 82 | val AMERICA_BOISE by lazy { ZoneId.of("America/Boise") } 83 | val AMERICA_CAMBRIDGE_BAY by lazy { ZoneId.of("America/Cambridge_Bay") } 84 | val AMERICA_CAMPO_GRANDE by lazy { ZoneId.of("America/Campo_Grande") } 85 | val AMERICA_CANCUN by lazy { ZoneId.of("America/Cancun") } 86 | val AMERICA_CARACAS by lazy { ZoneId.of("America/Caracas") } 87 | val AMERICA_CATAMARCA by lazy { ZoneId.of("America/Catamarca") } 88 | val AMERICA_CIUDAD_JUAREZ by lazy { ZoneId.of("America/Ciudad_Juarez") } 89 | val AMERICA_CORDOBA by lazy { ZoneId.of("America/Cordoba") } 90 | val AMERICA_CURACAO by lazy { ZoneId.of("America/Curacao") } 91 | val AMERICA_CUIABA by lazy { ZoneId.of("America/Cuiaba") } 92 | val AMERICA_CAYENNE by lazy { ZoneId.of("America/Cayenne") } 93 | val AMERICA_CAYMAN by lazy { ZoneId.of("America/Cayman") } 94 | val AMERICA_CHICAGO by lazy { ZoneId.of("America/Chicago") } 95 | val AMERICA_CHIHUAHUA by lazy { ZoneId.of("America/Chihuahua") } 96 | val AMERICA_CRESTON by lazy { ZoneId.of("America/Creston") } 97 | val AMERICA_DANMARKSHAVN by lazy { ZoneId.of("America/Danmarkshavn") } 98 | val AMERICA_DAWSON by lazy { ZoneId.of("America/Dawson") } 99 | val AMERICA_DAWSON_CREEK by lazy { ZoneId.of("America/Dawson_Creek") } 100 | val AMERICA_DETROIT by lazy { ZoneId.of("America/Detroit") } 101 | val AMERICA_DOMINICA by lazy { ZoneId.of("America/Dominica") } 102 | val AMERICA_EDMONTON by lazy { ZoneId.of("America/Edmonton") } 103 | val AMERICA_EL_SALVADOR by lazy { ZoneId.of("America/El_Salvador") } 104 | val AMERICA_ENSENADA by lazy { ZoneId.of("America/Ensenada") } 105 | val AMERICA_EIRUNEPE by lazy { ZoneId.of("America/Eirunepe") } 106 | val AMERICA_FORTALEZA by lazy { ZoneId.of("America/Fortaleza") } 107 | val AMERICA_FORT_WAYNE by lazy { ZoneId.of("America/Fort_Wayne") } 108 | val AMERICA_FORT_NELSON by lazy { ZoneId.of("America/Fort_Nelson") } 109 | val AMERICA_GLACE_BAY by lazy { ZoneId.of("America/Glace_Bay") } 110 | val AMERICA_GODTHAB by lazy { ZoneId.of("America/Godthab") } 111 | val AMERICA_GRAND_TURK by lazy { ZoneId.of("America/Grand_Turk") } 112 | val AMERICA_GUADELOUPE by lazy { ZoneId.of("America/Guadeloupe") } 113 | val AMERICA_GUATEMALA by lazy { ZoneId.of("America/Guatemala") } 114 | val AMERICA_GUAYAQUIL by lazy { ZoneId.of("America/Guayaquil") } 115 | val AMERICA_GUYANA by lazy { ZoneId.of("America/Guyana") } 116 | val AMERICA_HALIFAX by lazy { ZoneId.of("America/Halifax") } 117 | val AMERICA_HAVANA by lazy { ZoneId.of("America/Havana") } 118 | val AMERICA_HERMOSILLO by lazy { ZoneId.of("America/Hermosillo") } 119 | val AMERICA_INDIANA_Indianapolis by lazy { ZoneId.of("America/Indiana/Indianapolis") } 120 | val AMERICA_INDIANA_Knox by lazy { ZoneId.of("America/Indiana/Knox") } 121 | val AMERICA_INDIANA_Marengo by lazy { ZoneId.of("America/Indiana/Marengo") } 122 | val AMERICA_INDIANA_Petersburg by lazy { ZoneId.of("America/Indiana/Petersburg") } 123 | val AMERICA_INDIANA_Tell_City by lazy { ZoneId.of("America/Indiana/Tell_City") } 124 | val AMERICA_INDIANA_Vevay by lazy { ZoneId.of("America/Indiana/Vevay") } 125 | val AMERICA_INDIANA_Vincennes by lazy { ZoneId.of("America/Indiana/Vincennes") } 126 | val AMERICA_INDIANAPOLIS by lazy { ZoneId.of("America/Indianapolis") } 127 | val AMERICA_INUVIK by lazy { ZoneId.of("America/Inuvik") } 128 | val AMERICA_IQALUIT by lazy { ZoneId.of("America/Iqaluit") } 129 | val AMERICA_JAMAICA by lazy { ZoneId.of("America/Jamaica") } 130 | val AMERICA_JUNEAU by lazy { ZoneId.of("America/Juneau") } 131 | val AMERICA_JUJUY by lazy { ZoneId.of("America/Jujuy") } 132 | val AMERICA_KENTUCKY_LOUISVILLE by lazy { ZoneId.of("America/Kentucky/Louisville") } 133 | val AMERICA_KENTUCKY_Monticello by lazy { ZoneId.of("America/Kentucky/Monticello") } 134 | val AMERICA_KNOX_IN by lazy { ZoneId.of("America/Knox_IN") } 135 | val AMERICA_KRALENDIJK by lazy { ZoneId.of("America/Kralendijk") } 136 | val AMERICA_LA_PAZ by lazy { ZoneId.of("America/La_Paz") } 137 | val AMERICA_LIMA by lazy { ZoneId.of("America/Lima") } 138 | val AMERICA_LOS_ANGELES by lazy { ZoneId.of("America/Los_Angeles") } 139 | val AMERICA_LOWER_PRINCES by lazy { ZoneId.of("America/Lower_Princes") } 140 | val AMERICA_MACEIO by lazy { ZoneId.of("America/Maceio") } 141 | val AMERICA_MANAGUA by lazy { ZoneId.of("America/Managua") } 142 | val AMERICA_MANAUS by lazy { ZoneId.of("America/Manaus") } 143 | val AMERICA_MARTINIQUE by lazy { ZoneId.of("America/Martinique") } 144 | val AMERICA_MATAMOROS by lazy { ZoneId.of("America/Matamoros") } 145 | val AMERICA_MAZATLAN by lazy { ZoneId.of("America/Mazatlan") } 146 | val AMERICA_MERIDA by lazy { ZoneId.of("America/Merida") } 147 | val AMERICA_METLAKATLA by lazy { ZoneId.of("America/Metlakatla") } 148 | val AMERICA_MEXICO_CITY by lazy { ZoneId.of("America/Mexico_City") } 149 | val AMERICA_MENDOZA by lazy { ZoneId.of("America/Mendoza") } 150 | val AMERICA_MONCTON by lazy { ZoneId.of("America/Moncton") } 151 | val AMERICA_MONTEVIDEO by lazy { ZoneId.of("America/Montevideo") } 152 | val AMERICA_MONTREAL by lazy { ZoneId.of("America/Montreal") } 153 | val AMERICA_MONTERREY by lazy { ZoneId.of("America/Monterrey") } 154 | val AMERICA_NASSAU by lazy { ZoneId.of("America/Nassau") } 155 | val AMERICA_NORONHA by lazy { ZoneId.of("America/Noronha") } 156 | val AMERICA_NOME by lazy { ZoneId.of("America/Nome") } 157 | val AMERICA_NORTH_DAKOTA_Beulah by lazy { ZoneId.of("America/North_Dakota/Beulah") } 158 | val AMERICA_NORTH_DAKOTA_Center by lazy { ZoneId.of("America/North_Dakota/Center") } 159 | val AMERICA_NORTH_DAKOTA_New_Salem by lazy { ZoneId.of("America/North_Dakota/New_Salem") } 160 | val AMERICA_NIPIGON by lazy { ZoneId.of("America/Nipigon") } 161 | val AMERICA_NUUK by lazy { ZoneId.of("America/Nuuk") } 162 | val AMERICA_NEW_YORK by lazy { ZoneId.of("America/New_York") } 163 | val AMERICA_OJINAGA by lazy { ZoneId.of("America/Ojinaga") } 164 | val AMERICA_PORTO_ACRE by lazy { ZoneId.of("America/Porto_Acre") } 165 | val AMERICA_PORT_OF_SPAIN by lazy { ZoneId.of("America/Port_of_Spain") } 166 | val AMERICA_PORTO_VELHO by lazy { ZoneId.of("America/Porto_Velho") } 167 | val AMERICA_PANAMA by lazy { ZoneId.of("America/Panama") } 168 | val AMERICA_PARAMARIBO by lazy { ZoneId.of("America/Paramaribo") } 169 | val AMERICA_PHOENIX by lazy { ZoneId.of("America/Phoenix") } 170 | val AMERICA_PUERTO_RICO by lazy { ZoneId.of("America/Puerto_Rico") } 171 | val AMERICA_PUNTA_ARENAS by lazy { ZoneId.of("America/Punta_Arenas") } 172 | val AMERICA_PANGNIRTUNG by lazy { ZoneId.of("America/Pangnirtung") } 173 | val AMERICA_RECIFE by lazy { ZoneId.of("America/Recife") } 174 | val AMERICA_REGINA by lazy { ZoneId.of("America/Regina") } 175 | val AMERICA_RESOLUTE by lazy { ZoneId.of("America/Resolute") } 176 | val AMERICA_ROSARIO by lazy { ZoneId.of("America/Rosario") } 177 | val AMERICA_RAINY_RIVER by lazy { ZoneId.of("America/Rainy_River") } 178 | val AMERICA_RANKIN_INLET by lazy { ZoneId.of("America/Rankin_Inlet") } 179 | val AMERICA_RIO_BRANCO by lazy { ZoneId.of("America/Rio_Branco") } 180 | val AMERICA_SANTIAGO by lazy { ZoneId.of("America/Santiago") } 181 | val AMERICA_SANTO_DOMINGO by lazy { ZoneId.of("America/Santo_Domingo") } 182 | val AMERICA_SAO_PAULO by lazy { ZoneId.of("America/Sao_Paulo") } 183 | val AMERICA_SANTA_ISABEL by lazy { ZoneId.of("America/Santa_Isabel") } 184 | val AMERICA_SANTAREM by lazy { ZoneId.of("America/Santarem") } 185 | val AMERICA_ST_BARTHELEMY by lazy { ZoneId.of("America/St_Barthelemy") } 186 | val AMERICA_ST_JOHNS by lazy { ZoneId.of("America/St_Johns") } 187 | val AMERICA_ST_KITTS by lazy { ZoneId.of("America/St_Kitts") } 188 | val AMERICA_ST_LUCIA by lazy { ZoneId.of("America/St_Lucia") } 189 | val AMERICA_ST_THOMAS by lazy { ZoneId.of("America/St_Thomas") } 190 | val AMERICA_ST_VINCENT by lazy { ZoneId.of("America/St_Vincent") } 191 | val AMERICA_SWIFT_CURRENT by lazy { ZoneId.of("America/Swift_Current") } 192 | val AMERICA_THUNDER_BAY by lazy { ZoneId.of("America/Thunder_Bay") } 193 | val AMERICA_TEGUCIGALPA by lazy { ZoneId.of("America/Tegucigalpa") } 194 | val AMERICA_TIJUANA by lazy { ZoneId.of("America/Tijuana") } 195 | val AMERICA_TORONTO by lazy { ZoneId.of("America/Toronto") } 196 | val AMERICA_TORTOLA by lazy { ZoneId.of("America/Tortola") } 197 | val AMERICA_VANCOUVER by lazy { ZoneId.of("America/Vancouver") } 198 | val AMERICA_VIRGIN by lazy { ZoneId.of("America/Virgin") } 199 | val AMERICA_WHITEHORSE by lazy { ZoneId.of("America/Whitehorse") } 200 | val AMERICA_YAKUTAT by lazy { ZoneId.of("America/Yakutat") } 201 | val AMERICA_YELLOWKNIFE by lazy { ZoneId.of("America/Yellowknife") } 202 | 203 | val ANTARCTICA_CASEY by lazy { ZoneId.of("Antarctica/Casey") } 204 | val ANTARCTICA_DAVIS by lazy { ZoneId.of("Antarctica/Davis") } 205 | val ANTARCTICA_DUMONTDURVILLE by lazy { ZoneId.of("Antarctica/DumontDUrville") } 206 | val ANTARCTICA_MACQUARIE by lazy { ZoneId.of("Antarctica/Macquarie") } 207 | val ANTARCTICA_MAWSON by lazy { ZoneId.of("Antarctica/Mawson") } 208 | val ANTARCTICA_MCMURDO by lazy { ZoneId.of("Antarctica/McMurdo") } 209 | val ANTARCTICA_PALMER by lazy { ZoneId.of("Antarctica/Palmer") } 210 | val ANTARCTICA_ROTHERA by lazy { ZoneId.of("Antarctica/Rothera") } 211 | val ANTARCTICA_SOUTH_POLE by lazy { ZoneId.of("Antarctica/South_Pole") } 212 | val ANTARCTICA_SYOWA by lazy { ZoneId.of("Antarctica/Syowa") } 213 | val ANTARCTICA_TROLL by lazy { ZoneId.of("Antarctica/Troll") } 214 | val ANTARCTICA_VOSTOK by lazy { ZoneId.of("Antarctica/Vostok") } 215 | 216 | val ARCTIC_LONGYEARBYEN by lazy { ZoneId.of("Arctic/Longyearbyen") } 217 | 218 | val ATLANTIC_AZORES by lazy { ZoneId.of("Atlantic/Azores") } 219 | val ATLANTIC_BERMUDA by lazy { ZoneId.of("Atlantic/Bermuda") } 220 | val ATLANTIC_CANARY by lazy { ZoneId.of("Atlantic/Canary") } 221 | val ATLANTIC_CAPE_VERDE by lazy { ZoneId.of("Atlantic/Cape_Verde") } 222 | val ATLANTIC_FAEROE by lazy { ZoneId.of("Atlantic/Faeroe") } 223 | val ATLANTIC_FAROE by lazy { ZoneId.of("Atlantic/Faroe") } 224 | val ATLANTIC_JAN_MAYEN by lazy { ZoneId.of("Atlantic/Jan_Mayen") } 225 | val ATLANTIC_MADEIRA by lazy { ZoneId.of("Atlantic/Madeira") } 226 | val ATLANTIC_REYKJAVIK by lazy { ZoneId.of("Atlantic/Reykjavik") } 227 | val ATLANTIC_ST_HELENA by lazy { ZoneId.of("Atlantic/St_Helena") } 228 | val ATLANTIC_STANLEY by lazy { ZoneId.of("Atlantic/Stanley") } 229 | 230 | val AUSTRALIA_ACT by lazy { ZoneId.of("Australia/ACT") } 231 | val AUSTRALIA_ADELAIDE by lazy { ZoneId.of("Australia/Adelaide") } 232 | val AUSTRALIA_BROKEN_HILL by lazy { ZoneId.of("Australia/Broken_Hill") } 233 | val AUSTRALIA_BRISBANE by lazy { ZoneId.of("Australia/Brisbane") } 234 | val AUSTRALIA_CANBERRA by lazy { ZoneId.of("Australia/Canberra") } 235 | val AUSTRALIA_CURRIE by lazy { ZoneId.of("Australia/Currie") } 236 | val AUSTRALIA_DARWIN by lazy { ZoneId.of("Australia/Darwin") } 237 | val AUSTRALIA_EUCLA by lazy { ZoneId.of("Australia/Eucla") } 238 | val AUSTRALIA_HOBART by lazy { ZoneId.of("Australia/Hobart") } 239 | val AUSTRALIA_LHI by lazy { ZoneId.of("Australia/LHI") } 240 | val AUSTRALIA_LINDEMAN by lazy { ZoneId.of("Australia/Lindeman") } 241 | val AUSTRALIA_LORD_HOWE by lazy { ZoneId.of("Australia/Lord_Howe") } 242 | val AUSTRALIA_MELBOURNE by lazy { ZoneId.of("Australia/Melbourne") } 243 | val AUSTRALIA_NORTH by lazy { ZoneId.of("Australia/North") } 244 | val AUSTRALIA_NSW by lazy { ZoneId.of("Australia/NSW") } 245 | val AUSTRALIA_PERTH by lazy { ZoneId.of("Australia/Perth") } 246 | val AUSTRALIA_QUEENSLAND by lazy { ZoneId.of("Australia/Queensland") } 247 | val AUSTRALIA_SOUTH by lazy { ZoneId.of("Australia/South") } 248 | val AUSTRALIA_TASMANIA by lazy { ZoneId.of("Australia/Tasmania") } 249 | val AUSTRALIA_VICTORIA by lazy { ZoneId.of("Australia/Victoria") } 250 | val AUSTRALIA_WEST by lazy { ZoneId.of("Australia/West") } 251 | 252 | val BRAZIL_ACRE by lazy { ZoneId.of("Brazil/Acre") } 253 | val BRAZIL_DENORONHA by lazy { ZoneId.of("Brazil/DeNoronha") } 254 | val BRAZIL_EAST by lazy { ZoneId.of("Brazil/East") } 255 | val BRAZIL_WEST by lazy { ZoneId.of("Brazil/West") } 256 | 257 | val CANADA_ATLANTIC by lazy { ZoneId.of("Canada/Atlantic") } 258 | val CANADA_CENTRAL by lazy { ZoneId.of("Canada/Central") } 259 | val CANADA_EASTERN by lazy { ZoneId.of("Canada/Eastern") } 260 | val CANADA_NEWFOUNDLAND by lazy { ZoneId.of("Canada/Newfoundland") } 261 | val CANADA_PACIFIC by lazy { ZoneId.of("Canada/Pacific") } 262 | val CANADA_SASKATCHEWAN by lazy { ZoneId.of("Canada/Saskatchewan") } 263 | val CANADA_MOUNTAIN by lazy { ZoneId.of("Canada/Mountain") } 264 | val CANADA_YUKON by lazy { ZoneId.of("Canada/Yukon") } 265 | 266 | val CHILE_CONTINENTAL by lazy { ZoneId.of("Chile/Continental") } 267 | val CHILE_EASTERISLAND by lazy { ZoneId.of("Chile/EasterIsland") } 268 | 269 | val CST6CDT by lazy { ZoneId.of("CST6CDT") } 270 | 271 | val CUBA by lazy { ZoneId.of("Cuba") } 272 | 273 | val CET by lazy { ZoneId.of("CET") } 274 | 275 | val EET by lazy { ZoneId.of("EET") } 276 | val EIRE by lazy { ZoneId.of("Eire") } 277 | val EST5EDT by lazy { ZoneId.of("EST5EDT") } 278 | 279 | val EGYPT by lazy { ZoneId.of("Egypt") } 280 | 281 | val EUROPE_AMSTERDAM by lazy { ZoneId.of("Europe/Amsterdam") } 282 | val EUROPE_ANDORRA by lazy { ZoneId.of("Europe/Andorra") } 283 | val EUROPE_ATHENS by lazy { ZoneId.of("Europe/Athens") } 284 | val EUROPE_ASTRAKHAN by lazy { ZoneId.of("Europe/Astrakhan") } 285 | val EUROPE_BELGRADE by lazy { ZoneId.of("Europe/Belgrade") } 286 | val EUROPE_BELFAST by lazy { ZoneId.of("Europe/Belfast") } 287 | val EUROPE_BERLIN by lazy { ZoneId.of("Europe/Berlin") } 288 | val EUROPE_BUDAPEST by lazy { ZoneId.of("Europe/Budapest") } 289 | val EUROPE_BUCHAREST by lazy { ZoneId.of("Europe/Bucharest") } 290 | val EUROPE_BRATISLAVA by lazy { ZoneId.of("Europe/Bratislava") } 291 | val EUROPE_BRUSSELS by lazy { ZoneId.of("Europe/Brussels") } 292 | val EUROPE_BUSINGEN by lazy { ZoneId.of("Europe/Busingen") } 293 | val EUROPE_COPENHAGEN by lazy { ZoneId.of("Europe/Copenhagen") } 294 | val EUROPE_CHISINAU by lazy { ZoneId.of("Europe/Chisinau") } 295 | val EUROPE_DUBLIN by lazy { ZoneId.of("Europe/Dublin") } 296 | val EUROPE_GIBRALTAR by lazy { ZoneId.of("Europe/Gibraltar") } 297 | val EUROPE_GUERNSEY by lazy { ZoneId.of("Europe/Guernsey") } 298 | val EUROPE_HELSINKI by lazy { ZoneId.of("Europe/Helsinki") } 299 | val EUROPE_ISTANBUL by lazy { ZoneId.of("Europe/Istanbul") } 300 | val EUROPE_ISLE_OF_MAN by lazy { ZoneId.of("Europe/Isle_of_Man") } 301 | val EUROPE_JERSEY by lazy { ZoneId.of("Europe/Jersey") } 302 | val EUROPE_KALININGRAD by lazy { ZoneId.of("Europe/Kaliningrad") } 303 | val EUROPE_KIEV by lazy { ZoneId.of("Europe/Kiev") } 304 | val EUROPE_KIROV by lazy { ZoneId.of("Europe/Kirov") } 305 | val EUROPE_KIEV_ALT by lazy { ZoneId.of("Europe/Kyiv") } 306 | val EUROPE_KYIV by lazy { ZoneId.of("Europe/Kyiv") } 307 | val EUROPE_LISBON by lazy { ZoneId.of("Europe/Lisbon") } 308 | val EUROPE_LJUBLJANA by lazy { ZoneId.of("Europe/Ljubljana") } 309 | val EUROPE_LONDON by lazy { ZoneId.of("Europe/London") } 310 | val EUROPE_LUXEMBOURG by lazy { ZoneId.of("Europe/Luxembourg") } 311 | val EUROPE_MADRID by lazy { ZoneId.of("Europe/Madrid") } 312 | val EUROPE_MALTA by lazy { ZoneId.of("Europe/Malta") } 313 | val EUROPE_MARIEHAMN by lazy { ZoneId.of("Europe/Mariehamn") } 314 | val EUROPE_MINSK by lazy { ZoneId.of("Europe/Minsk") } 315 | val EUROPE_MONACO by lazy { ZoneId.of("Europe/Monaco") } 316 | val EUROPE_MOSCOW by lazy { ZoneId.of("Europe/Moscow") } 317 | val EUROPE_NICOSIA by lazy { ZoneId.of("Europe/Nicosia") } 318 | val EUROPE_NICOSIA_ALT by lazy { ZoneId.of("Europe/Nicosia") } 319 | val EUROPE_OSLO by lazy { ZoneId.of("Europe/Oslo") } 320 | val EUROPE_PARIS by lazy { ZoneId.of("Europe/Paris") } 321 | val EUROPE_PODGORICA by lazy { ZoneId.of("Europe/Podgorica") } 322 | val EUROPE_PRAGUE by lazy { ZoneId.of("Europe/Prague") } 323 | val EUROPE_RIGA by lazy { ZoneId.of("Europe/Riga") } 324 | val EUROPE_ROME by lazy { ZoneId.of("Europe/Rome") } 325 | val EUROPE_SARAJEVO by lazy { ZoneId.of("Europe/Sarajevo") } 326 | val EUROPE_SAMARA by lazy { ZoneId.of("Europe/Samara") } 327 | val EUROPE_SAN_MARINO by lazy { ZoneId.of("Europe/San_Marino") } 328 | val EUROPE_SIMFEROPOL by lazy { ZoneId.of("Europe/Simferopol") } 329 | val EUROPE_SKOPJE by lazy { ZoneId.of("Europe/Skopje") } 330 | val EUROPE_SOFIA by lazy { ZoneId.of("Europe/Sofia") } 331 | val EUROPE_STOCKHOLM by lazy { ZoneId.of("Europe/Stockholm") } 332 | val EUROPE_TALLINN by lazy { ZoneId.of("Europe/Tallinn") } 333 | val EUROPE_TIRASPOL by lazy { ZoneId.of("Europe/Tiraspol") } 334 | val EUROPE_TIRANE by lazy { ZoneId.of("Europe/Tirane") } 335 | val EUROPE_ULYANOVSK by lazy { ZoneId.of("Europe/Ulyanovsk") } 336 | val EUROPE_UZHGOROD by lazy { ZoneId.of("Europe/Uzhgorod") } 337 | val EUROPE_VADUZ by lazy { ZoneId.of("Europe/Vaduz") } 338 | val EUROPE_VATICAN by lazy { ZoneId.of("Europe/Vatican") } 339 | val EUROPE_VIENNA by lazy { ZoneId.of("Europe/Vienna") } 340 | val EUROPE_VILNIUS by lazy { ZoneId.of("Europe/Vilnius") } 341 | val EUROPE_VOLGOGRAD by lazy { ZoneId.of("Europe/Volgograd") } 342 | val EUROPE_ZAGREB by lazy { ZoneId.of("Europe/Zagreb") } 343 | val EUROPE_ZAPOROZHYE by lazy { ZoneId.of("Europe/Zaporozhye") } 344 | 345 | val GB by lazy { ZoneId.of("GB") } 346 | val GB_EIRE by lazy { ZoneId.of("GB-Eire") } 347 | 348 | val GMT by lazy { ZoneId.of("GMT") } 349 | val GREENWICH by lazy { ZoneId.of("Greenwich") } 350 | 351 | val HONGKONG by lazy { ZoneId.of("Hongkong") } 352 | 353 | val ICELAND by lazy { ZoneId.of("Iceland") } 354 | 355 | val INDIAN_CHAGOS by lazy { ZoneId.of("Indian/Chagos") } 356 | val INDIAN_CHRISTMAS by lazy { ZoneId.of("Indian/Christmas") } 357 | val INDIAN_COCOS by lazy { ZoneId.of("Indian/Cocos") } 358 | val INDIAN_KERGUELEN by lazy { ZoneId.of("Indian/Kerguelen") } 359 | val INDIAN_MAHE by lazy { ZoneId.of("Indian/Mahe") } 360 | val INDIAN_MALDIVES by lazy { ZoneId.of("Indian/Maldives") } 361 | val INDIAN_MAYOTTE by lazy { ZoneId.of("Indian/Mayotte") } 362 | val INDIAN_REUNION by lazy { ZoneId.of("Indian/Reunion") } 363 | 364 | val IRAN by lazy { ZoneId.of("Iran") } 365 | 366 | val ISRAEL by lazy { ZoneId.of("Israel") } 367 | 368 | val JAPAN by lazy { ZoneId.of("Japan") } 369 | 370 | val JAMAICA by lazy { ZoneId.of("Jamaica") } 371 | 372 | val KWAJALEIN by lazy { ZoneId.of("Kwajalein") } 373 | 374 | val LIBYA by lazy { ZoneId.of("Libya") } 375 | 376 | val MET by lazy { ZoneId.of("MET") } 377 | 378 | val MEXICO_BAJANORTE by lazy { ZoneId.of("Mexico/BajaNorte") } 379 | val MEXICO_BAJASUR by lazy { ZoneId.of("Mexico/BajaSur") } 380 | val MEXICO_GENERAL by lazy { ZoneId.of("Mexico/General") } 381 | 382 | val NAVAJO by lazy { ZoneId.of("Navajo") } 383 | 384 | val NZ by lazy { ZoneId.of("NZ") } 385 | val NZ_CHAT by lazy { ZoneId.of("NZ-CHAT") } 386 | 387 | val POLAND by lazy { ZoneId.of("Poland") } 388 | 389 | val PORTUGAL by lazy { ZoneId.of("Portugal") } 390 | 391 | val PRC by lazy { ZoneId.of("PRC") } 392 | 393 | val PST8PDT by lazy { ZoneId.of("PST8PDT") } 394 | 395 | val ROK by lazy { ZoneId.of("ROK") } 396 | 397 | val SINGAPORE by lazy { ZoneId.of("Singapore") } 398 | 399 | val SYSTEMV_AST4 by lazy { ZoneId.of("SystemV/AST4") } 400 | val SYSTEMV_AST4ADT by lazy { ZoneId.of("SystemV/AST4ADT") } 401 | val SYSTEMV_CST6 by lazy { ZoneId.of("SystemV/CST6") } 402 | val SYSTEMV_CST6CDT by lazy { ZoneId.of("SystemV/CST6CDT") } 403 | val SYSTEMV_EST5EDT by lazy { ZoneId.of("SystemV/EST5EDT") } 404 | val SYSTEMV_HST10 by lazy { ZoneId.of("SystemV/HST10") } 405 | val SYSTEMV_MST7 by lazy { ZoneId.of("SystemV/MST7") } 406 | val SYSTEMV_MST7MDT by lazy { ZoneId.of("SystemV/MST7MDT") } 407 | val SYSTEMV_PST8 by lazy { ZoneId.of("SystemV/PST8") } 408 | val SYSTEMV_PST8PDT by lazy { ZoneId.of("SystemV/PST8PDT") } 409 | val SYSTEMV_YST9 by lazy { ZoneId.of("SystemV/YST9") } 410 | val SYSTEMV_YST9YDT by lazy { ZoneId.of("SystemV/YST9YDT") } 411 | 412 | val TURKEY by lazy { ZoneId.of("Turkey") } 413 | 414 | val US_ALEUTIAN by lazy { ZoneId.of("US/Aleutian") } 415 | val US_ALASKA by lazy { ZoneId.of("US/Alaska") } 416 | val US_ARIZONA by lazy { ZoneId.of("US/Arizona") } 417 | val US_CENTRAL by lazy { ZoneId.of("US/Central") } 418 | val US_EASTERN by lazy { ZoneId.of("US/Eastern") } 419 | val US_EAST_Indiana by lazy { ZoneId.of("US/East-Indiana") } 420 | val US_HAWAII by lazy { ZoneId.of("US/Hawaii") } 421 | val US_INDIANA_Starke by lazy { ZoneId.of("US/Indiana-Starke") } 422 | val US_MICHIGAN by lazy { ZoneId.of("US/Michigan") } 423 | val US_MOUNTAIN by lazy { ZoneId.of("US/Mountain") } 424 | val US_PACIFIC by lazy { ZoneId.of("US/Pacific") } 425 | val US_SAMOA by lazy { ZoneId.of("US/Samoa") } 426 | 427 | val UTC by lazy { ZoneId.of("UTC") } 428 | 429 | val UCT by lazy { ZoneId.of("UCT") } 430 | 431 | val UNIVERSAL by lazy { ZoneId.of("Universal") } 432 | 433 | val WET by lazy { ZoneId.of("WET") } 434 | val W_SU by lazy { ZoneId.of("W-SU") } 435 | 436 | val ZULU by lazy { ZoneId.of("Zulu") } 437 | } 438 | -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/calendar/extensions/CalendarMutatingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.calendar.extensions 2 | 3 | import java.time.LocalDate 4 | import java.time.LocalDateTime 5 | import java.time.LocalTime 6 | import java.time.ZoneId 7 | import java.time.ZonedDateTime 8 | import java.util.Calendar 9 | 10 | fun Calendar.toZonedDateTime(): ZonedDateTime = 11 | ZonedDateTime.ofInstant(this.toInstant(), ZoneId.of(this.timeZone.id)) 12 | 13 | fun Calendar.toLocalDateTime(): LocalDateTime = 14 | LocalDateTime.ofInstant(this.toInstant(), ZoneId.of(this.timeZone.id)) 15 | 16 | fun Calendar.toLocalDate(): LocalDate = 17 | LocalDate.ofInstant(this.toInstant(), ZoneId.of(this.timeZone.id)) 18 | 19 | fun Calendar.toLocalTime(): LocalTime = 20 | this.toLocalDateTime().toLocalTime() 21 | -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/date/extensions/DateMutatingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.date.extensions 2 | 3 | import java.time.LocalDate 4 | import java.time.LocalDateTime 5 | import java.time.LocalTime 6 | import java.time.ZoneId 7 | import java.time.ZonedDateTime 8 | import java.util.Date 9 | 10 | fun Date.toZonedDateTime(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = 11 | ZonedDateTime.ofInstant(this.toInstant(), zoneId) 12 | 13 | fun Date.toLocalDateTime(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = 14 | LocalDateTime.ofInstant(this.toInstant(), zoneId) 15 | 16 | fun Date.toLocalDate(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = 17 | LocalDate.ofInstant(this.toInstant(), zoneId) 18 | 19 | fun Date.toLocalTime(zoneId: ZoneId = ZoneId.systemDefault()): LocalTime = 20 | this.toLocalDateTime(zoneId).toLocalTime() 21 | -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/LocalDateFactory.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate 2 | 3 | import java.time.Instant 4 | import java.time.LocalDate 5 | import java.time.Month 6 | import java.time.ZoneId 7 | 8 | /** 9 | * Contains helper functions that only serve to create new LocalDates. 10 | * Creation methods do not include parsing methods. 11 | */ 12 | object LocalDateFactory { 13 | 14 | /** 15 | * @param year Year, ie, 2020. 16 | * @param month Month with range 1-12, i.e. 1 for January. 17 | * @param day Day of the month with range 1-31. 18 | * @return LocalDate. 19 | */ 20 | fun new(year: Int, month: Int, day: Int): LocalDate = 21 | LocalDate.of(year, Month.of(month), day) 22 | 23 | /** 24 | * @param epochMilliseconds Epoch time, aka Unix time, are seconds elapsed since January 1st 1970 at 00:00:00 UTC. 25 | * @return LocalDate. 26 | */ 27 | fun new(epochMilliseconds: Long, zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = 28 | LocalDate.ofInstant(Instant.ofEpochMilli(epochMilliseconds), zoneId) 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/LocalDates.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate 2 | 3 | import javatimefun.localdate.extensions.getLast 4 | import javatimefun.localdate.extensions.getNext 5 | import java.time.DayOfWeek 6 | import java.time.LocalDate 7 | import java.time.ZoneId 8 | 9 | object LocalDates { 10 | fun now(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = LocalDate.now(zoneId) 11 | fun today(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = now(zoneId) 12 | fun yesterday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().minusDays(1) 13 | fun tomorrow(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().plusDays(1) 14 | 15 | fun lastMonday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.MONDAY) 16 | fun lastTuesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.TUESDAY) 17 | fun lastWednesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.WEDNESDAY) 18 | fun lastThursday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.THURSDAY) 19 | fun lastFriday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.FRIDAY) 20 | fun lastSaturday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.SATURDAY) 21 | fun lastSunday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getLast(DayOfWeek.SUNDAY) 22 | 23 | fun nextMonday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.MONDAY) 24 | fun nextTuesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.TUESDAY) 25 | fun nextWednesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.WEDNESDAY) 26 | fun nextThursday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.THURSDAY) 27 | fun nextFriday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.FRIDAY) 28 | fun nextSaturday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.SATURDAY) 29 | fun nextSunday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = today().getNext(DayOfWeek.SUNDAY) 30 | 31 | fun startOfYear(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = 32 | LocalDateFactory.new(today(zoneId).year, 1, 1) 33 | fun endOfYear(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate = 34 | LocalDateFactory.new(today(zoneId).year, 12, 31) 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/extensions/LocalDateAttributeExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate.extensions 2 | 3 | import java.time.LocalDate 4 | import java.time.Year 5 | 6 | fun LocalDate.isInLeapYear(): Boolean = Year.of(year).isLeap 7 | 8 | fun LocalDate.getMonthBaseZero(): Int = this.monthValue - 1 9 | 10 | fun LocalDate.getDaysInMonth(): Int = this.month.length(isInLeapYear()) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/extensions/LocalDateComparisonExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate.extensions 2 | 3 | import java.time.Duration 4 | import java.time.LocalDate 5 | import java.time.temporal.ChronoUnit 6 | 7 | // region Day Comparisons 8 | fun LocalDate.compareDay(toDate: LocalDate): Int { 9 | val dayDifference = this.getDayDifference(toDate) 10 | return when { 11 | dayDifference > 0 -> -1 12 | dayDifference < 0 -> 1 13 | else -> 0 14 | } 15 | } 16 | 17 | fun LocalDate.isEqualDay(b: LocalDate): Boolean = compareDay(b) == 0 18 | 19 | fun LocalDate.isBeforeDay(b: LocalDate): Boolean = compareDay(b) < 0 20 | 21 | fun LocalDate.isBeforeEqualDay(b: LocalDate): Boolean = compareDay(b) <= 0 22 | 23 | fun LocalDate.isAfterDay(b: LocalDate): Boolean = compareDay(b) > 0 24 | 25 | fun LocalDate.isAfterEqualDay(b: LocalDate): Boolean = compareDay(b) >= 0 26 | // endregion 27 | 28 | // region Month Comparisons 29 | fun LocalDate.compareMonth(localDateB: LocalDate): Int = 30 | when { 31 | isBeforeMonth(localDateB) -> -1 32 | isEqualMonth(localDateB) -> 0 33 | else -> 1 34 | } 35 | 36 | fun LocalDate.isBeforeMonth(localDateB: LocalDate): Boolean { 37 | if (this.year > localDateB.year) return false 38 | if (this.year < localDateB.year) return true 39 | return this.month < localDateB.month 40 | } 41 | 42 | fun LocalDate.isBeforeEqualMonth(localDateB: LocalDate): Boolean { 43 | if (this.year > localDateB.year) return false 44 | if (this.year < localDateB.year) return true 45 | return this.month <= localDateB.month 46 | } 47 | 48 | fun LocalDate.isEqualMonth(localDateB: LocalDate): Boolean = 49 | this.year == localDateB.year && this.month == localDateB.month 50 | 51 | fun LocalDate.isAfterEqualMonth(localDateB: LocalDate): Boolean { 52 | if (this.year > localDateB.year) return true 53 | if (this.year < localDateB.year) return false 54 | return this.month >= localDateB.month 55 | } 56 | 57 | fun LocalDate.isAfterMonth(localDateB: LocalDate): Boolean { 58 | if (this.year > localDateB.year) return true 59 | if (this.year < localDateB.year) return false 60 | return this.month > localDateB.month 61 | } 62 | // endregion 63 | 64 | // region Year Comparisons 65 | fun LocalDate.compareYear(localDateB: LocalDate): Int = this.year.compareTo(localDateB.year) 66 | 67 | fun LocalDate.isBeforeYear(localDateB: LocalDate): Boolean = this.year < localDateB.year 68 | 69 | fun LocalDate.isBeforeEqualYear(localDateB: LocalDate): Boolean = this.year <= localDateB.year 70 | 71 | fun LocalDate.isEqualYear(localDateB: LocalDate): Boolean = this.year == localDateB.year 72 | 73 | fun LocalDate.isAfterEqualYear(localDateB: LocalDate): Boolean = this.year >= localDateB.year 74 | 75 | fun LocalDate.isAfterYear(localDateB: LocalDate): Boolean = this.year > localDateB.year 76 | // endregion 77 | 78 | fun LocalDate.getSecondDifference(localDateB: LocalDate): Long = 79 | Duration.between(this.atStartOfDay(), localDateB.atStartOfDay()).toSeconds() 80 | 81 | fun LocalDate.getMinuteDifference(localDateB: LocalDate): Long = 82 | Duration.between(this.atStartOfDay(), localDateB.atStartOfDay()).toMinutes() 83 | 84 | fun LocalDate.getHourDifference(localDateB: LocalDate): Long = 85 | Duration.between(this.atStartOfDay(), localDateB.atStartOfDay()).toHours() 86 | 87 | fun LocalDate.getDayDifference(localDateB: LocalDate): Long = 88 | ChronoUnit.DAYS.between(this, localDateB) 89 | 90 | fun LocalDate.getMonthDifference(localDateB: LocalDate): Long = 91 | ChronoUnit.MONTHS.between(this, localDateB) 92 | 93 | fun LocalDate.getYearDifference(localDateB: LocalDate): Long = 94 | ChronoUnit.YEARS.between(this, localDateB) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/extensions/LocalDateMutatingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate.extensions 2 | 3 | import java.time.DayOfWeek 4 | import java.time.LocalDate 5 | import java.time.LocalDateTime 6 | import java.time.LocalTime 7 | 8 | fun LocalDate.getLast(dayOfWeek: DayOfWeek, countingInThisDay: Boolean = false): LocalDate { 9 | if (countingInThisDay && this.dayOfWeek == dayOfWeek) { 10 | return this 11 | } 12 | var mostRecentDay = this 13 | if (mostRecentDay.dayOfWeek == dayOfWeek) { 14 | mostRecentDay = mostRecentDay.minusDays(1) 15 | } 16 | while (mostRecentDay.dayOfWeek != dayOfWeek) { 17 | mostRecentDay = mostRecentDay.minusDays(1) 18 | } 19 | return mostRecentDay 20 | } 21 | 22 | fun LocalDate.getNext(dayOfWeek: DayOfWeek, countingInThisDay: Boolean = false): LocalDate { 23 | if (countingInThisDay && this.dayOfWeek == dayOfWeek) { 24 | return this 25 | } 26 | var nextLocalDate = this 27 | if (nextLocalDate.dayOfWeek == dayOfWeek) { 28 | nextLocalDate = nextLocalDate.plusDays(1) 29 | } 30 | while (nextLocalDate.dayOfWeek != dayOfWeek) { 31 | nextLocalDate = nextLocalDate.plusDays(1) 32 | } 33 | return nextLocalDate 34 | } 35 | 36 | fun LocalDate.atEndOfDay(): LocalDateTime = LocalDateTime.of(this, LocalTime.MAX) 37 | -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/extensions/LocalDateParsingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate.extensions 2 | 3 | import java.time.LocalDate 4 | import java.time.format.DateTimeFormatter 5 | import java.time.format.DateTimeParseException 6 | 7 | /** 8 | * Works off of String representations of date, without time, nor time zone. 9 | * When a format is present, it'll try parsing using that format alone, & return null if it fails. 10 | * 11 | * @param this String representation of LocalDate. 12 | * @param format String representing format that should solely be used when parsing the date. 13 | * @return LocalDate? Null means couldn't parse, else parsed LocalDate. 14 | */ 15 | fun String.toLocalDate(format: String? = null): LocalDate? = 16 | if (format.isNullOrEmpty()) { 17 | try { 18 | LocalDate.parse(this) 19 | } catch (e: DateTimeParseException) { 20 | null 21 | } catch (e: IllegalArgumentException) { 22 | null 23 | } 24 | } else { 25 | try { 26 | LocalDate.parse(this, DateTimeFormatter.ofPattern(format)) 27 | } catch (e: DateTimeParseException) { 28 | null 29 | } catch (e: IllegalArgumentException) { 30 | null 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdate/extensions/LocalDatePrintExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdate.extensions 2 | 3 | import java.time.LocalDate 4 | import java.time.format.DateTimeFormatterBuilder 5 | import java.util.Locale 6 | 7 | fun LocalDate.print(format: String, locale: Locale = Locale.US): String = 8 | this.format(DateTimeFormatterBuilder().appendPattern(format).toFormatter(locale)) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/LocalDateTimeFactory.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime 2 | 3 | import java.time.Instant 4 | import java.time.LocalDateTime 5 | import java.time.Month 6 | import java.time.ZoneId 7 | 8 | /** 9 | * Contains helper functions that only serve to create new LocalDateTimes. 10 | * Creation methods do not include parsing methods. 11 | */ 12 | object LocalDateTimeFactory { 13 | 14 | /** 15 | * @param year Year, ie, 2020. 16 | * @param month Month with range 1-12, i.e. 1 for January. 17 | * @param day Day of the month with range 1-31. 18 | * @param hourIn24 Hour of the day with range 0-23. 19 | * @param minute Minute of the hour with range 0-59. 20 | * @param second Second of the minute with range 0-59. 21 | * @param nano Nano-of-second to represent, from 0 to 999,999,999. 22 | * @return LocalDateTime. 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 | ): LocalDateTime = LocalDateTime.of(year, Month.of(month), day, hourIn24, minute, second, nano) 33 | 34 | 35 | /** 36 | * @param epochMilliseconds Epoch time, aka Unix time, are seconds elapsed since January 1st 1970 at 00:00:00 UTC. 37 | * @return LocalDateTime. 38 | */ 39 | fun new(epochMilliseconds: Long, zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = 40 | LocalDateTime.ofInstant( 41 | Instant.ofEpochMilli(epochMilliseconds), 42 | zoneId 43 | ) 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/LocalDateTimes.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime 2 | 3 | import javatimefun.localdatetime.extensions.atEndOfDay 4 | import javatimefun.localdatetime.extensions.atStartOfDay 5 | import javatimefun.localdatetime.extensions.getLast 6 | import javatimefun.localdatetime.extensions.getNext 7 | import java.time.DayOfWeek 8 | import java.time.LocalDateTime 9 | import java.time.ZoneId 10 | 11 | object LocalDateTimes { 12 | fun now(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = LocalDateTime.now(zoneId) 13 | fun today(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = now(zoneId).atStartOfDay() 14 | fun yesterday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).minusDays(1) 15 | fun tomorrow(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).plusDays(1) 16 | 17 | fun lastMonday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.MONDAY) 18 | fun lastTuesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.TUESDAY) 19 | fun lastWednesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.WEDNESDAY) 20 | fun lastThursday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.THURSDAY) 21 | fun lastFriday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.FRIDAY) 22 | fun lastSaturday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.SATURDAY) 23 | fun lastSunday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getLast(DayOfWeek.SUNDAY) 24 | 25 | fun nextMonday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.MONDAY) 26 | fun nextTuesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.TUESDAY) 27 | fun nextWednesday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.WEDNESDAY) 28 | fun nextThursday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.THURSDAY) 29 | fun nextFriday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.FRIDAY) 30 | fun nextSaturday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.SATURDAY) 31 | fun nextSunday(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = today(zoneId).getNext(DayOfWeek.SUNDAY) 32 | 33 | fun startOfYear(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = 34 | LocalDateTimeFactory.new(today(zoneId).year, 1, 1) 35 | fun endOfYear(zoneId: ZoneId = ZoneId.systemDefault()): LocalDateTime = 36 | LocalDateTimeFactory.new(today(zoneId).year, 12, 31) 37 | .atEndOfDay() 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/extensions/LocalDateTimeAttributeExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime.extensions 2 | 3 | import java.time.LocalDateTime 4 | import java.time.Year 5 | 6 | fun LocalDateTime.isInLeapYear(): Boolean = Year.of(year).isLeap 7 | 8 | fun LocalDateTime.isAtStartOfDay(): Boolean = this.isEqualTime(this.atStartOfDay()) 9 | 10 | fun LocalDateTime.isAtEndOfDay(): Boolean = this.isEqualTime(this.atEndOfDay()) 11 | 12 | fun LocalDateTime.getMonthBaseZero(): Int = this.monthValue - 1 13 | 14 | fun LocalDateTime.getDaysInMonth(): Int = this.month.length(isInLeapYear()) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/extensions/LocalDateTimeComparisonExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime.extensions 2 | 3 | import java.time.Duration 4 | import java.time.LocalDateTime 5 | import java.time.temporal.ChronoUnit 6 | 7 | // region Day Comparisons 8 | fun LocalDateTime.compareDay(toDate: LocalDateTime): Int { 9 | val dayDifference = this.getDayDifference(toDate) 10 | return when { 11 | dayDifference > 0 -> -1 12 | dayDifference < 0 -> 1 13 | else -> 0 14 | } 15 | } 16 | 17 | fun LocalDateTime.isEqualDay(b: LocalDateTime): Boolean = this.compareDay(b) == 0 18 | 19 | fun LocalDateTime.isBeforeDay(b: LocalDateTime): Boolean = this.compareDay(b) < 0 20 | 21 | fun LocalDateTime.isBeforeEqualDay(b: LocalDateTime): Boolean = this.compareDay(b) <= 0 22 | 23 | fun LocalDateTime.isAfterDay(b: LocalDateTime): Boolean = this.compareDay(b) > 0 24 | 25 | fun LocalDateTime.isAfterEqualDay(b: LocalDateTime): Boolean = this.compareDay(b) >= 0 26 | // endregion 27 | 28 | // region Month Comparisons 29 | fun LocalDateTime.compareMonth(localDateTimeB: LocalDateTime): Int = 30 | when { 31 | this.isBeforeMonth(localDateTimeB) -> -1 32 | this.isEqualMonth(localDateTimeB) -> 0 33 | else -> 1 34 | } 35 | 36 | fun LocalDateTime.isBeforeMonth(localDateTimeB: LocalDateTime): Boolean { 37 | if (this.year > localDateTimeB.year) return false 38 | if (this.year < localDateTimeB.year) return true 39 | return this.month < localDateTimeB.month 40 | } 41 | 42 | fun LocalDateTime.isBeforeEqualMonth(localDateTimeB: LocalDateTime): Boolean { 43 | if (this.year > localDateTimeB.year) return false 44 | if (this.year < localDateTimeB.year) return true 45 | return this.month <= localDateTimeB.month 46 | } 47 | 48 | fun LocalDateTime.isEqualMonth(localDateTimeB: LocalDateTime): Boolean = 49 | this.year == localDateTimeB.year && this.month == localDateTimeB.month 50 | 51 | fun LocalDateTime.isAfterEqualMonth(localDateTimeB: LocalDateTime): Boolean { 52 | if (this.year > localDateTimeB.year) return true 53 | if (this.year < localDateTimeB.year) return false 54 | return this.month >= localDateTimeB.month 55 | } 56 | 57 | fun LocalDateTime.isAfterMonth(localDateTimeB: LocalDateTime): Boolean { 58 | if (this.year > localDateTimeB.year) return true 59 | if (this.year < localDateTimeB.year) return false 60 | return this.month > localDateTimeB.month 61 | } 62 | // endregion 63 | 64 | // region Year Comparisons 65 | fun LocalDateTime.compareYear(localDateTimeB: LocalDateTime): Int = 66 | this.year.compareTo(localDateTimeB.year) 67 | 68 | fun LocalDateTime.isBeforeYear(localDateTimeB: LocalDateTime): Boolean = 69 | this.year < localDateTimeB.year 70 | 71 | fun LocalDateTime.isBeforeEqualYear(localDateTimeB: LocalDateTime): Boolean = 72 | this.year <= localDateTimeB.year 73 | 74 | fun LocalDateTime.isEqualYear(localDateTimeB: LocalDateTime): Boolean = 75 | this.year == localDateTimeB.year 76 | 77 | fun LocalDateTime.isAfterEqualYear(localDateTimeB: LocalDateTime): Boolean = 78 | this.year >= localDateTimeB.year 79 | 80 | fun LocalDateTime.isAfterYear(localDateTimeB: LocalDateTime): Boolean = 81 | this.year > localDateTimeB.year 82 | // endregion 83 | 84 | // region Time Comparisons 85 | fun LocalDateTime.compareTime(toDate: LocalDateTime): Int = 86 | when { 87 | this.isEqualTime(toDate) -> 0 88 | this.isBeforeTime(toDate) -> -1 89 | else -> 1 90 | } 91 | 92 | fun LocalDateTime.isEqualTime(b: LocalDateTime): Boolean = this.isEqual(b) 93 | 94 | fun LocalDateTime.isBeforeTime(b: LocalDateTime): Boolean = this.isBefore(b) 95 | 96 | fun LocalDateTime.isBeforeEqualTime(b: LocalDateTime): Boolean = this.compareTime(b) <= 0 97 | 98 | fun LocalDateTime.isAfterTime(b: LocalDateTime): Boolean = this.compareTime(b) > 0 99 | 100 | fun LocalDateTime.isAfterEqualTime(b: LocalDateTime): Boolean = this.compareTime(b) >= 0 101 | // endregion 102 | 103 | fun LocalDateTime.getSecondDifference(localDateTimeB: LocalDateTime): Long = 104 | Duration.between(this, localDateTimeB).toSeconds() 105 | 106 | fun LocalDateTime.getMinuteDifference(localDateTimeB: LocalDateTime): Long = 107 | Duration.between(this, localDateTimeB).toMinutes() 108 | 109 | fun LocalDateTime.getHourDifference(localDateTimeB: LocalDateTime): Long = 110 | Duration.between(this, localDateTimeB).toHours() 111 | 112 | fun LocalDateTime.getDayDifference(localDateTimeB: LocalDateTime): Long = 113 | Duration.between(this.atStartOfDay(), localDateTimeB.atStartOfDay()).toDays() 114 | 115 | fun LocalDateTime.getMonthDifference(localDateTimeB: LocalDateTime): Long = 116 | ChronoUnit.MONTHS.between(this, localDateTimeB) 117 | 118 | fun LocalDateTime.getYearDifference(localDateTimeB: LocalDateTime): Long = 119 | ChronoUnit.YEARS.between(this, localDateTimeB) -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/extensions/LocalDateTimeMutatingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime.extensions 2 | 3 | import java.time.DayOfWeek 4 | import java.time.LocalDateTime 5 | import java.time.LocalTime 6 | 7 | fun LocalDateTime.atStartOfDay(): LocalDateTime = this.withLocalTime(LocalTime.MIN) 8 | 9 | fun LocalDateTime.atEndOfDay(): LocalDateTime = this.withLocalTime(LocalTime.MAX) 10 | 11 | fun LocalDateTime.withLocalTime(localTime: LocalTime): LocalDateTime = 12 | this.withHour(localTime.hour) 13 | .withMinute(localTime.minute) 14 | .withSecond(localTime.second) 15 | .withNano(localTime.nano) 16 | 17 | fun LocalDateTime.getLast(dayOfWeek: DayOfWeek, countingInThisDay: Boolean = false): LocalDateTime { 18 | if (countingInThisDay && this.dayOfWeek == dayOfWeek) { 19 | return this 20 | } 21 | var mostRecentDay = this 22 | if (mostRecentDay.dayOfWeek == dayOfWeek) { 23 | mostRecentDay = mostRecentDay.minusDays(1) 24 | } 25 | while (mostRecentDay.dayOfWeek != dayOfWeek) { 26 | mostRecentDay = mostRecentDay.minusDays(1) 27 | } 28 | return mostRecentDay 29 | } 30 | 31 | fun LocalDateTime.getNext(dayOfWeek: DayOfWeek, countingInThisDay: Boolean = false): LocalDateTime { 32 | if (countingInThisDay && this.dayOfWeek == dayOfWeek) { 33 | return this 34 | } 35 | var nextLocalDate = this 36 | if (nextLocalDate.dayOfWeek == dayOfWeek) { 37 | nextLocalDate = nextLocalDate.plusDays(1) 38 | } 39 | while (nextLocalDate.dayOfWeek != dayOfWeek) { 40 | nextLocalDate = nextLocalDate.plusDays(1) 41 | } 42 | return nextLocalDate 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/javatimefun/localdatetime/extensions/LocalDateTimeParsingExtensions.kt: -------------------------------------------------------------------------------- 1 | package javatimefun.localdatetime.extensions 2 | 3 | import java.time.LocalDateTime 4 | import java.time.format.DateTimeFormatter 5 | import java.time.format.DateTimeParseException 6 | 7 | private const val flexibleIso8601Format = "yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSSSS][.SSSS][.SSS][.SS][.S]['Z']" 8 | 9 | /** 10 | * Works off of String representations of dateTime and parses through the following attempts in order when 11 | * no format is present: 12 | *

    13 | *
  • First, tries parsing as LocalDateTime with format if provided, 14 | *
  • Lastly, if fails, tries parsing using a more flexible ISO 8601 format 15 | *

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 | } --------------------------------------------------------------------------------