pax_global_header00006660000000000000000000000064134345117410014515gustar00rootroot0000000000000052 comment=b92b352ceed45194b7d2cdce0844da6c27d81ce5 threeten-extra-1.5.0/000077500000000000000000000000001343451174100144575ustar00rootroot00000000000000threeten-extra-1.5.0/.gitattributes000066400000000000000000000004311343451174100173500ustar00rootroot00000000000000# Manage line endings, info from GitHub # Set default behaviour, in case users don't have core.autocrlf set. * text=auto # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary *.jar binary *.dat binary *.zip binary *.tar binary *.gz binary threeten-extra-1.5.0/.github/000077500000000000000000000000001343451174100160175ustar00rootroot00000000000000threeten-extra-1.5.0/.github/maven-settings.xml000066400000000000000000000006401343451174100215050ustar00rootroot00000000000000 github ${GITHUB_TOKEN} threeten-extra-1.5.0/.github/website.sh000066400000000000000000000010201343451174100200060ustar00rootroot00000000000000 echo "## setup..." git config --global user.name "Stephen Colebourne (CI)" git config --global user.email "scolebourne@joda.org" cd target echo "## clone..." git clone https://${GITHUB_TOKEN}@github.com/ThreeTen/threeten.github.io.git cd threeten.github.io git status echo "## copy..." rm -rf threeten-extra/ cp -R ../site threeten-extra/ echo "## update..." git add -A git status git commit --message "Update threeten-extra from Travis: Build $TRAVIS_BUILD_NUMBER" echo "## push..." git push origin master echo "## done" threeten-extra-1.5.0/.gitignore000066400000000000000000000003751343451174100164540ustar00rootroot00000000000000/build/ /bin/ /lib/ /dist/ /test-output/ /temp* /.settings/ /nbproject/private/ /target/ .classpath .project .checkstyle *.class *.jar *.war *.ear **TempTest.java # IDEA meta files /.idea/ /*.iml /*.ipr /*.iws /pom.xml.releaseBackup /release.properties threeten-extra-1.5.0/.travis.yml000066400000000000000000000015351343451174100165740ustar00rootroot00000000000000sudo: false language: java jdk: - openjdk11 - oraclejdk9 - oraclejdk8 cache: directories: - "$HOME/.m2/repository" before_cache: - rm -rf $HOME/.m2/repository/org/threeten install: - mvn --version script: - mvn install site -e -B - if [[ $TRAVIS_TAG =~ ^website.*$ ]] && [ "$TRAVIS_JDK_VERSION" == "openjdk11" ]; then chmod +x ./.github/website.sh; .github/website.sh; fi - if [[ $TRAVIS_TAG =~ ^v.*$ ]] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_JDK_VERSION" == "openjdk11" ]; then chmod +x ./.github/website.sh; .github/website.sh; fi after_success: - bash <(curl -s https://codecov.io/bash) # secure keys for GITHUB_TOKEN env: global: - secure: FaC3K5L6pGdJgvPcttpq8kV5i+UUsj5FWvETqm82BZWzLTYlQsCL4fl2XfssATiH7hIVioRffYoZP8rQbkNatVV+Pok6wZePSM9kOf7/AUOn0dvwXH718sbWlohOf3R3886/l5rjyJDDmauMABXzLrXxsTlbiQ85QNA0mCbqXJ4= threeten-extra-1.5.0/LICENSE.txt000066400000000000000000000027641343451174100163130ustar00rootroot00000000000000Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos. All rights reserved. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of JSR-310 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. threeten-extra-1.5.0/README.md000066400000000000000000000036101343451174100157360ustar00rootroot00000000000000ThreeTen-Extra ============== ThreeTen-Extra provides additional date-time classes that complement those in JDK 8. Not every piece of date/time logic is destined for the JDK. Some concepts are too specialized or too bulky to make it in. This project provides some of those additional classes as a well-tested and reliable jar. ### Documentation Various documentation is available: * The [home page](https://www.threeten.org/threeten-extra/) * The [user guide](https://www.threeten.org/threeten-extra/userguide.html) * The [Javadoc](https://www.threeten.org/threeten-extra/apidocs/org.threeten.extra/module-summary.html) ### Releases Release 1.5.0 is the current release. This release is considered stable and worthy of the 1.x tag. ThreeTen-Extra requires Java SE 8 or later and has no dependencies. Available in the [Maven Central repository](https://search.maven.org/search?q=g:org.threeten%20AND%20a:threeten-extra&core=gav) ![Tidelift dependency check](https://tidelift.com/badges/github/ThreeTen/threeten-extra) ### Support Please use [Stack Overflow](https://stackoverflow.com/search?q=threeten-extra) for general usage questions. GitHub [issues](https://github.com/ThreeTen/threeten-extra/issues) and [pull requests](https://github.com/ThreeTen/threeten-extra/pulls) should be used when you want to help advance the project. Commercial support is available via the [Tidelift subscription](https://tidelift.com/subscription/pkg/maven-org-threeten-threeten-extra?utm_source=maven-org-threeten-threeten-extra&utm_medium=referral&utm_campaign=readme). To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ### Release process * Update version (README.md, index.md, changes.xml) * Commit and push * `mvn clean release:clean release:prepare release:perform` * Website will be built and released by Travis threeten-extra-1.5.0/pom.xml000066400000000000000000000772341343451174100160110ustar00rootroot00000000000000 4.0.0 org.threeten threeten-extra jar ThreeTen-Extra 1.5.0 Additional functionality that enhances JSR-310 dates and times in Java SE 8 and later https://www.threeten.org/threeten-extra GitHub https://github.com/ThreeTen/threeten-extra/issues 2010 jodastephen Stephen Colebourne Project Lead 0 https://github.com/jodastephen Carlo Dapor https://github.com/catull Octavi Fornés https://github.com/ofornes Jim Gough https://github.com/jpgough Monica Guzik https://github.com/monicagg Christian Heinemann https://github.com/cheinema John Hill https://github.com/jjcard Michael Hixson https://github.com/michaelhixson Stephen A. Imhoff https://github.com/Clockwork-Muse Bruno P. Kinoshita https://github.com/kinow Steven McCoy https://github.com/steve-o Bjorn Raupach https://github.com/raupachz Michal Sobkiewicz https://github.com/perceptron8 Nils Sommer https://github.com/nsommer BSD 3-clause https://raw.githubusercontent.com/ThreeTen/threeten-extra/master/LICENSE.txt repo scm:git:https://github.com/ThreeTen/threeten-extra.git scm:git:https://github.com/ThreeTen/threeten-extra.git https://github.com/ThreeTen/threeten-extra v1.5.0 ThreeTen.org https://www.threeten.org src/main/resources META-INF ${project.basedir} LICENSE.txt org.apache.maven.plugins maven-checkstyle-plugin org.jacoco jacoco-maven-plugin org.apache.maven.plugins maven-enforcer-plugin enforce-maven enforce 3.5.0 org.apache.felix maven-bundle-plugin ${maven-bundle-plugin.version} biz.aQute.bnd biz.aQute.bndlib ${bndlib.version} bundle-manifest process-classes manifest org.threeten.extra,org.threeten.extra.chrono,org.threeten.extra.scale osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" org.apache.maven.plugins maven-jar-plugin ${project.build.outputDirectory}/META-INF/MANIFEST.MF true true org.apache.maven.plugins maven-source-plugin attach-sources package jar-no-fork org.apache.maven.plugins maven-assembly-plugin ${maven-assembly-plugin.version} org.apache.maven.plugins maven-changes-plugin ${maven-changes-plugin.version} org.apache.maven.plugins maven-clean-plugin ${maven-clean-plugin.version} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} org.apache.maven.plugins maven-deploy-plugin ${maven-deploy-plugin.version} org.apache.maven.plugins maven-dependency-plugin ${maven-dependency-plugin.version} org.apache.maven.plugins maven-enforcer-plugin ${maven-enforcer-plugin.version} org.apache.maven.plugins maven-gpg-plugin ${maven-gpg-plugin.version} org.apache.maven.plugins maven-install-plugin ${maven-install-plugin.version} org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} org.apache.maven.plugins maven-jxr-plugin ${maven-jxr-plugin.version} org.apache.maven.plugins maven-plugin-plugin ${maven-plugin-plugin.version} org.apache.maven.plugins maven-pmd-plugin ${maven-pmd-plugin.version} org.apache.maven.plugins maven-project-info-reports-plugin ${maven-project-info-reports-plugin.version} org.apache.maven.plugins maven-repository-plugin ${maven-repository-plugin.version} org.apache.maven.plugins maven-resources-plugin ${maven-resources-plugin.version} org.apache.maven.plugins maven-source-plugin ${maven-source-plugin.version} org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} org.apache.maven.plugins maven-surefire-report-plugin ${maven-surefire-report-plugin.version} org.apache.maven.plugins maven-toolchains-plugin ${maven-toolchains-plugin.version} org.apache.maven.plugins maven-release-plugin ${maven-release-plugin.version} -Doss.repo true v@{project.version} true org.apache.maven.plugins maven-checkstyle-plugin ${maven-checkstyle-plugin.version} run-checkstyle process-sources checkstyle module-info.java com.puppycrawl.tools checkstyle ${checkstyle.version} com.github.spotbugs spotbugs-maven-plugin ${spotbugs-maven-plugin.version} org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} jacoco-initialize prepare-agent jacoco-site package report org.apache.maven.plugins maven-site-plugin ${maven-site-plugin.version} true org.joda.external reflow-velocity-tools 1.2 org.eclipse.m2e lifecycle-mapping 1.0.0 org.apache.felix maven-bundle-plugin [2.5.4,) manifest org.joda joda-convert 2.2.0 compile true com.google.guava guava 26.0-jre test junit junit 4.12 test com.tngtech.java junit-dataprovider 1.10.0 test org.apache.maven.plugins maven-project-info-reports-plugin ${maven-project-info-reports-plugin.version} ci-management dependencies dependency-info issue-management licenses team scm summary org.apache.maven.plugins maven-checkstyle-plugin ${maven-checkstyle-plugin.version} false false false module-info.java org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} javadoc org.apache.maven.plugins maven-surefire-report-plugin ${maven-surefire-report-plugin.version} true org.apache.maven.plugins maven-changes-plugin ${maven-changes-plugin.version} changes-report org.apache.maven.plugins maven-pmd-plugin ${maven-pmd-plugin.version} 100 ${maven.compiler.target} module-info.java com.github.spotbugs spotbugs-maven-plugin ${spotbugs-maven-plugin.version} org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} report sonatype-threeten-staging Sonatype OSS staging repository https://oss.sonatype.org/service/local/staging/deploy/maven2/ default false sonatype-threeten-snapshot Sonatype OSS snapshot repository https://oss.sonatype.org/content/repositories/threeten-snapshots default https://oss.sonatype.org/content/repositories/threeten-releases java8 [1.6,9) org.apache.maven.plugins maven-compiler-plugin module-info.java org.apache.maven.plugins maven-javadoc-plugin module-info.java org.apache.maven.plugins maven-javadoc-plugin true java9plus [9,) org.apache.maven.plugins maven-dependency-plugin copy-dependencies compile copy-dependencies ${project.build.directory}/dependencies true true org.apache.maven.plugins maven-surefire-plugin default-test true --add-modules org.joda.convert --module-path ${project.build.directory}/dependencies ${argLine} test-without-modules test test false org.apache.maven.plugins maven-javadoc-plugin attach-javadocs package jar --module-path ${project.build.directory}/dependencies org.apache.maven.plugins maven-compiler-plugin default-compile 9 base-compile compile module-info.java 8 release-artifacts oss.repo org.apache.maven.plugins maven-enforcer-plugin enforce-java enforce [9,) org.apache.maven.plugins maven-gpg-plugin sign-artifacts verify sign de.jutzig github-release-plugin 1.1.1 Release v${project.version} See the [change notes](https://www.threeten.org/threeten-extra/changes-report.html) for more information. v${project.version} true github-releases deploy release org.sonatype.plugins nexus-staging-maven-plugin ${nexus-staging-maven-plugin.version} true https://oss.sonatype.org/ sonatype-joda-staging Releasing ${project.groupId}:${project.artifactId}:${project.version} true true 3.1.1 4.1.0 2.12.1 2.17 3.1.0 3.8.0 3.0.0-M1 3.1.1 3.0.0-M2 1.6 3.0.0-M1 3.1.1 3.0.1 3.0.0 3.6.0 3.11.0 3.0.0 2.5.3 2.4 3.1.0 3.7.1 3.0.1 3.0.0-M3 3.0.0-M3 1.1 4.1.0 0.8.3 1.6.8 3.1.11 1.8 1.8 1.8 true false true none 8.17 src/main/checkstyle/checkstyle.xml false UTF-8 UTF-8 threeten-extra-1.5.0/src/000077500000000000000000000000001343451174100152465ustar00rootroot00000000000000threeten-extra-1.5.0/src/changes/000077500000000000000000000000001343451174100166565ustar00rootroot00000000000000threeten-extra-1.5.0/src/changes/changes.xml000066400000000000000000000225031343451174100210120ustar00rootroot00000000000000 ThreeTen-Extra - Changes Stephen Colebourne Add stream-returning method YearQuarter.quartersUntil(YearQuarter). Fixes #122. Add word-based period formatting. Note that textual data can only be altered by PRs to ThreeTen-Extra. Based on original code from Joda-Time. Fixes #113, #41. Add Catalan translation for word-based formatting. Fixes #123. Add Joda-Convert annotations. The additional Joda-Convert dependency is optional (except that on Scala it is apparently mandatory). Add Tidelift commercial support and security policy. Enhance LocalDateRange. Add more factory methods for empty and unbounded. Ensure that unbounded ranges are more clearly specified. Reject certain ranges near LocalDate.MIN/LocalDate.MAX. Alter behaviour of lengthInDays() and toPeriod(). Fixes #100. Fix build for Java 9. Resource files cannot be read from other modules in Java 9. As such, the `LeapSeconds.txt` file has moved to be under META-INF, `META-INF/org/threeten/extra/scale/LeapSeconds.txt`. Fix OSGi for Java 9. Now that the build is on Java 9, the OSGi data had to be updated. See #92, #94. Add Temporals.nextWorkingDayOrSame() and Temporals.previousWorkingDayOrSame(). Fixes #101. Fix test parameter order. See #98, #99. Fix build for Java 8. Found actual issue with Javac was in the pom.xml. See #91. Fix build for Java 8. Javac release flag is not correctly ignoring new overloaded methods. Fixes #91. Support Java 9. Update and redesign build. Switch from TestNG to JUnit 4. Error message and Javadoc fixes in Interval. See #89. Interval.parse now handles Instant.MIN/MAX. See #80. YearWeek.atDay now correctly handles the end of the year. See #87. Add MutableClock. See #83, #84. Add plusYears/minusYears to YearWeek. See #78. Add plusWeeks/minusWeeks to YearWeek. See #78. Add PeriodDuration, combining Period and Duration. See #74. Fix incorrect method name in Hours. toPeriod() should have been toDuration(). Fixes #76. Extend formats parsed by Hours, Minutes and Seconds. Fixes #77. Extend formats parsed by Interval, allowing end instant to have offset inferred from start instant. See #75. Extend formats parsed by Interval, allowing years, months, weeks and days. See #70. Add Seconds temporal amount class. See #73. Add Temporals.parseFirstMatching(). This allows text to be parsed against a number of different formats. See #56. Add LocalDateRange, a range of dates, matching style of Google Guava's Range. Enhance Interval to match. See #53. Add leap second at end of 2016. Allow Interval to parse offset date-time. Fixes #66, #67. Fix Symmetry010 week calculations. Fixes #61. Add PackedFields. Provide packed integer representations of dates and times Fixes #57. Clarify YearQuarter docs. Fixes #58. Add isBefore/isAfter to Interval. Fixes #48. Extend Interval to parse more formats. Fixes #50. Add Hours and Minutes classes. Fixes #52, #14. Add YearWeek class, handling the ISO week-based-year. Fixes #36, #38. Add Symmetry calendar systems. Fixes #42, #43. Add International Fixed calendar system. Fixes #31. Make YearQuarter.parse case insensitive. Make YearQuarter.toString output '+' for large years. Add British Cutover calendar system. Models British Julian-Gregorian cutover on 1752-09-14 (ISO). Fixes #29. Add Interval class. Fixes #2. Add Accounting calendar system. Fixes #27. Add Discordian calendar system. Fixes #23. Add Temporals helper methods to convert TimeUnit to/from ChronoUnit. Fixes #22. Add Pax calendar system. Fixes #17. Fix handling of with(era). Fixes #18 / #20. Add Ethiopic calendar system. Fixes #11. Add Julian calendar system. Fixes #10. Add Weeks class. Fixes #3. Extend conversion between amount types. Handle conversions in from(TemporalAmount) using Temporals.convertAmount(...). Fixes #7. Add Temporals.convertAmount(...). Allows an amount in one unit to be converted to an amount in a different unit. Fixes #6. Add Months.ofYears(int). Support weeks in Months.from(TemporalAmount). Support weeks in Months.parse(CharSequence). Fixes #5. Add Days.ofWeeks(int). Support weeks in Days.from(TemporalAmount). Support weeks in Days.parse(CharSequence). Fixes #4. First modernized version. threeten-extra-1.5.0/src/main/000077500000000000000000000000001343451174100161725ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/checkstyle/000077500000000000000000000000001343451174100203305ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/checkstyle/checkstyle.xml000066400000000000000000000134771343451174100232240ustar00rootroot00000000000000 threeten-extra-1.5.0/src/main/java/000077500000000000000000000000001343451174100171135ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/java/module-info.java000066400000000000000000000054131343451174100221770ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * ThreeTen-Extra provides additional date-time classes that complement those in Java SE 8. *

* Not every piece of date/time logic is destined for the JDK. Some concepts are too * specialized or too bulky to make it in. This project provides some of those additional * classes as a well-tested and reliable module. */ module org.threeten.extra { // only annotations are used, thus they are optional requires static org.joda.convert; // export all packages exports org.threeten.extra; exports org.threeten.extra.chrono; exports org.threeten.extra.scale; // provide the services provides java.time.chrono.Chronology with org.threeten.extra.chrono.BritishCutoverChronology, org.threeten.extra.chrono.CopticChronology, org.threeten.extra.chrono.DiscordianChronology, org.threeten.extra.chrono.EthiopicChronology, org.threeten.extra.chrono.InternationalFixedChronology, org.threeten.extra.chrono.JulianChronology, org.threeten.extra.chrono.PaxChronology, org.threeten.extra.chrono.Symmetry010Chronology, org.threeten.extra.chrono.Symmetry454Chronology; } threeten-extra-1.5.0/src/main/java/org/000077500000000000000000000000001343451174100177025ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/java/org/threeten/000077500000000000000000000000001343451174100215205ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/java/org/threeten/extra/000077500000000000000000000000001343451174100226435ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/java/org/threeten/extra/AmPm.java000066400000000000000000000372431343451174100243510ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoUnit.HALF_DAYS; import java.time.DateTimeException; import java.time.format.DateTimeFormatterBuilder; import java.time.format.TextStyle; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Calendar; import java.util.Locale; /** * A half-day before or after midday, with the values 'AM' and 'PM'. *

* {@code AmPm} is an enum representing the half-day concepts of AM and PM. * AM is defined as from 00:00 to 11:59, while PM is defined from 12:00 to 23:59. *

* All date-time fields have an {@code int} value. * The {@code int} value follows {@link Calendar}, assigning 0 to AM and 1 to PM. * It is recommended that applications use the enum rather than the {@code int} value * to ensure code clarity. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code AmPm}. * Use {@code getValue()} instead. *

* This enum represents a common concept that is found in many calendar systems. * As such, this enum may be used by any calendar system that has the AM/PM * concept defined exactly equivalent to the ISO calendar system. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum AmPm implements TemporalAccessor, TemporalAdjuster { /** * The singleton instance for the morning, AM - ante meridiem. * This has the numeric value of {@code 0}. */ AM, /** * The singleton instance for the afternoon, PM - post meridiem. * This has the numeric value of {@code 1}. */ PM; //----------------------------------------------------------------------- /** * Obtains an instance of {@code AmPm} from an {@code int} value. *

* {@code AmPm} is an enum representing before and after midday. * This factory allows the enum to be obtained from the {@code int} value. * The {@code int} value follows {@link Calendar}, assigning 0 to AM and 1 to PM. * * @param amPmValue the AM/PM value to represent, from 0 (AM) to 1 (PM) * @return the AM/PM, not null * @throws DateTimeException if the am-pm is invalid */ public static AmPm of(int amPmValue) { switch (amPmValue) { case 0: return AM; case 1: return PM; default: throw new DateTimeException("Invalid value for AM/PM: " + amPmValue); } } /** * Obtains an instance of {@code AmPm} from an hour-of-day. *

* {@code AmPm} is an enum representing before and after midday. * This factory allows the enum to be obtained from the hour-of-day value, from 0 to 23. * * @param hourOfDay the hour-of-day to extract from, from 0 to 23 * @return the AM/PM, not null * @throws DateTimeException if the hour-of-day is invalid */ public static AmPm ofHour(int hourOfDay) { HOUR_OF_DAY.checkValidValue(hourOfDay); return hourOfDay < 12 ? AM : PM; } //----------------------------------------------------------------------- /** * Obtains an instance of {@code AmPm} from a temporal object. *

* This obtains an am-pm based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code AmPm}. *

* The conversion extracts the {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} field. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code AmPm::from}. * * @param temporal the temporal object to convert, not null * @return the AM/PM, not null * @throws DateTimeException if unable to convert to a {@code AmPm} */ public static AmPm from(TemporalAccessor temporal) { if (temporal instanceof AmPm) { return (AmPm) temporal; } try { return of(temporal.get(AMPM_OF_DAY)); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain AmPm from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Gets the AM/PM {@code int} value. *

* The values are numbered following {@link Calendar}, assigning 0 to AM and 1 to PM. * * @return the AM/PM value, from 0 (AM) to 1 (PM) */ public int getValue() { return ordinal(); } //----------------------------------------------------------------------- /** * Gets the textual representation, such as 'AM' or 'PM'. *

* This returns the textual name used to identify the am-pm, * suitable for presentation to the user. * The parameters control the style of the returned text and the locale. *

* If no textual mapping is found then the {@link #getValue() numeric value} is returned. * * @param style the length of the text required, not null * @param locale the locale to use, not null * @return the text value of the am-pm, not null */ public String getDisplayName(TextStyle style, Locale locale) { return new DateTimeFormatterBuilder().appendText(AMPM_OF_DAY, style).toFormatter(locale).format(this); } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *

* This checks if this am-pm can be queried for the specified field. * If false, then calling the {@link #range(TemporalField) range} and * {@link #get(TemporalField) get} methods will throw an exception. *

* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then * this method returns true. * All other {@code ChronoField} instances will return false. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the field is supported is determined by the field. * * @param field the field to check, null returns false * @return true if the field is supported on this am-pm, false if not */ @Override public boolean isSupported(TemporalField field) { if (field instanceof ChronoField) { return field == AMPM_OF_DAY; } return field != null && field.isSupportedBy(this); } /** * Gets the range of valid values for the specified field. *

* The range object expresses the minimum and maximum valid values for a field. * This am-pm is used to enhance the accuracy of the returned range. * If it is not possible to return the range, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then the * range of the am-pm, from 0 to 1, will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the range can be obtained is determined by the field. * * @param field the field to query the range for, not null * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported */ @Override public ValueRange range(TemporalField field) { if (field == AMPM_OF_DAY) { return field.range(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.rangeRefinedBy(this); } /** * Gets the value of the specified field from this am-pm as an {@code int}. *

* This queries this am-pm for the value for the specified field. * The returned value will always be within the valid range of values for the field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then the * value of the am-pm, from 0 (AM) to 1 (PM), will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field, within the valid range of values * @throws DateTimeException if a value for the field cannot be obtained or * the value is outside the range of valid values for the field * @throws UnsupportedTemporalTypeException if the field is not supported or * the range of values exceeds an {@code int} * @throws ArithmeticException if numeric overflow occurs */ @Override public int get(TemporalField field) { if (field == AMPM_OF_DAY) { return getValue(); } return range(field).checkValidIntValue(getLong(field), field); } /** * Gets the value of the specified field from this am-pm as a {@code long}. *

* This queries this am-pm for the value for the specified field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then the * value of the am-pm, from 0 (AM) to 1 (PM), will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long getLong(TemporalField field) { if (field == AMPM_OF_DAY) { return getValue(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } //----------------------------------------------------------------------- /** * Queries this am-pm using the specified query. *

* This queries this am-pm using the specified query strategy object. * The {@code TemporalQuery} object defines the logic to be used to * obtain the result. Read the documentation of the query to understand * what the result of this method will be. *

* The result of this method is obtained by invoking the * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the * specified query passing {@code this} as the argument. * * @param the type of the result * @param query the query to invoke, not null * @return the query result, null may be returned (defined by the query) * @throws DateTimeException if unable to query (defined by the query) * @throws ArithmeticException if numeric overflow occurs (defined by the query) */ @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.precision()) { return (R) HALF_DAYS; } return TemporalAccessor.super.query(query); } /** * Adjusts the specified temporal object to have this am-pm value. *

* This returns a temporal object of the same observable type as the input * with the am-pm changed to be the same as this. *

* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} * passing {@link ChronoField#AMPM_OF_DAY} as the field. * Note that this adjusts forwards or backwards within a day. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#with(TemporalAdjuster)}: *

     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisAmPm.adjustInto(temporal);
     *   temporal = temporal.with(thisAmPm);
     * 
*

* This instance is immutable and unaffected by this method call. * * @param temporal the target object to be adjusted, not null * @return the adjusted object, not null * @throws DateTimeException if unable to make the adjustment * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal adjustInto(Temporal temporal) { return temporal.with(AMPM_OF_DAY, getValue()); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/AmountFormats.java000066400000000000000000000360471343451174100263170ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import java.time.Duration; import java.time.Period; import java.time.temporal.TemporalAmount; import java.util.Locale; import java.util.Objects; import java.util.ResourceBundle; import java.util.function.IntPredicate; import java.util.regex.Pattern; import java.util.stream.Stream; /** * Provides the ability to format a temporal amount. *

* This allows a {@link TemporalAmount}, such as {@link Duration} or {@link Period}, * to be formatted. Only selected formatting options are provided. * *

Implementation Requirements:

* This class is immutable and thread-safe. */ public final class AmountFormats { /** * The number of days per week. */ private static final int DAYS_PER_WEEK = 7; /** * The number of hours per day. */ private static final int HOURS_PER_DAY = 24; /** * The number of minutes per hour. */ private static final int MINUTES_PER_HOUR = 60; /** * The number of seconds per minute. */ private static final int SECONDS_PER_MINUTE = 60; /** * The number of nanosecond per millisecond. */ private static final int NANOS_PER_MILLIS = 1000_000; /** * The resource bundle name. */ private static final String BUNDLE_NAME = "org.threeten.extra.wordbased"; /** * The pattern to split lists with. */ private static final Pattern SPLITTER = Pattern.compile("[|][|][|]"); /** * The property file key for the separator ", ". */ private static final String WORDBASED_COMMASPACE = "WordBased.commaspace"; /** * The property file key for the separator " and ". */ private static final String WORDBASED_SPACEANDSPACE = "WordBased.spaceandspace"; /** * The property file key for the word "year". */ private static final String WORDBASED_YEAR = "WordBased.year"; /** * The property file key for the word "month". */ private static final String WORDBASED_MONTH = "WordBased.month"; /** * The property file key for the word "week". */ private static final String WORDBASED_WEEK = "WordBased.week"; /** * The property file key for the word "day". */ private static final String WORDBASED_DAY = "WordBased.day"; /** * The property file key for the word "hour". */ private static final String WORDBASED_HOUR = "WordBased.hour"; /** * The property file key for the word "minute". */ private static final String WORDBASED_MINUTE = "WordBased.minute"; /** * The property file key for the word "second". */ private static final String WORDBASED_SECOND = "WordBased.second"; /** * The property file key for the word "millisecond". */ private static final String WORDBASED_MILLISECOND = "WordBased.millisecond"; /** * The predicate that matches 1 or -1. */ private static final IntPredicate PREDICATE_1 = value -> value == 1 || value == -1; /** * The predicate that matches numbers ending 2, 3 or 4, but not ending 12, 13 or 14. */ private static final IntPredicate PREDICATE_END234_NOTTEENS = value -> { int abs = Math.abs(value); int last = abs % 10; int secondLast = (abs % 100) / 10; return (last >= 2 && last <= 4 && secondLast != 1); }; //----------------------------------------------------------------------- /** * Formats a period and duration to a string in ISO-8601 format. *

* To obtain the ISO-8601 format of a {@code Period} or {@code Duration} * individually, simply call {@code toString()}. * See also {@link PeriodDuration}. * * @param period the period to format * @param duration the duration to format * @return the ISO-8601 format for the period and duration */ public static String iso8601(Period period, Duration duration) { Objects.requireNonNull(period, "period must not be null"); Objects.requireNonNull(duration, "duration must not be null"); if (period.isZero()) { return duration.toString(); } if (duration.isZero()) { return period.toString(); } return period.toString() + duration.toString().substring(1); } //------------------------------------------------------------------------- /** * Formats a period to a string in a localized word-based format. *

* This returns a word-based format for the period. * The words are configured in a resource bundle text file - * {@code org.threeten.extra.wordbased.properties} - with overrides per language. * * @param period the period to format * @param locale the locale to use * @return the localized word-based format for the period */ public static String wordBased(Period period, Locale locale) { Objects.requireNonNull(period, "period must not be null"); Objects.requireNonNull(locale, "locale must not be null"); ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale); UnitFormat[] formats = { UnitFormat.of(bundle, WORDBASED_YEAR), UnitFormat.of(bundle, WORDBASED_MONTH), UnitFormat.of(bundle, WORDBASED_WEEK), UnitFormat.of(bundle, WORDBASED_DAY)}; WordBased wb = new WordBased(formats, bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE)); Period normPeriod = period.normalized(); int weeks = 0, days = 0; if (normPeriod.getDays() % DAYS_PER_WEEK == 0) { weeks = normPeriod.getDays() / DAYS_PER_WEEK; } else { days = normPeriod.getDays(); } int[] values = {normPeriod.getYears(), normPeriod.getMonths(), weeks, days}; return wb.format(values); } /** * Formats a duration to a string in a localized word-based format. *

* This returns a word-based format for the duration. * The words are configured in a resource bundle text file - * {@code org.threeten.extra.wordbased.properties} - with overrides per language. * * @param duration the duration to format * @param locale the locale to use * @return the localized word-based format for the duration */ public static String wordBased(Duration duration, Locale locale) { Objects.requireNonNull(duration, "duration must not be null"); Objects.requireNonNull(locale, "locale must not be null"); ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale); UnitFormat[] formats = { UnitFormat.of(bundle, WORDBASED_HOUR), UnitFormat.of(bundle, WORDBASED_MINUTE), UnitFormat.of(bundle, WORDBASED_SECOND), UnitFormat.of(bundle, WORDBASED_MILLISECOND)}; WordBased wb = new WordBased(formats, bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE)); long hours = duration.toHours(); long mins = duration.toMinutes() % MINUTES_PER_HOUR; long secs = duration.getSeconds() % SECONDS_PER_MINUTE; int millis = duration.getNano() / NANOS_PER_MILLIS; int[] values = {(int) hours, (int) mins, (int) secs, millis}; return wb.format(values); } /** * Formats a period and duration to a string in a localized word-based format. *

* This returns a word-based format for the period. * The words are configured in a resource bundle text file - * {@code org.threeten.extra.wordbased.properties} - with overrides per language. * * @param period the period to format * @param duration the duration to format * @param locale the locale to use * @return the localized word-based format for the period and duration */ public static String wordBased(Period period, Duration duration, Locale locale) { Objects.requireNonNull(period, "period must not be null"); Objects.requireNonNull(duration, "duration must not be null"); Objects.requireNonNull(locale, "locale must not be null"); ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale); UnitFormat[] formats = { UnitFormat.of(bundle, WORDBASED_YEAR), UnitFormat.of(bundle, WORDBASED_MONTH), UnitFormat.of(bundle, WORDBASED_WEEK), UnitFormat.of(bundle, WORDBASED_DAY), UnitFormat.of(bundle, WORDBASED_HOUR), UnitFormat.of(bundle, WORDBASED_MINUTE), UnitFormat.of(bundle, WORDBASED_SECOND), UnitFormat.of(bundle, WORDBASED_MILLISECOND)}; WordBased wb = new WordBased(formats, bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE)); Period normPeriod = period.normalized(); int weeks = 0, days = 0; if (normPeriod.getDays() % DAYS_PER_WEEK == 0) { weeks = normPeriod.getDays() / DAYS_PER_WEEK; } else { days = normPeriod.getDays(); } long totalHours = duration.toHours(); days += (int) (totalHours / HOURS_PER_DAY); int hours = (int) (totalHours % HOURS_PER_DAY); int mins = (int) (duration.toMinutes() % MINUTES_PER_HOUR); int secs = (int) (duration.getSeconds() % SECONDS_PER_MINUTE); int millis = duration.getNano() / NANOS_PER_MILLIS; int[] values = { normPeriod.getYears(), normPeriod.getMonths(), weeks, days, (int) hours, mins, secs, millis}; return wb.format(values); } private AmountFormats() { } //------------------------------------------------------------------------- // data holder for word-based formats static final class WordBased { private final UnitFormat[] units; private final String separator; private final String lastSeparator; public WordBased(UnitFormat[] units, String separator, String lastSeparator) { this.units = units; this.separator = separator; this.lastSeparator = lastSeparator; } String format(int[] values) { StringBuilder buf = new StringBuilder(32); int nonZeroCount = 0; for (int i = 0; i < values.length; i++) { if (values[i] != 0) { nonZeroCount++; } } int count = 0; for (int i = 0; i < values.length; i++) { if (values[i] != 0 || (count == 0 && i == values.length - 1)) { units[i].formatTo(values[i], buf); if (count < nonZeroCount - 2) { buf.append(separator); } else if (count == nonZeroCount - 2) { buf.append(lastSeparator); } count++; } } return buf.toString(); } } // data holder for single/plural formats static interface UnitFormat { static UnitFormat of(ResourceBundle bundle, String keyStem) { if (bundle.containsKey(keyStem + "s.predicates")) { String predicateList = bundle.getString(keyStem + "s.predicates"); String textList = bundle.getString(keyStem + "s.list"); String[] regexes = SPLITTER.split(predicateList); String[] text = SPLITTER.split(textList); return new PredicateFormat(regexes, text); } else { String single = bundle.getString(keyStem); String plural = bundle.getString(keyStem + "s"); return new SinglePluralFormat(single, plural); } } void formatTo(int value, StringBuilder buf); } // data holder for single/plural formats static final class SinglePluralFormat implements UnitFormat { private final String single; private final String plural; SinglePluralFormat(String single, String plural) { this.single = single; this.plural = plural; } @Override public void formatTo(int value, StringBuilder buf) { buf.append(value).append(value == 1 || value == -1 ? single : plural); } } // data holder for predicate formats static final class PredicateFormat implements UnitFormat { private final IntPredicate[] predicates; private final String[] text; PredicateFormat(String[] predicateStrs, String[] text) { if (predicateStrs.length + 1 != text.length) { throw new IllegalStateException("Invalid word-based resource"); } this.predicates = Stream.of(predicateStrs) .map(predicateStr -> findPredicate(predicateStr)) .toArray(IntPredicate[]::new); this.text = text; } private IntPredicate findPredicate(String predicateStr) { switch (predicateStr) { case "One": return PREDICATE_1; case "End234NotTeens": return PREDICATE_END234_NOTTEENS; default: throw new IllegalStateException("Invalid word-based resource"); } } @Override public void formatTo(int value, StringBuilder buf) { for (int i = 0; i < predicates.length; i++) { if (predicates[i].test(value)) { buf.append(value).append(text[i]); return; } } buf.append(value).append(text[predicates.length]); return; } } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/DayOfMonth.java000066400000000000000000000526641343451174100255330ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Month; import java.time.MonthDay; import java.time.YearMonth; import java.time.ZoneId; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Objects; /** * A day-of-month in the ISO-8601 calendar system. *

* {@code DayOfMonth} is an immutable date-time object that represents a day-of-month. * It is a type-safe way of representing a day-of-month in an application. * Any field that can be derived from a day-of-month can be obtained. *

* This class does not store or represent a year, month, time or time-zone. * For example, the value "21" can be stored in a {@code DayOfMonth} and * would represent the 21st day of any month. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class DayOfMonth implements TemporalAccessor, TemporalAdjuster, Comparable, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -8840172642009917873L; /** * Cache of singleton instances. */ private static final DayOfMonth[] VALUES = new DayOfMonth[31]; static { for (int i = 0; i < 31; i++) { VALUES[i] = new DayOfMonth(i + 1); } } /** * The day-of-month being represented, from 1 to 31. */ private final int day; //----------------------------------------------------------------------- /** * Obtains the current day-of-month from the system clock in the default time-zone. *

* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current day-of-month. * The zone and offset will be set based on the time-zone in the clock. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current day-of-month using the system clock and default time-zone, not null */ public static DayOfMonth now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current day-of-month from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current day-of-month. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current day-of-month using the system clock, not null */ public static DayOfMonth now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current day-of-month from the specified clock. *

* This will query the specified clock to obtain the current day-of-month. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current day-of-month, not null */ public static DayOfMonth now(Clock clock) { final LocalDate now = LocalDate.now(clock); // called once return DayOfMonth.of(now.getDayOfMonth()); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code DayOfMonth}. *

* A day-of-month object represents one of the 31 days of the month, from 1 to 31. * * @param dayOfMonth the day-of-month to represent, from 1 to 31 * @return the day-of-month, not null * @throws DateTimeException if the day-of-month is invalid */ public static DayOfMonth of(int dayOfMonth) { try { return VALUES[dayOfMonth - 1]; } catch (IndexOutOfBoundsException ex) { throw new DateTimeException("Invalid value for DayOfMonth: " + dayOfMonth); } } //----------------------------------------------------------------------- /** * Obtains an instance of {@code DayOfMonth} from a date-time object. *

* This obtains a day-of-month based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code DayOfMonth}. *

* The conversion extracts the {@link ChronoField#DAY_OF_MONTH day-of-month} field. * The extraction is only permitted if the temporal object has an ISO * chronology, or can be converted to a {@code LocalDate}. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used in queries via method reference, {@code DayOfMonth::from}. * * @param temporal the temporal object to convert, not null * @return the day-of-month, not null * @throws DateTimeException if unable to convert to a {@code DayOfMonth} */ public static DayOfMonth from(TemporalAccessor temporal) { if (temporal instanceof DayOfMonth) { return (DayOfMonth) temporal; } Objects.requireNonNull(temporal, "temporal"); try { if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { temporal = LocalDate.from(temporal); } return of(temporal.get(DAY_OF_MONTH)); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain DayOfMonth from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Constructor. * * @param dayOfMonth the day-of-month to represent */ private DayOfMonth(int dayOfMonth) { this.day = dayOfMonth; } /** * Validates the input. * * @return the valid object, not null */ private Object readResolve() { return of(day); } //----------------------------------------------------------------------- /** * Gets the day-of-month value. * * @return the day-of-month, from 1 to 31 */ public int getValue() { return day; } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *

* This checks if this day-of-month can be queried for the specified field. * If false, then calling the {@link #range(TemporalField) range}, * {@link #get(TemporalField) get} and {@link #getLong(TemporalField) getLong} * methods will throw an exception. *

* If the field is a {@link ChronoField} then the query is implemented here. * The supported fields are: *

    *
  • {@code DAY_OF_MONTH} *
* All other {@code ChronoField} instances will return false. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the field is supported is determined by the field. * * @param field the field to check, null returns false * @return true if the field is supported on this day-of-month, false if not */ @Override public boolean isSupported(TemporalField field) { if (field instanceof ChronoField) { return field == DAY_OF_MONTH; } return field != null && field.isSupportedBy(this); } //----------------------------------------------------------------------- /** * Gets the range of valid values for the specified field. *

* The range object expresses the minimum and maximum valid values for a field. * This day-of-month is used to enhance the accuracy of the returned range. * If it is not possible to return the range, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return * appropriate range instances. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the range can be obtained is determined by the field. * * @param field the field to query the range for, not null * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported */ @Override public ValueRange range(TemporalField field) { return TemporalAccessor.super.range(field); } /** * Gets the value of the specified field from this day-of-month as an {@code int}. *

* This queries this day-of-month for the value for the specified field. * The returned value will always be within the valid range of values for the field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return valid * values based on this day-of-month. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained or * the value is outside the range of valid values for the field * @throws UnsupportedTemporalTypeException if the field is not supported or * the range of values exceeds an {@code int} * @throws ArithmeticException if numeric overflow occurs */ @Override public int get(TemporalField field) { return TemporalAccessor.super.get(field); } /** * Gets the value of the specified field from this day-of-month as a {@code long}. *

* This queries this day-of-month for the value for the specified field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return valid * values based on this day-of-month. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long getLong(TemporalField field) { if (field == DAY_OF_MONTH) { return day; } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } //----------------------------------------------------------------------- /** * Checks if the year-month is valid for this year. *

* This method checks whether this day and the input year and month form * a valid date. * * @param yearMonth the year month to validate, null returns false * @return true if the year and month are valid for this day */ public boolean isValidYearMonth(YearMonth yearMonth) { return yearMonth != null && yearMonth.isValidDay(day); } //----------------------------------------------------------------------- /** * Queries this day-of-month using the specified query. *

* This queries this day-of-month using the specified query strategy object. * The {@code TemporalQuery} object defines the logic to be used to * obtain the result. Read the documentation of the query to understand * what the result of this method will be. *

* The result of this method is obtained by invoking the * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the * specified query passing {@code this} as the argument. * * @param the type of the result * @param query the query to invoke, not null * @return the query result, null may be returned (defined by the query) * @throws DateTimeException if unable to query (defined by the query) * @throws ArithmeticException if numeric overflow occurs (defined by the query) */ @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.chronology()) { return (R) IsoChronology.INSTANCE; } return TemporalAccessor.super.query(query); } /** * Adjusts the specified temporal object to have this day-of-month. *

* This returns a temporal object of the same observable type as the input * with the day-of-month changed to be the same as this. *

* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} * passing {@link ChronoField#DAY_OF_MONTH} as the field. * If the specified temporal object does not use the ISO calendar system then * a {@code DateTimeException} is thrown. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#with(TemporalAdjuster)}: *

     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisDay.adjustInto(temporal);
     *   temporal = temporal.with(thisDay);
     * 
*

* This instance is immutable and unaffected by this method call. * * @param temporal the target object to be adjusted, not null * @return the adjusted object, not null * @throws DateTimeException if unable to make the adjustment * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal adjustInto(Temporal temporal) { if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) { throw new DateTimeException("Adjustment only supported on ISO date-time"); } return temporal.with(DAY_OF_MONTH, day); } //----------------------------------------------------------------------- /** * Combines this day-of-month with a month to create a {@code MonthDay}. *

* This returns a {@code MonthDay} formed from this day and the specified month. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = day.atMonth(month).atYear(year);
     * 
*

* If this day-of-month is invalid for the month then it will be changed * to the last valid date for the month. * * @param month the month-of-year to use, not null * @return the year-month formed from this year and the specified month, not null */ public MonthDay atMonth(Month month) { return MonthDay.of(month, Math.min(day, month.maxLength())); } /** * Combines this day-of-month with a month to create a {@code MonthDay}. *

* This returns a {@code MonthDay} formed from this day and the specified month. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = day.atMonth(month).atYear(year);
     * 
*

* If this day-of-month is invalid for the month then it will be changed * to the last valid date for the month. * * @param month the month-of-year to use, from 1 (January) to 12 (December) * @return the year-month formed from this year and the specified month, not null * @throws DateTimeException if the month is invalid */ public MonthDay atMonth(int month) { return atMonth(Month.of(month)); } /** * Combines this day-of-month with a year-month to create a {@code LocalDate}. *

* This returns a {@code LocalDate} formed from this year and the specified year-month. *

* If this day-of-month is invalid for the year-month then it will be changed * to the last valid date for the month. * * @param yearMonth the year-month to use, not null * @return the local date formed from this year and the specified year-month, not null */ public LocalDate atYearMonth(YearMonth yearMonth) { return yearMonth.atDay(Math.min(day, yearMonth.lengthOfMonth())); } //----------------------------------------------------------------------- /** * Compares this day-of-month to another. *

* The comparison is based on the value of the day. * It is "consistent with equals", as defined by {@link Comparable}. * * @param other the other day-of-month instance, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(DayOfMonth other) { return day - other.day; } //----------------------------------------------------------------------- /** * Checks if this day-of-month is equal to another day-of-month. * * @param obj the other day-of-month instance, null returns false * @return true if the day-of-month is the same */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof DayOfMonth) { return day == ((DayOfMonth) obj).day; } return false; } /** * A hash code for this day-of-month. * * @return a suitable hash code */ @Override public int hashCode() { return day; } //----------------------------------------------------------------------- /** * Outputs this day-of-month as a {@code String}. * * @return a string representation of this day-of-month, not null */ @Override public String toString() { return "DayOfMonth:" + day; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/DayOfYear.java000066400000000000000000000512211343451174100253320ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Year; import java.time.ZoneId; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Objects; /** * A day-of-year in the ISO-8601 calendar system. *

* {@code DayOfYear} is an immutable date-time object that represents a day-of-year. * It is a type-safe way of representing a day-of-year in an application. * Any field that can be derived from a day-of-year can be obtained. *

* This class does not store or represent a year, month, time or time-zone. * For example, the value "51" can be stored in a {@code DayOfYear} and * would represent the 51st day of any year. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class DayOfYear implements TemporalAccessor, TemporalAdjuster, Comparable, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -8789692114017384034L; /** * Cache of singleton instances. */ private static final DayOfYear[] VALUES = new DayOfYear[366]; static { for (int i = 0; i < 366; i++) { VALUES[i] = new DayOfYear(i + 1); } } /** * The day-of-year being represented, from 1 to 366. */ private final int day; //----------------------------------------------------------------------- /** * Obtains the current day-of-year from the system clock in the default time-zone. *

* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current day-of-year. * The zone and offset will be set based on the time-zone in the clock. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current day-of-year using the system clock and default time-zone, not null */ public static DayOfYear now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current day-of-year from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current day-of-year. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current day-of-year using the system clock, not null */ public static DayOfYear now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current day-of-year from the specified clock. *

* This will query the specified clock to obtain the current day-of-year. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current day-of-year, not null */ public static DayOfYear now(Clock clock) { final LocalDate now = LocalDate.now(clock); // called once return DayOfYear.of(now.getDayOfYear()); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code DayOfYear}. *

* A day-of-year object represents one of the 366 days of the year, from 1 to 366. * * @param dayOfYear the day-of-year to represent, from 1 to 366 * @return the day-of-year, not null * @throws DateTimeException if the day-of-year is invalid */ public static DayOfYear of(int dayOfYear) { try { return VALUES[dayOfYear - 1]; } catch (IndexOutOfBoundsException ex) { throw new DateTimeException("Invalid value for DayOfYear: " + dayOfYear); } } //----------------------------------------------------------------------- /** * Obtains an instance of {@code DayOfYear} from a date-time object. *

* This obtains a day-of-year based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code DayOfYear}. *

* The conversion extracts the {@link ChronoField#DAY_OF_YEAR day-of-year} field. * The extraction is only permitted if the temporal object has an ISO * chronology, or can be converted to a {@code LocalDate}. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used in queries via method reference, {@code DayOfYear::from}. * * @param temporal the temporal object to convert, not null * @return the day-of-year, not null * @throws DateTimeException if unable to convert to a {@code DayOfYear} */ public static DayOfYear from(TemporalAccessor temporal) { if (temporal instanceof DayOfYear) { return (DayOfYear) temporal; } Objects.requireNonNull(temporal, "temporal"); try { if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { temporal = LocalDate.from(temporal); } return of(temporal.get(DAY_OF_YEAR)); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain DayOfYear from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Constructor. * * @param dayOfYear the day-of-year to represent */ private DayOfYear(int dayOfYear) { this.day = dayOfYear; } /** * Validates the input. * * @return the valid object, not null */ private Object readResolve() { return of(day); } //----------------------------------------------------------------------- /** * Gets the day-of-year value. * * @return the day-of-year, from 1 to 366 */ public int getValue() { return day; } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *

* This checks if this day-of-year can be queried for the specified field. * If false, then calling the {@link #range(TemporalField) range}, * {@link #get(TemporalField) get} and {@link #getLong(TemporalField) getLong} * methods will throw an exception. *

* If the field is a {@link ChronoField} then the query is implemented here. * The supported fields are: *

    *
  • {@code DAY_OF_YEAR} *
* All other {@code ChronoField} instances will return false. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the field is supported is determined by the field. * * @param field the field to check, null returns false * @return true if the field is supported on this day-of-year, false if not */ @Override public boolean isSupported(TemporalField field) { if (field instanceof ChronoField) { return field == DAY_OF_YEAR; } return field != null && field.isSupportedBy(this); } //----------------------------------------------------------------------- /** * Gets the range of valid values for the specified field. *

* The range object expresses the minimum and maximum valid values for a field. * This day-of-year is used to enhance the accuracy of the returned range. * If it is not possible to return the range, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return * appropriate range instances. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the range can be obtained is determined by the field. * * @param field the field to query the range for, not null * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported */ @Override public ValueRange range(TemporalField field) { return TemporalAccessor.super.range(field); } /** * Gets the value of the specified field from this day-of-year as an {@code int}. *

* This queries this day-of-year for the value for the specified field. * The returned value will always be within the valid range of values for the field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return valid * values based on this day-of-year. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained or * the value is outside the range of valid values for the field * @throws UnsupportedTemporalTypeException if the field is not supported or * the range of values exceeds an {@code int} * @throws ArithmeticException if numeric overflow occurs */ @Override public int get(TemporalField field) { return TemporalAccessor.super.get(field); } /** * Gets the value of the specified field from this day-of-year as a {@code long}. *

* This queries this day-of-year for the value for the specified field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return valid * values based on this day-of-year. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long getLong(TemporalField field) { if (field == DAY_OF_YEAR) { return day; } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } //----------------------------------------------------------------------- /** * Checks if the year is valid for this day-of-year. *

* This method checks whether this day-of-yearand the input year form * a valid date. This can only return false for day-of-year 366. * * @param year the year to validate * @return true if the year is valid for this day-of-year */ public boolean isValidYear(int year) { return (day < 366 || Year.isLeap(year)); } //----------------------------------------------------------------------- /** * Queries this day-of-year using the specified query. *

* This queries this day-of-year using the specified query strategy object. * The {@code TemporalQuery} object defines the logic to be used to * obtain the result. Read the documentation of the query to understand * what the result of this method will be. *

* The result of this method is obtained by invoking the * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the * specified query passing {@code this} as the argument. * * @param the type of the result * @param query the query to invoke, not null * @return the query result, null may be returned (defined by the query) * @throws DateTimeException if unable to query (defined by the query) * @throws ArithmeticException if numeric overflow occurs (defined by the query) */ @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.chronology()) { return (R) IsoChronology.INSTANCE; } return TemporalAccessor.super.query(query); } /** * Adjusts the specified temporal object to have this day-of-year. *

* This returns a temporal object of the same observable type as the input * with the day-of-year changed to be the same as this. *

* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} * passing {@link ChronoField#DAY_OF_YEAR} as the field. * If the specified temporal object does not use the ISO calendar system then * a {@code DateTimeException} is thrown. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#with(TemporalAdjuster)}: *

     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisDay.adjustInto(temporal);
     *   temporal = temporal.with(thisDay);
     * 
*

* This instance is immutable and unaffected by this method call. * * @param temporal the target object to be adjusted, not null * @return the adjusted object, not null * @throws DateTimeException if unable to make the adjustment * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal adjustInto(Temporal temporal) { if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) { throw new DateTimeException("Adjustment only supported on ISO date-time"); } return temporal.with(DAY_OF_YEAR, day); } //----------------------------------------------------------------------- /** * Combines this day-of-year with a year to create a {@code LocalDate}. *

* This returns a {@code LocalDate} formed from this day and the specified year. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = day.atYear(year);
     * 
*

* The day-of-year value 366 is only valid in a leap year. * * @param year the year to use, not null * @return the local date formed from this day and the specified year, not null * @throws DateTimeException if the year is invalid or this is day 366 and the year is not a leap year */ public LocalDate atYear(Year year) { Objects.requireNonNull(year, "year"); return year.atDay(day); } /** * Combines this day-of-year with a year to create a {@code LocalDate}. *

* This returns a {@code LocalDate} formed from this day and the specified year. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = day.atYear(year);
     * 
*

* The day-of-year value 366 is only valid in a leap year. * * @param year the year to use, from MIN_YEAR to MAX_YEAR * @return the local date formed from this day and the specified year, not null * @throws DateTimeException if the year is invalid or this is day 366 and the year is not a leap year */ public LocalDate atYear(int year) { return LocalDate.ofYearDay(year, day); } //----------------------------------------------------------------------- /** * Compares this day-of-year to another. *

* The comparison is based on the value of the day. * It is "consistent with equals", as defined by {@link Comparable}. * * @param other the other day-of-year instance, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(DayOfYear other) { return day - other.day; } //----------------------------------------------------------------------- /** * Checks if this day-of-year is equal to another day-of-year. * * @param obj the other day-of-year instance, null returns false * @return true if the day-of-year is the same */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof DayOfYear) { return day == ((DayOfYear) obj).day; } return false; } /** * A hash code for this day-of-year. * * @return a suitable hash code */ @Override public int hashCode() { return day; } //----------------------------------------------------------------------- /** * Outputs this day-of-year as a {@code String}. * * @return a string representation of this day-of-year, not null */ @Override public String toString() { return "DayOfYear:" + day; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Days.java000066400000000000000000000521221343451174100244100ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.DAYS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A day-based amount of time, such as '12 days'. *

* This class models a quantity or amount of time in terms of days. * It is a type-safe way of representing a number of days in an application. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Days implements TemporalAmount, Comparable, Serializable { /** * A constant for zero days. */ public static final Days ZERO = new Days(0); /** * A constant for one day. */ public static final Days ONE = new Days(1); /** * A serialization identifier for this class. */ private static final long serialVersionUID = -8903767091325669093L; /** * The number of days per week. */ private static final int DAYS_PER_WEEK = 7; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P" + "(?:([-+]?[0-9]+)W)?" + "(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE); /** * The number of days. */ private final int days; /** * Obtains a {@code Days} representing a number of days. *

* The resulting amount will have the specified days. * * @param days the number of days, positive or negative * @return the number of days, not null */ public static Days of(int days) { if (days == 0) { return ZERO; } else if (days == 1) { return ONE; } return new Days(days); } /** * Obtains a {@code Days} representing the number of days * equivalent to a number of weeks. *

* The resulting amount will be day-based, with the number of days * equal to the number of weeks multiplied by 7. * * @param weeks the number of weeks, positive or negative * @return the amount with the input weeks converted to days, not null * @throws ArithmeticException if numeric overflow occurs */ public static Days ofWeeks(int weeks) { if (weeks == 0) { return ZERO; } return new Days(Math.multiplyExact(weeks, DAYS_PER_WEEK)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Days} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Days}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to days using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Days} * @throws ArithmeticException if numeric overflow occurs */ public static Days from(TemporalAmount amount) { if (amount instanceof Days) { return (Days) amount; } Objects.requireNonNull(amount, "amount"); int days = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, DAYS); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of days: " + value + " " + unit); } days = Math.addExact(days, Math.toIntExact(converted[0])); } } return of(days); } //----------------------------------------------------------------------- /** * Obtains a {@code Days} from a text string such as {@code PnD}. *

* This will parse the string produced by {@code toString()} which is * based on the ISO-8601 period formats {@code PnD} and {@code PnW}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * There are then two sections, each consisting of a number and a suffix. * At least one of the two sections must be present. * The sections have suffixes in ASCII of "W" and "D" for weeks and days, * accepted in upper or lower case. The suffixes must occur in order. * The number part of each section must consist of ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. * The number must parse to an {@code int}. *

* The leading plus/minus sign, and negative values for weeks and days are * not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "P2D"             -- Days.of(2)
     *   "P-2D"            -- Days.of(-2)
     *   "-P2D"            -- Days.of(-2)
     *   "-P-2D"           -- Days.of(2)
     *   "P3W"             -- Days.of(3 * 7)
     *   "P3W-2D"          -- Days.of(3 * 7 - 2)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Days parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String weeksStr = matcher.group(2); String daysStr = matcher.group(3); if (weeksStr != null || daysStr != null) { int days = 0; if (daysStr != null) { try { days = Integer.parseInt(daysStr); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to a Days, non-numeric days", text, 0, ex); } } if (weeksStr != null) { try { int weeks = Math.multiplyExact(Integer.parseInt(weeksStr), DAYS_PER_WEEK); days = Math.addExact(days, weeks); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to a Days, non-numeric weeks", text, 0, ex); } } return of(Math.multiplyExact(days, negate)); } } throw new DateTimeParseException("Text cannot be parsed to a Days", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Days} consisting of the number of days between two dates. *

* The start date is included, but the end date is not. * The result of this method can be negative if the end is before the start. * * @param startDateInclusive the start date, inclusive, not null * @param endDateExclusive the end date, exclusive, not null * @return the number of days between this date and the end date, not null */ public static Days between(Temporal startDateInclusive, Temporal endDateExclusive) { return of(Math.toIntExact(DAYS.between(startDateInclusive, endDateExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of days. * * @param days the days to use */ private Days(int days) { super(); this.days = days; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Days.of(days); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#DAYS DAYS}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == DAYS) { return days; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#DAYS DAYS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} * to access the entire state of the amount. * * @return a list containing the days unit, not null */ @Override public List getUnits() { return Collections.singletonList(DAYS); } //----------------------------------------------------------------------- /** * Gets the number of days in this amount. * * @return the number of days */ public int getAmount() { return days; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Days#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Days} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Days plus(TemporalAmount amountToAdd) { return plus(Days.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of days added. *

* This instance is immutable and unaffected by this method call. * * @param days the amount of days to add, may be negative * @return a {@code Days} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Days plus(int days) { if (days == 0) { return this; } return of(Math.addExact(this.days, days)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Days#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Days} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Days minus(TemporalAmount amountToAdd) { return minus(Days.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of days subtracted. *

* This instance is immutable and unaffected by this method call. * * @param days the amount of days to add, may be negative * @return a {@code Days} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Days minus(int days) { if (days == 0) { return this; } return of(Math.subtractExact(this.days, days)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Days multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(days, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Days dividedBy(int divisor) { if (divisor == 1) { return this; } return of(days / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Days negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Days abs() { return days < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of days as a {@code Period}. *

* This returns a period with the same number of days. * * @return the equivalent period, not null */ public Period toPeriod() { return Period.ofDays(days); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the DAYS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (days != 0) { temporal = temporal.plus(days, DAYS); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the DAYS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (days != 0) { temporal = temporal.minus(days, DAYS); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Days}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Days otherAmount) { int thisValue = this.days; int otherValue = otherAmount.days; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Days}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Days) { Days other = (Days) otherAmount; return this.days == other.days; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return days; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of days. * This will be in the format 'PnD' where n is the number of days. * * @return the number of days in ISO-8601 string format */ @Override @ToString public String toString() { return "P" + days + "D"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Hours.java000066400000000000000000000520331343451174100246110ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.HOURS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A hour-based amount of time, such as '4 hours'. *

* This class models a quantity or amount of time in terms of hours. * It is a type-safe way of representing a number of hours in an application. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Hours implements TemporalAmount, Comparable, Serializable { /** * A constant for zero hours. */ public static final Hours ZERO = new Hours(0); /** * A serialization identifier for this class. */ private static final long serialVersionUID = -8494096666041369608L; /** * The number of hours per day. */ private static final int HOURS_PER_DAY = 24; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P" + "(?:([-+]?[0-9]+)D)?" + "(?:T" + "(?:([-+]?[0-9]+)H)?)?", Pattern.CASE_INSENSITIVE); /** * The number of hours. */ private final int hours; /** * Obtains an {@code Hours} representing a number of hours. *

* The resulting amount will have the specified hours. * * @param hours the number of hours, positive or negative * @return the number of hours, not null */ public static Hours of(int hours) { if (hours == 0) { return ZERO; } else { return new Hours(hours); } } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Hours} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Hours}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to hours using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Hours} * @throws ArithmeticException if numeric overflow occurs */ public static Hours from(TemporalAmount amount) { if (amount instanceof Hours) { return (Hours) amount; } Objects.requireNonNull(amount, "amount"); int hours = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, HOURS); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of hours: " + value + " " + unit); } hours = Math.addExact(hours, Math.toIntExact(converted[0])); } } return of(hours); } //----------------------------------------------------------------------- /** * Obtains a {@code Hours} from a text string such as {@code PTnH}. *

* This will parse the string produced by {@code toString()} and other * related formats based on ISO-8601 {@code PnDTnH}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * There are two sections consisting of a number and a suffix. * There is one section for days suffixed by "D", * followed by one section for hours suffixed by "H". * At least one section must be present. * If the hours section is present it must be prefixed by "T". * If the hours section is omitted the "T" must be omitted. * Letters must be in ASCII upper or lower case. * The number part of each section must consist of ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. * The number must parse to an {@code int}. *

* The leading plus/minus sign, and negative values for days and hours * are not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "PT2H"            -- Hours.of(2)
     *   "PT-HM"           -- Hours.of(-2)
     *   "-PT2H"           -- Hours.of(-2)
     *   "-PT-2H"          -- Hours.of(2)
     *   "P3D"             -- Hours.of(3 * 24)
     *   "P3DT2H"          -- Hours.of(3 * 24 + 2)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Hours parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String daysStr = matcher.group(2); String hoursStr = matcher.group(3); if (daysStr != null || hoursStr != null) { int hours = 0; if (hoursStr != null) { try { hours = Integer.parseInt(hoursStr); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Hours, non-numeric hours", text, 0, ex); } } if (daysStr != null) { try { int daysAsHours = Math.multiplyExact(Integer.parseInt(daysStr), HOURS_PER_DAY); hours = Math.addExact(hours, daysAsHours); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Hours, non-numeric days", text, 0, ex); } } return of(Math.multiplyExact(hours, negate)); } } throw new DateTimeParseException("Text cannot be parsed to Hours", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Hours} consisting of the number of hours between two temporals. *

* The start temporal is included, but the end temporal is not. * The result of this method can be negative if the end is before the start. * * @param startInclusive the start temporal, inclusive, not null * @param endExclusive the end temporal, exclusive, not null * @return the number of hours between the start and end temporals, not null */ public static Hours between(Temporal startInclusive, Temporal endExclusive) { return of(Math.toIntExact(HOURS.between(startInclusive, endExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of hours. * * @param hours the amount of hours */ private Hours(int hours) { this.hours = hours; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Hours.of(hours); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#HOURS HOURS}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == HOURS) { return hours; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#HOURS HOURS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} to * access the entire state of the amount. * * @return a list containing the hours unit, not null */ @Override public List getUnits() { return Collections.singletonList(HOURS); } //----------------------------------------------------------------------- /** * Gets the number of hours in this amount. * * @return the number of hours */ public int getAmount() { return hours; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Hours#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Hours} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Hours plus(TemporalAmount amountToAdd) { return plus(Hours.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of hours added. *

* This instance is immutable and unaffected by this method call. * * @param hours the amount of hours to add, may be negative * @return a {@code Hours} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Hours plus(int hours) { if (hours == 0) { return this; } return of(Math.addExact(this.hours, hours)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Hours#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Hours} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Hours minus(TemporalAmount amountToAdd) { return minus(Hours.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of hours subtracted. *

* This instance is immutable and unaffected by this method call. * * @param hours the amount of hours to add, may be negative * @return a {@code Hours} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Hours minus(int hours) { if (hours == 0) { return this; } return of(Math.subtractExact(this.hours, hours)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Hours multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(hours, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Hours dividedBy(int divisor) { if (divisor == 1) { return this; } return of(hours / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Hours negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Hours abs() { return hours < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of hours as a {@code Duration}. *

* This returns a duration with the same number of hours. * * @return the equivalent duration, not null * @deprecated Use {@link #toDuration()} */ @Deprecated public Duration toPeriod() { return Duration.ofHours(hours); } //------------------------------------------------------------------------- /** * Gets the number of hours as a {@code Duration}. *

* This returns a duration with the same number of hours. * * @return the equivalent duration, not null */ public Duration toDuration() { return Duration.ofHours(hours); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the HOURS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (hours != 0) { temporal = temporal.plus(hours, HOURS); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the HOURS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (hours != 0) { temporal = temporal.minus(hours, HOURS); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Hours}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Hours otherAmount) { int thisValue = this.hours; int otherValue = otherAmount.hours; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Hours}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Hours) { Hours other = (Hours) otherAmount; return this.hours == other.hours; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return hours; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of hours. * This will be in the format 'PTnH' where n is the number of hours. * * @return the number of hours in ISO-8601 string format */ @Override @ToString public String toString() { return "PT" + hours + "H"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Interval.java000066400000000000000000000605541343451174100253040ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; import java.util.Objects; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * An immutable interval of time between two instants. *

* An interval represents the time on the time-line between two {@link Instant}s. * The class stores the start and end instants, with the start inclusive and the end exclusive. * The end instant is always greater than or equal to the start instant. *

* The {@link Duration} of an interval can be obtained, but is a separate concept. * An interval is connected to the time-line, whereas a duration is not. *

* Intervals are not comparable. To compare the length of two intervals, it is * generally recommended to compare their durations. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Interval implements Serializable { /** * An interval over the whole time-line. */ public static final Interval ALL = new Interval(Instant.MIN, Instant.MAX); /** * Serialization version. */ private static final long serialVersionUID = 8375285238652L; /** * The start instant (inclusive). */ private final Instant start; /** * The end instant (exclusive). */ private final Instant end; //----------------------------------------------------------------------- /** * Obtains an instance of {@code Interval} from the start and end instant. *

* The end instant must not be before the start instant. * * @param startInclusive the start instant, inclusive, MIN_DATE treated as unbounded, not null * @param endExclusive the end instant, exclusive, MAX_DATE treated as unbounded, not null * @return the half-open interval, not null * @throws DateTimeException if the end is before the start */ public static Interval of(Instant startInclusive, Instant endExclusive) { Objects.requireNonNull(startInclusive, "startInclusive"); Objects.requireNonNull(endExclusive, "endExclusive"); if (endExclusive.isBefore(startInclusive)) { throw new DateTimeException("End instant must be equal or after start instant"); } return new Interval(startInclusive, endExclusive); } /** * Obtains an instance of {@code Interval} from the start and a duration. *

* The end instant is calculated as the start plus the duration. * The duration must not be negative. * * @param startInclusive the start instant, inclusive, not null * @param duration the duration from the start to the end, not null * @return the interval, not null * @throws DateTimeException if the end is before the start, * or if the duration addition cannot be made * @throws ArithmeticException if numeric overflow occurs when adding the duration */ public static Interval of(Instant startInclusive, Duration duration) { Objects.requireNonNull(startInclusive, "startInclusive"); Objects.requireNonNull(duration, "duration"); if (duration.isNegative()) { throw new DateTimeException("Duration must not be negative"); } return new Interval(startInclusive, startInclusive.plus(duration)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Interval} from a text string such as * {@code 2007-12-03T10:15:30Z/2007-12-04T10:15:30Z}, where the end instant is exclusive. *

* The string must consist of one of the following four formats: *

    *
  • a representations of an {@link OffsetDateTime}, followed by a forward slash, * followed by a representation of a {@link OffsetDateTime} *
  • a representations of an {@link OffsetDateTime}, followed by a forward slash, * followed by a representation of a {@link LocalDateTime}, where the end offset is implied. *
  • a representation of an {@link OffsetDateTime}, followed by a forward slash, * followed by a representation of a {@link PeriodDuration} *
  • a representation of a {@link PeriodDuration}, followed by a forward slash, * followed by a representation of an {@link OffsetDateTime} *
*

* ISO-8601 supports a very wide range of possible inputs, many of which are not supported here. * For example, basic format, week-based dates, ordinal dates and date-style period formats are not supported. * * @param text the text to parse, not null * @return the parsed interval, not null * @throws DateTimeParseException if the text cannot be parsed */ @FromString public static Interval parse(CharSequence text) { Objects.requireNonNull(text, "text"); for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '/') { return parseSplit(text.subSequence(0, i), text.subSequence(i + 1, text.length())); } } throw new DateTimeParseException("Interval cannot be parsed, no forward slash found", text, 0); } private static Interval parseSplit(CharSequence startStr, CharSequence endStr) { char firstChar = startStr.charAt(0); if (firstChar == 'P' || firstChar == 'p') { // duration followed by instant PeriodDuration amount = PeriodDuration.parse(startStr); try { OffsetDateTime end = OffsetDateTime.parse(endStr); return Interval.of(end.minus(amount).toInstant(), end.toInstant()); } catch (DateTimeParseException ex) { // handle case where Instant is outside the bounds of OffsetDateTime Instant end = Instant.parse(endStr); // addition of PeriodDuration only supported by OffsetDateTime, // but to make that work need to move point being subtracted from closer to EPOCH long move = end.isBefore(Instant.EPOCH) ? 1000 * 86400 : -1000 * 86400; Instant start = end.plusSeconds(move).atOffset(ZoneOffset.UTC).minus(amount).toInstant().minusSeconds(move); return Interval.of(start, end); } } // instant followed by instant or duration OffsetDateTime start; try { start = OffsetDateTime.parse(startStr); } catch (DateTimeParseException ex) { return parseStartExtended(startStr, endStr); } if (endStr.length() > 0) { char c = endStr.charAt(0); if (c == 'P' || c == 'p') { PeriodDuration amount = PeriodDuration.parse(endStr); return Interval.of(start.toInstant(), start.plus(amount).toInstant()); } } return parseEndDateTime(start.toInstant(), start.getOffset(), endStr); } // handle case where Instant is outside the bounds of OffsetDateTime private static Interval parseStartExtended(CharSequence startStr, CharSequence endStr) { Instant start = Instant.parse(startStr); if (endStr.length() > 0) { char c = endStr.charAt(0); if (c == 'P' || c == 'p') { PeriodDuration amount = PeriodDuration.parse(endStr); // addition of PeriodDuration only supported by OffsetDateTime, // but to make that work need to move point being added to closer to EPOCH long move = start.isBefore(Instant.EPOCH) ? 1000 * 86400 : -1000 * 86400; Instant end = start.plusSeconds(move).atOffset(ZoneOffset.UTC).plus(amount).toInstant().minusSeconds(move); return Interval.of(start, end); } } // infer offset from start if not specified by end return parseEndDateTime(start, ZoneOffset.UTC, endStr); } // parse when there are two date-times private static Interval parseEndDateTime(Instant start, ZoneOffset offset, CharSequence endStr) { try { TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest(endStr, OffsetDateTime::from, LocalDateTime::from); if (temporal instanceof OffsetDateTime) { OffsetDateTime odt = (OffsetDateTime) temporal; return Interval.of(start, odt.toInstant()); } else { // infer offset from start if not specified by end LocalDateTime ldt = (LocalDateTime) temporal; return Interval.of(start, ldt.toInstant(offset)); } } catch (DateTimeParseException ex) { Instant end = Instant.parse(endStr); return Interval.of(start, end); } } //----------------------------------------------------------------------- /** * Constructor. * * @param startInclusive the start instant, inclusive, validated not null * @param endExclusive the end instant, exclusive, validated not null */ private Interval(Instant startInclusive, Instant endExclusive) { this.start = startInclusive; this.end = endExclusive; } //----------------------------------------------------------------------- /** * Gets the start of this time interval, inclusive. *

* This will return {@link Instant#MIN} if the range is unbounded at the start. * In this case, the range includes all dates into the far-past. * * @return the start of the time interval */ public Instant getStart() { return start; } /** * Gets the end of this time interval, exclusive. *

* This will return {@link Instant#MAX} if the range is unbounded at the end. * In this case, the range includes all dates into the far-future. * * @return the end of the time interval, exclusive */ public Instant getEnd() { return end; } //----------------------------------------------------------------------- /** * Checks if the range is empty. *

* An empty range occurs when the start date equals the inclusive end date. * * @return true if the range is empty */ public boolean isEmpty() { return start.equals(end); } /** * Checks if the start of the interval is unbounded. * * @return true if start is unbounded */ public boolean isUnboundedStart() { return start.equals(Instant.MIN); } /** * Checks if the end of the interval is unbounded. * * @return true if end is unbounded */ public boolean isUnboundedEnd() { return end.equals(Instant.MAX); } //----------------------------------------------------------------------- /** * Returns a copy of this range with the specified start instant. * * @param start the start instant for the new interval, not null * @return an interval with the end from this interval and the specified start * @throws DateTimeException if the resulting interval has end before start */ public Interval withStart(Instant start) { return Interval.of(start, end); } /** * Returns a copy of this range with the specified end instant. * * @param end the end instant for the new interval, not null * @return an interval with the start from this interval and the specified end * @throws DateTimeException if the resulting interval has end before start */ public Interval withEnd(Instant end) { return Interval.of(start, end); } //----------------------------------------------------------------------- /** * Checks if this interval contains the specified instant. *

* This checks if the specified instant is within the bounds of this interval. * If this range has an unbounded start then {@code contains(Instant#MIN)} returns true. * If this range has an unbounded end then {@code contains(Instant#MAX)} returns true. * If this range is empty then this method always returns false. * * @param instant the instant, not null * @return true if this interval contains the instant */ public boolean contains(Instant instant) { Objects.requireNonNull(instant, "instant"); return start.compareTo(instant) <= 0 && (instant.compareTo(end) < 0 || isUnboundedEnd()); } /** * Checks if this interval encloses the specified interval. *

* This checks if the bounds of the specified interval are within the bounds of this interval. * An empty interval encloses itself. * * @param other the other interval, not null * @return true if this interval contains the other interval */ public boolean encloses(Interval other) { Objects.requireNonNull(other, "other"); return start.compareTo(other.start) <= 0 && other.end.compareTo(end) <= 0; } /** * Checks if this interval abuts the specified interval. *

* The result is true if the end of this interval is the start of the other, or vice versa. * An empty interval does not abut itself. * * @param other the other interval, not null * @return true if this interval abuts the other interval */ public boolean abuts(Interval other) { Objects.requireNonNull(other, "other"); return end.equals(other.start) ^ start.equals(other.end); } /** * Checks if this interval is connected to the specified interval. *

* The result is true if the two intervals have an enclosed interval in common, even if that interval is empty. * An empty interval is connected to itself. *

* This is equivalent to {@code (overlaps(other) || abuts(other))}. * * @param other the other interval, not null * @return true if this interval is connected to the other interval */ public boolean isConnected(Interval other) { Objects.requireNonNull(other, "other"); return this.equals(other) || (start.compareTo(other.end) <= 0 && other.start.compareTo(end) <= 0); } /** * Checks if this interval overlaps the specified interval. *

* The result is true if the the two intervals share some part of the time-line. * An empty interval overlaps itself. *

* This is equivalent to {@code (isConnected(other) && !abuts(other))}. * * @param other the time interval to compare to, null means a zero length interval now * @return true if the time intervals overlap */ public boolean overlaps(Interval other) { Objects.requireNonNull(other, "other"); return other.equals(this) || (start.compareTo(other.end) < 0 && other.start.compareTo(end) < 0); } //----------------------------------------------------------------------- /** * Calculates the interval that is the intersection of this interval and the specified interval. *

* This finds the intersection of two intervals. * This throws an exception if the two intervals are not {@linkplain #isConnected(Interval) connected}. * * @param other the other interval to check for, not null * @return the interval that is the intersection of the two intervals * @throws DateTimeException if the intervals do not connect */ public Interval intersection(Interval other) { Objects.requireNonNull(other, "other"); if (isConnected(other) == false) { throw new DateTimeException("Intervals do not connect: " + this + " and " + other); } int cmpStart = start.compareTo(other.start); int cmpEnd = end.compareTo(other.end); if (cmpStart >= 0 && cmpEnd <= 0) { return this; } else if (cmpStart <= 0 && cmpEnd >= 0) { return other; } else { Instant newStart = (cmpStart >= 0 ? start : other.start); Instant newEnd = (cmpEnd <= 0 ? end : other.end); return Interval.of(newStart, newEnd); } } /** * Calculates the interval that is the union of this interval and the specified interval. *

* This finds the union of two intervals. * This throws an exception if the two intervals are not {@linkplain #isConnected(Interval) connected}. * * @param other the other interval to check for, not null * @return the interval that is the union of the two intervals * @throws DateTimeException if the intervals do not connect */ public Interval union(Interval other) { Objects.requireNonNull(other, "other"); if (isConnected(other) == false) { throw new DateTimeException("Intervals do not connect: " + this + " and " + other); } int cmpStart = start.compareTo(other.start); int cmpEnd = end.compareTo(other.end); if (cmpStart >= 0 && cmpEnd <= 0) { return other; } else if (cmpStart <= 0 && cmpEnd >= 0) { return this; } else { Instant newStart = (cmpStart >= 0 ? other.start : start); Instant newEnd = (cmpEnd <= 0 ? other.end : end); return Interval.of(newStart, newEnd); } } /** * Calculates the smallest interval that encloses this interval and the specified interval. *

* The result of this method will {@linkplain #encloses(Interval) enclose} * this interval and the specified interval. * * @param other the other interval to check for, not null * @return the interval that spans the two intervals */ public Interval span(Interval other) { Objects.requireNonNull(other, "other"); int cmpStart = start.compareTo(other.start); int cmpEnd = end.compareTo(other.end); Instant newStart = (cmpStart >= 0 ? other.start : start); Instant newEnd = (cmpEnd <= 0 ? other.end : end); return Interval.of(newStart, newEnd); } //------------------------------------------------------------------------- /** * Checks if this interval is after the specified instant. *

* The result is true if this instant starts after the specified instant. * An empty interval behaves as though it is an instant for comparison purposes. * * @param instant the other instant to compare to, not null * @return true if the start of this interval is after the specified instant */ public boolean isAfter(Instant instant) { return start.compareTo(instant) > 0; } /** * Checks if this interval is before the specified instant. *

* The result is true if this instant ends before the specified instant. * Since intervals do not include their end points, this will return true if the * instant equals the end of the interval. * An empty interval behaves as though it is an instant for comparison purposes. * * @param instant the other instant to compare to, not null * @return true if the start of this interval is before the specified instant */ public boolean isBefore(Instant instant) { return end.compareTo(instant) <= 0 && start.compareTo(instant) < 0; } //------------------------------------------------------------------------- /** * Checks if this interval is after the specified interval. *

* The result is true if this instant starts after the end of the specified interval. * Since intervals do not include their end points, this will return true if the * instant equals the end of the interval. * An empty interval behaves as though it is an instant for comparison purposes. * * @param interval the other interval to compare to, not null * @return true if this instant is after the specified instant */ public boolean isAfter(Interval interval) { return start.compareTo(interval.end) >= 0 && !interval.equals(this); } /** * Checks if this interval is before the specified interval. *

* The result is true if this instant ends before the start of the specified interval. * Since intervals do not include their end points, this will return true if the * two intervals abut. * An empty interval behaves as though it is an instant for comparison purposes. * * @param interval the other interval to compare to, not null * @return true if this instant is before the specified instant */ public boolean isBefore(Interval interval) { return end.compareTo(interval.start) <= 0 && !interval.equals(this); } //----------------------------------------------------------------------- /** * Obtains the duration of this interval. *

* An {@code Interval} is associated with two specific instants on the time-line. * A {@code Duration} is simply an amount of time, separate from the time-line. * * @return the duration of the time interval * @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration} */ public Duration toDuration() { return Duration.between(start, end); } //----------------------------------------------------------------------- /** * Checks if this interval is equal to another interval. *

* Compares this {@code Interval} with another ensuring that the two instants are the same. * Only objects of type {@code Interval} are compared, other types return false. * * @param obj the object to check, null returns false * @return true if this is equal to the other interval */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof Interval) { Interval other = (Interval) obj; return start.equals(other.start) && end.equals(other.end); } return false; } /** * A hash code for this interval. * * @return a suitable hash code */ @Override public int hashCode() { return start.hashCode() ^ end.hashCode(); } //----------------------------------------------------------------------- /** * Outputs this interval as a {@code String}, such as {@code 2007-12-03T10:15:30/2007-12-04T10:15:30}. *

* The output will be the ISO-8601 format formed by combining the * {@code toString()} methods of the two instants, separated by a forward slash. * * @return a string representation of this instant, not null */ @Override @ToString public String toString() { return start.toString() + '/' + end.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/LocalDateRange.java000066400000000000000000000760761343451174100263330ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAdjuster; import java.util.Comparator; import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A range of local dates. *

* A {@code LocalDateRange} represents a range of dates, from a start date to an end date. * Instances can be constructed from either a half-open or a closed range of dates. * Internally, the class stores the start and end dates, with the start inclusive and the end exclusive. * The end date is always greater than or equal to the start date. *

* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used * to indicate an unbounded far-past or far-future. Note that there is no difference * between a half-open and a closed range when the end is {@code LocalDate.MAX}. * Empty ranges are allowed. *

* No range can end at {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}. * No range can start at {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}. * No empty range can exist at {@code LocalDate.MIN} or {@code LocalDate.MAX}. *

* Date ranges are not comparable. To compare the length of two ranges, it is * generally recommended to compare the number of days they contain. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class LocalDateRange implements Serializable { /** * The day after the MIN date. */ private static final LocalDate MINP1 = LocalDate.MIN.plusDays(1); /** * The day before the MAX date. */ private static final LocalDate MAXM1 = LocalDate.MAX.minusDays(1); /** * A range over the whole time-line. */ public static final LocalDateRange ALL = new LocalDateRange(LocalDate.MIN, LocalDate.MAX); /** * Serialization version. */ private static final long serialVersionUID = 3358656715467L; /** * The start date (inclusive). */ private final LocalDate start; /** * The end date (exclusive). */ private final LocalDate end; //----------------------------------------------------------------------- /** * Obtains a half-open range of dates, including the start and excluding the end. *

* The range includes the start date and excludes the end date, unless the end is {@code LocalDate.MAX}. * The end date must be equal to or after the start date. * This definition permits an empty range located at a specific date. *

* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used * to indicate an unbounded far-past or far-future. *

* The start inclusive date must not be {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}. * The end inclusive date must not be {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}. * No empty range can exist at {@code LocalDate.MIN} or {@code LocalDate.MAX}. * * @param startInclusive the inclusive start date, not null * @param endExclusive the exclusive end date, not null * @return the half-open range, not null * @throws DateTimeException if the end is before the start, * or the start date is {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}, * or the end date is {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)} */ public static LocalDateRange of(LocalDate startInclusive, LocalDate endExclusive) { Objects.requireNonNull(startInclusive, "startInclusive"); Objects.requireNonNull(endExclusive, "endExclusive"); return new LocalDateRange(startInclusive, endExclusive); } /** * Obtains a closed range of dates, including the start and end. *

* The range includes the start date and the end date. * The end date must be equal to or after the start date. *

* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used * to indicate an unbounded far-past or far-future. In addition, an end date of * {@code LocalDate.MAX.minusDays(1)} will also create an unbounded far-future range. *

* The start inclusive date must not be {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}. * The end inclusive date must not be {@code LocalDate.MIN}. * * @param startInclusive the inclusive start date, not null * @param endInclusive the inclusive end date, not null * @return the closed range * @throws DateTimeException if the end is before the start, * or the start date is {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}, * or the end date is {@code LocalDate.MIN} */ public static LocalDateRange ofClosed(LocalDate startInclusive, LocalDate endInclusive) { Objects.requireNonNull(startInclusive, "startInclusive"); Objects.requireNonNull(endInclusive, "endInclusive"); if (endInclusive.isBefore(startInclusive)) { throw new DateTimeException("Start date must be on or before end date"); } LocalDate end = (endInclusive.equals(LocalDate.MAX) ? LocalDate.MAX : endInclusive.plusDays(1)); return new LocalDateRange(startInclusive, end); } /** * Obtains an instance of {@code LocalDateRange} from the start and a period. *

* The end date is calculated as the start plus the duration. * The period must not be negative. *

* The constant {@code LocalDate.MIN} can be used to indicate an unbounded far-past. *

* The period must not be zero or one day when the start date is {@code LocalDate.MIN}. * * @param startInclusive the inclusive start date, not null * @param period the period from the start to the end, not null * @return the range, not null * @throws DateTimeException if the end is before the start, * or if the period addition cannot be made * @throws ArithmeticException if numeric overflow occurs when adding the period */ public static LocalDateRange of(LocalDate startInclusive, Period period) { Objects.requireNonNull(startInclusive, "startInclusive"); Objects.requireNonNull(period, "period"); if (period.isNegative()) { throw new DateTimeException("Period must not be zero or negative"); } return new LocalDateRange(startInclusive, startInclusive.plus(period)); } /** * Obtains an empty date range located at the specified date. *

* The empty range has zero length and contains no other dates or ranges. * An empty range cannot be located at {@code LocalDate.MIN}, {@code LocalDate.MIN.plusDays(1)}, * {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}. * * @param date the date where the empty range is located, not null * @return the empty range, not null * @throws DateTimeException if the date is {@code LocalDate.MIN}, {@code LocalDate.MIN.plusDays(1)}, * {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)} */ public static LocalDateRange ofEmpty(LocalDate date) { Objects.requireNonNull(date, "date"); return new LocalDateRange(date, date); } /** * Obtains a range that is unbounded at the start and end. * * @return the range, with an unbounded start and unbounded end */ public static LocalDateRange ofUnbounded() { return ALL; } /** * Obtains a range up to, but not including, the specified end date. *

* The range includes all dates from the unbounded start, denoted by {@code LocalDate.MIN}, to the end date. * The end date is exclusive and cannot be {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}. * * @param endExclusive the exclusive end date, {@code LocalDate.MAX} treated as unbounded, not null * @return the range, with an unbounded start * @throws DateTimeException if the end date is {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)} */ public static LocalDateRange ofUnboundedStart(LocalDate endExclusive) { return LocalDateRange.of(LocalDate.MIN, endExclusive); } /** * Obtains a range from and including the specified start date. *

* The range includes all dates from the start date to the unbounded end, denoted by {@code LocalDate.MAX}. * The start date is inclusive and cannot be {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}. * * @param startInclusive the inclusive start date, {@code LocalDate.MIN} treated as unbounded, not null * @return the range, with an unbounded end * @throws DateTimeException if the start date is {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)} */ public static LocalDateRange ofUnboundedEnd(LocalDate startInclusive) { return LocalDateRange.of(startInclusive, LocalDate.MAX); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code LocalDateRange} from a text string such as * {@code 2007-12-03/2007-12-04}, where the end date is exclusive. *

* The string must consist of one of the following three formats: *

    *
  • a representations of an {@link LocalDate}, followed by a forward slash, * followed by a representation of a {@link LocalDate} *
  • a representation of an {@link LocalDate}, followed by a forward slash, * followed by a representation of a {@link Period} *
  • a representation of a {@link Period}, followed by a forward slash, * followed by a representation of an {@link LocalDate} *
* * @param text the text to parse, not null * @return the parsed range, not null * @throws DateTimeParseException if the text cannot be parsed */ @FromString public static LocalDateRange parse(CharSequence text) { Objects.requireNonNull(text, "text"); for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '/') { char firstChar = text.charAt(0); if (firstChar == 'P' || firstChar == 'p') { // period followed by date Period duration = Period.parse(text.subSequence(0, i)); LocalDate end = LocalDate.parse(text.subSequence(i + 1, text.length())); return LocalDateRange.of(end.minus(duration), end); } else { // date followed by date or period LocalDate start = LocalDate.parse(text.subSequence(0, i)); if (i + 1 < text.length()) { char c = text.charAt(i + 1); if (c == 'P' || c == 'p') { Period duration = Period.parse(text.subSequence(i + 1, text.length())); return LocalDateRange.of(start, start.plus(duration)); } } LocalDate end = LocalDate.parse(text.subSequence(i + 1, text.length())); return LocalDateRange.of(start, end); } } } throw new DateTimeParseException("LocalDateRange cannot be parsed, no forward slash found", text, 0); } //----------------------------------------------------------------------- /** * Constructor. * * @param startInclusive the start date, inclusive, validated not null * @param endExclusive the end date, exclusive, validated not null */ private LocalDateRange(LocalDate startInclusive, LocalDate endExclusive) { if (endExclusive.isBefore(startInclusive)) { throw new DateTimeException("End date must be on or after start date"); } if (startInclusive.equals(MAXM1)) { throw new DateTimeException("Range must not start at LocalDate.MAX.minusDays(1)"); } if (endExclusive.equals(MINP1)) { throw new DateTimeException("Range must not end at LocalDate.MIN.plusDays(1)"); } if (endExclusive.equals(LocalDate.MIN) || startInclusive.equals(LocalDate.MAX)) { throw new DateTimeException("Empty range must not be at LocalDate.MIN or LocalDate.MAX"); } this.start = startInclusive; this.end = endExclusive; } //----------------------------------------------------------------------- /** * Gets the start date of this range, inclusive. *

* This will return {@code LocalDate#MIN} if the range is unbounded at the start. * In this case, the range includes all dates into the far-past. *

* This never returns {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}. * * @return the start date */ public LocalDate getStart() { return start; } /** * Gets the end date of this range, exclusive. *

* This will return {@code LocalDate.MAX} if the range is unbounded at the end. * In this case, the range includes all dates into the far-future. *

* This never returns {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}. * * @return the end date, exclusive */ public LocalDate getEnd() { return end; } /** * Gets the end date of this range, inclusive. *

* This will return {@code LocalDate.MAX} if the range is unbounded at the end. * In this case, the range includes all dates into the far-future. *

* This returns the date before the end date. *

* This never returns {@code LocalDate.MIN}. * * @return the end date, inclusive */ public LocalDate getEndInclusive() { if (isUnboundedEnd()) { return LocalDate.MAX; } return end.minusDays(1); } //----------------------------------------------------------------------- /** * Checks if the range is empty. *

* An empty range occurs when the start date equals the end date. *

* An empty range is never unbounded. * * @return true if the range is empty */ public boolean isEmpty() { return start.equals(end); } /** * Checks if the start of the range is unbounded. *

* An unbounded range is never empty. * * @return true if start is unbounded */ public boolean isUnboundedStart() { return start.equals(LocalDate.MIN); } /** * Checks if the end of the range is unbounded. *

* An unbounded range is never empty. * * @return true if end is unbounded */ public boolean isUnboundedEnd() { return end.equals(LocalDate.MAX); } //----------------------------------------------------------------------- /** * Returns a copy of this range with the start date adjusted. *

* This returns a new instance with the start date altered. * Since {@code LocalDate} implements {@code TemporalAdjuster} any * local date can simply be passed in. *

* For example, to adjust the start to one week earlier: *

     *  range = range.withStart(date -> date.minus(1, ChronoUnit.WEEKS));
     * 
* * @param adjuster the adjuster to use, not null * @return a copy of this range with the start date adjusted * @throws DateTimeException if the new start date is after the current end date */ public LocalDateRange withStart(TemporalAdjuster adjuster) { return LocalDateRange.of(start.with(adjuster), end); } /** * Returns a copy of this range with the end date adjusted. *

* This returns a new instance with the exclusive end date altered. * Since {@code LocalDate} implements {@code TemporalAdjuster} any * local date can simply be passed in. *

* For example, to adjust the end to one week later: *

     *  range = range.withEnd(date -> date.plus(1, ChronoUnit.WEEKS));
     * 
* * @param adjuster the adjuster to use, not null * @return a copy of this range with the end date adjusted * @throws DateTimeException if the new end date is before the current start date */ public LocalDateRange withEnd(TemporalAdjuster adjuster) { return LocalDateRange.of(start, end.with(adjuster)); } //----------------------------------------------------------------------- /** * Checks if this range contains the specified date. *

* This checks if the specified date is within the bounds of this range. * If this range is empty then this method always returns false. * Else if this range has an unbounded start then {@code contains(LocalDate#MIN)} returns true. * Else if this range has an unbounded end then {@code contains(LocalDate#MAX)} returns true. * * @param date the date to check for, not null * @return true if this range contains the date */ public boolean contains(LocalDate date) { Objects.requireNonNull(date, "date"); return start.compareTo(date) <= 0 && (date.compareTo(end) < 0 || isUnboundedEnd()); } /** * Checks if this range encloses the specified range. *

* This checks if the bounds of the specified range are within the bounds of this range. * An empty range encloses itself. * * @param other the other range to check for, not null * @return true if this range contains all dates in the other range */ public boolean encloses(LocalDateRange other) { Objects.requireNonNull(other, "other"); return start.compareTo(other.start) <= 0 && other.end.compareTo(end) <= 0; } /** * Checks if this range abuts the specified range. *

* The result is true if the end of this range is the start of the other, or vice versa. * An empty range does not abut itself. * * @param other the other range, not null * @return true if this range abuts the other range */ public boolean abuts(LocalDateRange other) { Objects.requireNonNull(other, "other"); return end.equals(other.start) ^ start.equals(other.end); } /** * Checks if this range is connected to the specified range. *

* The result is true if the two ranges have an enclosed range in common, even if that range is empty. * An empty range is connected to itself. *

* This is equivalent to {@code (overlaps(other) || abuts(other))}. * * @param other the other range, not null * @return true if this range is connected to the other range */ public boolean isConnected(LocalDateRange other) { Objects.requireNonNull(other, "other"); return this.equals(other) || (start.compareTo(other.end) <= 0 && other.start.compareTo(end) <= 0); } /** * Checks if this range overlaps the specified range. *

* The result is true if the the two ranges share some part of the time-line. * An empty range overlaps itself. *

* This is equivalent to {@code (isConnected(other) && !abuts(other))}. * * @param other the time range to compare to, null means a zero length range now * @return true if the time ranges overlap */ public boolean overlaps(LocalDateRange other) { Objects.requireNonNull(other, "other"); return other.equals(this) || (start.compareTo(other.end) < 0 && other.start.compareTo(end) < 0); } //----------------------------------------------------------------------- /** * Calculates the range that is the intersection of this range and the specified range. *

* This finds the intersection of two ranges. * This throws an exception if the two ranges are not {@linkplain #isConnected(LocalDateRange) connected}. * * @param other the other range to check for, not null * @return the range that is the intersection of the two ranges * @throws DateTimeException if the ranges do not connect */ public LocalDateRange intersection(LocalDateRange other) { Objects.requireNonNull(other, "other"); if (isConnected(other) == false) { throw new DateTimeException("Ranges do not connect: " + this + " and " + other); } int cmpStart = start.compareTo(other.start); int cmpEnd = end.compareTo(other.end); if (cmpStart >= 0 && cmpEnd <= 0) { return this; } else if (cmpStart <= 0 && cmpEnd >= 0) { return other; } else { LocalDate newStart = (cmpStart >= 0 ? start : other.start); LocalDate newEnd = (cmpEnd <= 0 ? end : other.end); return LocalDateRange.of(newStart, newEnd); } } /** * Calculates the range that is the union of this range and the specified range. *

* This finds the union of two ranges. * This throws an exception if the two ranges are not {@linkplain #isConnected(LocalDateRange) connected}. * * @param other the other range to check for, not null * @return the range that is the union of the two ranges * @throws DateTimeException if the ranges do not connect */ public LocalDateRange union(LocalDateRange other) { Objects.requireNonNull(other, "other"); if (isConnected(other) == false) { throw new DateTimeException("Ranges do not connect: " + this + " and " + other); } int cmpStart = start.compareTo(other.start); int cmpEnd = end.compareTo(other.end); if (cmpStart >= 0 && cmpEnd <= 0) { return other; } else if (cmpStart <= 0 && cmpEnd >= 0) { return this; } else { LocalDate newStart = (cmpStart >= 0 ? other.start : start); LocalDate newEnd = (cmpEnd <= 0 ? other.end : end); return LocalDateRange.of(newStart, newEnd); } } /** * Calculates the smallest range that encloses this range and the specified range. *

* The result of this method will {@linkplain #encloses(LocalDateRange) enclose} * this range and the specified range. * * @param other the other range to check for, not null * @return the range that spans the two ranges */ public LocalDateRange span(LocalDateRange other) { Objects.requireNonNull(other, "other"); int cmpStart = start.compareTo(other.start); int cmpEnd = end.compareTo(other.end); LocalDate newStart = (cmpStart >= 0 ? other.start : start); LocalDate newEnd = (cmpEnd <= 0 ? other.end : end); return LocalDateRange.of(newStart, newEnd); } //----------------------------------------------------------------------- /** * Streams the set of dates included in the range. *

* This returns a stream consisting of each date in the range. * The stream is ordered. * * @return the stream of dates from the start to the end */ public Stream stream() { long count = end.toEpochDay() - start.toEpochDay() + (isUnboundedEnd() ? 1 : 0); Spliterator spliterator = new Spliterators.AbstractSpliterator( count, Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SORTED | Spliterator.SIZED | Spliterator.SUBSIZED) { private LocalDate current = start; @Override public boolean tryAdvance(Consumer action) { if (current != null) { if (current.isBefore(end)) { action.accept(current); current = current.plusDays(1); return true; } if (current.equals(LocalDate.MAX)) { action.accept(LocalDate.MAX); current = null; return true; } } return false; } @Override public Comparator getComparator() { return null; } }; return StreamSupport.stream(spliterator, false); } //----------------------------------------------------------------------- /** * Checks if this range is after the specified date. *

* The result is true if every date in this range is after the specified date. * An empty range behaves as though it is a date for comparison purposes. * * @param date the other date to compare to, not null * @return true if the start of this range is after the specified date */ public boolean isAfter(LocalDate date) { return start.compareTo(date) > 0; } /** * Checks if this range is before the specified date. *

* The result is true if every date in this range is before the specified date. * An empty range behaves as though it is a date for comparison purposes. * * @param date the other date to compare to, not null * @return true if the start of this range is before the specified date */ public boolean isBefore(LocalDate date) { return end.compareTo(date) <= 0 && start.compareTo(date) < 0; } //----------------------------------------------------------------------- /** * Checks if this range is after the specified range. *

* The result is true if every date in this range is after every date in the specified range. * An empty range behaves as though it is a date for comparison purposes. * * @param other the other range to compare to, not null * @return true if every date in this range is after every date in the other range */ public boolean isAfter(LocalDateRange other) { return start.compareTo(other.end) >= 0 && !other.equals(this); } /** * Checks if this range is before the specified range. *

* The result is true if every date in this range is before every date in the specified range. * An empty range behaves as though it is a date for comparison purposes. * * @param range the other range to compare to, not null * @return true if every date in this range is before every date in the other range */ public boolean isBefore(LocalDateRange range) { return end.compareTo(range.start) <= 0 && !range.equals(this); } //----------------------------------------------------------------------- /** * Obtains the length of this range in days. *

* This returns the number of days between the start and end dates. * If the range is too large, the length will be {@code Integer.MAX_VALUE}. * Unbounded ranges return {@code Integer.MAX_VALUE}. * * @return the length in days, Integer.MAX_VALUE if unbounded or too large */ public int lengthInDays() { if (isUnboundedStart() || isUnboundedEnd()) { return Integer.MAX_VALUE; } long length = end.toEpochDay() - start.toEpochDay(); return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length; } /** * Obtains the length of this range as a period. *

* This returns the {@link Period} between the start and end dates. * Unbounded ranges throw {@link ArithmeticException}. * * @return the period of the range * @throws ArithmeticException if the calculation exceeds the capacity of {@code Period}, * or the range is unbounded */ public Period toPeriod() { if (isUnboundedStart() || isUnboundedEnd()) { throw new ArithmeticException("Unbounded range cannot be converted to a Period"); } return Period.between(start, end); } //----------------------------------------------------------------------- /** * Checks if this range is equal to another range. *

* Compares this {@code LocalDateRange} with another ensuring that the two dates are the same. * Only objects of type {@code LocalDateRange} are compared, other types return false. * * @param obj the object to check, null returns false * @return true if this is equal to the other range */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof LocalDateRange) { LocalDateRange other = (LocalDateRange) obj; return start.equals(other.start) && end.equals(other.end); } return false; } /** * A hash code for this range. * * @return a suitable hash code */ @Override public int hashCode() { return start.hashCode() ^ end.hashCode(); } //----------------------------------------------------------------------- /** * Outputs this range as a {@code String}, such as {@code 2007-12-03/2007-12-04}. *

* The output will be the ISO-8601 format formed by combining the * {@code toString()} methods of the two dates, separated by a forward slash. * * @return a string representation of this date, not null */ @Override @ToString public String toString() { return start.toString() + '/' + end.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Minutes.java000066400000000000000000000546261343451174100251470ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.MINUTES; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A minute-based amount of time, such as '8 minutes'. *

* This class models a quantity or amount of time in terms of minutes. * It is a type-safe way of representing a number of minutes in an application. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Minutes implements TemporalAmount, Comparable, Serializable { /** * A constant for zero minutes. */ public static final Minutes ZERO = new Minutes(0); /** * A serialization identifier for this class. */ private static final long serialVersionUID = 2602801843170589407L; /** * The number of minutes per day. */ private static final int MINUTES_PER_DAY = 24 * 60; /** * The number of minutes per hour. */ private static final int MINUTES_PER_HOUR = 60; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P" + "(?:([-+]?[0-9]+)D)?" + "(?:T" + "(?:([-+]?[0-9]+)H)?" + "(?:([-+]?[0-9]+)M)?)?", Pattern.CASE_INSENSITIVE); /** * The number of minutes. */ private final int minutes; /** * Obtains a {@code Minutes} representing a number of minutes. *

* The resulting amount will have the specified minutes. * * @param minutes the number of minutes, positive or negative * @return the number of minutes, not null */ public static Minutes of(int minutes) { if (minutes == 0) { return ZERO; } return new Minutes(minutes); } /** * Obtains a {@code Minutes} representing the number of minutes * equivalent to a number of hours. *

* The resulting amount will be minute-based, with the number of minutes * equal to the number of hours multiplied by 60. * * @param hours the number of hours, positive or negative * @return the amount with the input hours converted to minutes, not null * @throws ArithmeticException if numeric overflow occurs */ public static Minutes ofHours(int hours) { if (hours == 0) { return ZERO; } return new Minutes(Math.multiplyExact(hours, MINUTES_PER_HOUR)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Minutes} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Minutes}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to minutes using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Minutes} * @throws ArithmeticException if numeric overflow occurs */ public static Minutes from(TemporalAmount amount) { if (amount instanceof Minutes) { return (Minutes) amount; } Objects.requireNonNull(amount, "amount"); int minutes = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, MINUTES); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of minutes: " + value + " " + unit); } minutes = Math.addExact(minutes, Math.toIntExact(converted[0])); } } return of(minutes); } //----------------------------------------------------------------------- /** * Obtains a {@code Minutes} from a text string such as {@code PTnM}. *

* This will parse the string produced by {@code toString()} and other * related formats based on ISO-8601 {@code PnDTnHnM}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * There are three sections consisting of a number and a suffix. * There is one section for days suffixed by "D", * followed by one section for hours suffixed by "H", * followed by one section for minutes suffixed by "M". * At least one section must be present. * If the hours or minutes section is present it must be prefixed by "T". * If the hours or minutes section is omitted the "T" must be omitted. * Letters must be in ASCII upper or lower case. * The number part of each section must consist of ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. * The number must parse to an {@code int}. *

* The leading plus/minus sign, and negative values for days, hours and * minutes are not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "PT2M"            -- Minutes.of(2)
     *   "PT-2M"           -- Minutes.of(-2)
     *   "-PT2M"           -- Minutes.of(-2)
     *   "-PT-2M"          -- Minutes.of(2)
     *   "PT3H"            -- Minutes.of(3 * 60)
     *   "PT3H-2M"         -- Minutes.of(3 * 60 - 2)
     *   "P3D"             -- Minutes.of(3 * 24 * 60)
     *   "P3DT2M"          -- Minutes.of(3 * 24 * 60 + 2)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Minutes parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String daysStr = matcher.group(2); String hoursStr = matcher.group(3); String minutesStr = matcher.group(4); if (daysStr != null || hoursStr != null || minutesStr != null) { int minutes = 0; if (minutesStr != null) { try { minutes = Integer.parseInt(minutesStr); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Minutes, non-numeric minutes", text, 0, ex); } } if (hoursStr != null) { try { int hoursAsMins = Math.multiplyExact(Integer.parseInt(hoursStr), MINUTES_PER_HOUR); minutes = Math.addExact(minutes, hoursAsMins); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Minutes, non-numeric hours", text, 0, ex); } } if (daysStr != null) { try { int daysAsMins = Math.multiplyExact(Integer.parseInt(daysStr), MINUTES_PER_DAY); minutes = Math.addExact(minutes, daysAsMins); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Minutes, non-numeric days", text, 0, ex); } } return of(Math.multiplyExact(minutes, negate)); } } throw new DateTimeParseException("Text cannot be parsed to Minutes", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Minutes} consisting of the number of minutes between two temporals. *

* The start temporal is included, but the end temporal is not. * The result of this method can be negative if the end is before the start. * * @param startInclusive the start temporal, inclusive, not null * @param endExclusive the end temporal, exclusive, not null * @return the number of minutes between the start and end temporals, not null */ public static Minutes between(Temporal startInclusive, Temporal endExclusive) { return of(Math.toIntExact(MINUTES.between(startInclusive, endExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of minutes. * * @param minutes the amount of minutes */ private Minutes(int minutes) { this.minutes = minutes; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Minutes.of(minutes); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#MINUTES MINUTES}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == MINUTES) { return minutes; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#MINUTES MINUTES}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} to * access the entire state of the amount. * * @return a list containing the minutes unit, not null */ @Override public List getUnits() { return Collections.singletonList(MINUTES); } //----------------------------------------------------------------------- /** * Gets the number of minutes in this amount. * * @return the number of minutes */ public int getAmount() { return minutes; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Minutes#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Minutes} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Minutes plus(TemporalAmount amountToAdd) { return plus(Minutes.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of minutes added. *

* This instance is immutable and unaffected by this method call. * * @param minutes the amount of minutes to add, may be negative * @return a {@code Minutes} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Minutes plus(int minutes) { if (minutes == 0) { return this; } return of(Math.addExact(this.minutes, minutes)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Minutes#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Minutes} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Minutes minus(TemporalAmount amountToAdd) { return minus(Minutes.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of minutes subtracted. *

* This instance is immutable and unaffected by this method call. * * @param minutes the amount of minutes to add, may be negative * @return a {@code Minutes} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Minutes minus(int minutes) { if (minutes == 0) { return this; } return of(Math.subtractExact(this.minutes, minutes)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Minutes multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(minutes, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Minutes dividedBy(int divisor) { if (divisor == 1) { return this; } return of(minutes / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Minutes negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Minutes abs() { return minutes < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of minutes as a {@code Duration}. *

* This returns a duration with the same number of minutes. * * @return the equivalent duration, not null */ public Duration toDuration() { return Duration.ofMinutes(minutes); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the MINUTES unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (minutes != 0) { temporal = temporal.plus(minutes, MINUTES); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the MINUTES unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (minutes != 0) { temporal = temporal.minus(minutes, MINUTES); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Minutes}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Minutes otherAmount) { int thisValue = this.minutes; int otherValue = otherAmount.minutes; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Minutes}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Minutes) { Minutes other = (Minutes) otherAmount; return this.minutes == other.minutes; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return minutes; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of minutes. * This will be in the format 'PTnM' where n is the number of minutes. * * @return the number of minutes in ISO-8601 string format */ @Override @ToString public String toString() { return "PT" + minutes + "M"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Months.java000066400000000000000000000525751343451174100247740ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.MONTHS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A month-based amount of time, such as '12 months'. *

* This class models a quantity or amount of time in terms of months. * It is a type-safe way of representing a number of months in an application. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Months implements TemporalAmount, Comparable, Serializable { /** * A constant for zero months. */ public static final Months ZERO = new Months(0); /** * A constant for one month. */ public static final Months ONE = new Months(1); /** * A serialization identifier for this class. */ private static final long serialVersionUID = -8903767091325669093L; /** * The number of months per year. */ private static final int MONTHS_PER_YEAR = 12; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P" + "(?:([-+]?[0-9]+)Y)?" + "(?:([-+]?[0-9]+)M)?", Pattern.CASE_INSENSITIVE); /** * The number of months. */ private final int months; /** * Obtains a {@code Months} representing a number of months. *

* The resulting amount will have the specified months. * * @param months the number of months, positive or negative * @return the number of months, not null */ public static Months of(int months) { if (months == 0) { return ZERO; } else if (months == 1) { return ONE; } return new Months(months); } /** * Obtains a {@code Months} representing the number of months * equivalent to a number of years. *

* The resulting amount will be month-based, with the number of months * equal to the number of years multiplied by 12. * * @param years the number of years, positive or negative * @return the amount with the input years converted to months, not null * @throws ArithmeticException if numeric overflow occurs */ public static Months ofYears(int years) { if (years == 0) { return ZERO; } return new Months(Math.multiplyExact(years, MONTHS_PER_YEAR)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Months} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Months}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to months using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Months} * @throws ArithmeticException if numeric overflow occurs */ public static Months from(TemporalAmount amount) { if (amount instanceof Months) { return (Months) amount; } Objects.requireNonNull(amount, "amount"); int months = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, MONTHS); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of months: " + value + " " + unit); } months = Math.addExact(months, Math.toIntExact(converted[0])); } } return of(months); } //----------------------------------------------------------------------- /** * Obtains a {@code Months} from a text string such as {@code PnM}. *

* This will parse the string produced by {@code toString()} which is * based on the ISO-8601 period format {@code PnYnM}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * There are then two sections, each consisting of a number and a suffix. * At least one of the two sections must be present. * The sections have suffixes in ASCII of "Y" and "M" for years and months, * accepted in upper or lower case. The suffixes must occur in order. * The number part of each section must consist of ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. * The number must parse to an {@code int}. *

* The leading plus/minus sign, and negative values for years and months are * not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "P2M"             -- Months.of(2)
     *   "P-2M"            -- Months.of(-2)
     *   "-P2M"            -- Months.of(-2)
     *   "-P-2M"           -- Months.of(2)
     *   "P3Y"             -- Months.of(3 * 12)
     *   "P3Y-2M"          -- Months.of(3 * 12 - 2)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Months parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String weeksStr = matcher.group(2); String daysStr = matcher.group(3); if (weeksStr != null || daysStr != null) { int months = 0; if (daysStr != null) { try { months = Integer.parseInt(daysStr); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to a Months, non-numeric months", text, 0, ex); } } if (weeksStr != null) { try { int years = Math.multiplyExact(Integer.parseInt(weeksStr), MONTHS_PER_YEAR); months = Math.addExact(months, years); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to a Months, non-numeric years", text, 0, ex); } } return of(Math.multiplyExact(months, negate)); } } throw new DateTimeParseException("Text cannot be parsed to a Months", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Months} consisting of the number of months between two dates. *

* The start date is included, but the end date is not. * The result of this method can be negative if the end is before the start. * * @param startDateInclusive the start date, inclusive, not null * @param endDateExclusive the end date, exclusive, not null * @return the number of months between this date and the end date, not null */ public static Months between(Temporal startDateInclusive, Temporal endDateExclusive) { return of(Math.toIntExact(MONTHS.between(startDateInclusive, endDateExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of months. * * @param months the months to use */ private Months(int months) { super(); this.months = months; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Months.of(months); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#MONTHS MONTHS}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == MONTHS) { return months; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#MONTHS MONTHS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} * to access the entire state of the amount. * * @return a list containing the months unit, not null */ @Override public List getUnits() { return Collections.singletonList(MONTHS); } //----------------------------------------------------------------------- /** * Gets the number of months in this amount. * * @return the number of months */ public int getAmount() { return months; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Months#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Months} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Months plus(TemporalAmount amountToAdd) { return plus(Months.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of months added. *

* This instance is immutable and unaffected by this method call. * * @param months the amount of months to add, may be negative * @return a {@code Months} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Months plus(int months) { if (months == 0) { return this; } return of(Math.addExact(this.months, months)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Months#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Months} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Months minus(TemporalAmount amountToAdd) { return minus(Months.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of months subtracted. *

* This instance is immutable and unaffected by this method call. * * @param months the amount of months to add, may be negative * @return a {@code Months} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Months minus(int months) { if (months == 0) { return this; } return of(Math.subtractExact(this.months, months)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Months multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(months, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Months dividedBy(int divisor) { if (divisor == 1) { return this; } return of(months / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Months negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Months abs() { return months < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of months as a {@code Period}. *

* This returns a period with the same number of months. * * @return the equivalent period, not null */ public Period toPeriod() { return Period.ofMonths(months); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the MONTHS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (months != 0) { temporal = temporal.plus(months, MONTHS); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the MONTHS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (months != 0) { temporal = temporal.minus(months, MONTHS); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Months}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Months otherAmount) { int thisValue = this.months; int otherValue = otherAmount.months; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Months}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Months) { Months other = (Months) otherAmount; return this.months == other.months; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return months; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of months. * This will be in the format 'PnM' where n is the number of months. * * @return the number of months in ISO-8601 string format */ @Override @ToString public String toString() { return "P" + months + "M"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/MutableClock.java000066400000000000000000000400621343451174100260550ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.Instant.EPOCH; import static java.time.ZoneOffset.UTC; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Objects; /** * A clock that does not advance on its own and that must be updated manually. *

* This class is designed for testing clock-sensitive components by simulating * the passage of time. This class differs from {@link * Clock#fixed(Instant, ZoneId)} and {@link Clock#offset(Clock, Duration)} in * that it permits arbitrary, unrestricted updates to its instant. This allows * for testing patterns that are not well-supported by the {@code fixed} and * {@code offset} clocks such as the following pattern: *

    *
  1. Create the clock-sensitive component to be tested *
  2. Verify some behavior of the component in the initial state *
  3. Advance the clock without recreating the component *
  4. Verify that the component behaves as expected given the (artificial) * delta in clock time since the initial state *
*

* This class is mutable. The time-zone of the clock is fixed, but the instant * may be updated at will. *

* The instant may be set to any value even if that new value is less than the * previous value. Caution should be exercised when moving the clock backwards, * since clock-sensitive components are likely to assume that time is * monotonically increasing. *

* Update semantics are expressed in terms of {@link ZonedDateTime}. The steps * of each update are as follows: *

    *
  1. The clock captures its own state in a {@code ZonedDateTime} via {@link * ZonedDateTime#now(Clock)} (or the equivalent thereof) *
  2. The update operation is applied to that {@code ZonedDateTime}, producing * a new {@code ZonedDateTime} *
  3. The resulting {@code ZonedDateTime} is converted to an instant via {@link * ZonedDateTime#toInstant()} (or the equivalent thereof) *
  4. The clock's instant is set to that new instant *
*

* Therefore, whenever there is a question about what argument types, units, * fields, or values an update operation supports, or what the result will be, * refer to the corresponding method of {@code ZonedDateTime}. Links are * provided from the documentation of each update operation of this class to the * corresponding method of {@code ZonedDateTime}. * *

Implementation Requirements:

* This class is thread-safe. Updates are atomic and synchronized. *

* While update semantics are expressed in terms of {@code ZonedDateTime}, that * imposes no requirements on implementation details. The implementation may * avoid using {@code ZonedDateTime} completely or only sometimes, for * convenience, efficiency, or any other reason. * * @serial exclude */ public final class MutableClock extends Clock implements Serializable { /** * Serialization version. */ private static final long serialVersionUID = -6152029959790119695L; /** * The mutable instant of this clock. */ private final transient InstantHolder instantHolder; /** * The fixed time-zone of this clock. */ private final transient ZoneId zone; /** * Obtains a new {@code MutableClock} set to the epoch of * 1970-01-01T00:00:00Z, converting to date and time using the UTC * time-zone. *

* Use this method when a {@code MutableClock} is needed and neither its * initial value nor its time-zone are important. This is often true when * testing behavior that depends on elapsed relative time rather * than absolute time. * * @return a new {@code MutableClock}, not null */ public static MutableClock epochUTC() { return MutableClock.of(EPOCH, UTC); } /** * Obtains a new {@code MutableClock} set to the specified instant, * converting to date and time using the specified time-zone. * * @param instant the initial value for the clock, not null * @param zone the time-zone to use, not null * @return a new {@code MutableClock}, not null */ public static MutableClock of(Instant instant, ZoneId zone) { Objects.requireNonNull(instant, "instant"); Objects.requireNonNull(zone, "zone"); return new MutableClock(new InstantHolder(instant), zone); } /** * Constructor. * * @param instantHolder the mutable instant, validated not null * @param zone the fixed time-zone, validated not null */ private MutableClock(InstantHolder instantHolder, ZoneId zone) { this.instantHolder = instantHolder; this.zone = zone; } /** * Overrides the instant of this clock with the specified value. * * @param instant the new instant for this clock, not null */ public void setInstant(Instant instant) { Objects.requireNonNull(instant, "instant"); instantHolder.set(instant); } /** * Adds the specified amount to this clock. *

* Atomically updates this clock to the value of the following expression: *

     *   ZonedDateTime.now(thisClock)
     *                .plus(amountToAdd)
     *                .toInstant()
     * 
* * @param amountToAdd the amount to add, not null * @throws DateTimeException if the addition cannot be made * @throws ArithmeticException if numeric overflow occurs * @see ZonedDateTime#plus(TemporalAmount) */ public void add(TemporalAmount amountToAdd) { Objects.requireNonNull(amountToAdd, "amountToAdd"); synchronized (instantHolder) { ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone); ZonedDateTime result = current.plus(amountToAdd); instantHolder.set(result.toInstant()); } } /** * Adds the specified amount to this clock. *

* Atomically updates this clock to the value of the following expression: *

     *   ZonedDateTime.now(thisClock)
     *                .plus(amountToAdd, unit)
     *                .toInstant()
     * 
* * @param amountToAdd the amount of the specified unit to add, may be negative * @param unit the unit of the amount to add, not null * @throws DateTimeException if the unit cannot be added * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs * @see ZonedDateTime#plus(long, TemporalUnit) */ public void add(long amountToAdd, TemporalUnit unit) { Objects.requireNonNull(unit, "unit"); synchronized (instantHolder) { ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone); ZonedDateTime result = current.plus(amountToAdd, unit); instantHolder.set(result.toInstant()); } } /** * Adjusts this clock. *

* Atomically updates this clock to the value of the following expression: *

     *   ZonedDateTime.now(thisClock)
     *                .with(adjuster)
     *                .toInstant()
     * 
* * @param adjuster the adjuster to use, not null * @throws DateTimeException if the adjustment cannot be made * @throws ArithmeticException if numeric overflow occurs * @see ZonedDateTime#with(TemporalAdjuster) */ public void set(TemporalAdjuster adjuster) { Objects.requireNonNull(adjuster, "adjuster"); synchronized (instantHolder) { ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone); ZonedDateTime result = current.with(adjuster); instantHolder.set(result.toInstant()); } } /** * Alters the specified field of this clock. *

* Atomically updates this clock to the value of the following expression: *

     *   ZonedDateTime.now(thisClock)
     *                .with(field, newValue)
     *                .toInstant()
     * 
* * @param field the field to set, not null * @param newValue the new value of the field * @throws DateTimeException if the field cannot be set * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs * @see ZonedDateTime#with(TemporalField, long) */ public void set(TemporalField field, long newValue) { Objects.requireNonNull(field, "field"); synchronized (instantHolder) { ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone); ZonedDateTime result = current.with(field, newValue); instantHolder.set(result.toInstant()); } } @Override public ZoneId getZone() { return zone; } /** * Returns a {@code MutableClock} that uses the specified time-zone and that * has shared updates with this clock. *

* Two clocks with shared updates always have the same instant, and all * updates applied to either clock affect both clocks. * * @param zone the time-zone to use for the returned clock, not null * @return a view of this clock in the specified time-zone, not null */ @Override public MutableClock withZone(ZoneId zone) { Objects.requireNonNull(zone, "zone"); if (zone.equals(this.zone)) { return this; } return new MutableClock(instantHolder, zone); } @Override public Instant instant() { return instantHolder.get(); } /** * Returns {@code true} if {@code obj} is a {@code MutableClock} that uses * the same time-zone as this clock and has shared updates with this clock. *

* Two clocks with shared updates always have the same instant, and all * updates applied to either clock affect both clocks. *

* A deserialized {@code MutableClock} is not equal to the original clock * that was serialized, since the two clocks do not have shared updates. * * @param obj the object to check, null returns {@code false} * @return {@code true} if this is equal to the other clock */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof MutableClock) { MutableClock other = (MutableClock) obj; return instantHolder == other.instantHolder && zone.equals(other.zone); } return false; } /** * A hash code for this clock, which is constant for this instance. * * @return a constant hash code for this instance */ @Override public int hashCode() { return System.identityHashCode(instantHolder) ^ zone.hashCode(); } @Override public String toString() { return "MutableClock[" + instant() + "," + getZone() + "]"; } /** * Returns the serialization proxy to replace this {@code MutableClock}. * * @return the serialization proxy, not null */ private Object writeReplace() { return new SerializationProxy(this); } /** * Throws {@link InvalidObjectException}. * * @param s ignored * @throws InvalidObjectException always */ private void readObject(ObjectInputStream s) throws InvalidObjectException { throw new InvalidObjectException("Proxy required"); } /** * The serialized form of a {@code MutableClock}. * * @serial include */ private static final class SerializationProxy implements Serializable { /** * Serialization version. */ private static final long serialVersionUID = 8602110640241828260L; /** * A snapshot of the instant of the {@code MutableClock}, taken when the * clock was serialized, not null. * * @serial */ private final Instant instant; /** * The time-zone of the {@code MutableClock}, not null. * * @serial */ private final ZoneId zone; /** * Constructor. * * @param clock the {@code MutableClock} to be serialized, not null */ SerializationProxy(MutableClock clock) { instant = clock.instant(); zone = clock.getZone(); } /** * Returns the {@code MutableClock} to replace this serialization proxy. * * @return the {@code MutableClock}, not null * @throws InvalidObjectException if the instant or time-zone is null */ private Object readResolve() throws InvalidObjectException { if (instant == null) { throw new InvalidObjectException("null instant"); } if (zone == null) { throw new InvalidObjectException("null zone"); } return MutableClock.of(instant, zone); } } /** * An identity-having holder object for a mutable instant value. *

* Clocks have shared updates when they share a holder object. Clocks rely * on the identity of the holder object in their {@code equals} and {@code * hashCode} methods. *

* Reads of the value are volatile and are never stale. Blind writes to the * value are volatile and do not need to synchronize. Atomic read-and-write * operations must synchronize on the holder object instance. */ private static final class InstantHolder { /** * The current value. */ private volatile Instant value; /** * Constructor. * * @param value the initial value, validated not null */ InstantHolder(Instant value) { this.value = value; } /** * Reads the value. * * @return the current value, not null */ Instant get() { return value; } /** * Writes the value. * * @param value the new value, validated not null */ void set(Instant value) { this.value = value; } } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/PackedFields.java000066400000000000000000000360271343451174100260340ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.SECONDS; import java.time.DateTimeException; import java.time.LocalDate; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; import java.time.format.ResolverStyle; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.ValueRange; import java.util.Map; /** * Temporal fields based on a packed representation. *

* This provides three fields that use a packed integer representation for dates and times. */ public final class PackedFields { /** * Packed date field. *

* This returns the date as a single integer value. * Only dates from year 1000 to year 9999 are supported. * The output is always an 8 digit integer. * For example, the date 2015-12-03 is packed to the integer 20151203. *

* This field has invalid values within the range of value values. * For example, 20121301 is invalid as it implies month 13. *

* When parsing in {@linkplain ResolverStyle#LENIENT lenient mode}, invalid * dates will be accepted. For example, 20121301 will result in 2013-01-01. */ public static final TemporalField PACKED_DATE = PackedDate.INSTANCE; /** * Packed hour-minute time field. *

* This returns the time as a single integer value. * The output is an integer from 0 to 2359. * For example, the date 11:30 is packed to the integer 1130. *

* This field has invalid values within the range of value values. * For example, 1073 is invalid as it implies the minute is 73. *

* When parsing in {@linkplain ResolverStyle#LENIENT lenient mode}, invalid * times will be accepted. For example, 1073 will result in 11:13. */ public static final TemporalField PACKED_HOUR_MIN = PackedHourMin.INSTANCE; /** * Packed hour-minute-second time field. *

* This returns the time as a single integer value. * The output is an integer from 0 to 235959. * For example, the date 11:30:52 is packed to the integer 113052. *

* This field has invalid values within the range of value values. * For example, 107310 is invalid as it implies the minute is 73. *

* When parsing in {@linkplain ResolverStyle#LENIENT lenient mode}, invalid * times will be accepted. For example, 107310 will result in 11:13:10. */ public static final TemporalField PACKED_TIME = PackedTime.INSTANCE; /** * Restricted constructor. */ private PackedFields() { } //------------------------------------------------------------------------- /** * Implementation of packed date. */ private static enum PackedDate implements TemporalField { INSTANCE; private static final ValueRange RANGE = ValueRange.of(10000101, 99991231); private static final long serialVersionUID = -38752465672576L; //----------------------------------------------------------------------- @Override public TemporalUnit getBaseUnit() { return DAYS; } @Override public TemporalUnit getRangeUnit() { return FOREVER; } @Override public boolean isDateBased() { return true; } @Override public boolean isTimeBased() { return false; } @Override public ValueRange range() { return RANGE; } //----------------------------------------------------------------------- @Override public boolean isSupportedBy(TemporalAccessor temporal) { return temporal.isSupported(EPOCH_DAY); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (!temporal.isSupported(this)) { throw new DateTimeException("Unsupported field: " + this); } return range(); } @Override public long getFrom(TemporalAccessor temporal) { LocalDate date = LocalDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); int year = date.getYear(); if (year < 1000 || year > 9999) { throw new DateTimeException("Unable to obtain PackedDate from LocalDate: " + date); } int moy = date.getMonthValue(); int dom = date.getDayOfMonth(); return year * 10000 + moy * 100 + dom; } @SuppressWarnings("unchecked") @Override public R adjustInto(R temporal, long newValue) { LocalDate date = toDate(newValue); return (R) temporal.with(date); } private LocalDate toDate(long newValue) { if (range().isValidValue(newValue) == false) { throw new DateTimeException("Invalid value: PackedDate " + newValue); } int val = (int) newValue; int year = val / 10000; int moy = (val % 10000) / 100; int dom = val % 100; return LocalDate.of(year, moy, dom); } //----------------------------------------------------------------------- @Override public ChronoLocalDate resolve( Map fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) { long value = fieldValues.remove(this); LocalDate date; if (resolverStyle == ResolverStyle.LENIENT) { int year = Math.toIntExact(value / 10000); int moy = (int) ((value % 10000) / 100); long dom = value % 100; date = LocalDate.of(year, 1, 1).plusMonths(moy - 1).plusDays(dom - 1); } else { date = toDate(value); } Chronology chrono = Chronology.from(partialTemporal); return chrono.date(date); } //----------------------------------------------------------------------- @Override public String toString() { return "PackedDate"; } } //------------------------------------------------------------------------- /** * Implementation of packed hour-min. */ private static enum PackedHourMin implements TemporalField { INSTANCE; private static final ValueRange RANGE = ValueRange.of(0, 2359); private static final long serialVersionUID = -871357658587L; //----------------------------------------------------------------------- @Override public TemporalUnit getBaseUnit() { return MINUTES; } @Override public TemporalUnit getRangeUnit() { return DAYS; } @Override public boolean isDateBased() { return false; } @Override public boolean isTimeBased() { return true; } @Override public ValueRange range() { return RANGE; } //----------------------------------------------------------------------- @Override public boolean isSupportedBy(TemporalAccessor temporal) { return temporal.isSupported(MINUTE_OF_DAY); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (!temporal.isSupported(this)) { throw new DateTimeException("Unsupported field: " + this); } return range(); } @Override public long getFrom(TemporalAccessor temporal) { int mod = temporal.get(MINUTE_OF_DAY); int hour = mod / 60; int min = mod % 60; return hour * 100 + min; } @SuppressWarnings("unchecked") @Override public R adjustInto(R temporal, long newValue) { long hour = newValue / 100; long min = newValue % 100; HOUR_OF_DAY.checkValidValue(hour); MINUTE_OF_HOUR.checkValidValue(min); return (R) temporal.with(HOUR_OF_DAY, hour).with(MINUTE_OF_HOUR, min); } //----------------------------------------------------------------------- @Override public ChronoLocalDate resolve( Map fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) { long value = fieldValues.remove(this); long hour = value / 100; long min = value % 100; if (resolverStyle != ResolverStyle.LENIENT) { HOUR_OF_DAY.checkValidValue(hour); MINUTE_OF_HOUR.checkValidValue(min); } long mod = hour * 60 + min; updateCheckConflict(fieldValues, this, MINUTE_OF_DAY, mod); return null; } //----------------------------------------------------------------------- @Override public String toString() { return "PackedHourMin"; } } //------------------------------------------------------------------------- /** * Implementation of packed hour-min-sec. */ private static enum PackedTime implements TemporalField { INSTANCE; private static final ValueRange RANGE = ValueRange.of(0, 235959); private static final long serialVersionUID = -98266827687L; //----------------------------------------------------------------------- @Override public TemporalUnit getBaseUnit() { return SECONDS; } @Override public TemporalUnit getRangeUnit() { return DAYS; } @Override public boolean isDateBased() { return false; } @Override public boolean isTimeBased() { return true; } @Override public ValueRange range() { return RANGE; } //----------------------------------------------------------------------- @Override public boolean isSupportedBy(TemporalAccessor temporal) { return temporal.isSupported(SECOND_OF_DAY); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (!temporal.isSupported(this)) { throw new DateTimeException("Unsupported field: " + this); } return range(); } @Override public long getFrom(TemporalAccessor temporal) { int sod = temporal.get(SECOND_OF_DAY); int hour = sod / 3600; int min = (sod / 60) % 60; int sec = sod % 60; return hour * 10000 + min * 100 + sec; } @SuppressWarnings("unchecked") @Override public R adjustInto(R temporal, long newValue) { RANGE.checkValidValue(newValue, INSTANCE); long hour = newValue / 10000; long min = (newValue % 10000) / 100; long sec = newValue % 100; HOUR_OF_DAY.checkValidValue(hour); MINUTE_OF_HOUR.checkValidValue(min); SECOND_OF_MINUTE.checkValidValue(sec); long sod = 3600 * hour + 60 * min + sec; return (R) temporal.with(SECOND_OF_DAY, sod); } //----------------------------------------------------------------------- @Override public ChronoLocalDate resolve( Map fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) { long value = fieldValues.remove(this); long hour = value / 10000; long min = (value % 10000) / 100; long sec = value % 100; if (resolverStyle != ResolverStyle.LENIENT) { HOUR_OF_DAY.checkValidValue(hour); MINUTE_OF_HOUR.checkValidValue(min); SECOND_OF_MINUTE.checkValidValue(sec); } long sod = 3600 * hour + 60 * min + sec; updateCheckConflict(fieldValues, this, SECOND_OF_DAY, sod); return null; } //----------------------------------------------------------------------- @Override public String toString() { return "PackedTime"; } } //------------------------------------------------------------------------- private static void updateCheckConflict( Map fieldValues, TemporalField targetField, TemporalField changeField, long changeValue) { Long old = fieldValues.put(changeField, changeValue); if (old != null && changeValue != old.longValue()) { throw new DateTimeException( "Conflict found: " + changeField + " " + old + " differs from " + changeField + " " + changeValue + " while resolving " + targetField); } } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/PeriodDuration.java000066400000000000000000000663351343451174100264530ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.SECONDS; import static java.time.temporal.ChronoUnit.YEARS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.LocalTime; import java.time.Period; import java.time.chrono.ChronoPeriod; import java.time.chrono.IsoChronology; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * An amount of time in the ISO-8601 calendar system that combines a period and a duration. *

* This class models a quantity or amount of time in terms of a {@code Period} and {@code Duration}. * A period is a date-based amount of time, consisting of years, months and days. * A duration is a time-based amount of time, consisting of seconds and nanoseconds. * See the {@link Period} and {@link Duration} classes for more details. *

* The days in a period take account of daylight saving changes (23 or 25 hour days). * When performing calculations, the period is added first, then the duration. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class PeriodDuration implements TemporalAmount, Serializable { /** * A constant for a duration of zero. */ public static final PeriodDuration ZERO = new PeriodDuration(Period.ZERO, Duration.ZERO); /** * A serialization identifier for this class. */ private static final long serialVersionUID = 8815521625671589L; /** * The supported units. */ private static final List SUPPORTED_UNITS = Collections.unmodifiableList(Arrays.asList(YEARS, MONTHS, DAYS, SECONDS, NANOS)); /** * The number of seconds per day. */ private static final long SECONDS_PER_DAY = 86400; /** * The period. */ private final Period period; /** * The duration. */ private final Duration duration; //----------------------------------------------------------------------- /** * Obtains an instance based on a period and duration. *

* The total amount of time of the resulting instance is the period plus the duration. * * @param period the period, not null * @param duration the duration, not null * @return the combined period-duration, not null */ public static PeriodDuration of(Period period, Duration duration) { Objects.requireNonNull(period, "The period must not be null"); Objects.requireNonNull(duration, "The duration must not be null"); return new PeriodDuration(period, duration); } /** * Obtains an instance based on a period. *

* The duration will be zero. * * @param period the period, not null * @return the combined period-duration, not null */ public static PeriodDuration of(Period period) { Objects.requireNonNull(period, "The period must not be null"); return new PeriodDuration(period, Duration.ZERO); } /** * Obtains an instance based on a duration. *

* The period will be zero. * * @param duration the duration, not null * @return the combined period-duration, not null */ public static PeriodDuration of(Duration duration) { Objects.requireNonNull(duration, "The duration must not be null"); return new PeriodDuration(Period.ZERO, duration); } //----------------------------------------------------------------------- /** * Obtains an instance from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time which this factory * extracts to a {@code PeriodDuration}. *

* The result is calculated by looping around each unit in the specified amount. * Any amount that is zero is ignore. * If a unit has an exact duration, it will be totalled using {@link Duration#plus(Duration)}. * If the unit is days or weeks, it will be totalled into the days part of the period. * If the unit is months or quarters, it will be totalled into the months part of the period. * If the unit is years, decades, centuries or millennia, it will be totalled into the years part of the period. * * @param amount the temporal amount to convert, not null * @return the equivalent duration, not null * @throws DateTimeException if unable to convert to a {@code Duration} * @throws ArithmeticException if numeric overflow occurs */ public static PeriodDuration from(TemporalAmount amount) { if (amount instanceof PeriodDuration) { return (PeriodDuration) amount; } if (amount instanceof Period) { return PeriodDuration.of((Period) amount); } if (amount instanceof Duration) { return PeriodDuration.of((Duration) amount); } if (amount instanceof ChronoPeriod) { if (IsoChronology.INSTANCE.equals(((ChronoPeriod) amount).getChronology()) == false) { throw new DateTimeException("Period requires ISO chronology: " + amount); } } Objects.requireNonNull(amount, "amount"); int years = 0; int months = 0; int days = 0; Duration duration = Duration.ZERO; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { // ignore unless non-zero if (unit.isDurationEstimated()) { if (unit == ChronoUnit.DAYS) { days = Math.addExact(days, Math.toIntExact(value)); } else if (unit == ChronoUnit.WEEKS) { days = Math.addExact(days, Math.toIntExact(Math.multiplyExact(value, 7))); } else if (unit == ChronoUnit.MONTHS) { months = Math.addExact(months, Math.toIntExact(value)); } else if (unit == IsoFields.QUARTER_YEARS) { months = Math.addExact(months, Math.toIntExact(Math.multiplyExact(value, 3))); } else if (unit == ChronoUnit.YEARS) { years = Math.addExact(years, Math.toIntExact(value)); } else if (unit == ChronoUnit.DECADES) { years = Math.addExact(years, Math.toIntExact(Math.multiplyExact(value, 10))); } else if (unit == ChronoUnit.CENTURIES) { years = Math.addExact(years, Math.toIntExact(Math.multiplyExact(value, 100))); } else if (unit == ChronoUnit.MILLENNIA) { years = Math.addExact(years, Math.toIntExact(Math.multiplyExact(value, 1000))); } else { throw new DateTimeException("Unknown unit: " + unit); } } else { // total of exact durations duration = duration.plus(amount.get(unit), unit); } } } return PeriodDuration.of(Period.of(years, months, days), duration); } //----------------------------------------------------------------------- /** * Obtains an instance from a text string such as {@code PnYnMnDTnHnMnS}. *

* This will parse the string produced by {@code toString()} which is * based on the ISO-8601 period formats {@code PnYnMnDTnHnMnS} and {@code PnW}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * There are then a number of sections, each consisting of a number and a suffix. * At least one of the sections must be present. * The sections have suffixes in ASCII of "Y" for years, "M" for months, * "W" for weeks, "D" for days, "H" for hours, "M" for minutes, "S" for seconds, * accepted in upper or lower case. Note that the ASCII letter "T" separates * the date and time parts and must be present if any time part is present. * The suffixes must occur in order. * The number part of each section must consist of ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. * The number must parse to an {@code int}. * Any week-based input is multiplied by 7 and treated as a number of days. *

* The leading plus/minus sign, and negative values for weeks and days are * not part of the ISO-8601 standard. *

* Note that the date style format {@code PYYYY-MM-DDTHH:MM:SS} is not supported. *

* For example, the following are valid inputs: *

     *   "P2Y"             -- PeriodDuration.of(Period.ofYears(2))
     *   "P3M"             -- PeriodDuration.of(Period.ofMonths(3))
     *   "P4W"             -- PeriodDuration.of(Period.ofWeeks(4))
     *   "P5D"             -- PeriodDuration.of(Period.ofDays(5))
     *   "PT6H"            -- PeriodDuration.of(Duration.ofHours(6))
     *   "P1Y2M3D"         -- PeriodDuration.of(Period.of(1, 2, 3))
     *   "P1Y2M3W4DT8H"    -- PeriodDuration.of(Period.of(1, 2, 25), Duration.ofHours(8))
     *   "P-1Y2M"          -- PeriodDuration.of(Period.of(-1, 2, 0))
     *   "-P1Y2M"          -- PeriodDuration.of(Period.of(-1, -2, 0))
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static PeriodDuration parse(CharSequence text) { Objects.requireNonNull(text, "text"); String upper = text.toString().toUpperCase(Locale.ENGLISH); String negate = ""; if (upper.startsWith("+")) { upper = upper.substring(1); } else if (upper.startsWith("-")) { upper = upper.substring(1); negate = "-"; } // duration only, parse original text so it does negation if (upper.startsWith("PT")) { return PeriodDuration.of(Duration.parse(text)); } // period only, parse original text so it does negation int tpos = upper.indexOf('T'); if (tpos < 0) { return PeriodDuration.of(Period.parse(text)); } // period and duration Period period = Period.parse(negate + upper.substring(0, tpos)); Duration duration = Duration.parse(negate + "P" + upper.substring(tpos)); return PeriodDuration.of(period, duration); } //----------------------------------------------------------------------- /** * Obtains an instance consisting of the amount of time between two temporals. *

* The start is included, but the end is not. * The result of this method can be negative if the end is before the start. *

* The calculation examines the temporals and extracts {@link LocalDate} and {@link LocalTime}. * If the time is missing, it will be defaulted to midnight. * If one date is missing, it will be defaulted to the other date. * It then finds the amount of time between the two dates and between the two times. * * @param startInclusive the start, inclusive, not null * @param endExclusive the end, exclusive, not null * @return the number of days between this date and the end date, not null */ public static PeriodDuration between(Temporal startInclusive, Temporal endExclusive) { LocalDate startDate = startInclusive.query(TemporalQueries.localDate()); LocalDate endDate = endExclusive.query(TemporalQueries.localDate()); Period period = Period.ZERO; if (startDate != null && endDate != null) { period = Period.between(startDate, endDate); } LocalTime startTime = startInclusive.query(TemporalQueries.localTime()); LocalTime endTime = endExclusive.query(TemporalQueries.localTime()); startTime = startTime != null ? startTime : LocalTime.MIDNIGHT; endTime = endTime != null ? endTime : LocalTime.MIDNIGHT; Duration duration = Duration.between(startTime, endTime); return PeriodDuration.of(period, duration); } //----------------------------------------------------------------------- /** * Constructs an instance. * * @param period the period * @param duration the duration */ private PeriodDuration(Period period, Duration duration) { this.period = period; this.duration = duration; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return PeriodDuration.of(period, duration); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported units - {@link ChronoUnit#YEARS}, * {@link ChronoUnit#MONTHS}, {@link ChronoUnit#DAYS}, {@link ChronoUnit#SECONDS} * and {@link ChronoUnit#NANOS}. * All other units throw an exception. * Note that hours and minutes throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case YEARS: return period.getYears(); case MONTHS: return period.getMonths(); case DAYS: return period.getDays(); case SECONDS: return duration.getSeconds(); case NANOS: return duration.getNano(); default: break; } } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* This returns the list {@link ChronoUnit#YEARS}, {@link ChronoUnit#MONTHS}, * {@link ChronoUnit#DAYS}, {@link ChronoUnit#SECONDS} and {@link ChronoUnit#NANOS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} * to access the entire state of the amount. * * @return a list containing the days unit, not null */ @Override public List getUnits() { return SUPPORTED_UNITS; } //----------------------------------------------------------------------- /** * Gets the period part. * * @return the period part */ public Period getPeriod() { return period; } /** * Returns a copy of this period-duration with a different period. *

* This instance is immutable and unaffected by this method call. * * @param period the new period * @return the updated period-duration */ public PeriodDuration withPeriod(Period period) { return PeriodDuration.of(period, duration); } /** * Gets the duration part. * * @return the duration part */ public Duration getDuration() { return duration; } /** * Returns a copy of this period-duration with a different duration. *

* This instance is immutable and unaffected by this method call. * * @param duration the new duration * @return the updated period-duration */ public PeriodDuration withDuration(Duration duration) { return PeriodDuration.of(period, duration); } //----------------------------------------------------------------------- /** * Checks if all parts of this amount are zero. *

* This returns true if both {@link Period#isZero()} and {@link Duration#isZero()} * return true. * * @return true if this period is zero-length */ public boolean isZero() { return period.isZero() && duration.isZero(); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link PeriodDuration#from(TemporalAmount)}. * The period and duration are combined separately. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Days} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public PeriodDuration plus(TemporalAmount amountToAdd) { PeriodDuration other = PeriodDuration.from(amountToAdd); return of(period.plus(other.period), duration.plus(other.duration)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link PeriodDuration#from(TemporalAmount)}. * The period and duration are combined separately. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Days} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public PeriodDuration minus(TemporalAmount amountToAdd) { PeriodDuration other = PeriodDuration.from(amountToAdd); return of(period.minus(other.period), duration.minus(other.duration)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public PeriodDuration multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(period.multipliedBy(scalar), duration.multipliedBy(scalar)); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public PeriodDuration negated() { return multipliedBy(-1); } //----------------------------------------------------------------------- /** * Returns a copy of this instance with the years and months exactly normalized. *

* This normalizes the years and months units, leaving the days unit unchanged. * The result is exact, always representing the same amount of time. *

* The months unit is adjusted to have an absolute value less than 11, * with the years unit being adjusted to compensate. For example, a period of * "1 year and 15 months" will be normalized to "2 years and 3 months". *

* The sign of the years and months units will be the same after normalization. * For example, a period of "1 year and -25 months" will be normalized to * "-1 year and -1 month". *

* Note that no normalization is performed on the days or duration. *

* This instance is immutable and unaffected by this method call. * * @return a {@code PeriodDuration} based on this one with excess months normalized to years, not null * @throws ArithmeticException if numeric overflow occurs */ public PeriodDuration normalizedYears() { return withPeriod(period.normalized()); } /** * Returns a copy of this instance with the days and duration normalized using the standard day of 24 hours. *

* This normalizes the days and duration, leaving the years and months unchanged. * The result uses a standard day length of 24 hours. *

* This combines the duration seconds with the number of days and shares the total * seconds between the two fields. For example, a period of * "2 days and 86401 seconds" will be normalized to "3 days and 1 second". *

* The sign of the days and duration will be the same after normalization. * For example, a period of "1 day and -172801 seconds" will be normalized to * "-1 day and -1 second". *

* Note that no normalization is performed on the years or months. *

* This instance is immutable and unaffected by this method call. * * @return a {@code PeriodDuration} based on this one with excess duration normalized to days, not null * @throws ArithmeticException if numeric overflow occurs */ public PeriodDuration normalizedStandardDays() { long totalSecs = period.getDays() * SECONDS_PER_DAY + duration.getSeconds(); int splitDays = Math.toIntExact(totalSecs / SECONDS_PER_DAY); long splitSecs = totalSecs % SECONDS_PER_DAY; if (splitDays == period.getDays() && splitSecs == duration.getSeconds()) { return this; } return PeriodDuration.of(period.withDays(splitDays), duration.withSeconds(splitSecs)); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. This simply adds the period and duration to the temporal. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the DAYS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { return temporal.plus(period).plus(duration); } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. This simply subtracts the period and duration from the temporal. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the DAYS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { return temporal.minus(period).minus(duration); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code PeriodDuration}. *

* The comparison is based on the underlying period and duration. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof PeriodDuration) { PeriodDuration other = (PeriodDuration) otherAmount; return this.period.equals(other.period) && this.duration.equals(other.duration); } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return period.hashCode() ^ duration.hashCode(); } //----------------------------------------------------------------------- /** * Returns a string representation of the amount. * This will be in the format 'PnYnMnDTnHnMnS', with sections omitted as necessary. * An empty amount will return "PT0S". * * @return the period in ISO-8601 string format */ @Override @ToString public String toString() { if (period.isZero()) { return duration.toString(); } if (duration.isZero()) { return period.toString(); } return period.toString() + duration.toString().substring(1); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Quarter.java000066400000000000000000000514061343451174100251370ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.IsoFields.QUARTER_OF_YEAR; import static java.time.temporal.IsoFields.QUARTER_YEARS; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Month; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatterBuilder; import java.time.format.TextStyle; import java.time.temporal.ChronoField; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Locale; /** * A quarter-of-year, such as 'Q2'. *

* {@code Quarter} is an enum representing the 4 quarters of the year - * Q1, Q2, Q3 and Q4. These are defined as January to March, April to June, * July to September and October to December. *

* The {@code int} value follows the quarter, from 1 (Q1) to 4 (Q4). * It is recommended that applications use the enum rather than the {@code int} value * to ensure code clarity. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code Quarter}. * Use {@code getValue()} instead. *

* This enum represents a common concept that is found in many calendar systems. * As such, this enum may be used by any calendar system that has the quarter-of-year * concept defined exactly equivalent to the ISO calendar system. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum Quarter implements TemporalAccessor, TemporalAdjuster { /** * The singleton instance for the first quarter-of-year, from January to March. * This has the numeric value of {@code 1}. */ Q1, /** * The singleton instance for the second quarter-of-year, from April to June. * This has the numeric value of {@code 2}. */ Q2, /** * The singleton instance for the third quarter-of-year, from July to September. * This has the numeric value of {@code 3}. */ Q3, /** * The singleton instance for the fourth quarter-of-year, from October to December. * This has the numeric value of {@code 4}. */ Q4; //----------------------------------------------------------------------- /** * Obtains an instance of {@code Quarter} from an {@code int} value. *

* {@code Quarter} is an enum representing the 4 quarters of the year. * This factory allows the enum to be obtained from the {@code int} value. * The {@code int} value follows the quarter, from 1 (Q1) to 4 (Q4). * * @param quarterOfYear the quarter-of-year to represent, from 1 (Q1) to 4 (Q4) * @return the quarter-of-year, not null * @throws DateTimeException if the quarter-of-year is invalid */ public static Quarter of(int quarterOfYear) { switch (quarterOfYear) { case 1: return Q1; case 2: return Q2; case 3: return Q3; case 4: return Q4; default: throw new DateTimeException("Invalid value for Quarter: " + quarterOfYear); } } /** * Obtains an instance of {@code Quarter} from a month-of-year. *

* {@code Quarter} is an enum representing the 4 quarters of the year. * This factory allows the enum to be obtained from the {@code Month} value. *

* January to March are Q1, April to June are Q2, July to September are Q3 * and October to December are Q4. * * @param monthOfYear the month-of-year to convert from, from 1 to 12 * @return the quarter-of-year, not null * @throws DateTimeException if the month-of-year is invalid */ public static Quarter ofMonth(int monthOfYear) { MONTH_OF_YEAR.range().checkValidValue(monthOfYear, MONTH_OF_YEAR); return of((monthOfYear - 1) / 3 + 1); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Quarter} from a temporal object. *

* This obtains a quarter based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code Quarter}. *

* The conversion extracts the {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} field. * The extraction is only permitted if the temporal object has an ISO * chronology, or can be converted to a {@code LocalDate}. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used in queries via method reference, {@code Quarter::from}. * * @param temporal the temporal-time object to convert, not null * @return the quarter-of-year, not null * @throws DateTimeException if unable to convert to a {@code Quarter} */ public static Quarter from(TemporalAccessor temporal) { if (temporal instanceof Quarter) { return (Quarter) temporal; } else if (temporal instanceof Month) { Month month = (Month) temporal; return of(month.ordinal() / 3 + 1); } try { if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { temporal = LocalDate.from(temporal); } // need to use getLong() as JDK Parsed class get() doesn't work properly int qoy = Math.toIntExact(temporal.getLong(QUARTER_OF_YEAR)); return of(qoy); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain Quarter from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Gets the quarter-of-year {@code int} value. *

* The values are numbered following the ISO-8601 standard, * from 1 (Q1) to 4 (Q4). * * @return the quarter-of-year, from 1 (Q1) to 4 (Q4) */ public int getValue() { return ordinal() + 1; } //----------------------------------------------------------------------- /** * Gets the textual representation, such as 'Q1' or '4th quarter'. *

* This returns the textual name used to identify the quarter-of-year, * suitable for presentation to the user. * The parameters control the style of the returned text and the locale. *

* If no textual mapping is found then the {@link #getValue() numeric value} is returned. * * @param style the length of the text required, not null * @param locale the locale to use, not null * @return the text value of the quarter-of-year, not null */ public String getDisplayName(TextStyle style, Locale locale) { return new DateTimeFormatterBuilder().appendText(QUARTER_OF_YEAR, style).toFormatter(locale).format(this); } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *

* This checks if this quarter-of-year can be queried for the specified field. * If false, then calling the {@link #range(TemporalField) range} and * {@link #get(TemporalField) get} methods will throw an exception. *

* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then * this method returns true. * All {@code ChronoField} instances will return false. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the field is supported is determined by the field. * * @param field the field to check, null returns false * @return true if the field is supported on this quarter-of-year, false if not */ @Override public boolean isSupported(TemporalField field) { if (field == QUARTER_OF_YEAR) { return true; } else if (field instanceof ChronoField) { return false; } return field != null && field.isSupportedBy(this); } /** * Gets the range of valid values for the specified field. *

* The range object expresses the minimum and maximum valid values for a field. * This quarter is used to enhance the accuracy of the returned range. * If it is not possible to return the range, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then the * range of the quarter-of-year, from 1 to 4, will be returned. * All {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the range can be obtained is determined by the field. * * @param field the field to query the range for, not null * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported */ @Override public ValueRange range(TemporalField field) { if (field == QUARTER_OF_YEAR) { return field.range(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return TemporalAccessor.super.range(field); } /** * Gets the value of the specified field from this quarter-of-year as an {@code int}. *

* This queries this quarter for the value for the specified field. * The returned value will always be within the valid range of values for the field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then the * value of the quarter-of-year, from 1 to 4, will be returned. * All {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field, within the valid range of values * @throws DateTimeException if a value for the field cannot be obtained or * the value is outside the range of valid values for the field * @throws UnsupportedTemporalTypeException if the field is not supported or * the range of values exceeds an {@code int} * @throws ArithmeticException if numeric overflow occurs */ @Override public int get(TemporalField field) { if (field == QUARTER_OF_YEAR) { return getValue(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return TemporalAccessor.super.get(field); } /** * Gets the value of the specified field from this quarter-of-year as a {@code long}. *

* This queries this quarter for the value for the specified field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then the * value of the quarter-of-year, from 1 to 4, will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long getLong(TemporalField field) { if (field == QUARTER_OF_YEAR) { return getValue(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } //----------------------------------------------------------------------- /** * Returns the quarter that is the specified number of quarters after this one. *

* The calculation rolls around the end of the year from Q4 to Q1. * The specified period may be negative. *

* This instance is immutable and unaffected by this method call. * * @param quarters the quarters to add, positive or negative * @return the resulting quarter, not null */ public Quarter plus(long quarters) { int amount = (int) quarters % 4; return values()[(ordinal() + (amount + 4)) % 4]; } /** * Returns the quarter that is the specified number of quarters before this one. *

* The calculation rolls around the start of the year from Q1 to Q4. * The specified period may be negative. *

* This instance is immutable and unaffected by this method call. * * @param quarters the quarters to subtract, positive or negative * @return the resulting quarter, not null */ public Quarter minus(long quarters) { return plus(-(quarters % 4)); } //----------------------------------------------------------------------- /** * Gets the length of this quarter in days. *

* This takes a flag to determine whether to return the length for a leap year or not. *

* Q1 has 90 in a standard year and 91 days in a leap year. * Q2 has 91 days. * Q3 and Q4 have 92 days. * * @param leapYear true if the length is required for a leap year * @return the length of this month in days, from 90 to 92 */ public int length(boolean leapYear) { switch (this) { case Q1: return (leapYear ? 91 : 90); case Q2: return 91; default: return 92; } } //----------------------------------------------------------------------- /** * Gets the first of the three months that this quarter refers to. *

* Q1 will return January.
* Q2 will return April.
* Q3 will return July.
* Q4 will return October. *

* To obtain the other two months of the quarter, simply use {@link Month#plus(long)} * on the returned month. * * @return the first month in the quarter, not null */ public Month firstMonth() { switch (this) { case Q1: return Month.JANUARY; case Q2: return Month.APRIL; case Q3: return Month.JULY; case Q4: return Month.OCTOBER; default: throw new IllegalStateException("Unreachable"); } } //----------------------------------------------------------------------- /** * Queries this quarter-of-year using the specified query. *

* This queries this quarter-of-year using the specified query strategy object. * The {@code TemporalQuery} object defines the logic to be used to * obtain the result. Read the documentation of the query to understand * what the result of this method will be. *

* The result of this method is obtained by invoking the * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the * specified query passing {@code this} as the argument. * * @param the type of the result * @param query the query to invoke, not null * @return the query result, null may be returned (defined by the query) * @throws DateTimeException if unable to query (defined by the query) * @throws ArithmeticException if numeric overflow occurs (defined by the query) */ @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.chronology()) { return (R) IsoChronology.INSTANCE; } else if (query == TemporalQueries.precision()) { return (R) QUARTER_YEARS; } return TemporalAccessor.super.query(query); } /** * Adjusts the specified temporal object to have this quarter-of-year. *

* This returns a temporal object of the same observable type as the input * with the quarter-of-year changed to be the same as this. *

* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} * passing {@link IsoFields#QUARTER_OF_YEAR} as the field. * If the specified temporal object does not use the ISO calendar system then * a {@code DateTimeException} is thrown. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#with(TemporalAdjuster)}: *

     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisQuarter.adjustInto(temporal);
     *   temporal = temporal.with(thisQuarter);
     * 
*

* For example, given a date in May, the following are output: *

     *   dateInMay.with(Q1);    // three months earlier
     *   dateInMay.with(Q2);    // no change
     *   dateInMay.with(Q3);    // three months later
     *   dateInMay.with(Q4);    // six months later
     * 
*

* This instance is immutable and unaffected by this method call. * * @param temporal the target object to be adjusted, not null * @return the adjusted object, not null * @throws DateTimeException if unable to make the adjustment * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal adjustInto(Temporal temporal) { if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) { throw new DateTimeException("Adjustment only supported on ISO date-time"); } return temporal.with(QUARTER_OF_YEAR, getValue()); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Seconds.java000066400000000000000000000577601343451174100251230ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.SECONDS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A second-based amount of time, such as '8 seconds'. *

* This class models a quantity or amount of time in terms of seconds. * It is a type-safe way of representing a number of seconds in an application. * Note that {@link Duration} also models time in terms of seconds, but that * class allows nanoseconds, which this class does not. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Seconds implements TemporalAmount, Comparable, Serializable { /** * A constant for zero seconds. */ public static final Seconds ZERO = new Seconds(0); /** * A serialization identifier for this class. */ private static final long serialVersionUID = 2602801843170589407L; /** * The number of seconds per day. */ private static final int SECONDS_PER_DAY = 86400; /** * The number of seconds per hour. */ private static final int SECONDS_PER_HOUR = 3600; /** * The number of seconds per minute. */ private static final int SECONDS_PER_MINUTE = 60; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P" + "(?:([-+]?[0-9]+)D)?" + "(?:T" + "(?:([-+]?[0-9]+)H)?" + "(?:([-+]?[0-9]+)M)?" + "(?:([-+]?[0-9]+)S)?)?", Pattern.CASE_INSENSITIVE); /** * The number of seconds. */ private final int seconds; /** * Obtains a {@code Seconds} representing a number of seconds. *

* The resulting amount will have the specified seconds. * * @param seconds the number of seconds, positive or negative * @return the number of seconds, not null */ public static Seconds of(int seconds) { if (seconds == 0) { return ZERO; } return new Seconds(seconds); } /** * Obtains a {@code Seconds} representing the number of seconds * equivalent to a number of hours. *

* The resulting amount will be second-based, with the number of seconds * equal to the number of hours multiplied by 3600. * * @param hours the number of hours, positive or negative * @return the amount with the input hours converted to seconds, not null * @throws ArithmeticException if numeric overflow occurs */ public static Seconds ofHours(int hours) { if (hours == 0) { return ZERO; } return new Seconds(Math.multiplyExact(hours, SECONDS_PER_HOUR)); } /** * Obtains a {@code Seconds} representing the number of seconds * equivalent to a number of hours. *

* The resulting amount will be second-based, with the number of seconds * equal to the number of minutes multiplied by 60. * * @param minutes the number of minutes, positive or negative * @return the amount with the input minutes converted to seconds, not null * @throws ArithmeticException if numeric overflow occurs */ public static Seconds ofMinutes(int minutes) { if (minutes == 0) { return ZERO; } return new Seconds(Math.multiplyExact(minutes, SECONDS_PER_MINUTE)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Seconds} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Seconds}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to seconds using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Seconds} * @throws ArithmeticException if numeric overflow occurs */ public static Seconds from(TemporalAmount amount) { if (amount instanceof Seconds) { return (Seconds) amount; } Objects.requireNonNull(amount, "amount"); int seconds = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, SECONDS); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of seconds: " + value + " " + unit); } seconds = Math.addExact(seconds, Math.toIntExact(converted[0])); } } return of(seconds); } //----------------------------------------------------------------------- /** * Obtains a {@code Seconds} from a text string such as {@code PTnS}. *

* This will parse the string produced by {@code toString()} and other * related formats based on ISO-8601 {@code PnDTnHnMnS}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * There are four sections consisting of a number and a suffix. * There is one section for days suffixed by "D", * followed by one section for hours suffixed by "H", * followed by one section for minutes suffixed by "M", * followed by one section for seconds suffixed by "S". * At least one section must be present. * If the hours, minutes or seconds section is present it must be prefixed by "T". * If the hours, minutes or seconds section is omitted the "T" must be omitted. * Letters must be in ASCII upper or lower case. * The number part of each section must consist of ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. * The number must parse to an {@code int}. *

* The leading plus/minus sign, and negative values for days, hours, minutes * and seconds are not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "PT2S"            -- Seconds.of(2)
     *   "PT-2S"           -- Seconds.of(-2)
     *   "-PT2S"           -- Seconds.of(-2)
     *   "-PT-2S"          -- Seconds.of(2)
     *   "PT3S"            -- Seconds.of(3 * 60)
     *   "PT3H-2M7S"       -- Seconds.of(3 * 3600 - 2 * 60 + 7)
     *   "P2D"             -- Seconds.of(2 * 86400)
     *   "P2DT3H"          -- Seconds.of(2 * 86400 + 3 * 3600)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Seconds parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String daysStr = matcher.group(2); String hoursStr = matcher.group(3); String minutesStr = matcher.group(4); String secondsStr = matcher.group(5); if (daysStr != null || hoursStr != null || minutesStr != null || secondsStr != null) { int seconds = 0; if (secondsStr != null) { try { seconds = Integer.parseInt(secondsStr); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric seconds", text, 0, ex); } } if (minutesStr != null) { try { int minutesAsSecs = Math.multiplyExact(Integer.parseInt(minutesStr), SECONDS_PER_MINUTE); seconds = Math.addExact(seconds, minutesAsSecs); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric minutes", text, 0, ex); } } if (hoursStr != null) { try { int hoursAsSecs = Math.multiplyExact(Integer.parseInt(hoursStr), SECONDS_PER_HOUR); seconds = Math.addExact(seconds, hoursAsSecs); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric hours", text, 0, ex); } } if (daysStr != null) { try { int daysAsSecs = Math.multiplyExact(Integer.parseInt(daysStr), SECONDS_PER_DAY); seconds = Math.addExact(seconds, daysAsSecs); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric days", text, 0, ex); } } return of(Math.multiplyExact(seconds, negate)); } } throw new DateTimeParseException("Text cannot be parsed to Seconds", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Seconds} consisting of the number of seconds between two temporals. *

* The start temporal is included, but the end temporal is not. * The result of this method can be negative if the end is before the start. * * @param startInclusive the start temporal, inclusive, not null * @param endExclusive the end temporal, exclusive, not null * @return the number of seconds between the start and end temporals, not null */ public static Seconds between(Temporal startInclusive, Temporal endExclusive) { return of(Math.toIntExact(SECONDS.between(startInclusive, endExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of seconds. * * @param seconds the amount of seconds */ private Seconds(int seconds) { this.seconds = seconds; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Seconds.of(seconds); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#SECONDS SECONDS}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == SECONDS) { return seconds; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#SECONDS SECONDS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} to * access the entire state of the amount. * * @return a list containing the seconds unit, not null */ @Override public List getUnits() { return Collections.singletonList(SECONDS); } //----------------------------------------------------------------------- /** * Gets the number of seconds in this amount. * * @return the number of seconds */ public int getAmount() { return seconds; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Seconds#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Seconds} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Seconds plus(TemporalAmount amountToAdd) { return plus(Seconds.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of seconds added. *

* This instance is immutable and unaffected by this method call. * * @param seconds the amount of seconds to add, may be negative * @return a {@code Seconds} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Seconds plus(int seconds) { if (seconds == 0) { return this; } return of(Math.addExact(this.seconds, seconds)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Seconds#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Seconds} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Seconds minus(TemporalAmount amountToAdd) { return minus(Seconds.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of seconds subtracted. *

* This instance is immutable and unaffected by this method call. * * @param seconds the amount of seconds to add, may be negative * @return a {@code Seconds} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Seconds minus(int seconds) { if (seconds == 0) { return this; } return of(Math.subtractExact(this.seconds, seconds)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Seconds multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(seconds, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Seconds dividedBy(int divisor) { if (divisor == 1) { return this; } return of(seconds / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Seconds negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Seconds abs() { return seconds < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of seconds as a {@code Duration}. *

* This returns a duration with the same number of seconds. * * @return the equivalent duration, not null */ public Duration toDuration() { return Duration.ofSeconds(seconds); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the SECONDS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (seconds != 0) { temporal = temporal.plus(seconds, SECONDS); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the SECONDS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (seconds != 0) { temporal = temporal.minus(seconds, SECONDS); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Seconds}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Seconds otherAmount) { int thisValue = this.seconds; int otherValue = otherAmount.seconds; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Seconds}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Seconds) { Seconds other = (Seconds) otherAmount; return this.seconds == other.seconds; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return seconds; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of seconds. * This will be in the format 'PTnS' where n is the number of seconds. * * @return the number of seconds in ISO-8601 string format */ @Override @ToString public String toString() { return "PT" + seconds + "S"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Temporals.java000066400000000000000000000361631343451174100254650ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.WEEKS; import java.text.ParsePosition; import java.time.DateTimeException; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * Additional utilities for working with temporal classes. *

* This includes: *

    *
  • adjusters that ignore Saturday/Sunday weekends *
  • conversion between {@code TimeUnit} and {@code ChronoUnit} *
  • converting an amount to another unit *
* *

Implementation Requirements:

* This is a thread-safe utility class. * All returned classes are immutable and thread-safe. */ public final class Temporals { /** * Restricted constructor. */ private Temporals() { } //------------------------------------------------------------------------- /** * Returns an adjuster that returns the next working day, ignoring Saturday and Sunday. *

* Some territories have weekends that do not consist of Saturday and Sunday. * No implementation is supplied to support this, however an adjuster * can be easily written to do so. * * @return the next working day adjuster, not null */ public static TemporalAdjuster nextWorkingDay() { return Adjuster.NEXT_WORKING; } /** * Returns an adjuster that returns the next working day or same day if already working day, ignoring Saturday and Sunday. *

* Some territories have weekends that do not consist of Saturday and Sunday. * No implementation is supplied to support this, however an adjuster * can be easily written to do so. * * @return the next working day or same adjuster, not null */ public static TemporalAdjuster nextWorkingDayOrSame() { return Adjuster.NEXT_WORKING_OR_SAME; } /** * Returns an adjuster that returns the previous working day, ignoring Saturday and Sunday. *

* Some territories have weekends that do not consist of Saturday and Sunday. * No implementation is supplied to support this, however an adjuster * can be easily written to do so. * * @return the previous working day adjuster, not null */ public static TemporalAdjuster previousWorkingDay() { return Adjuster.PREVIOUS_WORKING; } /** * Returns an adjuster that returns the previous working day or same day if already working day, ignoring Saturday and Sunday. *

* Some territories have weekends that do not consist of Saturday and Sunday. * No implementation is supplied to support this, however an adjuster * can be easily written to do so. * * @return the previous working day or same adjuster, not null */ public static TemporalAdjuster previousWorkingDayOrSame() { return Adjuster.PREVIOUS_WORKING_OR_SAME; } //----------------------------------------------------------------------- /** * Enum implementing the adjusters. */ private static enum Adjuster implements TemporalAdjuster { /** Next working day adjuster. */ NEXT_WORKING { @Override public Temporal adjustInto(Temporal temporal) { int dow = temporal.get(DAY_OF_WEEK); switch (dow) { case 6: // Saturday return temporal.plus(2, DAYS); case 5: // Friday return temporal.plus(3, DAYS); default: return temporal.plus(1, DAYS); } } }, /** Previous working day adjuster. */ PREVIOUS_WORKING { @Override public Temporal adjustInto(Temporal temporal) { int dow = temporal.get(DAY_OF_WEEK); switch (dow) { case 1: // Monday return temporal.minus(3, DAYS); case 7: // Sunday return temporal.minus(2, DAYS); default: return temporal.minus(1, DAYS); } } }, /** Next working day or same adjuster. */ NEXT_WORKING_OR_SAME { @Override public Temporal adjustInto(Temporal temporal) { int dow = temporal.get(DAY_OF_WEEK); switch (dow) { case 6: // Saturday return temporal.plus(2, DAYS); case 7: // Sunday return temporal.plus(1, DAYS); default: return temporal; } } }, /** Previous working day or same adjuster. */ PREVIOUS_WORKING_OR_SAME { @Override public Temporal adjustInto(Temporal temporal) { int dow = temporal.get(DAY_OF_WEEK); switch (dow) { case 6: //Saturday return temporal.minus(1, DAYS); case 7: // Sunday return temporal.minus(2, DAYS); default: return temporal; } } } } //------------------------------------------------------------------------- /** * Parses the text using one of the formatters. *

* This will try each formatter in turn, attempting to fully parse the specified text. * The temporal query is typically a method reference to a {@code from(TemporalAccessor)} method. * For example: *

     *  LocalDateTime dt = Temporals.parseFirstMatching(str, LocalDateTime::from, fmt1, fm2, fm3);
     * 
* If the parse completes without reading the entire length of the text, * or a problem occurs during parsing or merging, then an exception is thrown. * * @param the type of the parsed date-time * @param text the text to parse, not null * @param query the query defining the type to parse to, not null * @param formatters the formatters to try, not null * @return the parsed date-time, not null * @throws DateTimeParseException if unable to parse the requested result */ public static T parseFirstMatching(CharSequence text, TemporalQuery query, DateTimeFormatter... formatters) { Objects.requireNonNull(text, "text"); Objects.requireNonNull(query, "query"); Objects.requireNonNull(formatters, "formatters"); if (formatters.length == 0) { throw new DateTimeParseException("No formatters specified", text, 0); } if (formatters.length == 1) { return formatters[0].parse(text, query); } for (DateTimeFormatter formatter : formatters) { try { ParsePosition pp = new ParsePosition(0); formatter.parseUnresolved(text, pp); int len = text.length(); if (pp.getErrorIndex() == -1 && pp.getIndex() == len) { return formatter.parse(text, query); } } catch (RuntimeException ex) { // should not happen, but ignore if it does } } throw new DateTimeParseException("Text '" + text + "' could not be parsed", text, 0); } //------------------------------------------------------------------------- /** * Converts a {@code TimeUnit} to a {@code ChronoUnit}. *

* This handles the seven units declared in {@code TimeUnit}. * * @param unit the unit to convert, not null * @return the converted unit, not null */ public static ChronoUnit chronoUnit(TimeUnit unit) { Objects.requireNonNull(unit, "unit"); switch (unit) { case NANOSECONDS: return ChronoUnit.NANOS; case MICROSECONDS: return ChronoUnit.MICROS; case MILLISECONDS: return ChronoUnit.MILLIS; case SECONDS: return ChronoUnit.SECONDS; case MINUTES: return ChronoUnit.MINUTES; case HOURS: return ChronoUnit.HOURS; case DAYS: return ChronoUnit.DAYS; default: throw new IllegalArgumentException("Unknown TimeUnit constant"); } } /** * Converts a {@code ChronoUnit} to a {@code TimeUnit}. *

* This handles the seven units declared in {@code TimeUnit}. * * @param unit the unit to convert, not null * @return the converted unit, not null * @throws IllegalArgumentException if the unit cannot be converted */ public static TimeUnit timeUnit(ChronoUnit unit) { Objects.requireNonNull(unit, "unit"); switch (unit) { case NANOS: return TimeUnit.NANOSECONDS; case MICROS: return TimeUnit.MICROSECONDS; case MILLIS: return TimeUnit.MILLISECONDS; case SECONDS: return TimeUnit.SECONDS; case MINUTES: return TimeUnit.MINUTES; case HOURS: return TimeUnit.HOURS; case DAYS: return TimeUnit.DAYS; default: throw new IllegalArgumentException("ChronoUnit cannot be converted to TimeUnit: " + unit); } } //------------------------------------------------------------------------- /** * Converts an amount from one unit to another. *

* This works on the units in {@code ChronoUnit} and {@code IsoFields}. * The {@code DAYS} and {@code WEEKS} units are handled as exact multiple of 24 hours. * The {@code ERAS} and {@code FOREVER} units are not supported. * * @param amount the input amount in terms of the {@code fromUnit} * @param fromUnit the unit to convert from, not null * @param toUnit the unit to convert to, not null * @return the conversion array, * element 0 is the signed whole number, * element 1 is the signed remainder in terms of the input unit, * not null * @throws DateTimeException if the units cannot be converted * @throws UnsupportedTemporalTypeException if the units are not supported * @throws ArithmeticException if numeric overflow occurs */ public static long[] convertAmount(long amount, TemporalUnit fromUnit, TemporalUnit toUnit) { Objects.requireNonNull(fromUnit, "fromUnit"); Objects.requireNonNull(toUnit, "toUnit"); validateUnit(fromUnit); validateUnit(toUnit); if (fromUnit.equals(toUnit)) { return new long[] {amount, 0}; } // precise-based if (isPrecise(fromUnit) && isPrecise(toUnit)) { long fromNanos = fromUnit.getDuration().toNanos(); long toNanos = toUnit.getDuration().toNanos(); if (fromNanos > toNanos) { long multiple = fromNanos / toNanos; return new long[] {Math.multiplyExact(amount, multiple), 0}; } else { long multiple = toNanos / fromNanos; return new long[] {amount / multiple, amount % multiple}; } } // month-based int fromMonthFactor = monthMonthFactor(fromUnit, fromUnit, toUnit); int toMonthFactor = monthMonthFactor(toUnit, fromUnit, toUnit); if (fromMonthFactor > toMonthFactor) { long multiple = fromMonthFactor / toMonthFactor; return new long[] {Math.multiplyExact(amount, multiple), 0}; } else { long multiple = toMonthFactor / fromMonthFactor; return new long[] {amount / multiple, amount % multiple}; } } private static void validateUnit(TemporalUnit unit) { if (unit instanceof ChronoUnit) { if (unit.equals(ERAS) || unit.equals(FOREVER)) { throw new UnsupportedTemporalTypeException("Unsupported TemporalUnit: " + unit); } } else if (unit.equals(IsoFields.QUARTER_YEARS) == false) { throw new UnsupportedTemporalTypeException("Unsupported TemporalUnit: " + unit); } } private static boolean isPrecise(TemporalUnit unit) { return unit instanceof ChronoUnit && ((ChronoUnit) unit).compareTo(WEEKS) <= 0; } private static int monthMonthFactor(TemporalUnit unit, TemporalUnit fromUnit, TemporalUnit toUnit) { if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case MONTHS: return 1; case YEARS: return 12; case DECADES: return 120; case CENTURIES: return 1200; case MILLENNIA: return 12000; default: throw new DateTimeException( String.format("Unable to convert between units: %s to %s", fromUnit, toUnit)); } } return 3; // quarters } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Weeks.java000066400000000000000000000465211343451174100245740ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.WEEKS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A week-based amount of time, such as '12 weeks'. *

* This class models a quantity or amount of time in terms of weeks. * It is a type-safe way of representing a number of weeks in an application. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Weeks implements TemporalAmount, Comparable, Serializable { /** * A constant for zero weeks. */ public static final Weeks ZERO = new Weeks(0); /** * A constant for one week. */ public static final Weeks ONE = new Weeks(1); /** * A serialization identifier for this class. */ private static final long serialVersionUID = -8903767091325669093L; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P([-+]?[0-9]+)W", Pattern.CASE_INSENSITIVE); /** * The number of weeks. */ private final int weeks; /** * Obtains a {@code Weeks} representing a number of weeks. *

* The resulting amount will have the specified weeks. * * @param weeks the number of weeks, positive or negative * @return the number of weeks, not null */ public static Weeks of(int weeks) { if (weeks == 0) { return ZERO; } else if (weeks == 1) { return ONE; } return new Weeks(weeks); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Weeks} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Weeks}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to weeks using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * For example, "7 days" can be converted to weeks but "6 days" cannot. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Weeks} * @throws ArithmeticException if numeric overflow occurs */ public static Weeks from(TemporalAmount amount) { if (amount instanceof Weeks) { return (Weeks) amount; } Objects.requireNonNull(amount, "amount"); int weeks = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, WEEKS); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of weeks: " + value + " " + unit); } weeks = Math.addExact(weeks, Math.toIntExact(converted[0])); } } return of(weeks); } //----------------------------------------------------------------------- /** * Obtains a {@code Weeks} from a text string such as {@code PnW}. *

* This will parse the string produced by {@code toString()} which is * based on the ISO-8601 period formats {@code PnW}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * The ASCII integer amount is next, which may be negative. * The ASCII letter "W" is next in upper or lower case. *

* The leading plus/minus sign, and negative values for weeks are * not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "P2W"             -- Weeks.of(2)
     *   "P-2W"            -- Weeks.of(-2)
     *   "-P2W"            -- Weeks.of(-2)
     *   "-P-2W"           -- Weeks.of(2)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Weeks parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String str = matcher.group(2); try { int val = Integer.parseInt(str); return of(Math.multiplyExact(val, negate)); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to a Weeks", text, 0, ex); } } throw new DateTimeParseException("Text cannot be parsed to a Weeks", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Weeks} consisting of the number of weeks between two dates. *

* The start date is included, but the end date is not. * The result of this method can be negative if the end is before the start. * * @param startDateInclusive the start date, inclusive, not null * @param endDateExclusive the end date, exclusive, not null * @return the number of weeks between this date and the end date, not null */ public static Weeks between(Temporal startDateInclusive, Temporal endDateExclusive) { return of(Math.toIntExact(WEEKS.between(startDateInclusive, endDateExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of weeks. * * @param weeks the weeks to use */ private Weeks(int weeks) { super(); this.weeks = weeks; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Weeks.of(weeks); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#WEEKS WEEKS}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == WEEKS) { return weeks; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#WEEKS WEEKS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} * to access the entire state of the amount. * * @return a list containing the weeks unit, not null */ @Override public List getUnits() { return Collections.singletonList(WEEKS); } //----------------------------------------------------------------------- /** * Gets the number of weeks in this amount. * * @return the number of weeks */ public int getAmount() { return weeks; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Weeks#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Weeks} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Weeks plus(TemporalAmount amountToAdd) { return plus(Weeks.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of weeks added. *

* This instance is immutable and unaffected by this method call. * * @param weeks the amount of weeks to add, may be negative * @return a {@code Weeks} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Weeks plus(int weeks) { if (weeks == 0) { return this; } return of(Math.addExact(this.weeks, weeks)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Weeks#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Weeks} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Weeks minus(TemporalAmount amountToAdd) { return minus(Weeks.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of weeks subtracted. *

* This instance is immutable and unaffected by this method call. * * @param weeks the amount of weeks to add, may be negative * @return a {@code Weeks} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Weeks minus(int weeks) { if (weeks == 0) { return this; } return of(Math.subtractExact(this.weeks, weeks)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Weeks multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(weeks, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Weeks dividedBy(int divisor) { if (divisor == 1) { return this; } return of(weeks / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Weeks negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Weeks abs() { return weeks < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of weeks as a {@code Period}. *

* This returns a period with the same number of weeks. * * @return the equivalent period, not null */ public Period toPeriod() { return Period.ofWeeks(weeks); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the WEEKS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (weeks != 0) { temporal = temporal.plus(weeks, WEEKS); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the WEEKS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (weeks != 0) { temporal = temporal.minus(weeks, WEEKS); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Weeks}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Weeks otherAmount) { int thisValue = this.weeks; int otherValue = otherAmount.weeks; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Weeks}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Weeks) { Weeks other = (Weeks) otherAmount; return this.weeks == other.weeks; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return weeks; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of weeks. * This will be in the format 'PnW' where n is the number of weeks. * * @return the number of weeks in ISO-8601 string format */ @Override @ToString public String toString() { return "P" + weeks + "W"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/YearQuarter.java000066400000000000000000001534271343451174100257660ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.YEARS; import static java.time.temporal.IsoFields.DAY_OF_QUARTER; import static java.time.temporal.IsoFields.QUARTER_OF_YEAR; import static java.time.temporal.IsoFields.QUARTER_YEARS; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Month; import java.time.Period; import java.time.Year; import java.time.ZoneId; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.format.SignStyle; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Objects; import java.util.stream.LongStream; import java.util.stream.Stream; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A year-quarter in the ISO-8601 calendar system, such as {@code 2007-Q2}. *

* {@code YearQuarter} is an immutable date-time object that represents the combination * of a year and quarter. Any field that can be derived from a year and quarter can be obtained. * A quarter is defined by {@link Quarter} and {@link Month#firstMonthOfQuarter()} - Q1, Q2, Q3 and Q4. * Q1 is January to March, Q2 is April to June, Q3 is July to September and Q4 is October to December. *

* This class does not store or represent a day, time or time-zone. * For example, the value "2nd quarter 2007" can be stored in a {@code YearQuarter}. *

* The ISO-8601 calendar system is the modern civil calendar system used today * in most of the world. It is equivalent to the proleptic Gregorian calendar * system, in which today's rules for leap years are applied for all time. * For most applications written today, the ISO-8601 rules are entirely suitable. * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. * Note that the ISO-8601 standard does not define or refer to quarters. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class YearQuarter implements Temporal, TemporalAdjuster, Comparable, Serializable { /** * Serialization version. */ private static final long serialVersionUID = 4183400860270640070L; /** * Parser. */ private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() .parseCaseInsensitive() .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendLiteral('-') .appendLiteral('Q') .appendValue(QUARTER_OF_YEAR, 1) .toFormatter(); /** * The year. */ private final int year; /** * The quarter-of-year, not null. */ private final Quarter quarter; //----------------------------------------------------------------------- /** * Obtains the current year-quarter from the system clock in the default time-zone. *

* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current year-quarter. * The zone and offset will be set based on the time-zone in the clock. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current year-quarter using the system clock and default time-zone, not null */ public static YearQuarter now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current year-quarter from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current year-quarter. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current year-quarter using the system clock, not null */ public static YearQuarter now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current year-quarter from the specified clock. *

* This will query the specified clock to obtain the current year-quarter. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current year-quarter, not null */ public static YearQuarter now(Clock clock) { final LocalDate now = LocalDate.now(clock); // called once return YearQuarter.of(now.getYear(), Quarter.from(now.getMonth())); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code YearQuarter} from a year and quarter. * * @param year the year to represent, from MIN_YEAR to MAX_YEAR * @param quarter the quarter-of-year to represent, not null * @return the year-quarter, not null * @throws DateTimeException if the year value is invalid */ public static YearQuarter of(int year, Quarter quarter) { YEAR.checkValidValue(year); Objects.requireNonNull(quarter, "quarter"); return new YearQuarter(year, quarter); } /** * Obtains an instance of {@code YearQuarter} from a year and quarter. * * @param year the year to represent, from MIN_YEAR to MAX_YEAR * @param quarter the quarter-of-year to represent, from 1 to 4 * @return the year-quarter, not null * @throws DateTimeException if either field value is invalid */ public static YearQuarter of(int year, int quarter) { YEAR.checkValidValue(year); return new YearQuarter(year, Quarter.of(quarter)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code YearQuarter} from a temporal object. *

* This obtains a year-quarter based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code YearQuarter}. *

* The conversion extracts the {@link ChronoField#YEAR YEAR} and * {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} fields. * The extraction is only permitted if the temporal object has an ISO * chronology, or can be converted to a {@code LocalDate}. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used in queries via method reference, {@code YearQuarter::from}. * * @param temporal the temporal object to convert, not null * @return the year-quarter, not null * @throws DateTimeException if unable to convert to a {@code YearQuarter} */ public static YearQuarter from(TemporalAccessor temporal) { if (temporal instanceof YearQuarter) { return (YearQuarter) temporal; } Objects.requireNonNull(temporal, "temporal"); try { if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) { temporal = LocalDate.from(temporal); } // need to use getLong() as JDK Parsed class get() doesn't work properly int year = Math.toIntExact(temporal.getLong(YEAR)); int qoy = Math.toIntExact(temporal.getLong(QUARTER_OF_YEAR)); return of(year, qoy); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain YearQuarter from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Obtains an instance of {@code YearQuarter} from a text string such as {@code 2007-Q2}. *

* The string must represent a valid year-quarter. * The format must be {@code uuuu-'Q'Q} where the 'Q' is case insensitive. * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol. * * @param text the text to parse such as "2007-Q2", not null * @return the parsed year-quarter, not null * @throws DateTimeParseException if the text cannot be parsed */ @FromString public static YearQuarter parse(CharSequence text) { return parse(text, PARSER); } /** * Obtains an instance of {@code YearQuarter} from a text string using a specific formatter. *

* The text is parsed using the formatter, returning a year-quarter. * * @param text the text to parse, not null * @param formatter the formatter to use, not null * @return the parsed year-quarter, not null * @throws DateTimeParseException if the text cannot be parsed */ public static YearQuarter parse(CharSequence text, DateTimeFormatter formatter) { Objects.requireNonNull(formatter, "formatter"); return formatter.parse(text, YearQuarter::from); } //----------------------------------------------------------------------- /** * Constructor. * * @param year the year to represent, validated from MIN_YEAR to MAX_YEAR * @param quarter the quarter-of-year to represent, validated not null */ private YearQuarter(int year, Quarter quarter) { this.year = year; this.quarter = quarter; } /** * Validates the input. * * @return the valid object, not null */ private Object readResolve() { return of(year, quarter); } /** * Returns a copy of this year-quarter with the new year and quarter, checking * to see if a new object is in fact required. * * @param newYear the year to represent, validated from MIN_YEAR to MAX_YEAR * @param newQuarter the quarter-of-year to represent, validated not null * @return the year-quarter, not null */ private YearQuarter with(int newYear, Quarter newQuarter) { if (year == newYear && quarter == newQuarter) { return this; } return new YearQuarter(newYear, newQuarter); } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *

* This checks if this year-quarter can be queried for the specified field. * If false, then calling the {@link #range(TemporalField) range}, * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)} * methods will throw an exception. *

* If the field is a {@link ChronoField} then the query is implemented here. * The supported fields are: *

    *
  • {@code QUARTER_OF_YEAR} *
  • {@code YEAR_OF_ERA} *
  • {@code YEAR} *
  • {@code ERA} *
* All other {@code ChronoField} instances will return false. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the field is supported is determined by the field. * * @param field the field to check, null returns false * @return true if the field is supported on this year-quarter, false if not */ @Override public boolean isSupported(TemporalField field) { if (field == QUARTER_OF_YEAR) { return true; } else if (field instanceof ChronoField) { return field == YEAR || field == YEAR_OF_ERA || field == ERA; } return field != null && field.isSupportedBy(this); } /** * Checks if the specified unit is supported. *

* This checks if the specified unit can be added to, or subtracted from, this date-time. * If false, then calling the {@link #plus(long, TemporalUnit)} and * {@link #minus(long, TemporalUnit) minus} methods will throw an exception. *

* If the unit is a {@link ChronoUnit} then the query is implemented here. * The supported units are: *

    *
  • {@code QUARTER_YEARS} *
  • {@code YEARS} *
  • {@code DECADES} *
  • {@code CENTURIES} *
  • {@code MILLENNIA} *
  • {@code ERAS} *
* All other {@code ChronoUnit} instances will return false. *

* If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)} * passing {@code this} as the argument. * Whether the unit is supported is determined by the unit. * * @param unit the unit to check, null returns false * @return true if the unit can be added/subtracted, false if not */ @Override public boolean isSupported(TemporalUnit unit) { if (unit == QUARTER_YEARS) { return true; } else if (unit instanceof ChronoUnit) { return unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS; } return unit != null && unit.isSupportedBy(this); } //----------------------------------------------------------------------- /** * Gets the range of valid values for the specified field. *

* The range object expresses the minimum and maximum valid values for a field. * This year-quarter is used to enhance the accuracy of the returned range. * If it is not possible to return the range, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return * appropriate range instances. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the range can be obtained is determined by the field. * * @param field the field to query the range for, not null * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported */ @Override public ValueRange range(TemporalField field) { if (field == QUARTER_OF_YEAR) { return QUARTER_OF_YEAR.range(); } if (field == YEAR_OF_ERA) { return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE)); } return Temporal.super.range(field); } /** * Gets the value of the specified field from this year-quarter as an {@code int}. *

* This queries this year-quarter for the value for the specified field. * The returned value will always be within the valid range of values for the field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return valid * values based on this year-quarter,. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained or * the value is outside the range of valid values for the field * @throws UnsupportedTemporalTypeException if the field is not supported or * the range of values exceeds an {@code int} * @throws ArithmeticException if numeric overflow occurs */ @Override public int get(TemporalField field) { if (field == QUARTER_OF_YEAR) { return quarter.getValue(); } else if (field instanceof ChronoField) { switch ((ChronoField) field) { case YEAR_OF_ERA: return (year < 1 ? 1 - year : year); case YEAR: return year; case ERA: return (year < 1 ? 0 : 1); default: throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } } return Temporal.super.get(field); } /** * Gets the value of the specified field from this year-quarter as a {@code long}. *

* This queries this year-quarter for the value for the specified field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the query is implemented here. * The {@link #isSupported(TemporalField) supported fields} will return valid * values based on this year-quarter. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)} * passing {@code this} as the argument. Whether the value can be obtained, * and what the value represents, is determined by the field. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long getLong(TemporalField field) { if (field == QUARTER_OF_YEAR) { return quarter.getValue(); } else if (field instanceof ChronoField) { switch ((ChronoField) field) { case YEAR_OF_ERA: return (year < 1 ? 1 - year : year); case YEAR: return year; case ERA: return (year < 1 ? 0 : 1); default: throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } } return field.getFrom(this); } private long getProlepticQuarter() { return year * 4L + (quarter.getValue() - 1); } //----------------------------------------------------------------------- /** * Gets the year field. *

* This method returns the primitive {@code int} value for the year. *

* The year returned by this method is proleptic as per {@code get(YEAR)}. * * @return the year, from MIN_YEAR to MAX_YEAR */ public int getYear() { return year; } /** * Gets the quarter-of-year field from 1 to 4. *

* This method returns the quarter as an {@code int} from 1 to 4. * Application code is frequently clearer if the enum {@link Quarter} * is used by calling {@link #getQuarter()}. * * @return the quarter-of-year, from 1 to 4 * @see #getQuarter() */ public int getQuarterValue() { return quarter.getValue(); } /** * Gets the quarter-of-year field using the {@code Quarter} enum. *

* This method returns the enum {@link Quarter} for the quarter. * This avoids confusion as to what {@code int} values mean. * If you need access to the primitive {@code int} value then the enum * provides the {@link Quarter#getValue() int value}. * * @return the quarter-of-year, not null * @see #getQuarterValue() */ public Quarter getQuarter() { return quarter; } //----------------------------------------------------------------------- /** * Checks if the year is a leap year, according to the ISO proleptic * calendar system rules. *

* This method applies the current rules for leap years across the whole time-line. * In general, a year is a leap year if it is divisible by four without * remainder. However, years divisible by 100, are not leap years, with * the exception of years divisible by 400 which are. *

* For example, 1904 is a leap year it is divisible by 4. * 1900 was not a leap year as it is divisible by 100, however 2000 was a * leap year as it is divisible by 400. *

* The calculation is proleptic - applying the same rules into the far future and far past. * This is historically inaccurate, but is correct for the ISO-8601 standard. * * @return true if the year is leap, false otherwise */ public boolean isLeapYear() { return IsoChronology.INSTANCE.isLeapYear(year); } /** * Checks if the day-of-quarter is valid for this year-quarter. *

* This method checks whether this year and quarter and the input day form * a valid date. * * @param dayOfQuarter the day-of-quarter to validate, from 1 to 92, invalid value returns false * @return true if the day is valid for this year-quarter */ public boolean isValidDay(int dayOfQuarter) { return dayOfQuarter >= 1 && dayOfQuarter <= lengthOfQuarter(); } /** * Returns the length of the quarter, taking account of the year. *

* This returns the length of the quarter in days. * * @return the length of the quarter in days, from 90 to 92 */ public int lengthOfQuarter() { return quarter.length(isLeapYear()); } /** * Returns the length of the year. *

* This returns the length of the year in days, either 365 or 366. * * @return 366 if the year is leap, 365 otherwise */ public int lengthOfYear() { return (isLeapYear() ? 366 : 365); } //----------------------------------------------------------------------- /** * Returns an adjusted copy of this year-quarter. *

* This returns a {@code YearQuarter}, based on this one, with the year-quarter adjusted. * The adjustment takes place using the specified adjuster strategy object. * Read the documentation of the adjuster to understand what adjustment will be made. *

* A simple adjuster might simply set the one of the fields, such as the year field. * A more complex adjuster might set the year-quarter to the next quarter that * Halley's comet will pass the Earth. *

* The result of this method is obtained by invoking the * {@link TemporalAdjuster#adjustInto(Temporal)} method on the * specified adjuster passing {@code this} as the argument. *

* This instance is immutable and unaffected by this method call. * * @param adjuster the adjuster to use, not null * @return a {@code YearQuarter} based on {@code this} with the adjustment made, not null * @throws DateTimeException if the adjustment cannot be made * @throws ArithmeticException if numeric overflow occurs */ @Override public YearQuarter with(TemporalAdjuster adjuster) { return (YearQuarter) adjuster.adjustInto(this); } /** * Returns a copy of this year-quarter with the specified field set to a new value. *

* This returns a {@code YearQuarter}, based on this one, with the value * for the specified field changed. * This can be used to change any supported field, such as the year or quarter. * If it is not possible to set the value, because the field is not supported or for * some other reason, an exception is thrown. *

* If the field is a {@link ChronoField} then the adjustment is implemented here. * The supported fields behave as follows: *

    *
  • {@code QUARTER_OF_YEAR} - * Returns a {@code YearQuarter} with the specified quarter-of-year. * The year will be unchanged. *
  • {@code YEAR_OF_ERA} - * Returns a {@code YearQuarter} with the specified year-of-era * The quarter and era will be unchanged. *
  • {@code YEAR} - * Returns a {@code YearQuarter} with the specified year. * The quarter will be unchanged. *
  • {@code ERA} - * Returns a {@code YearQuarter} with the specified era. * The quarter and year-of-era will be unchanged. *
*

* In all cases, if the new value is outside the valid range of values for the field * then a {@code DateTimeException} will be thrown. *

* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)} * passing {@code this} as the argument. In this case, the field determines * whether and how to adjust the instant. *

* This instance is immutable and unaffected by this method call. * * @param field the field to set in the result, not null * @param newValue the new value of the field in the result * @return a {@code YearQuarter} based on {@code this} with the specified field set, not null * @throws DateTimeException if the field cannot be set * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public YearQuarter with(TemporalField field, long newValue) { if (field == QUARTER_OF_YEAR) { return withQuarter(QUARTER_OF_YEAR.range().checkValidIntValue(newValue, QUARTER_OF_YEAR)); } else if (field instanceof ChronoField) { ChronoField f = (ChronoField) field; f.checkValidValue(newValue); switch (f) { case YEAR_OF_ERA: return withYear((int) (year < 1 ? 1 - newValue : newValue)); case YEAR: return withYear((int) newValue); case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year)); default: throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } } return field.adjustInto(this, newValue); } //----------------------------------------------------------------------- /** * Returns a copy of this {@code YearQuarter} with the year altered. *

* This instance is immutable and unaffected by this method call. * * @param year the year to set in the returned year-quarter, from MIN_YEAR to MAX_YEAR * @return a {@code YearQuarter} based on this year-quarter with the requested year, not null * @throws DateTimeException if the year value is invalid */ public YearQuarter withYear(int year) { YEAR.checkValidValue(year); return with(year, quarter); } /** * Returns a copy of this {@code YearQuarter} with the quarter-of-year altered. *

* This instance is immutable and unaffected by this method call. * * @param quarter the quarter-of-year to set in the returned year-quarter, from 1 to 4 * @return a {@code YearQuarter} based on this year-quarter with the requested quarter, not null * @throws DateTimeException if the quarter-of-year value is invalid */ public YearQuarter withQuarter(int quarter) { QUARTER_OF_YEAR.range().checkValidValue(quarter, QUARTER_OF_YEAR); return with(year, Quarter.of(quarter)); } //----------------------------------------------------------------------- /** * Returns a copy of this year-quarter with the specified amount added. *

* This returns a {@code YearQuarter}, based on this one, with the specified amount added. * The amount is typically {@link Period} but may be any other type implementing * the {@link TemporalAmount} interface. *

* The calculation is delegated to the amount object by calling * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free * to implement the addition in any way it wishes, however it typically * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation * of the amount implementation to determine if it can be successfully added. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code YearQuarter} based on this year-quarter with the addition made, not null * @throws DateTimeException if the addition cannot be made * @throws ArithmeticException if numeric overflow occurs */ @Override public YearQuarter plus(TemporalAmount amountToAdd) { return (YearQuarter) amountToAdd.addTo(this); } /** * Returns a copy of this year-quarter with the specified amount added. *

* This returns a {@code YearQuarter}, based on this one, with the amount * in terms of the unit added. If it is not possible to add the amount, because the * unit is not supported or for some other reason, an exception is thrown. *

* If the field is a {@link ChronoUnit} then the addition is implemented here. * The supported fields behave as follows: *

    *
  • {@code QUARTER_YEARS} - * Returns a {@code YearQuarter} with the specified number of quarters added. * This is equivalent to {@link #plusQuarters(long)}. *
  • {@code YEARS} - * Returns a {@code YearQuarter} with the specified number of years added. * This is equivalent to {@link #plusYears(long)}. *
  • {@code DECADES} - * Returns a {@code YearQuarter} with the specified number of decades added. * This is equivalent to calling {@link #plusYears(long)} with the amount * multiplied by 10. *
  • {@code CENTURIES} - * Returns a {@code YearQuarter} with the specified number of centuries added. * This is equivalent to calling {@link #plusYears(long)} with the amount * multiplied by 100. *
  • {@code MILLENNIA} - * Returns a {@code YearQuarter} with the specified number of millennia added. * This is equivalent to calling {@link #plusYears(long)} with the amount * multiplied by 1,000. *
  • {@code ERAS} - * Returns a {@code YearQuarter} with the specified number of eras added. * Only two eras are supported so the amount must be one, zero or minus one. * If the amount is non-zero then the year is changed such that the year-of-era * is unchanged. *
*

* All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}. *

* If the field is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)} * passing {@code this} as the argument. In this case, the unit determines * whether and how to perform the addition. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount of the unit to add to the result, may be negative * @param unit the unit of the amount to add, not null * @return a {@code YearQuarter} based on this year-quarter with the specified amount added, not null * @throws DateTimeException if the addition cannot be made * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public YearQuarter plus(long amountToAdd, TemporalUnit unit) { if (unit == QUARTER_YEARS) { return plusQuarters(amountToAdd); } else if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case YEARS: return plusYears(amountToAdd); case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); default: throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } } return unit.addTo(this, amountToAdd); } /** * Returns a copy of this year-quarter with the specified period in years added. *

* This instance is immutable and unaffected by this method call. * * @param yearsToAdd the years to add, may be negative * @return a {@code YearQuarter} based on this year-quarter with the years added, not null * @throws DateTimeException if the result exceeds the supported range */ public YearQuarter plusYears(long yearsToAdd) { if (yearsToAdd == 0) { return this; } int newYear = YEAR.checkValidIntValue(year + yearsToAdd); // safe overflow return with(newYear, quarter); } /** * Returns a copy of this year-quarter with the specified period in quarters added. *

* This instance is immutable and unaffected by this method call. * * @param quartersToAdd the quarters to add, may be negative * @return a {@code YearQuarter} based on this year-quarter with the quarters added, not null * @throws DateTimeException if the result exceeds the supported range */ public YearQuarter plusQuarters(long quartersToAdd) { if (quartersToAdd == 0) { return this; } long quarterCount = year * 4L + (quarter.getValue() - 1); long calcQuarters = quarterCount + quartersToAdd; // safe overflow int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcQuarters, 4)); int newQuarter = (int) Math.floorMod(calcQuarters, 4) + 1; return with(newYear, Quarter.of(newQuarter)); } //----------------------------------------------------------------------- /** * Returns a copy of this year-quarter with the specified amount subtracted. *

* This returns a {@code YearQuarter}, based on this one, with the specified amount subtracted. * The amount is typically {@link Period} but may be any other type implementing * the {@link TemporalAmount} interface. *

* The calculation is delegated to the amount object by calling * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free * to implement the subtraction in any way it wishes, however it typically * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation * of the amount implementation to determine if it can be successfully subtracted. *

* This instance is immutable and unaffected by this method call. * * @param amountToSubtract the amount to subtract, not null * @return a {@code YearQuarter} based on this year-quarter with the subtraction made, not null * @throws DateTimeException if the subtraction cannot be made * @throws ArithmeticException if numeric overflow occurs */ @Override public YearQuarter minus(TemporalAmount amountToSubtract) { return (YearQuarter) amountToSubtract.subtractFrom(this); } /** * Returns a copy of this year-quarter with the specified amount subtracted. *

* This returns a {@code YearQuarter}, based on this one, with the amount * in terms of the unit subtracted. If it is not possible to subtract the amount, * because the unit is not supported or for some other reason, an exception is thrown. *

* This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated. * See that method for a full description of how addition, and thus subtraction, works. *

* This instance is immutable and unaffected by this method call. * * @param amountToSubtract the amount of the unit to subtract from the result, may be negative * @param unit the unit of the amount to subtract, not null * @return a {@code YearQuarter} based on this year-quarter with the specified amount subtracted, not null * @throws DateTimeException if the subtraction cannot be made * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public YearQuarter minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } /** * Returns a copy of this year-quarter with the specified period in years subtracted. *

* This instance is immutable and unaffected by this method call. * * @param yearsToSubtract the years to subtract, may be negative * @return a {@code YearQuarter} based on this year-quarter with the years subtracted, not null * @throws DateTimeException if the result exceeds the supported range */ public YearQuarter minusYears(long yearsToSubtract) { return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract)); } /** * Returns a copy of this year-quarter with the specified period in quarters subtracted. *

* This instance is immutable and unaffected by this method call. * * @param quartersToSubtract the quarters to subtract, may be negative * @return a {@code YearQuarter} based on this year-quarter with the quarters subtracted, not null * @throws DateTimeException if the result exceeds the supported range */ public YearQuarter minusQuarters(long quartersToSubtract) { return (quartersToSubtract == Long.MIN_VALUE ? plusQuarters(Long.MAX_VALUE).plusQuarters(1) : plusQuarters(-quartersToSubtract)); } //----------------------------------------------------------------------- /** * Queries this year-quarter using the specified query. *

* This queries this year-quarter using the specified query strategy object. * The {@code TemporalQuery} object defines the logic to be used to * obtain the result. Read the documentation of the query to understand * what the result of this method will be. *

* The result of this method is obtained by invoking the * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the * specified query passing {@code this} as the argument. * * @param the type of the result * @param query the query to invoke, not null * @return the query result, null may be returned (defined by the query) * @throws DateTimeException if unable to query (defined by the query) * @throws ArithmeticException if numeric overflow occurs (defined by the query) */ @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.chronology()) { return (R) IsoChronology.INSTANCE; } else if (query == TemporalQueries.precision()) { return (R) QUARTER_YEARS; } return Temporal.super.query(query); } /** * Adjusts the specified temporal object to have this year-quarter. *

* This returns a temporal object of the same observable type as the input * with the year and quarter changed to be the same as this. *

* The adjustment is equivalent to using {@link Temporal#plus(long, TemporalUnit)} * passing the number of quarters to adjust by. * If the specified temporal object does not use the ISO calendar system then * a {@code DateTimeException} is thrown. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#with(TemporalAdjuster)}: *

     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisYearQuarter.adjustInto(temporal);
     *   temporal = temporal.with(thisYearQuarter);
     * 
*

* This instance is immutable and unaffected by this method call. * * @param temporal the target object to be adjusted, not null * @return the adjusted object, not null * @throws DateTimeException if unable to make the adjustment * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal adjustInto(Temporal temporal) { if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) { throw new DateTimeException("Adjustment only supported on ISO date-time"); } long newProlepticQuarter = getProlepticQuarter(); long oldProlepticQuarter = temporal.get(YEAR) * 4L + (temporal.get(QUARTER_OF_YEAR) - 1); return temporal.plus(newProlepticQuarter - oldProlepticQuarter, QUARTER_YEARS); } /** * Calculates the amount of time until another year-quarter in terms of the specified unit. *

* This calculates the amount of time between two {@code YearQuarter} * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified year-quarter. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method is converted to a * {@code YearQuarter} using {@link #from(TemporalAccessor)}. * For example, the period in years between two year-quarters can be calculated * using {@code startYearQuarter.until(endYearQuarter, YEARS)}. *

* The calculation returns a whole number, representing the number of * complete units between the two year-quarters. * For example, the period in decades between 2012-Q3 and 2032-Q2 * will only be one decade as it is one quarter short of two decades. *

* There are two equivalent ways of using this method. * The first is to invoke this method. * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}: *

     *   // these two lines are equivalent
     *   amount = start.until(end, QUARTER_YEARS);
     *   amount = QUARTER_YEARS.between(start, end);
     * 
* The choice should be made based on which makes the code more readable. *

* The calculation is implemented in this method for {@link ChronoUnit}. * The units {@code QUARTER_YEARS}, {@code YEARS}, {@code DECADES}, * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported. * Other {@code ChronoUnit} values will throw an exception. *

* If the unit is not a {@code ChronoUnit}, then the result of this method * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} * passing {@code this} as the first argument and the converted input temporal * as the second argument. *

* This instance is immutable and unaffected by this method call. * * @param endExclusive the end date, exclusive, which is converted to a {@code YearQuarter}, not null * @param unit the unit to measure the amount in, not null * @return the amount of time between this year-quarter and the end year-quarter * @throws DateTimeException if the amount cannot be calculated, or the end * temporal cannot be converted to a {@code YearQuarter} * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long until(Temporal endExclusive, TemporalUnit unit) { YearQuarter end = YearQuarter.from(endExclusive); long quartersUntil = end.getProlepticQuarter() - getProlepticQuarter(); // no overflow if (unit == QUARTER_YEARS) { return quartersUntil; } else if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case YEARS: return quartersUntil / 4; case DECADES: return quartersUntil / 40; case CENTURIES: return quartersUntil / 400; case MILLENNIA: return quartersUntil / 4000; case ERAS: return end.getLong(ERA) - getLong(ERA); default: throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } } return unit.between(this, end); } /** * Returns a sequential ordered stream of year-quarter. The returned stream starts from this year-quarter * (inclusive) and goes to {@code endExclusive} (exclusive) by an incremental step of 1 {@code QUARTER_YEARS}. *

* This instance is immutable and unaffected by this method call. * * @param endExclusive the end year-quarter, exclusive, not null * @return a sequential {@code Stream} for the range of {@code YearQuarter} values * @throws IllegalArgumentException if end year-quarter is before this year-quarter */ public Stream quartersUntil(YearQuarter endExclusive) { if (endExclusive.isBefore(this)) { throw new IllegalArgumentException(endExclusive + " < " + this); } long intervalLength = until(endExclusive, QUARTER_YEARS); return LongStream.range(0,intervalLength).mapToObj(n -> plusQuarters(n)); } /** * Formats this year-quarter using the specified formatter. *

* This year-quarter will be passed to the formatter to produce a string. * * @param formatter the formatter to use, not null * @return the formatted year-quarter string, not null * @throws DateTimeException if an error occurs during printing */ public String format(DateTimeFormatter formatter) { Objects.requireNonNull(formatter, "formatter"); return formatter.format(this); } //----------------------------------------------------------------------- /** * Combines this year-quarter with a day-of-quarter to create a {@code LocalDate}. *

* This returns a {@code LocalDate} formed from this year-quarter and the specified day-of-quarter. *

* The day-of-quarter value must be valid for the year-quarter. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = yearQuarter.atDay(day);
     * 
* * @param dayOfQuarter the day-of-quarter to use, from 1 to 92 * @return the date formed from this year-quarter and the specified day, not null * @throws DateTimeException if the day is invalid for the year-quarter * @see #isValidDay(int) */ public LocalDate atDay(int dayOfQuarter) { ValueRange.of(1, lengthOfQuarter()).checkValidValue(dayOfQuarter, DAY_OF_QUARTER); boolean leap = Year.isLeap(year); Month month = quarter.firstMonth(); while (dayOfQuarter > month.length(leap)) { dayOfQuarter -= month.length(leap); month = month.plus(1); } return LocalDate.of(year, month, dayOfQuarter); } /** * Returns a {@code LocalDate} at the end of the quarter. *

* This returns a {@code LocalDate} based on this year-quarter. * The day-of-quarter is set to the last valid day of the quarter, taking * into account leap years. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = year.atQuarter(quarter).atEndOfQuarter();
     * 
* * @return the last valid date of this year-quarter, not null */ public LocalDate atEndOfQuarter() { Month month = quarter.firstMonth().plus(2); return LocalDate.of(year, month, month.maxLength()); } //----------------------------------------------------------------------- /** * Compares this year-quarter to another *

* The comparison is based first on the value of the year, then on the value of the quarter. * It is "consistent with equals", as defined by {@link Comparable}. * * @param other the other year-quarter to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(YearQuarter other) { int cmp = (year - other.year); if (cmp == 0) { cmp = quarter.compareTo(other.quarter); } return cmp; } /** * Is this year-quarter after the specified year-quarter. * * @param other the other year-quarter to compare to, not null * @return true if this is after the specified year-quarter */ public boolean isAfter(YearQuarter other) { return compareTo(other) > 0; } /** * Is this year-quarter before the specified year-quarter. * * @param other the other year-quarter to compare to, not null * @return true if this point is before the specified year-quarter */ public boolean isBefore(YearQuarter other) { return compareTo(other) < 0; } //----------------------------------------------------------------------- /** * Checks if this year-quarter is equal to another year-quarter. *

* The comparison is based on the time-line position of the year-quarters. * * @param obj the object to check, null returns false * @return true if this is equal to the other year-quarter */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof YearQuarter) { YearQuarter other = (YearQuarter) obj; return year == other.year && quarter == other.quarter; } return false; } /** * A hash code for this year-quarter. * * @return a suitable hash code */ @Override public int hashCode() { return year ^ (quarter.getValue() << 27); } //----------------------------------------------------------------------- /** * Outputs this year-quarter as a {@code String}, such as {@code 2007-Q2}. *

* The output will be in the format {@code uuuu-'Q'Q}: * * @return a string representation of this year-quarter, not null */ @Override @ToString public String toString() { int absYear = Math.abs(year); StringBuilder buf = new StringBuilder(10); if (absYear < 1000) { if (year < 0) { buf.append(year - 10000).deleteCharAt(1); } else { buf.append(year + 10000).deleteCharAt(0); } } else { if (year > 9999) { buf.append('+'); } buf.append(year); } return buf.append('-').append(quarter).toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/YearWeek.java000066400000000000000000000756711343451174100252420ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.DayOfWeek.THURSDAY; import static java.time.DayOfWeek.WEDNESDAY; import static java.time.temporal.IsoFields.WEEK_BASED_YEAR; import static java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.Year; import java.time.ZoneId; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.format.SignStyle; import java.time.temporal.ChronoField; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Objects; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A year-week in the ISO week date system such as {@code 2015-W13} *

* {@code YearWeek} is an immutable date-time object that represents the combination * of a week-based-year and week-of-week-based-year. * Any field that can be derived from those two fields can be obtained. *

* This class does not store or represent a day, time or time-zone. * For example, the value "13th week of 2007" can be stored in a {@code YearWeek}. *

* The ISO-8601 calendar system is the modern civil calendar system used today * in most of the world. It is equivalent to the proleptic Gregorian calendar * system, in which today's rules for leap years are applied for all time. * For most applications written today, the ISO-8601 rules are entirely suitable. * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. *

* ISO-8601 defines the week as always starting with Monday. * The first week is the week which contains the first Thursday of the calendar year. * As such, the week-based-year used in this class does not align with the calendar year. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class YearWeek implements TemporalAccessor, TemporalAdjuster, Comparable, Serializable { /** * Serialization version. */ private static final long serialVersionUID = 3381384054271883921L; /** * Parser. */ private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() .parseCaseInsensitive() .appendValue(WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendLiteral("-W") .appendValue(WEEK_OF_WEEK_BASED_YEAR, 2) .toFormatter(); /** * The week-based-year. */ private final int year; /** * The week-of-week-based-year */ private final int week; //----------------------------------------------------------------------- /** * Obtains the current year-week from the system clock in the default time-zone. *

* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current year-week. * The zone and offset will be set based on the time-zone in the clock. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current year-week using the system clock and default time-zone, not null */ public static YearWeek now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current year-week from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current year-week. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current year-week using the system clock, not null */ public static YearWeek now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current year-week from the specified clock. *

* This will query the specified clock to obtain the current year-week. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current year-week, not null */ public static YearWeek now(Clock clock) { final LocalDate now = LocalDate.now(clock); // called once return YearWeek.of(now.get(WEEK_BASED_YEAR), now.get(WEEK_OF_WEEK_BASED_YEAR)); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code YearWeek} from a week-based-year and week. *

* If the week is 53 and the year does not have 53 weeks, week one of the following * year is selected. * * @param weekBasedYear the week-based-year to represent, from MIN_YEAR to MAX_YEAR * @param week the week-of-week-based-year to represent, from 1 to 53 * @return the year-week, not null * @throws DateTimeException if either field is invalid */ public static YearWeek of(int weekBasedYear, int week) { WEEK_BASED_YEAR.range().checkValidValue(weekBasedYear, WEEK_BASED_YEAR); WEEK_OF_WEEK_BASED_YEAR.range().checkValidValue(week, WEEK_OF_WEEK_BASED_YEAR); if (week == 53 && weekRange(weekBasedYear) < 53) { week = 1; weekBasedYear++; WEEK_BASED_YEAR.range().checkValidValue(weekBasedYear, WEEK_BASED_YEAR); } return new YearWeek(weekBasedYear, week); } // from IsoFields in ThreeTen-Backport private static int weekRange(int weekBasedYear) { LocalDate date = LocalDate.of(weekBasedYear, 1, 1); // 53 weeks if year starts on Thursday, or Wed in a leap year if (date.getDayOfWeek() == THURSDAY || (date.getDayOfWeek() == WEDNESDAY && date.isLeapYear())) { return 53; } return 52; } //----------------------------------------------------------------------- /** * Obtains an instance of {@code YearWeek} from a temporal object. *

* This obtains a year-week based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code YearWeek}. *

* The conversion extracts the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and * {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields. * The extraction is only permitted if the temporal object has an ISO * chronology, or can be converted to a {@code LocalDate}. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used in queries via method reference, {@code YearWeek::from}. * * @param temporal the temporal object to convert, not null * @return the year-week, not null * @throws DateTimeException if unable to convert to a {@code YearWeek} */ public static YearWeek from(TemporalAccessor temporal) { if (temporal instanceof YearWeek) { return (YearWeek) temporal; } Objects.requireNonNull(temporal, "temporal"); try { if (!IsoChronology.INSTANCE.equals(Chronology.from(temporal))) { temporal = LocalDate.from(temporal); } // need to use getLong() as JDK Parsed class get() doesn't work properly int year = Math.toIntExact(temporal.getLong(WEEK_BASED_YEAR)); int week = Math.toIntExact(temporal.getLong(WEEK_OF_WEEK_BASED_YEAR)); return of(year, week); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain YearWeek from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Obtains an instance of {@code YearWeek} from a text string such as {@code 2007-W13}. *

* The string must represent a valid year-week. * Week 53 will be adjusted to week 1 of the following year if necessary. * The format must be {@code YYYY-'W'ww}. * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol. * * @param text the text to parse such as "2007-W13", not null * @return the parsed year-week, not null * @throws DateTimeParseException if the text cannot be parsed */ @FromString public static YearWeek parse(CharSequence text) { return parse(text, PARSER); } /** * Obtains an instance of {@code YearWeek} from a text string using a specific formatter. *

* The text is parsed using the formatter, returning a year-week. * * @param text the text to parse, not null * @param formatter the formatter to use, not null * @return the parsed year-week, not null * @throws DateTimeParseException if the text cannot be parsed */ public static YearWeek parse(CharSequence text, DateTimeFormatter formatter) { Objects.requireNonNull(formatter, "formatter"); return formatter.parse(text, YearWeek::from); } //----------------------------------------------------------------------- /** * Constructor. * * @param weekBasedYear the week-based-year to represent, validated from MIN_YEAR to MAX_YEAR * @param week the week to represent, validated */ private YearWeek(int weekBasedYear, int week) { this.year = weekBasedYear; this.week = week; } /** * Validates the input. * * @return the valid object, not null */ private Object readResolve() { return of(year, week); } /** * Returns a copy of this year-week with the new year and week, checking * to see if a new object is in fact required. * * @param newYear the year to represent, validated from MIN_YEAR to MAX_YEAR * @param newWeek the week to represent, validated from 1 to 53 * @return the year-week, not null */ private YearWeek with(int newYear, int newWeek) { if (year == newYear && week == newWeek) { return this; } return of(newYear, newWeek); } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *

* This checks if this year-week can be queried for the specified field. * If false, then calling the {@link #range(TemporalField) range} and * {@link #get(TemporalField) get} methods will throw an exception. *

* If the field is a {@link ChronoField} then the query is implemented here. * The supported fields are: *

    *
  • {@code WEEK_OF_WEEK_BASED_YEAR} *
  • {@code WEEK_BASED_YEAR} *
* All {@code ChronoField} instances will return false. *

* If the field is not a {@code ChronoField}, then the result of this method * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)} * passing {@code this} as the argument. * Whether the field is supported is determined by the field. * * @param field the field to check, null returns false * @return true if the field is supported on this year-week, false if not */ @Override public boolean isSupported(TemporalField field) { if (field == WEEK_OF_WEEK_BASED_YEAR || field == WEEK_BASED_YEAR) { return true; } else if (field instanceof ChronoField) { return false; } return field != null && field.isSupportedBy(this); } //----------------------------------------------------------------------- /** * Gets the range of valid values for the specified field. *

* The range object expresses the minimum and maximum valid values for a field. * This year-week is used to enhance the accuracy of the returned range. * If it is not possible to return the range, because the field is not supported * or for some other reason, an exception is thrown. *

* The range for the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and * {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields is returned. * All {@link ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. * * @param field the field to query the range for, not null * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported */ @Override public ValueRange range(TemporalField field) { if (field == WEEK_BASED_YEAR) { return WEEK_BASED_YEAR.range(); } if (field == WEEK_OF_WEEK_BASED_YEAR) { return ValueRange.of(1, weekRange(year)); } return TemporalAccessor.super.range(field); } /** * Gets the value of the specified field from this year-week as an {@code int}. *

* This queries this year-week for the value for the specified field. * The returned value will always be within the valid range of values for the field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* The value for the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and * {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields is returned. * All {@link ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained or * the value is outside the range of valid values for the field * @throws UnsupportedTemporalTypeException if the field is not supported or * the range of values exceeds an {@code int} * @throws ArithmeticException if numeric overflow occurs */ @Override public int get(TemporalField field) { if (field == WEEK_BASED_YEAR) { return year; } if (field == WEEK_OF_WEEK_BASED_YEAR) { return week; } return TemporalAccessor.super.get(field); } /** * Gets the value of the specified field from this year-week as a {@code long}. *

* This queries this year-week for the value for the specified field. * If it is not possible to return the value, because the field is not supported * or for some other reason, an exception is thrown. *

* The value for the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and * {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields is returned. * All {@link ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. * * @param field the field to get, not null * @return the value for the field * @throws DateTimeException if a value for the field cannot be obtained * @throws UnsupportedTemporalTypeException if the field is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public long getLong(TemporalField field) { if (field == WEEK_BASED_YEAR) { return year; } if (field == WEEK_OF_WEEK_BASED_YEAR) { return week; } if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } //----------------------------------------------------------------------- /** * Gets the week-based-year field. *

* This method returns the primitive {@code int} value for the week-based-year. *

* Note that the ISO week-based-year does not align with the standard Gregorian/ISO calendar year. * * @return the week-based-year */ public int getYear() { return year; } /** * Gets the week-of-week-based-year field. *

* This method returns the primitive {@code int} value for the week of the week-based-year. * * @return the week-of-week-based-year */ public int getWeek() { return week; } //----------------------------------------------------------------------- /** * Checks if the week-based-year has 53 weeks. *

* This determines if the year has 53 weeks, returning true. * If false, the year has 52 weeks. * * @return true if the year has 53 weeks, false otherwise */ public boolean is53WeekYear() { return weekRange(year) == 53; } /** * Returns the length of the week-based-year. *

* This returns the length of the year in days, either 364 or 371. * * @return 364 if the year has 52 weeks, 371 if it has 53 weeks */ public int lengthOfYear() { return (is53WeekYear() ? 371 : 364); } //----------------------------------------------------------------------- /** * Returns a copy of this {@code YearWeek} with the week-based-year altered. *

* This returns a year-week with the specified week-based-year. * If the week of this instance is 53 and the new year does not have 53 weeks, * the week will be adjusted to be 52. *

* This instance is immutable and unaffected by this method call. * * @param weekBasedYear the week-based-year to set in the returned year-week * @return a {@code YearWeek} based on this year-week with the requested year, not null * @throws DateTimeException if the week-based-year value is invalid */ public YearWeek withYear(int weekBasedYear) { if (week == 53 && weekRange(weekBasedYear) < 53) { return YearWeek.of(weekBasedYear, 52); } return with(weekBasedYear, week); } /** * Returns a copy of this {@code YearWeek} with the week altered. *

* This returns a year-week with the specified week-of-week-based-year. * If the new week is 53 and the year does not have 53 weeks, week one of the * following year is selected. *

* This instance is immutable and unaffected by this method call. * * @param week the week-of-week-based-year to set in the returned year-week * @return a {@code YearWeek} based on this year-week with the requested week, not null * @throws DateTimeException if the week-of-week-based-year value is invalid */ public YearWeek withWeek(int week) { return with(year, week); } //----------------------------------------------------------------------- /** * Returns a copy of this year-week with the specified number of years added. *

* If the week of this instance is 53 and the new year does not have 53 weeks, * the week will be adjusted to be 52. *

* This instance is immutable and unaffected by this method call. * * @param yearsToAdd the years to add, may be negative * @return the year-week with the years added, not null */ public YearWeek plusYears(long yearsToAdd) { if (yearsToAdd == 0) { return this; } int newYear = Math.toIntExact(Math.addExact(year, yearsToAdd)); return withYear(newYear); } /** * Returns a copy of this year-week with the specified number of weeks added. *

* This instance is immutable and unaffected by this method call. * * @param weeksToAdd the weeks to add, may be negative * @return the year-week with the weeks added, not null */ public YearWeek plusWeeks(long weeksToAdd) { if (weeksToAdd == 0) { return this; } LocalDate mondayOfWeek = atDay(DayOfWeek.MONDAY).plusWeeks(weeksToAdd); return YearWeek.from(mondayOfWeek); } /** * Returns a copy of this year-week with the specified number of years subtracted. *

* If the week of this instance is 53 and the new year does not have 53 weeks, * the week will be adjusted to be 52. *

* This instance is immutable and unaffected by this method call. * * @param yearsToSubtract the years to subtract, may be negative * @return the year-week with the years subtracted, not null */ public YearWeek minusYears(long yearsToSubtract) { if (yearsToSubtract == 0) { return this; } int newYear = Math.toIntExact(Math.subtractExact(year, yearsToSubtract)); return withYear(newYear); } /** * Returns a copy of this year-week with the specified number of weeks subtracted. *

* This instance is immutable and unaffected by this method call. * * @param weeksToSubtract the weeks to subtract, may be negative * @return the year-week with the weeks subtracted, not null */ public YearWeek minusWeeks(long weeksToSubtract) { if (weeksToSubtract == 0) { return this; } LocalDate mondayOfWeek = atDay(DayOfWeek.MONDAY).minusWeeks(weeksToSubtract); return YearWeek.from(mondayOfWeek); } //----------------------------------------------------------------------- /** * Queries this year-week using the specified query. *

* This queries this year-week using the specified query strategy object. * The {@code TemporalQuery} object defines the logic to be used to * obtain the result. Read the documentation of the query to understand * what the result of this method will be. *

* The result of this method is obtained by invoking the * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the * specified query passing {@code this} as the argument. * * @param the type of the result * @param query the query to invoke, not null * @return the query result, null may be returned (defined by the query) * @throws DateTimeException if unable to query (defined by the query) * @throws ArithmeticException if numeric overflow occurs (defined by the query) */ @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.chronology()) { return (R) IsoChronology.INSTANCE; } return TemporalAccessor.super.query(query); } /** * Adjusts the specified temporal object to have this year-week. *

* This returns a temporal object of the same observable type as the input * with the week-based-year and week changed to be the same as this. *

* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} * twice, passing {@link IsoFields#WEEK_BASED_YEAR} and * {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR} as the fields. * If the specified temporal object does not use the ISO calendar system then * a {@code DateTimeException} is thrown. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#with(TemporalAdjuster)}: *

     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisYearWeek.adjustInto(temporal);
     *   temporal = temporal.with(thisYearWeek);
     * 
*

* This instance is immutable and unaffected by this method call. * * @param temporal the target object to be adjusted, not null * @return the adjusted object, not null * @throws DateTimeException if unable to make the adjustment * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal adjustInto(Temporal temporal) { if (!Chronology.from(temporal).equals(IsoChronology.INSTANCE)) { throw new DateTimeException("Adjustment only supported on ISO date-time"); } return temporal.with(WEEK_BASED_YEAR, year).with(WEEK_OF_WEEK_BASED_YEAR, week); } /** * Formats this year-week using the specified formatter. *

* This year-week will be passed to the formatter to produce a string. * * @param formatter the formatter to use, not null * @return the formatted year-week string, not null * @throws DateTimeException if an error occurs during printing */ public String format(DateTimeFormatter formatter) { Objects.requireNonNull(formatter, "formatter"); return formatter.format(this); } //----------------------------------------------------------------------- /** * Combines this year-week with a day-of-week to create a {@code LocalDate}. *

* This returns a {@code LocalDate} formed from this year-week and the specified day-of-Week. *

* This method can be used as part of a chain to produce a date: *

     *  LocalDate date = yearWeek.atDay(MONDAY);
     * 
* * @param dayOfWeek the day-of-week to use, not null * @return the date formed from this year-week and the specified day, not null */ public LocalDate atDay(DayOfWeek dayOfWeek) { Objects.requireNonNull(dayOfWeek, "dayOfWeek"); int correction = LocalDate.of(year, 1, 4).getDayOfWeek().getValue() + 3; int dayOfYear = week * 7 + dayOfWeek.getValue() - correction; int maxDaysOfYear = Year.isLeap(year) ? 366 : 365; if (dayOfYear > maxDaysOfYear) { return LocalDate.ofYearDay(year + 1, dayOfYear - maxDaysOfYear); } if (dayOfYear > 0) { return LocalDate.ofYearDay(year, dayOfYear); } else { int daysOfPreviousYear = Year.isLeap(year - 1) ? 366 : 365; return LocalDate.ofYearDay(year - 1, daysOfPreviousYear + dayOfYear); } } //----------------------------------------------------------------------- /** * Compares this year-week to another *

* The comparison is based first on the value of the year, then on the value of the week. * It is "consistent with equals", as defined by {@link Comparable}. * * @param other the other year-week to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(YearWeek other) { int cmp = (year - other.year); if (cmp == 0) { cmp = (week - other.week); } return cmp; } /** * Is this year-week after the specified year-week. * * @param other the other year-week to compare to, not null * @return true if this is after the specified year-week */ public boolean isAfter(YearWeek other) { return compareTo(other) > 0; } /** * Is this year-week before the specified year-week. * * @param other the other year-week to compare to, not null * @return true if this point is before the specified year-week */ public boolean isBefore(YearWeek other) { return compareTo(other) < 0; } //----------------------------------------------------------------------- /** * Checks if this year-week is equal to another year-week. * * @param obj the object to check, null returns false * @return true if this is equal to the other year-week */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof YearWeek) { YearWeek other = (YearWeek) obj; return year == other.year && week == other.week; } return false; } /** * A hash code for this year-week. * * @return a suitable hash code */ @Override public int hashCode() { return year ^ (week << 25); } //----------------------------------------------------------------------- /** * Outputs this year-week as a {@code String}, such as {@code 2015-W13}. *

* The output will be in the format {@code YYYY-'W'ww}: * * @return a string representation of this year-week, not null */ @Override @ToString public String toString() { int absYear = Math.abs(year); StringBuilder buf = new StringBuilder(10); if (absYear < 1000) { if (year < 0) { buf.append(year - 10000).deleteCharAt(1); } else { buf.append(year + 10000).deleteCharAt(0); } } else { if (year > 9999) { buf.append('+'); } buf.append(year); } return buf.append(week < 10 ? "-W0" : "-W").append(week).toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/Years.java000066400000000000000000000465271343451174100246070ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.YEARS; import java.io.Serializable; import java.time.DateTimeException; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * A year-based amount of time, such as '12 years'. *

* This class models a quantity or amount of time in terms of years. * It is a type-safe way of representing a number of years in an application. *

* The model is of a directed amount, meaning that the amount may be negative. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Years implements TemporalAmount, Comparable, Serializable { /** * A constant for zero years. */ public static final Years ZERO = new Years(0); /** * A constant for one year. */ public static final Years ONE = new Years(1); /** * A serialization identifier for this class. */ private static final long serialVersionUID = -8903767091325669093L; /** * The pattern for parsing. */ private static final Pattern PATTERN = Pattern.compile("([-+]?)P([-+]?[0-9]+)Y", Pattern.CASE_INSENSITIVE); /** * The number of years. */ private final int years; /** * Obtains a {@code Years} representing a number of years. *

* The resulting amount will have the specified years. * * @param years the number of years, positive or negative * @return the number of years, not null */ public static Years of(int years) { if (years == 0) { return ZERO; } else if (years == 1) { return ONE; } return new Years(years); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Years} from a temporal amount. *

* This obtains an instance based on the specified amount. * A {@code TemporalAmount} represents an amount of time, which may be * date-based or time-based, which this factory extracts to a {@code Years}. *

* The result is calculated by looping around each unit in the specified amount. * Each amount is converted to years using {@link Temporals#convertAmount}. * If the conversion yields a remainder, an exception is thrown. * If the amount is zero, the unit is ignored. * For example, "12 months" can be converted to years but "11 months" cannot. * * @param amount the temporal amount to convert, not null * @return the equivalent amount, not null * @throws DateTimeException if unable to convert to a {@code Years} * @throws ArithmeticException if numeric overflow occurs */ public static Years from(TemporalAmount amount) { if (amount instanceof Years) { return (Years) amount; } Objects.requireNonNull(amount, "amount"); int years = 0; for (TemporalUnit unit : amount.getUnits()) { long value = amount.get(unit); if (value != 0) { long[] converted = Temporals.convertAmount(value, unit, YEARS); if (converted[1] != 0) { throw new DateTimeException( "Amount could not be converted to a whole number of years: " + value + " " + unit); } years = Math.addExact(years, Math.toIntExact(converted[0])); } } return of(years); } //----------------------------------------------------------------------- /** * Obtains a {@code Years} from a text string such as {@code PnY}. *

* This will parse the string produced by {@code toString()} which is * based on the ISO-8601 period formats {@code PnY}. *

* The string starts with an optional sign, denoted by the ASCII negative * or positive symbol. If negative, the whole amount is negated. * The ASCII letter "P" is next in upper or lower case. * The ASCII integer amount is next, which may be negative. * The ASCII letter "Y" is next in upper or lower case. *

* The leading plus/minus sign, and negative values for years are * not part of the ISO-8601 standard. *

* For example, the following are valid inputs: *

     *   "P2Y"             -- Years.of(2)
     *   "P-2Y"            -- Years.of(-2)
     *   "-P2Y"            -- Years.of(-2)
     *   "-P-2Y"           -- Years.of(2)
     * 
* * @param text the text to parse, not null * @return the parsed period, not null * @throws DateTimeParseException if the text cannot be parsed to a period */ @FromString public static Years parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PATTERN.matcher(text); if (matcher.matches()) { int negate = "-".equals(matcher.group(1)) ? -1 : 1; String str = matcher.group(2); try { int val = Integer.parseInt(str); return of(Math.multiplyExact(val, negate)); } catch (NumberFormatException ex) { throw new DateTimeParseException("Text cannot be parsed to a Years", text, 0, ex); } } throw new DateTimeParseException("Text cannot be parsed to a Years", text, 0); } //----------------------------------------------------------------------- /** * Obtains a {@code Years} consisting of the number of years between two dates. *

* The start date is included, but the end date is not. * The result of this method can be negative if the end is before the start. * * @param startDateInclusive the start date, inclusive, not null * @param endDateExclusive the end date, exclusive, not null * @return the number of years between this date and the end date, not null */ public static Years between(Temporal startDateInclusive, Temporal endDateExclusive) { return of(Math.toIntExact(YEARS.between(startDateInclusive, endDateExclusive))); } //----------------------------------------------------------------------- /** * Constructs an instance using a specific number of years. * * @param years the years to use */ private Years(int years) { super(); this.years = years; } /** * Resolves singletons. * * @return the singleton instance */ private Object readResolve() { return Years.of(years); } //----------------------------------------------------------------------- /** * Gets the value of the requested unit. *

* This returns a value for the supported unit - {@link ChronoUnit#YEARS YEARS}. * All other units throw an exception. * * @param unit the {@code TemporalUnit} for which to return the value * @return the long value of the unit * @throws UnsupportedTemporalTypeException if the unit is not supported */ @Override public long get(TemporalUnit unit) { if (unit == YEARS) { return years; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } /** * Gets the set of units supported by this amount. *

* The single supported unit is {@link ChronoUnit#YEARS YEARS}. *

* This set can be used in conjunction with {@link #get(TemporalUnit)} * to access the entire state of the amount. * * @return a list containing the years unit, not null */ @Override public List getUnits() { return Collections.singletonList(YEARS); } //----------------------------------------------------------------------- /** * Gets the number of years in this amount. * * @return the number of years */ public int getAmount() { return years; } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount added. *

* The parameter is converted using {@link Years#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Years} based on this instance with the requested amount added, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Years plus(TemporalAmount amountToAdd) { return plus(Years.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of years added. *

* This instance is immutable and unaffected by this method call. * * @param years the amount of years to add, may be negative * @return a {@code Years} based on this instance with the requested amount added, not null * @throws ArithmeticException if the result overflows an int */ public Years plus(int years) { if (years == 0) { return this; } return of(Math.addExact(this.years, years)); } //----------------------------------------------------------------------- /** * Returns a copy of this amount with the specified amount subtracted. *

* The parameter is converted using {@link Years#from(TemporalAmount)}. *

* This instance is immutable and unaffected by this method call. * * @param amountToAdd the amount to add, not null * @return a {@code Years} based on this instance with the requested amount subtracted, not null * @throws DateTimeException if the specified amount contains an invalid unit * @throws ArithmeticException if numeric overflow occurs */ public Years minus(TemporalAmount amountToAdd) { return minus(Years.from(amountToAdd).getAmount()); } /** * Returns a copy of this amount with the specified number of years subtracted. *

* This instance is immutable and unaffected by this method call. * * @param years the amount of years to add, may be negative * @return a {@code Years} based on this instance with the requested amount subtracted, not null * @throws ArithmeticException if the result overflows an int */ public Years minus(int years) { if (years == 0) { return this; } return of(Math.subtractExact(this.years, years)); } //----------------------------------------------------------------------- /** * Returns an instance with the amount multiplied by the specified scalar. *

* This instance is immutable and unaffected by this method call. * * @param scalar the scalar to multiply by, not null * @return the amount multiplied by the specified scalar, not null * @throws ArithmeticException if numeric overflow occurs */ public Years multipliedBy(int scalar) { if (scalar == 1) { return this; } return of(Math.multiplyExact(years, scalar)); } /** * Returns an instance with the amount divided by the specified divisor. *

* The calculation uses integer division, thus 3 divided by 2 is 1. *

* This instance is immutable and unaffected by this method call. * * @param divisor the amount to divide by, may be negative * @return the amount divided by the specified divisor, not null * @throws ArithmeticException if the divisor is zero */ public Years dividedBy(int divisor) { if (divisor == 1) { return this; } return of(years / divisor); } /** * Returns an instance with the amount negated. *

* This instance is immutable and unaffected by this method call. * * @return the negated amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Years negated() { return multipliedBy(-1); } /** * Returns a copy of this duration with a positive length. *

* This method returns a positive duration by effectively removing the sign from any negative total length. *

* This instance is immutable and unaffected by this method call. * * @return the absolute amount, not null * @throws ArithmeticException if numeric overflow occurs, which only happens if * the amount is {@code Long.MIN_VALUE} */ public Years abs() { return years < 0 ? negated() : this; } //------------------------------------------------------------------------- /** * Gets the number of years as a {@code Period}. *

* This returns a period with the same number of years. * * @return the equivalent period, not null */ public Period toPeriod() { return Period.ofYears(years); } //----------------------------------------------------------------------- /** * Adds this amount to the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount added. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#plus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.addTo(dateTime);
     *   dateTime = dateTime.plus(thisAmount);
     * 
*

* Only non-zero amounts will be added. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws UnsupportedTemporalTypeException if the YEARS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { if (years != 0) { temporal = temporal.plus(years, YEARS); } return temporal; } /** * Subtracts this amount from the specified temporal object. *

* This returns a temporal object of the same observable type as the input * with this amount subtracted. *

* In most cases, it is clearer to reverse the calling pattern by using * {@link Temporal#minus(TemporalAmount)}. *

     *   // these two lines are equivalent, but the second approach is recommended
     *   dateTime = thisAmount.subtractFrom(dateTime);
     *   dateTime = dateTime.minus(thisAmount);
     * 
*

* Only non-zero amounts will be subtracted. *

* This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws UnsupportedTemporalTypeException if the YEARS unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { if (years != 0) { temporal = temporal.minus(years, YEARS); } return temporal; } //----------------------------------------------------------------------- /** * Compares this amount to the specified {@code Years}. *

* The comparison is based on the total length of the amounts. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherAmount the other amount, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(Years otherAmount) { int thisValue = this.years; int otherValue = otherAmount.years; return Integer.compare(thisValue, otherValue); } //----------------------------------------------------------------------- /** * Checks if this amount is equal to the specified {@code Years}. *

* The comparison is based on the total length of the durations. * * @param otherAmount the other amount, null returns false * @return true if the other amount is equal to this one */ @Override public boolean equals(Object otherAmount) { if (this == otherAmount) { return true; } if (otherAmount instanceof Years) { Years other = (Years) otherAmount; return this.years == other.years; } return false; } /** * A hash code for this amount. * * @return a suitable hash code */ @Override public int hashCode() { return years; } //----------------------------------------------------------------------- /** * Returns a string representation of the number of years. * This will be in the format 'PnY' where n is the number of years. * * @return the number of years in ISO-8601 string format */ @Override @ToString public String toString() { return "P" + years + "Y"; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/000077500000000000000000000000001343451174100241335ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AbstractDate.java000066400000000000000000000357721343451174100273550ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.YEAR; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; /** * An abstract date based on a year, month and day. * *

Implementation Requirements

* Implementations must be immutable and thread-safe. */ abstract class AbstractDate implements ChronoLocalDate { /** * Creates an instance. */ AbstractDate() { } //----------------------------------------------------------------------- abstract int getProlepticYear(); abstract int getMonth(); abstract int getDayOfMonth(); abstract int getDayOfYear(); AbstractDate withDayOfYear(int value) { return plusDays(value - getDayOfYear()); } int lengthOfWeek() { return 7; } int lengthOfYearInMonths() { return 12; } abstract ValueRange rangeAlignedWeekOfMonth(); abstract AbstractDate resolvePrevious(int newYear, int newMonth, int dayOfMonth); AbstractDate resolveEpochDay(long epochDay) { return (AbstractDate) getChronology().dateEpochDay(epochDay); } //----------------------------------------------------------------------- @Override public ValueRange range(TemporalField field) { if (field instanceof ChronoField) { if (isSupported(field)) { return rangeChrono((ChronoField) field); } throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.rangeRefinedBy(this); } ValueRange rangeChrono(ChronoField field) { switch (field) { case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth()); case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear()); case ALIGNED_WEEK_OF_MONTH: return rangeAlignedWeekOfMonth(); default: break; } return getChronology().range(field); } //----------------------------------------------------------------------- @Override public long getLong(TemporalField field) { if (field instanceof ChronoField) { switch ((ChronoField) field) { case DAY_OF_WEEK: return getDayOfWeek(); case ALIGNED_DAY_OF_WEEK_IN_MONTH: return getAlignedDayOfWeekInMonth(); case ALIGNED_DAY_OF_WEEK_IN_YEAR: return getAlignedDayOfWeekInYear(); case DAY_OF_MONTH: return getDayOfMonth(); case DAY_OF_YEAR: return getDayOfYear(); case EPOCH_DAY: return toEpochDay(); case ALIGNED_WEEK_OF_MONTH: return getAlignedWeekOfMonth(); case ALIGNED_WEEK_OF_YEAR: return getAlignedWeekOfYear(); case MONTH_OF_YEAR: return getMonth(); case PROLEPTIC_MONTH: return getProlepticMonth(); case YEAR_OF_ERA: return getYearOfEra(); case YEAR: return getProlepticYear(); case ERA: return (getProlepticYear() >= 1 ? 1 : 0); default: break; } throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } int getAlignedDayOfWeekInMonth() { return ((getDayOfMonth() - 1) % lengthOfWeek()) + 1; } int getAlignedDayOfWeekInYear() { return ((getDayOfYear() - 1) % lengthOfWeek()) + 1; } int getAlignedWeekOfMonth() { return ((getDayOfMonth() - 1) / lengthOfWeek()) + 1; } int getAlignedWeekOfYear() { return ((getDayOfYear() - 1) / lengthOfWeek()) + 1; } int getDayOfWeek() { return (int) (Math.floorMod(toEpochDay() + 3, 7) + 1); } long getProlepticMonth() { return getProlepticYear() * lengthOfYearInMonths() + getMonth() - 1; } int getYearOfEra() { return getProlepticYear() >= 1 ? getProlepticYear() : 1 - getProlepticYear(); } //------------------------------------------------------------------------- @Override public AbstractDate with(TemporalField field, long newValue) { if (field instanceof ChronoField) { ChronoField f = (ChronoField) field; getChronology().range(f).checkValidValue(newValue, f); int nvalue = (int) newValue; switch (f) { case DAY_OF_WEEK: return plusDays(newValue - getDayOfWeek()); case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH)); case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR)); case DAY_OF_MONTH: return resolvePrevious(getProlepticYear(), getMonth(), nvalue); case DAY_OF_YEAR: return withDayOfYear(nvalue); case EPOCH_DAY: return resolveEpochDay(newValue); case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * lengthOfWeek()); case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * lengthOfWeek()); case MONTH_OF_YEAR: return resolvePrevious(getProlepticYear(), nvalue, getDayOfMonth()); case PROLEPTIC_MONTH: return plusMonths(newValue - getProlepticMonth()); case YEAR_OF_ERA: return resolvePrevious(getProlepticYear() >= 1 ? nvalue : 1 - nvalue, getMonth(), getDayOfMonth()); case YEAR: return resolvePrevious(nvalue, getMonth(), getDayOfMonth()); case ERA: return newValue == getLong(ERA) ? this : resolvePrevious(1 - getProlepticYear(), getMonth(), getDayOfMonth()); default: break; } throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.adjustInto(this, newValue); } @Override public AbstractDate plus(long amountToAdd, TemporalUnit unit) { if (unit instanceof ChronoUnit) { ChronoUnit f = (ChronoUnit) unit; switch (f) { case DAYS: return plusDays(amountToAdd); case WEEKS: return plusWeeks(amountToAdd); case MONTHS: return plusMonths(amountToAdd); case YEARS: return plusYears(amountToAdd); case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); default: break; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } return unit.addTo(this, amountToAdd); } AbstractDate plusYears(long yearsToAdd) { if (yearsToAdd == 0) { return this; } int newYear = YEAR.checkValidIntValue(Math.addExact(getProlepticYear(), yearsToAdd)); return resolvePrevious(newYear, getMonth(), getDayOfMonth()); } AbstractDate plusMonths(long months) { if (months == 0) { return this; } long curEm = getProlepticMonth(); long calcEm = Math.addExact(curEm, months); int newYear = Math.toIntExact(Math.floorDiv(calcEm, lengthOfYearInMonths())); int newMonth = (int) (Math.floorMod(calcEm, lengthOfYearInMonths()) + 1); return resolvePrevious(newYear, newMonth, getDayOfMonth()); } AbstractDate plusWeeks(long amountToAdd) { return plusDays(Math.multiplyExact(amountToAdd, lengthOfWeek())); } AbstractDate plusDays(long days) { if (days == 0) { return this; } return resolveEpochDay(Math.addExact(toEpochDay(), days)); } //------------------------------------------------------------------------- long until(AbstractDate end, TemporalUnit unit) { if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case DAYS: return daysUntil(end); case WEEKS: return weeksUntil(end); case MONTHS: return monthsUntil(end); case YEARS: return monthsUntil(end) / lengthOfYearInMonths(); case DECADES: return monthsUntil(end) / (lengthOfYearInMonths() * 10); case CENTURIES: return monthsUntil(end) / (lengthOfYearInMonths() * 100); case MILLENNIA: return monthsUntil(end) / (lengthOfYearInMonths() * 1000); case ERAS: return end.getLong(ERA) - getLong(ERA); default: break; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } return unit.between(this, end); } long daysUntil(ChronoLocalDate end) { return end.toEpochDay() - toEpochDay(); // no overflow } long weeksUntil(AbstractDate end) { return daysUntil(end) / lengthOfWeek(); } long monthsUntil(AbstractDate end) { long packed1 = getProlepticMonth() * 256L + getDayOfMonth(); // no overflow long packed2 = end.getProlepticMonth() * 256L + end.getDayOfMonth(); // no overflow return (packed2 - packed1) / 256L; } ChronoPeriod doUntil(AbstractDate end) { long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe int days = end.getDayOfMonth() - this.getDayOfMonth(); if (totalMonths > 0 && days < 0) { totalMonths--; AbstractDate calcDate = this.plusMonths(totalMonths); days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe } else if (totalMonths < 0 && days > 0) { totalMonths++; days -= end.lengthOfMonth(); } long years = totalMonths / lengthOfYearInMonths(); // safe int months = (int) (totalMonths % lengthOfYearInMonths()); // safe return getChronology().period(Math.toIntExact(years), months, days); } //------------------------------------------------------------------------- /** * Compares this date to another date, including the chronology. *

* Compares this date with another ensuring that the date is the same. *

* Only objects of this concrete type are compared, other types return false. * To compare the dates of two {@code TemporalAccessor} instances, including dates * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator. * * @param obj the object to check, null returns false * @return true if this is equal to the other date */ @Override // override for performance public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && this.getClass() == obj.getClass()) { AbstractDate otherDate = (AbstractDate) obj; return this.getProlepticYear() == otherDate.getProlepticYear() && this.getMonth() == otherDate.getMonth() && this.getDayOfMonth() == otherDate.getDayOfMonth(); } return false; } /** * A hash code for this date. * * @return a suitable hash code based only on the Chronology and the date */ @Override // override for performance public int hashCode() { return getChronology().getId().hashCode() ^ ((getProlepticYear() & 0xFFFFF800) ^ ((getProlepticYear() << 11) + (getMonth() << 6) + (getDayOfMonth()))); } @Override public String toString() { StringBuilder buf = new StringBuilder(30); buf.append(getChronology().toString()) .append(" ") .append(getEra()) .append(" ") .append(getYearOfEra()) .append(getMonth() < 10 ? "-0" : "-").append(getMonth()) .append(getDayOfMonth() < 10 ? "-0" : "-").append(getDayOfMonth()); return buf.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AbstractNileChronology.java000066400000000000000000000103451343451174100314200ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.chrono.AbstractChronology; import java.time.temporal.ChronoField; import java.time.temporal.ValueRange; /** * A chronology describing one of the Nile river calendar systems. * *

Implementation Requirements

* Implementations must be immutable and thread-safe. */ abstract class AbstractNileChronology extends AbstractChronology { /** * Range of proleptic-year. */ static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999); /** * Range of year. */ static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999); /** * Range of proleptic month. */ static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998 * 13L, 999_999 * 13L + 12); /** * Range of months. */ static final ValueRange MOY_RANGE = ValueRange.of(1, 13); /** * Range of weeks. */ static final ValueRange ALIGNED_WOM_RANGE = ValueRange.of(1, 1, 5); /** * Range of days. */ static final ValueRange DOM_RANGE = ValueRange.of(1, 5, 30); /** * Range of days. */ static final ValueRange DOM_RANGE_NONLEAP = ValueRange.of(1, 5); /** * Range of days. */ static final ValueRange DOM_RANGE_LEAP = ValueRange.of(1, 6); /** * Private constructor. */ AbstractNileChronology() { } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* The proleptic-year is leap if the remainder after division by four equals three. * This method does not validate the year passed in, and only has a * well-defined result for years in the supported range. * * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long prolepticYear) { return Math.floorMod(prolepticYear, 4) == 3; } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case DAY_OF_MONTH: return DOM_RANGE; case ALIGNED_WEEK_OF_MONTH: return ALIGNED_WOM_RANGE; case MONTH_OF_YEAR: return MOY_RANGE; case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: return YOE_RANGE; case YEAR: return YEAR_RANGE; default: break; } return field.range(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AbstractNileDate.java000066400000000000000000000064171343451174100301570ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.temporal.ValueRange; /** * A date in one of the Nile river calendar systems. * *

Implementation Requirements

* Implementations must be immutable and thread-safe. */ abstract class AbstractNileDate extends AbstractDate { /** * Creates an instance. */ AbstractNileDate() { } //----------------------------------------------------------------------- abstract int getEpochDayDifference(); @Override int getDayOfYear() { return (getMonth() - 1) * 30 + getDayOfMonth(); } @Override AbstractDate withDayOfYear(int value) { return resolvePrevious(getProlepticYear(), ((value - 1) / 30) + 1, ((value - 1) % 30) + 1); } @Override int lengthOfYearInMonths() { return 13; } @Override ValueRange rangeAlignedWeekOfMonth() { return ValueRange.of(1, getMonth() == 13 ? 1 : 5); } //----------------------------------------------------------------------- /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * Months 1 to 12 have 30 days. Month 13 has 5 or 6 days. * * @return the length of the month in days, from 5 to 30 */ @Override public int lengthOfMonth() { if (getMonth() == 13) { return (isLeapYear() ? 6 : 5); } return 30; } @Override public long toEpochDay() { long year = (long) getProlepticYear(); long calendarEpochDay = ((year - 1) * 365) + Math.floorDiv(year, 4) + (getDayOfYear() - 1); return calendarEpochDay - getEpochDayDifference(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingChronology.java000066400000000000000000000612011343451174100311340ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static org.threeten.extra.chrono.AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.Instant; import java.time.LocalDate; import java.time.Month; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjusters; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; /** * An Accounting calendar system. *

* This chronology defines the rules of a proleptic 52/53-week Accounting calendar system. * This calendar system follows the rules as laid down in IRS Publication 538 * and the International Financial Reporting Standards. * The start of the Accounting calendar will vary against the ISO calendar. * Depending on options chosen, it can start as early as {@code 0000-01-26 (ISO)} or as late as {@code 0001-01-04 (ISO)}. *

* This class is proleptic. It implements Accounting chronology rules for the entire time-line. *

* The fields are defined as follows: *

    *
  • era - There are two eras, the current 'Current Era' (CE) and the previous era 'Before Current Era' (BCE). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. * For the previous era the year increases from one as time goes backwards. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the * current era. For the previous era, years have zero, then negative values. *
  • month-of-year - There are 12 or 13 months (periods) in an Accounting year, numbered from 1 to 12 or 13. *
  • day-of-month - There are 28 or 35 days in each Accounting month, numbered from 1 to 35. * Month length depends on how the year has been divided. * When the Accounting leap year occurs, a week (7 days) is added to a specific month; * this increases to maximum day-of-month numbering to 35 or 42. *
  • day-of-year - There are 364 days in a standard Accounting year and 371 in a leap year. * The days are numbered from 1 to 364 or 1 to 371. *
  • leap-year - Leap years usually occur every 5 or 6 years. Timing depends on settings chosen. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class AccountingChronology extends AbstractChronology implements Serializable { /** * Serialization version. */ private static final long serialVersionUID = 7291205177830286973L; /** * Range of proleptic month for 12-month (period) year. */ private static final ValueRange PROLEPTIC_MONTH_RANGE_12 = ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11); /** * Range of proleptic month for 13-month (period) year. */ private static final ValueRange PROLEPTIC_MONTH_RANGE_13 = ValueRange.of(-999_999 * 13L, 999_999 * 13L + 12); /** * Range of weeks in year. */ private static final ValueRange ALIGNED_WEEK_OF_YEAR_RANGE = ValueRange.of(1, 52, 53); /** * Range of days in year. */ static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, 364, 371); /** * The day of the week on which a given Accounting year ends. */ private final DayOfWeek endsOn; /** * Whether the calendar ends in the last week of a given Gregorian/ISO month, * or nearest to the last day of the month (will sometimes be in the next month). */ private final boolean inLastWeek; /** * Which Gregorian/ISO end-of-month the year ends in/is nearest to. */ private final Month end; /** * How to divide an accounting year. */ private final AccountingYearDivision division; /** * The month which will have the leap-week added. */ private final int leapWeekInMonth; /** * Difference in days between accounting year end and ISO month end, in ISO year 0. */ private final transient int yearZeroDifference; /** * Number of weeks in a month range. */ private final transient ValueRange alignedWeekOfMonthRange; /** * Number of days in a month range. */ private final transient ValueRange dayOfMonthRange; /** * Number of days from the start of Accounting year 1 (for this chronology) to the start of ISO 1970 */ private final transient int days0001ToIso1970; //----------------------------------------------------------------------- /** * Creates an {@code AccountingChronology} validating the input. * Package private as only meant to be called from the builder. * * @param endsOn The day-of-week a given year ends on. * @param end The month-end the year is based on. * @param inLastWeek Whether the year ends in the last week of the month, or nearest the end-of-month. * @param division How the year is divided. * @param leapWeekInMonth The month in which the leap-week resides. * @return The created Chronology, not null. * @throws DateTimeException if the chronology cannot be built. */ static AccountingChronology create(DayOfWeek endsOn, Month end, boolean inLastWeek, AccountingYearDivision division, int leapWeekInMonth) { if (endsOn == null || end == null || division == null || leapWeekInMonth == 0) { throw new IllegalStateException("AccountingCronology cannot be built: " + (endsOn == null ? "| ending day-of-week |" : "") + (end == null ? "| month ending in/nearest to |" : "") + (division == null ? "| how year divided |" : "") + (leapWeekInMonth == 0 ? "| leap-week month |" : "") + " not set."); } if (!division.getMonthsInYearRange().isValidValue(leapWeekInMonth)) { throw new IllegalStateException("Leap week cannot not be placed in non-existant month " + leapWeekInMonth + ", range is [" + division.getMonthsInYearRange() + "]."); } // Derive cached information. LocalDate endingLimit = inLastWeek ? LocalDate.of(0, end, 1).with(TemporalAdjusters.lastDayOfMonth()) : LocalDate.of(0, end, 1).with(TemporalAdjusters.lastDayOfMonth()).plusDays(3); LocalDate yearZeroEnd = endingLimit.with(TemporalAdjusters.previousOrSame(endsOn)); int yearZeroDifference = (int) yearZeroEnd.until(endingLimit, ChronoUnit.DAYS); // Longest/shortest month lengths and related int longestMonthLength = 0; int shortestMonthLength = Integer.MAX_VALUE; for (int month = 1; month <= division.getMonthsInYearRange().getMaximum(); month++) { int monthLength = division.getWeeksInMonth(month); shortestMonthLength = Math.min(shortestMonthLength, monthLength); longestMonthLength = Math.max(longestMonthLength, monthLength + (month == leapWeekInMonth ? 1 : 0)); } ValueRange alignedWeekOfMonthRange = ValueRange.of(1, shortestMonthLength, longestMonthLength); ValueRange dayOfMonthRange = ValueRange.of(1, shortestMonthLength * 7, longestMonthLength * 7); int daysToEpoch = Math.toIntExact(0 - yearZeroEnd.plusDays(1).toEpochDay()); return new AccountingChronology(endsOn, end, inLastWeek, division, leapWeekInMonth, yearZeroDifference, alignedWeekOfMonthRange, dayOfMonthRange, daysToEpoch); } //----------------------------------------------------------------------- /** * Creates an instance from validated data, and cached data. * * @param endsOn The day-of-week a given year ends on. * @param end The month-end the year is based on. * @param inLastWeek Whether the year ends in the last week of the month, or nearest the end-of-month. * @param division How the year is divided. * @param leapWeekInMonth The month in which the leap-week resides. * @param yearZeroDifference Difference in days between accounting year end and ISO month end, in ISO year 0. * @param alignedWeekOfMonthRange Range of weeks in month. * @param dayOfMonthRange Range of days in month. * @param daysToEpoch The number of days between the start of Accounting 1 and ISO 1970. */ private AccountingChronology(DayOfWeek endsOn, Month end, boolean inLastWeek, AccountingYearDivision division, int leapWeekInMonth, int yearZeroDifference, ValueRange alignedWeekOfMonthRange, ValueRange dayOfMonthRange, int daysToEpoch) { this.endsOn = endsOn; this.end = end; this.inLastWeek = inLastWeek; this.division = division; this.leapWeekInMonth = leapWeekInMonth; this.yearZeroDifference = yearZeroDifference; this.alignedWeekOfMonthRange = alignedWeekOfMonthRange; this.dayOfMonthRange = dayOfMonthRange; this.days0001ToIso1970 = daysToEpoch; } /** * Resolve stored instances. * * @return a built, validated instance. */ private Object readResolve() { return AccountingChronology.create(endsOn, end, inLastWeek, getDivision(), leapWeekInMonth); } //----------------------------------------------------------------------- AccountingYearDivision getDivision() { return division; } int getLeapWeekInMonth() { return leapWeekInMonth; } int getDays0001ToIso1970() { return days0001ToIso1970; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Accounting'. *

* The ID uniquely identifies the {@code Chronology}, * but does not differentiate between instances of {@code AccountingChronology}. * It cannot be used to lookup the {@code Chronology} using {@link Chronology#of(String)}, * because each instance requires setup. * * @return the chronology ID - 'Accounting' * @see #getCalendarType() */ @Override public String getId() { return "Accounting"; } /** * Gets the calendar type of the underlying calendar system, which is null. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for 52/53 week calendars used for accounting purposes, * and given that setup required is unlikely to do so. * For this reason, the calendar type is null. * * @return null, as the calendar is unlikely to be specified in LDML * @see #getId() */ @Override public String getCalendarType() { return null; } //----------------------------------------------------------------------- /** * Obtains a local date in Accounting calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Accounting era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Accounting local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code AccountingEra} */ @Override public AccountingDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Accounting calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Accounting local date, not null * @throws DateTimeException if unable to create the date */ @Override public AccountingDate date(int prolepticYear, int month, int dayOfMonth) { return AccountingDate.of(this, prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Accounting calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Accounting era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Accounting local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code AccountingEra} */ @Override public AccountingDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Accounting calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Accounting local date, not null * @throws DateTimeException if unable to create the date */ @Override public AccountingDate dateYearDay(int prolepticYear, int dayOfYear) { return AccountingDate.ofYearDay(this, prolepticYear, dayOfYear); } /** * Obtains a local date in the Accounting calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Accounting local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public AccountingDate dateEpochDay(long epochDay) { return AccountingDate.ofEpochDay(this, epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Accounting local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Accounting local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public AccountingDate dateNow() { return AccountingDate.now(this); } /** * Obtains the current Accounting local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Accounting local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public AccountingDate dateNow(ZoneId zone) { return AccountingDate.now(this, zone); } /** * Obtains the current Accounting local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Accounting local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public AccountingDate dateNow(Clock clock) { return AccountingDate.now(this, clock); } //------------------------------------------------------------------------- /** * Obtains a Accounting local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Accounting local date, not null * @throws DateTimeException if unable to create the date */ @Override public AccountingDate date(TemporalAccessor temporal) { return AccountingDate.from(this, temporal); } /** * Obtains a Accounting local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Accounting local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Accounting zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Accounting zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Accounting zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Accounting zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* An Accounting proleptic-year is leap if the time between the end of the previous year * and the end of the current year is 371 days. * This method does not validate the year passed in, and only has a * well-defined result for years in the supported range. * * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long prolepticYear) { return Math.floorMod(prolepticYear + getISOLeapYearCount(prolepticYear) + yearZeroDifference, 7) == 0 || Math.floorMod(prolepticYear + getISOLeapYearCount(prolepticYear + 1) + yearZeroDifference, 7) == 0; } /** * Return the number of ISO Leap Years since Accounting Year 1. *

* This method calculates how many ISO leap years have passed since year 1. * The count returned will be negative for years before 1. * This method does not validate the year passed in, and only has a * well-defined result for years in the supported range. * * @param prolepticYear the proleptic-year to check, not validated for range * @return the count of leap years since year 1. */ private long getISOLeapYearCount(long prolepticYear) { long offsetYear = prolepticYear - (end == Month.JANUARY ? 1 : 0) - 1; return Math.floorDiv(offsetYear, 4) - Math.floorDiv(offsetYear, 100) + Math.floorDiv(offsetYear, 400) + (end == Month.JANUARY ? 1 : 0); } /** * Returns the count of leap years since year 1. *

* This method calculates how many Accounting leap years have passed since year 1. * The count returned will be negative for years before 1. * This method does not validate the year passed in, and only has a * well-defined result for years in the supported range. * * @param prolepticYear the proleptic-year to check, not validated for range * @return the count of leap years since year 1. */ long previousLeapYears(long prolepticYear) { return Math.floorDiv(prolepticYear - 1 + getISOLeapYearCount(prolepticYear) + yearZeroDifference, 7); } @Override public int prolepticYear(Era era, int yearOfEra) { if (!(era instanceof AccountingEra)) { throw new ClassCastException("Era must be AccountingEra"); } return (era == AccountingEra.CE ? yearOfEra : 1 - yearOfEra); } @Override public AccountingEra eraOf(int era) { return AccountingEra.of(era); } @Override public List eras() { return Arrays.asList(AccountingEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case ALIGNED_WEEK_OF_MONTH: return alignedWeekOfMonthRange; case ALIGNED_WEEK_OF_YEAR: return ALIGNED_WEEK_OF_YEAR_RANGE; case DAY_OF_MONTH: return dayOfMonthRange; case DAY_OF_YEAR: return DAY_OF_YEAR_RANGE; case MONTH_OF_YEAR: return getDivision().getMonthsInYearRange(); case PROLEPTIC_MONTH: return getDivision() == THIRTEEN_EVEN_MONTHS_OF_4_WEEKS ? PROLEPTIC_MONTH_RANGE_13 : PROLEPTIC_MONTH_RANGE_12; default: break; } return field.range(); } //------------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof AccountingChronology) { AccountingChronology other = (AccountingChronology) obj; return this.endsOn == other.endsOn && this.inLastWeek == other.inLastWeek && this.end == other.end && this.getDivision() == other.getDivision() && this.leapWeekInMonth == other.leapWeekInMonth; } return false; } @Override public int hashCode() { final int prime = 31; int result = 0; result = prime * result + endsOn.hashCode(); result = prime * result + (inLastWeek ? 1231 : 1237); result = prime * result + end.hashCode(); result = prime * result + leapWeekInMonth; result = prime * result + getDivision().hashCode(); return result; } @Override public String toString() { StringBuilder bld = new StringBuilder(30); bld.append(getId()) .append(" calendar ends on ") .append(endsOn) .append(inLastWeek ? " in last week of " : " nearest end of ") .append(end) .append(", year divided in ") .append(getDivision()) .append(" with leap-week in month ") .append(leapWeekInMonth); return bld.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingChronologyBuilder.java000066400000000000000000000141511343451174100324450ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.Month; /** * Builder to create Accounting calendars. *

* Accounting calendars require setup before use, given how they are used. * The following information is required: *

    *
  • ending day-of-week - The day-of-week on which a given accounting year ends. *
  • last-in-month vs. nearest-end-of-month - Whether the ending day-of-week is the last in the month, * or the nearest to the end of the month (will sometimes be in the next month. *
  • month end - Which Gregorian/ISO end-of-month the year ends in/is nearest to. *
  • year division - How many 'months' (periods) to divide the accounting year into, * and how many weeks are in each. *
  • leap-week month - Which month will have the leap 'week' added to it. * In practice this is probably the last one, but this does not seem to be required. *
*

* There are approximately 7 x 2 x 12 x 4 x 12/13 = 4032 combinations. * *

Implementation Requirements

* This class is a mutable builder suitable for use from a single thread. */ public final class AccountingChronologyBuilder { /** * The day of the week on which a given Accounting year ends. */ private DayOfWeek endsOn; /** * Whether the calendar ends in the last week of a given Gregorian/ISO month, * or nearest to the last day of the month (will sometimes be in the next month). */ private boolean inLastWeek; /** * Which Gregorian/ISO end-of-month the year ends in/is nearest to. */ private Month end; /** * How to divide an accounting year. */ private AccountingYearDivision division; /** * The month which will have the leap-week added. */ private int leapWeekInMonth; /** * Constructs a new instance of the builder. */ public AccountingChronologyBuilder() { // Nothing to setup in the constructor. } /** * Sets the day-of-week on which a given Accounting calendar year ends. * * @param endsOn The day-of-week on which a given Accounting calendar year ends. * * @return this, for chaining, not null. */ public AccountingChronologyBuilder endsOn(DayOfWeek endsOn) { this.endsOn = endsOn; return this; } /** * Sets the Gregorian/ISO month-end which a given Accounting calendar year ends nearest to. * Calendars setup this way will occasionally end in the start of the next month. * For example, for July, the month ends on any day from July 28th to August 3rd. * * @param end The Gregorian/ISO month-end a given Accounting calendar year ends nearest to. * * @return this, for chaining, not null. */ public AccountingChronologyBuilder nearestEndOf(Month end) { this.inLastWeek = false; this.end = end; return this; } /** * Sets the Gregorian/ISO month-end in which a given Accounting calendar year ends. * Calendars setup this way will always end in the last week of the given month. * For example, for July, the month ends on any day from July 25th to July 31st. * * @param end The Gregorian/ISO month-end a given Accounting calendar year ends in the last week of. * * @return this, for chaining, not null. */ public AccountingChronologyBuilder inLastWeekOf(Month end) { this.inLastWeek = true; this.end = end; return this; } /** * Sets how a given Accounting year will be divided. * * @param division How to divide a given calendar year. * * @return this, for chaining, not null. */ public AccountingChronologyBuilder withDivision(AccountingYearDivision division) { this.division = division; return this; } /** * Sets the month in which the leap-week occurs. * * @param leapWeekInMonth The month in which the leap-week occurs. * * @return this, for chaining, not null. */ public AccountingChronologyBuilder leapWeekInMonth(int leapWeekInMonth) { this.leapWeekInMonth = leapWeekInMonth; return this; } /** * Completes this builder by creating the {@code AccountingChronology}. * * @return the created chronology, not null. * @throws DateTimeException if the chronology cannot be built. */ public AccountingChronology toChronology() { return AccountingChronology.create(endsOn, end, inLastWeek, division, leapWeekInMonth); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingDate.java000066400000000000000000000536511343451174100277000ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import static org.threeten.extra.chrono.AccountingChronology.DAY_OF_YEAR_RANGE; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.ValueRange; import java.util.Objects; /** * A date in an Accounting calendar system. *

* This date operates using a given {@linkplain AccountingChronology Accounting calendar}. * An Accounting calendar differs greatly from the ISO calendar. * The start of the Accounting calendar will vary against the ISO calendar. * Depending on options chosen, it can start as early as {@code 0000-01-26 (ISO)} or as late as {@code 0001-01-04 (ISO)}. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class AccountingDate extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -126140328940081914L; /** * Number of days in a week. */ private static final int DAYS_IN_WEEK = 7; /** * Number of weeks in a regular (non-leap) year. */ private static final int WEEKS_IN_YEAR = 52; /** * Number of days in a long (400-year) cycle. */ private static final int DAYS_PER_LONG_CYCLE = 400 * 365 + 3 * 24 + 1 * 25; /** * The chronology for manipulating this date. */ private final AccountingChronology chronology; /** * The proleptic year. */ private final int prolepticYear; /** * The month (period). */ private final short month; /** * The day. */ private final short day; //----------------------------------------------------------------------- /** * Obtains the current {@code AccountingDate} from the system clock in the default time-zone, * translated with the given AccountingChronology. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param chronology the Accounting chronology to base the date on, not null * @return the current date using the system clock and default time-zone, not null * @throws DateTimeException if the current date cannot be obtained, * NullPointerException if an AccountingChronology was not provided */ public static AccountingDate now(AccountingChronology chronology) { return now(chronology, Clock.systemDefaultZone()); } /** * Obtains the current {@code AccountingDate} from the system clock in the specified time-zone, * translated with the given AccountingChronology. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param chronology the Accounting chronology to base the date on, not null * @param zone the zone ID to use, not null * @return the current date using the system clock, not null * @throws DateTimeException if the current date cannot be obtained, * NullPointerException if an AccountingChronology was not provided */ public static AccountingDate now(AccountingChronology chronology, ZoneId zone) { return now(chronology, Clock.system(zone)); } /** * Obtains the current {@code AccountingDate} from the specified clock, * translated with the given AccountingChronology. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param chronology the Accounting chronology to base the date on, not null * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained, * NullPointerException if an AccountingChronology was not provided */ public static AccountingDate now(AccountingChronology chronology, Clock clock) { LocalDate now = LocalDate.now(clock); return ofEpochDay(chronology, now.toEpochDay()); } /** * Obtains a {@code AccountingDate} representing a date in the given accounting calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code AccountingDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * @param chronology the Accounting chronology to base the date on, not null * @param prolepticYear the Accounting proleptic-year * @param month the Accounting month-of-year, from 1 to 12 or 1 to 13 * @param dayOfMonth the Accounting day-of-month, from 1 to 35 or 1 to 42 * @return the date in the given Accounting calendar system, not null * @throws DateTimeException if the value of any field is out of range, * if the day-of-month is invalid for the month-year, * or if an AccountingChronology was not provided */ public static AccountingDate of(AccountingChronology chronology, int prolepticYear, int month, int dayOfMonth) { return create(chronology, prolepticYear, month, dayOfMonth); } /** * Obtains an {@code AccountingDate} from a temporal object. *

* This obtains a date in the specified Accounting calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code AccountingDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method almost matches the signature of the functional interface {@link TemporalQuery} * and must be used as a query via something that supplies the missing parameter, * such as a curried method reference, {@code temporal -> AccountingDate.from(chronology, temporal)} * (where {@code chronology} resolves to a set up {@code AccountingChronology}). * * @param chronology the Accounting chronology to base the date on, not null * @param temporal the temporal object to convert, not null * @return the date in Accounting calendar system, not null * @throws DateTimeException if unable to convert to an {@code AccountingDate}, * NullPointerException if an AccountingChronology was not provided */ public static AccountingDate from(AccountingChronology chronology, TemporalAccessor temporal) { if (temporal instanceof AccountingDate && ((AccountingDate) temporal).getChronology().equals(chronology)) { return (AccountingDate) temporal; } return ofEpochDay(chronology, temporal.getLong(EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains an {@code AccountingDate} representing a date in the given Accounting calendar * system from the proleptic-year and day-of-year fields. *

* This returns an {@code AccountingDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param chronology the Accounting chronology to base the date on, not null * @param prolepticYear the Accounting proleptic-year * @param dayOfYear the Accounting day-of-year, from 1 to 371 * @return the date in Accounting calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year, * NullPointerException if an AccountingChronology was not provided */ static AccountingDate ofYearDay(AccountingChronology chronology, int prolepticYear, int dayOfYear) { Objects.requireNonNull(chronology, "A previously setup chronology is required."); YEAR.checkValidValue(prolepticYear); DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, DAY_OF_YEAR); boolean leap = chronology.isLeapYear(prolepticYear); if (dayOfYear > WEEKS_IN_YEAR * DAYS_IN_WEEK && !leap) { throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year"); } int month = (leap ? chronology.getDivision().getMonthFromElapsedWeeks((dayOfYear - 1) / DAYS_IN_WEEK, chronology.getLeapWeekInMonth()) : chronology.getDivision().getMonthFromElapsedWeeks((dayOfYear - 1) / DAYS_IN_WEEK)); int dayOfMonth = dayOfYear - (leap ? chronology.getDivision().getWeeksAtStartOfMonth(month, chronology.getLeapWeekInMonth()) : chronology.getDivision().getWeeksAtStartOfMonth(month)) * DAYS_IN_WEEK; return new AccountingDate(chronology, prolepticYear, month, dayOfMonth); } /** * Obtains an {@code AccountingDate} representing a date in the given Accounting calendar * system from the epoch-day. * * @param chronology the Accounting chronology to base the date on, not null * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in given Accounting calendar system, not null * @throws DateTimeException if the epoch-day is out of range, * NullPointerException if an AccountingChronology was not provided */ static AccountingDate ofEpochDay(AccountingChronology chronology, long epochDay) { EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds // Use Accounting 1 to help with 0-counts. Leap years can occur at any time. long accountingEpochDay = epochDay + chronology.getDays0001ToIso1970(); int longCycle = (int) Math.floorDiv(accountingEpochDay, DAYS_PER_LONG_CYCLE); int daysInLongCycle = (int) Math.floorMod(accountingEpochDay, DAYS_PER_LONG_CYCLE); // Value is an estimate, as the floating leap-years make this difficult. int year = (daysInLongCycle - (daysInLongCycle / 365 + daysInLongCycle / (4 * 365 + 1) - daysInLongCycle / (100 * 365 + 24)) / 7) / (DAYS_IN_WEEK * WEEKS_IN_YEAR); int yearStart = (int) (WEEKS_IN_YEAR * (year - 1) + chronology.previousLeapYears(year)) * DAYS_IN_WEEK; // Despite the year being an estimate, the effect should still be within a few days. if (yearStart > daysInLongCycle) { year--; yearStart -= (WEEKS_IN_YEAR + (chronology.isLeapYear(year) ? 1 : 0)) * DAYS_IN_WEEK; } else if (daysInLongCycle - yearStart >= (WEEKS_IN_YEAR + (chronology.isLeapYear(year) ? 1 : 0)) * DAYS_IN_WEEK) { yearStart += (WEEKS_IN_YEAR + (chronology.isLeapYear(year) ? 1 : 0)) * DAYS_IN_WEEK; year++; } return ofYearDay(chronology, year + 400 * longCycle, daysInLongCycle - yearStart + 1); } private static AccountingDate resolvePreviousValid(AccountingChronology chronology, int prolepticYear, int month, int day) { day = Math.min(day, lengthOfMonth(chronology, prolepticYear, month)); return new AccountingDate(chronology, prolepticYear, month, day); } private static int lengthOfMonth(AccountingChronology chronology, int prolepticYear, int month) { return (chronology.isLeapYear(prolepticYear) ? chronology.getDivision().getWeeksInMonth(month, chronology.getLeapWeekInMonth()) : chronology.getDivision().getWeeksInMonth(month)) * DAYS_IN_WEEK; } /** * Creates an {@code AccountingDate} validating the input. * * @param chronology the Accounting chronology to base the date on, not null * @param prolepticYear the Accounting proleptic-year * @return the date in Accounting calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the month-year, * NullPointerException if an AccountingChronology was not provided */ static AccountingDate create(AccountingChronology chronology, int prolepticYear, int month, int dayOfMonth) { Objects.requireNonNull(chronology, "A previously setup chronology is required."); YEAR.checkValidValue(prolepticYear); chronology.range(MONTH_OF_YEAR).checkValidValue(month, MONTH_OF_YEAR); if (dayOfMonth < 1 || dayOfMonth > lengthOfMonth(chronology, prolepticYear, month)) { if (month == chronology.getLeapWeekInMonth() && dayOfMonth < (chronology.getDivision().getWeeksInMonth(month) + 1) * DAYS_IN_WEEK && !chronology.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid date '" + month + "/" + dayOfMonth + "' as '" + prolepticYear + "' is not a leap year"); } else { throw new DateTimeException("Invalid date '" + month + "/" + dayOfMonth + "'"); } } return new AccountingDate(chronology, prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param chronology the Accounting chronology to base the date on, not null * @param prolepticYear the Accounting proleptic-year * @param month the Accounting month (period), from 1 to 12 or 1 to 13 * @param dayOfMonth the Accounting day-of-month, from 1 to 35 or 1 to 42 */ private AccountingDate(AccountingChronology chronology, int prolepticYear, int month, int dayOfMonth) { this.chronology = chronology; this.prolepticYear = prolepticYear; this.month = (short) month; this.day = (short) dayOfMonth; } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return AccountingDate.create(chronology, prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { int weeksAtStartOfMonth = (isLeapYear() ? chronology.getDivision().getWeeksAtStartOfMonth(month, chronology.getLeapWeekInMonth()) : chronology.getDivision().getWeeksAtStartOfMonth(month)); return weeksAtStartOfMonth * DAYS_IN_WEEK + day; } @Override AbstractDate withDayOfYear(int value) { return plusDays(value - getDayOfYear()); } @Override int lengthOfYearInMonths() { return chronology.getDivision().lengthOfYearInMonths(); } @Override ValueRange rangeAlignedWeekOfMonth() { return ValueRange.of(1, (lengthOfMonth() - 1) / DAYS_IN_WEEK + 1); } @Override AccountingDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(chronology, newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is an Accounting calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Accounting chronology, not null */ @Override public AccountingChronology getChronology() { return chronology; } @Override public int lengthOfMonth() { return lengthOfMonth(chronology, prolepticYear, month); } @Override public int lengthOfYear() { return (WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0)) * DAYS_IN_WEEK; }; //------------------------------------------------------------------------- @Override public AccountingDate with(TemporalAdjuster adjuster) { return (AccountingDate) adjuster.adjustInto(this); } @Override public AccountingDate with(TemporalField field, long newValue) { return (AccountingDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public AccountingDate plus(TemporalAmount amount) { return (AccountingDate) amount.addTo(this); } @Override public AccountingDate plus(long amountToAdd, TemporalUnit unit) { return (AccountingDate) super.plus(amountToAdd, unit); } @Override public AccountingDate minus(TemporalAmount amount) { return (AccountingDate) amount.subtractFrom(this); } @Override public AccountingDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return super.until(AccountingDate.from(chronology, endExclusive), unit); } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { return super.doUntil(AccountingDate.from(chronology, endDateExclusive)); } //----------------------------------------------------------------------- @Override public long toEpochDay() { long year = prolepticYear; long accountingEpochDay = ((year - 1) * WEEKS_IN_YEAR + chronology.previousLeapYears(year)) * DAYS_IN_WEEK + (getDayOfYear() - 1); return accountingEpochDay - chronology.getDays0001ToIso1970(); } //------------------------------------------------------------------------- /** * Compares this date to another date, including the chronology. *

* Compares this date with another ensuring that the date is the same. *

* Only objects of this concrete type are compared, other types return false. * To compare the dates of two {@code TemporalAccessor} instances, including dates * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator. * * @param obj the object to check, null returns false * @return true if this is equal to the other date */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof AccountingDate) { AccountingDate other = (AccountingDate) obj; return this.prolepticYear == other.prolepticYear && this.month == other.month && this.day == other.day && this.chronology.equals(other.chronology); } return false; } /** * A hash code for this date. * * @return a suitable hash code based only on the Chronology and the date */ @Override // override for performance public int hashCode() { return chronology.hashCode() ^ ((prolepticYear & 0xFFFFF800) ^ ((prolepticYear << 11) + (month << 6) + (day))); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingEra.java000066400000000000000000000076371343451174100275350ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the Accounting calendar system. *

* The Accounting calendar system has two eras. * The current era, for years from 1 onwards, is known as the 'Current Era'. * All previous years, zero or earlier in the proleptic count or one and greater * in the year-of-era count, are part of the 'Before Current Era' era. *

* The start of accounting epochs {@code 0001-01-01 (Accounting)} will vary against the ISO calendar. * Depending on options chosen, it can start as early as {@code 0000-01-26 (ISO)} or as late as {@code 0001-01-04 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code AccountingEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum AccountingEra implements Era { /** * The singleton instance for the era before the current one, 'Before Current Era', * which has the numeric value 0. */ BCE, /** * The singleton instance for the current era, 'Current Era', * which has the numeric value 1. */ CE; //----------------------------------------------------------------------- /** * Obtains an instance of {@code AccountingEra} from an {@code int} value. *

* {@code AccountingEra} is an enum representing the Accounting eras of BCE/CE. * This factory allows the enum to be obtained from the {@code int} value. * * @param era the BCE/CE value to represent, from 0 (BCE) to 1 (CE) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static AccountingEra of(int era) { switch (era) { case 0: return BCE; case 1: return CE; default: throw new DateTimeException("Invalid era: " + era); } } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era BCE has the value 0, while the era CE has the value 1. * * @return the era value, from 0 (BCE) to 1 (CE) */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingYearDivision.java000066400000000000000000000221631343451174100314220ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.temporal.ChronoField; import java.time.temporal.ValueRange; import java.util.Arrays; /** * How an Accounting year is divided. *

* An Accounting calendar system generally divides a year into smaller periods, similar in length to regular calendar months. * The most common divisions either use 12 such 'months' (requiring one every quarter to be 5 weeks instead of 4), * or use 13 of 4 weeks each (making one quarter have an extra month, or each quarter have partial months). * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum AccountingYearDivision { /** * The singleton instance for a year divided into 4 quarters, * each having 3 months with lengths of 4, 4, and 5 weeks, respectively. */ QUARTERS_OF_PATTERN_4_4_5_WEEKS(new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}), /** * The singleton instance for a year divided into 4 quarters, * each having 3 months with lengths of 4, 5, and 4 weeks, respectively. */ QUARTERS_OF_PATTERN_4_5_4_WEEKS(new int[] {4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4}), /** * The singleton instance for a year divided into 4 quarters, * each having 3 months with lengths of 5, 4, and 4 weeks, respectively. */ QUARTERS_OF_PATTERN_5_4_4_WEEKS(new int[] {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4}), /** * The singleton instance for a year divided into 13 even months, * each having 4 weeks. */ THIRTEEN_EVEN_MONTHS_OF_4_WEEKS(new int[] {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}); /** * The number of weeks in each month. */ private final int[] weeksInMonths; /** * The range of months in each year. */ private final ValueRange monthsInYearRange; /** * The elapsed number of weeks at the start of each month. */ private final int[] elapsedWeeks; //----------------------------------------------------------------------- /** * Private constructor for enum, for creating cached info. * * @param weeksInMonths The number of weeks in each month (period). */ private AccountingYearDivision(int[] weeksInMonths) { this.weeksInMonths = weeksInMonths; this.monthsInYearRange = ValueRange.of(1, weeksInMonths.length); this.elapsedWeeks = new int[weeksInMonths.length]; for (int i = 1; i < weeksInMonths.length; i++) { elapsedWeeks[i] = elapsedWeeks[i - 1] + weeksInMonths[i - 1]; } } //----------------------------------------------------------------------- /** * Gets the range of months in a year. *

*

    *
  • The AccountingYearDivision {@code QUARTERS_OF_PATTERN_4_4_5_WEEKS} range is [1, 12]. *
  • The AccountingYearDivision {@code QUARTERS_OF_PATTERN_4_5_4_WEEKS} range is [1, 12]. *
  • The AccountingYearDivision {@code QUARTERS_OF_PATTERN_5_4_4_WEEKS} range is [1, 12]. *
  • The AccountingYearDivision {@code THIRTEEN_EVEN_MONTHS_OF_4_WEEKS} range is [1, 13]. *
* * @return the range of months (periods) in a year. */ ValueRange getMonthsInYearRange() { return monthsInYearRange; } /** * Gets the length of the year in months. * * @return The length of the year in months. */ int lengthOfYearInMonths() { return weeksInMonths.length; } //----------------------------------------------------------------------- /** * Get the number of weeks in the given month (period). * * @param month The month for which to get the count of weeks. * @return The count of weeks in the given month. * @throws DateTimeException if the month isn't in the valid range of months. */ int getWeeksInMonth(int month) { return getWeeksInMonth(month, 0); } /** * Get the number of weeks in the given month (period), with the leap year in the indicated month. * * @param month The month for which to get the count of weeks. * @param leapWeekInMonth The month in which the leap-week resides * @return The count of weeks in the given month, including any leap week. * @throws DateTimeException if the month isn't in the valid range of months. */ int getWeeksInMonth(int month, int leapWeekInMonth) { month = monthsInYearRange.checkValidIntValue(month, ChronoField.MONTH_OF_YEAR); leapWeekInMonth = (leapWeekInMonth == 0 ? 0 : monthsInYearRange.checkValidIntValue(leapWeekInMonth, ChronoField.MONTH_OF_YEAR)); return weeksInMonths[month - 1] + (month == leapWeekInMonth ? 1 : 0); } //----------------------------------------------------------------------- /** * Get the number of weeks elapsed before the start of the month. * * @param month The month * @return The number of weeks elapsed before the start of the month. * @throws DateTimeException if the month isn't in the valid range of months. */ int getWeeksAtStartOfMonth(int month) { return getWeeksAtStartOfMonth(month, 0); } /** * Get the number of weeks elapsed before the start of the month. * * @param month The month * @param leapWeekInMonth The month in which the leap-week resides * @return The number of weeks elapsed before the start of the month, including any leap week. * @throws DateTimeException if the month isn't in the valid range of months. */ int getWeeksAtStartOfMonth(int month, int leapWeekInMonth) { month = monthsInYearRange.checkValidIntValue(month, ChronoField.MONTH_OF_YEAR); leapWeekInMonth = (leapWeekInMonth == 0 ? 0 : monthsInYearRange.checkValidIntValue(leapWeekInMonth, ChronoField.MONTH_OF_YEAR)); return elapsedWeeks[month - 1] + (leapWeekInMonth != 0 && month > leapWeekInMonth ? 1 : 0); } /** * Get the month from a count of elapsed weeks. * * @param weeksElapsed The weeks elapsed since the start of the year. * @return the month * @throws DateTimeException if the month isn't in the valid range of months, * or the week isn't in the valid range. */ int getMonthFromElapsedWeeks(int weeksElapsed) { return getMonthFromElapsedWeeks(weeksElapsed, 0); } /** * Get the month from a count of elapsed weeks. * * @param weeksElapsed The weeks elapsed since the start of the year. * @param leapWeekInMonth The month in which the leap-week resides * @return the month * @throws DateTimeException if the month isn't in the valid range of months, * or the week isn't in the valid range. */ int getMonthFromElapsedWeeks(int weeksElapsed, int leapWeekInMonth) { if (weeksElapsed < 0 || weeksElapsed >= (leapWeekInMonth == 0 ? 52 : 53)) { throw new DateTimeException("Count of '" + elapsedWeeks.length + "' elapsed weeks not valid," + " should be in the range [0, " + (leapWeekInMonth == 0 ? 52 : 53) + ")"); } leapWeekInMonth = (leapWeekInMonth == 0 ? 0 : monthsInYearRange.checkValidIntValue(leapWeekInMonth, ChronoField.MONTH_OF_YEAR)); int month = Arrays.binarySearch(elapsedWeeks, weeksElapsed); // Binary search returns 0-indexed if found, negative - 1 for insert position if not. month = (month >= 0 ? month + 1 : 0 - month - 1); // Need to move to previous month if there was a leap week and in the first week. return leapWeekInMonth == 0 || month <= leapWeekInMonth || weeksElapsed > elapsedWeeks[month - 1] ? month : month - 1; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/BritishCutoverChronology.java000066400000000000000000000444111343451174100320220ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoChronology; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; import java.util.Map; /** * The British Julian-Gregorian cutover calendar system. *

* The British calendar system follows the rules of the Julian calendar * until 1752 and the rules of the Gregorian (ISO) calendar since then. * The Julian differs from the Gregorian only in terms of the leap year rule. *

* The Julian and Gregorian calendar systems are linked to Rome and the Vatican * with the Julian preceding the Gregorian. The Gregorian was introduced to * handle the drift of the seasons through the year due to the inaccurate * Julian leap year rules. When first introduced by the Vatican in 1582, * the cutover resulted in a "gap" of 10 days. *

* While the calendar was introduced in 1582, it was not adopted everywhere. * Britain did not adopt it until the 1752, when Wednesday 2nd September 1752 * was followed by Thursday 14th September 1752. *

* This chronology implements the proleptic Julian calendar system followed by * the proleptic Gregorian calendar system (identical to the ISO calendar system). * Dates are aligned such that {@code 0001-01-01 (British)} is {@code 0000-12-30 (ISO)}. *

* This class implements a calendar where January 1st is the start of the year. * The history of the start of the year is complex and using the current standard * is the most consistent. *

* The eras of this calendar system are defined by {@link JulianEra} to avoid unnecessary duplication. *

* The fields are defined as follows: *

    *
  • era - There are two eras, the current 'Anno Domini' (AD) and the previous era 'Before Christ' (BC). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. * For the previous era the year increases from one as time goes backwards. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the * current era. For the previous era, years have zero, then negative values. *
  • month-of-year - There are 12 months in a year, numbered from 1 to 12. *
  • day-of-month - There are between 28 and 31 days in each month, numbered from 1 to 31. * Months 4, 6, 9 and 11 have 30 days, Months 1, 3, 5, 7, 8, 10 and 12 have 31 days. * Month 2 has 28 days, or 29 in a leap year. * The cutover month, September 1752, has a value range from 1 to 30, but a length of 19. *
  • day-of-year - There are 365 days in a standard year and 366 in a leap year. * The days are numbered from 1 to 365 or 1 to 366. * The cutover year 1752 has values from 1 to 355 and a length of 355 days. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class BritishCutoverChronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Coptic chronology. */ public static final BritishCutoverChronology INSTANCE = new BritishCutoverChronology(); /** * The cutover date, Thursday 14th September 1752. */ public static final LocalDate CUTOVER = LocalDate.of(1752, 9, 14); /** * The number of cutover days. */ static final int CUTOVER_DAYS = 11; /** * The cutover year. */ static final int CUTOVER_YEAR = 1752; /** * Serialization version. */ private static final long serialVersionUID = 87235724675472657L; /** * Range of day-of-year. */ static final ValueRange DOY_RANGE = ValueRange.of(1, 355, 366); /** * Range of aligned-week-of-month. */ static final ValueRange ALIGNED_WOM_RANGE = ValueRange.of(1, 3, 5); /** * Range of aligned-week-of-year. */ static final ValueRange ALIGNED_WOY_RANGE = ValueRange.of(1, 51, 53); /** * Range of proleptic-year. */ static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999); /** * Range of year. */ static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999); /** * Range of proleptic month. */ static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998 * 12L, 999_999 * 12L + 11); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public BritishCutoverChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //------------------------------------------------------------------------- /** * Gets the cutover date between the Julian and Gregorian calendar. *

* The date returned is the first date that the Gregorian (ISO) calendar applies, * which is Thursday 14th September 1752. * * @return the first date after the cutover, not null */ public LocalDate getCutover() { return CUTOVER; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'BritishCutover'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'BritishCutover' * @see #getCalendarType() */ @Override public String getId() { return "BritishCutover"; } /** * Gets the calendar type of the underlying calendar system, which returns null. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for this calendar system, thus null is returned. * * @return the calendar system type, null * @see #getId() */ @Override public String getCalendarType() { return null; } //----------------------------------------------------------------------- /** * Obtains a local date in British Cutover calendar system from the * era, year-of-era, month-of-year and day-of-month fields. *

* Dates in the middle of the cutover gap, such as the 10th September 1752, * will not throw an exception. Instead, the date will be treated as a Julian date * and converted to an ISO date, with the day of month shifted by 11 days. * * @param era the British Cutover era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the British Cutover local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code JulianEra} */ @Override public BritishCutoverDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in British Cutover calendar system from the * proleptic-year, month-of-year and day-of-month fields. *

* Dates in the middle of the cutover gap, such as the 10th September 1752, * will not throw an exception. Instead, the date will be treated as a Julian date * and converted to an ISO date, with the day of month shifted by 11 days. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the British Cutover local date, not null * @throws DateTimeException if unable to create the date */ @Override public BritishCutoverDate date(int prolepticYear, int month, int dayOfMonth) { return BritishCutoverDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in British Cutover calendar system from the * era, year-of-era and day-of-year fields. *

* The day-of-year takes into account the cutover, thus there are only 355 days in 1752. * * @param era the British Cutover era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the British Cutover local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code JulianEra} */ @Override public BritishCutoverDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in British Cutover calendar system from the * proleptic-year and day-of-year fields. *

* The day-of-year takes into account the cutover, thus there are only 355 days in 1752. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the British Cutover local date, not null * @throws DateTimeException if unable to create the date */ @Override public BritishCutoverDate dateYearDay(int prolepticYear, int dayOfYear) { return BritishCutoverDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the British Cutover calendar system from the epoch-day. * * @param epochDay the epoch day * @return the British Cutover local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public BritishCutoverDate dateEpochDay(long epochDay) { return BritishCutoverDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current British Cutover local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current British Cutover local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public BritishCutoverDate dateNow() { return BritishCutoverDate.now(); } /** * Obtains the current British Cutover local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current British Cutover local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public BritishCutoverDate dateNow(ZoneId zone) { return BritishCutoverDate.now(zone); } /** * Obtains the current British Cutover local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current British Cutover local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public BritishCutoverDate dateNow(Clock clock) { return BritishCutoverDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a British Cutover local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the British Cutover local date, not null * @throws DateTimeException if unable to create the date */ @Override public BritishCutoverDate date(TemporalAccessor temporal) { return BritishCutoverDate.from(temporal); } /** * Obtains a British Cutover local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the British Cutover local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a British Cutover zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the British Cutover zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a British Cutover zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the British Cutover zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* The result will return the same as {@link JulianChronology#isLeapYear(long)} for * year 1752 and earlier, and {@link IsoChronology#isLeapYear(long)} otherwise. * This method does not validate the year passed in, and only has a * well-defined result for years in the supported range. * * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long prolepticYear) { if (prolepticYear <= CUTOVER_YEAR) { return JulianChronology.INSTANCE.isLeapYear(prolepticYear); } return IsoChronology.INSTANCE.isLeapYear(prolepticYear); } @Override public int prolepticYear(Era era, int yearOfEra) { if (era instanceof JulianEra == false) { throw new ClassCastException("Era must be JulianEra"); } return (era == JulianEra.AD ? yearOfEra : 1 - yearOfEra); } @Override public JulianEra eraOf(int eraValue) { return JulianEra.of(eraValue); } @Override public List eras() { return Arrays.asList(JulianEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case DAY_OF_YEAR: return DOY_RANGE; case ALIGNED_WEEK_OF_MONTH: return ALIGNED_WOM_RANGE; case ALIGNED_WEEK_OF_YEAR: return ALIGNED_WOY_RANGE; case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: return YOE_RANGE; case YEAR: return YEAR_RANGE; default: break; } return field.range(); } //----------------------------------------------------------------------- @Override // override for return type public BritishCutoverDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) { return (BritishCutoverDate) super.resolveDate(fieldValues, resolverStyle); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/BritishCutoverDate.java000066400000000000000000000516661343451174100305660ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static org.threeten.extra.chrono.BritishCutoverChronology.CUTOVER; import static org.threeten.extra.chrono.BritishCutoverChronology.CUTOVER_DAYS; import static org.threeten.extra.chrono.BritishCutoverChronology.CUTOVER_YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.ValueRange; import java.util.Objects; /** * A date in the British Cutover calendar system. *

* This date operates using the {@linkplain BritishCutoverChronology British Cutover calendar}. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class BritishCutoverDate extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -9626278512674L; /** * The underlying date. */ private final LocalDate isoDate; /** * The underlying Julian date if before the cutover. */ private final transient JulianDate julianDate; //----------------------------------------------------------------------- /** * Obtains the current {@code BritishCutoverDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static BritishCutoverDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code BritishCutoverDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static BritishCutoverDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code BritishCutoverDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static BritishCutoverDate now(Clock clock) { return new BritishCutoverDate(LocalDate.now(clock)); } /** * Obtains a {@code BritishCutoverDate} representing a date in the British Cutover calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code BritishCutoverDate} with the specified fields. *

* Dates in the middle of the cutover gap, such as the 10th September 1752, * will not throw an exception. Instead, the date will be treated as a Julian date * and converted to an ISO date, with the day of month shifted by 11 days. *

* Invalid dates, such as September 31st will throw an exception. * * @param prolepticYear the British Cutover proleptic-year * @param month the British Cutover month-of-year, from 1 to 12 * @param dayOfMonth the British Cutover day-of-month, from 1 to 31 * @return the date in British Cutover calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static BritishCutoverDate of(int prolepticYear, int month, int dayOfMonth) { return BritishCutoverDate.create(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code BritishCutoverDate} from a temporal object. *

* This obtains a date in the British Cutover calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code BritishCutoverDate}. *

* The conversion uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code BritishCutoverDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in British Cutover calendar system, not null * @throws DateTimeException if unable to convert to a {@code BritishCutoverDate} */ public static BritishCutoverDate from(TemporalAccessor temporal) { if (temporal instanceof BritishCutoverDate) { return (BritishCutoverDate) temporal; } return new BritishCutoverDate(LocalDate.from(temporal)); } //----------------------------------------------------------------------- /** * Obtains a {@code BritishCutoverDate} representing a date in the British Cutover calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code BritishCutoverDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the British Cutover proleptic-year * @param dayOfYear the British Cutover day-of-year, from 1 to 366 * @return the date in British Cutover calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static BritishCutoverDate ofYearDay(int prolepticYear, int dayOfYear) { if (prolepticYear < CUTOVER_YEAR || (prolepticYear == CUTOVER_YEAR && dayOfYear <= 246)) { JulianDate julian = JulianDate.ofYearDay(prolepticYear, dayOfYear); return new BritishCutoverDate(julian); } else if (prolepticYear == CUTOVER_YEAR) { LocalDate iso = LocalDate.ofYearDay(prolepticYear, dayOfYear + CUTOVER_DAYS); return new BritishCutoverDate(iso); } else { LocalDate iso = LocalDate.ofYearDay(prolepticYear, dayOfYear); return new BritishCutoverDate(iso); } } /** * Obtains a {@code BritishCutoverDate} representing a date in the British Cutover calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in British Cutover calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static BritishCutoverDate ofEpochDay(final long epochDay) { return new BritishCutoverDate(LocalDate.ofEpochDay(epochDay)); } /** * Creates a {@code BritishCutoverDate} validating the input. * * @param prolepticYear the British Cutover proleptic-year * @param month the British Cutover month-of-year, from 1 to 12 * @param dayOfMonth the British Cutover day-of-month, from 1 to 31 * @return the date in British Cutover calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ static BritishCutoverDate create(int prolepticYear, int month, int dayOfMonth) { if (prolepticYear < CUTOVER_YEAR) { JulianDate julian = JulianDate.of(prolepticYear, month, dayOfMonth); return new BritishCutoverDate(julian); } else { LocalDate iso = LocalDate.of(prolepticYear, month, dayOfMonth); if (iso.isBefore(CUTOVER)) { JulianDate julian = JulianDate.of(prolepticYear, month, dayOfMonth); return new BritishCutoverDate(julian); } return new BritishCutoverDate(iso); } } //----------------------------------------------------------------------- /** * Creates an instance from an ISO date. * * @param isoDate the standard local date, not null */ BritishCutoverDate(LocalDate isoDate) { Objects.requireNonNull(isoDate, "isoDate"); this.isoDate = isoDate; this.julianDate = (isoDate.isBefore(CUTOVER) ? JulianDate.from(isoDate) : null); } /** * Creates an instance from a Julian date. * * @param julianDate the Julian date before the cutover, not null */ BritishCutoverDate(JulianDate julianDate) { Objects.requireNonNull(julianDate, "julianDate"); this.isoDate = LocalDate.from(julianDate); this.julianDate = (isoDate.isBefore(CUTOVER) ? julianDate : null); } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return new BritishCutoverDate(isoDate); } //----------------------------------------------------------------------- private boolean isCutoverYear() { return isoDate.getYear() == CUTOVER_YEAR && isoDate.getDayOfYear() > CUTOVER_DAYS; } private boolean isCutoverMonth() { return isoDate.getYear() == CUTOVER_YEAR && isoDate.getMonthValue() == 9 && isoDate.getDayOfMonth() > CUTOVER_DAYS; } //------------------------------------------------------------------------- @Override int getAlignedDayOfWeekInMonth() { if (isCutoverMonth() && julianDate == null) { return ((getDayOfMonth() - 1 - CUTOVER_DAYS) % lengthOfWeek()) + 1; } return super.getAlignedDayOfWeekInMonth(); } @Override int getAlignedWeekOfMonth() { if (isCutoverMonth() && julianDate == null) { return ((getDayOfMonth() - 1 - CUTOVER_DAYS) / lengthOfWeek()) + 1; } return super.getAlignedWeekOfMonth(); } @Override int getProlepticYear() { return (julianDate != null ? julianDate.getProlepticYear() : isoDate.getYear()); } @Override int getMonth() { return (julianDate != null ? julianDate.getMonth() : isoDate.getMonthValue()); } @Override int getDayOfMonth() { return (julianDate != null ? julianDate.getDayOfMonth() : isoDate.getDayOfMonth()); } @Override int getDayOfYear() { if (julianDate != null) { return julianDate.getDayOfYear(); } if (isoDate.getYear() == CUTOVER_YEAR) { return isoDate.getDayOfYear() - CUTOVER_DAYS; } return isoDate.getDayOfYear(); } @Override public ValueRange rangeChrono(ChronoField field) { switch (field) { case DAY_OF_MONTH: // short length, but value range still 1 to 30 if (isCutoverMonth()) { return ValueRange.of(1, 30); } return ValueRange.of(1, lengthOfMonth()); case DAY_OF_YEAR: // 1 to 355 in cutover year, otherwise 1 to 365/366 return ValueRange.of(1, lengthOfYear()); case ALIGNED_WEEK_OF_MONTH: // 1 to 3 in cutover month, otherwise 1 to 4/5 return rangeAlignedWeekOfMonth(); case ALIGNED_WEEK_OF_YEAR: // 1 to 51 in cutover year, otherwise 1 to 53 if (isCutoverYear()) { return ValueRange.of(1, 51); } return ChronoField.ALIGNED_WEEK_OF_YEAR.range(); default: return getChronology().range(field); } } @Override ValueRange rangeAlignedWeekOfMonth() { if (isCutoverMonth()) { return ValueRange.of(1, 3); } return ValueRange.of(1, getMonth() == 2 && isLeapYear() == false ? 4 : 5); } @Override BritishCutoverDate resolvePrevious(int year, int month, int dayOfMonth) { switch (month) { case 2: dayOfMonth = Math.min(dayOfMonth, getChronology().isLeapYear(year) ? 29 : 28); break; case 4: case 6: case 9: case 11: dayOfMonth = Math.min(dayOfMonth, 30); break; default: break; } return create(year, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the British Cutover calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the British Cutover chronology, not null */ @Override public BritishCutoverChronology getChronology() { return BritishCutoverChronology.INSTANCE; } /** * Gets the era applicable at this date. *

* The British Cutover calendar system has two eras, 'AD' and 'BC', * defined by {@link JulianEra}. * * @return the era applicable at this date, not null */ @Override public JulianEra getEra() { return (getProlepticYear() >= 1 ? JulianEra.AD : JulianEra.BC); } /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * This takes into account the cutover, returning 19 in September 1752. * * @return the length of the month in days, from 19 to 31 */ @Override public int lengthOfMonth() { if (isCutoverMonth()) { return 19; } return (julianDate != null ? julianDate.lengthOfMonth() : isoDate.lengthOfMonth()); } /** * Returns the length of the year represented by this date. *

* This returns the length of the year in days. * This takes into account the cutover, returning 355 in 1752. * * @return the length of the month in days, from 19 to 31 */ @Override public int lengthOfYear() { if (isCutoverYear()) { return 355; } return (julianDate != null ? julianDate.lengthOfYear() : isoDate.lengthOfYear()); } //------------------------------------------------------------------------- @Override public BritishCutoverDate with(TemporalAdjuster adjuster) { return (BritishCutoverDate) adjuster.adjustInto(this); } @Override public BritishCutoverDate with(TemporalField field, long newValue) { return (BritishCutoverDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public BritishCutoverDate plus(TemporalAmount amount) { return (BritishCutoverDate) amount.addTo(this); } @Override public BritishCutoverDate plus(long amountToAdd, TemporalUnit unit) { return (BritishCutoverDate) super.plus(amountToAdd, unit); } @Override public BritishCutoverDate minus(TemporalAmount amount) { return (BritishCutoverDate) amount.subtractFrom(this); } @Override public BritishCutoverDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return super.until(BritishCutoverDate.from(endExclusive), unit); } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { BritishCutoverDate end = BritishCutoverDate.from(endDateExclusive); long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe int days = end.getDayOfMonth() - this.getDayOfMonth(); if (totalMonths == 0 && isCutoverMonth()) { if (julianDate != null && end.julianDate == null) { days -= CUTOVER_DAYS; } else if (julianDate == null && end.julianDate != null) { days += CUTOVER_DAYS; } } else if (totalMonths > 0) { if (julianDate != null && end.julianDate == null) { AbstractDate calcDate = this.plusMonths(totalMonths); days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe } if (days < 0) { totalMonths--; AbstractDate calcDate = this.plusMonths(totalMonths); days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe } } else if (totalMonths < 0 && days > 0) { totalMonths++; AbstractDate calcDate = this.plusMonths(totalMonths); days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe } int years = Math.toIntExact(totalMonths / lengthOfYearInMonths()); // safe int months = (int) (totalMonths % lengthOfYearInMonths()); // safe return getChronology().period(years, months, days); } //----------------------------------------------------------------------- @Override public long toEpochDay() { return isoDate.toEpochDay(); } @SuppressWarnings("unchecked") @Override public R query(TemporalQuery query) { if (query == TemporalQueries.localDate()) { return (R) isoDate; } return super.query(query); } //------------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof BritishCutoverDate) { BritishCutoverDate otherDate = (BritishCutoverDate) obj; return this.isoDate.equals(otherDate.isoDate); } return false; } /** * A hash code for this date. * * @return a suitable hash code based only on the Chronology and the date */ @Override public int hashCode() { return getChronology().getId().hashCode() ^ isoDate.hashCode(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/CopticChronology.java000066400000000000000000000316621343451174100302730ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.format.ResolverStyle; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Map; /** * The Coptic calendar system. *

* This chronology defines the rules of the Coptic calendar system. * This calendar system is primarily used in Christian Egypt. * Dates are aligned such that {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}. *

* The fields are defined as follows: *

    *
  • era - There are two eras, the current 'Era of the Martyrs' (AM) and the previous era (BEFORE_AM). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. * For the previous era the year increases from one as time goes backwards. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the * current era. For the previous era, years have zero, then negative values. *
  • month-of-year - There are 13 months in a Coptic year, numbered from 1 to 13. *
  • day-of-month - There are 30 days in each of the first 12 Coptic months, numbered 1 to 30. * The 13th month has 5 days, or 6 in a leap year, numbered 1 to 5 or 1 to 6. *
  • day-of-year - There are 365 days in a standard Coptic year and 366 in a leap year. * The days are numbered from 1 to 365 or 1 to 366. *
  • leap-year - Leap years occur every 4 years. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class CopticChronology extends AbstractNileChronology implements Serializable { /** * Singleton instance for the Coptic chronology. */ public static final CopticChronology INSTANCE = new CopticChronology(); /** * Serialization version. */ private static final long serialVersionUID = 7291205177830286973L; /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public CopticChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Coptic'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Coptic' * @see #getCalendarType() */ @Override public String getId() { return "Coptic"; } /** * Gets the calendar type of the underlying calendar system - 'coptic'. *

* The calendar type is an identifier defined by the * Unicode Locale Data Markup Language (LDML) specification. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * It can also be used as part of a locale, accessible via * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. * * @return the calendar system type - 'coptic' * @see #getId() */ @Override public String getCalendarType() { return "coptic"; } //----------------------------------------------------------------------- /** * Obtains a local date in Coptic calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Coptic era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Coptic local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code CopticEra} */ @Override public CopticDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Coptic calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Coptic local date, not null * @throws DateTimeException if unable to create the date */ @Override public CopticDate date(int prolepticYear, int month, int dayOfMonth) { return CopticDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Coptic calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Coptic era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Coptic local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code CopticEra} */ @Override public CopticDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Coptic calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Coptic local date, not null * @throws DateTimeException if unable to create the date */ @Override public CopticDate dateYearDay(int prolepticYear, int dayOfYear) { return CopticDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Coptic calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Coptic local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public CopticDate dateEpochDay(long epochDay) { return CopticDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Coptic local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Coptic local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public CopticDate dateNow() { return CopticDate.now(); } /** * Obtains the current Coptic local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Coptic local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public CopticDate dateNow(ZoneId zone) { return CopticDate.now(zone); } /** * Obtains the current Coptic local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Coptic local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public CopticDate dateNow(Clock clock) { return CopticDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Coptic local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Coptic local date, not null * @throws DateTimeException if unable to create the date */ @Override public CopticDate date(TemporalAccessor temporal) { return CopticDate.from(temporal); } /** * Obtains a Coptic local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Coptic local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Coptic zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Coptic zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Coptic zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Coptic zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- @Override public int prolepticYear(Era era, int yearOfEra) { if (era instanceof CopticEra == false) { throw new ClassCastException("Era must be CopticEra"); } return (era == CopticEra.AM ? yearOfEra : 1 - yearOfEra); } @Override public CopticEra eraOf(int eraValue) { return CopticEra.of(eraValue); } @Override public List eras() { return Arrays.asList(CopticEra.values()); } //----------------------------------------------------------------------- @Override // override for return type public CopticDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) { return (CopticDate) super.resolveDate(fieldValues, resolverStyle); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/CopticDate.java000066400000000000000000000363721343451174100270300ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; /** * A date in the Coptic calendar system. *

* This date operates using the {@linkplain CopticChronology Coptic calendar}. * This calendar system is primarily used in Christian Egypt. * Dates are aligned such that {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class CopticDate extends AbstractNileDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -7920528871688876868L; /** * The difference between the ISO and Coptic epoch day count. */ private static final int EPOCH_DAY_DIFFERENCE = 574971 + 40587; // MJD values /** * The proleptic year. */ private final int prolepticYear; /** * The month. */ private final short month; /** * The day. */ private final short day; //----------------------------------------------------------------------- /** * Obtains the current {@code CopticDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static CopticDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code CopticDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static CopticDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code CopticDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static CopticDate now(Clock clock) { LocalDate now = LocalDate.now(clock); return CopticDate.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code CopticDate} representing a date in the Coptic calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code CopticDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the Coptic proleptic-year * @param month the Coptic month-of-year, from 1 to 13 * @param dayOfMonth the Coptic day-of-month, from 1 to 30 * @return the date in Coptic calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static CopticDate of(int prolepticYear, int month, int dayOfMonth) { return CopticDate.create(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code CopticDate} from a temporal object. *

* This obtains a date in the Coptic calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code CopticDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code CopticDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in Coptic calendar system, not null * @throws DateTimeException if unable to convert to a {@code CopticDate} */ public static CopticDate from(TemporalAccessor temporal) { if (temporal instanceof CopticDate) { return (CopticDate) temporal; } return CopticDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code CopticDate} representing a date in the Coptic calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code CopticDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Coptic proleptic-year * @param dayOfYear the Coptic day-of-year, from 1 to 366 * @return the date in Coptic calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static CopticDate ofYearDay(int prolepticYear, int dayOfYear) { CopticChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); DAY_OF_YEAR.range().checkValidValue(dayOfYear, DAY_OF_YEAR); if (dayOfYear == 366 && CopticChronology.INSTANCE.isLeapYear(prolepticYear) == false) { throw new DateTimeException("Invalid date 'Nasie 6' as '" + prolepticYear + "' is not a leap year"); } return new CopticDate(prolepticYear, (dayOfYear - 1) / 30 + 1, (dayOfYear - 1) % 30 + 1); } /** * Obtains a {@code CopticDate} representing a date in the Coptic calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in Coptic calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static CopticDate ofEpochDay(final long epochDay) { EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds long copticED = epochDay + EPOCH_DAY_DIFFERENCE; int adjustment = 0; if (copticED < 0) { copticED = copticED + (1461L * (1_000_000L / 4)); adjustment = -1_000_000; } int prolepticYear = (int) (((copticED * 4) + 1463) / 1461); int startYearEpochDay = (prolepticYear - 1) * 365 + (prolepticYear / 4); int doy0 = (int) (copticED - startYearEpochDay); int month = doy0 / 30 + 1; int dom = doy0 % 30 + 1; return new CopticDate(prolepticYear + adjustment, month, dom); } private static CopticDate resolvePreviousValid(int prolepticYear, int month, int day) { if (month == 13 && day > 5) { day = CopticChronology.INSTANCE.isLeapYear(prolepticYear) ? 6 : 5; } return new CopticDate(prolepticYear, month, day); } /** * Creates a {@code CopticDate} validating the input. * * @param prolepticYear the Coptic proleptic-year * @param month the Coptic month-of-year, from 1 to 13 * @param dayOfMonth the Coptic day-of-month, from 1 to 30 * @return the date in Coptic calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ static CopticDate create(int prolepticYear, int month, int dayOfMonth) { CopticChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); CopticChronology.MOY_RANGE.checkValidValue(month, MONTH_OF_YEAR); CopticChronology.DOM_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH); if (month == 13 && dayOfMonth > 5) { if (CopticChronology.INSTANCE.isLeapYear(prolepticYear)) { if (dayOfMonth > 6) { throw new DateTimeException("Invalid date 'Nasie " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year"); } } else { if (dayOfMonth == 6) { throw new DateTimeException("Invalid date 'Nasie 6' as '" + prolepticYear + "' is not a leap year"); } else { throw new DateTimeException("Invalid date 'Nasie " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year"); } } } return new CopticDate(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Coptic proleptic-year * @param month the Coptic month, from 1 to 13 * @param dayOfMonth the Coptic day-of-month, from 1 to 30 */ private CopticDate(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = (short) month; this.day = (short) dayOfMonth; } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return CopticDate.create(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getEpochDayDifference() { return EPOCH_DAY_DIFFERENCE; } @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override CopticDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Coptic calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Coptic chronology, not null */ @Override public CopticChronology getChronology() { return CopticChronology.INSTANCE; } /** * Gets the era applicable at this date. *

* The Coptic calendar system has two eras, 'AM' and 'BEFORE_AM', * defined by {@link CopticEra}. * * @return the era applicable at this date, not null */ @Override public CopticEra getEra() { return (prolepticYear >= 1 ? CopticEra.AM : CopticEra.BEFORE_AM); } //------------------------------------------------------------------------- @Override public CopticDate with(TemporalAdjuster adjuster) { return (CopticDate) adjuster.adjustInto(this); } @Override public CopticDate with(TemporalField field, long newValue) { return (CopticDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public CopticDate plus(TemporalAmount amount) { return (CopticDate) amount.addTo(this); } @Override public CopticDate plus(long amountToAdd, TemporalUnit unit) { return (CopticDate) super.plus(amountToAdd, unit); } @Override public CopticDate minus(TemporalAmount amount) { return (CopticDate) amount.subtractFrom(this); } @Override public CopticDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return super.until(CopticDate.from(endExclusive), unit); } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { return super.doUntil(CopticDate.from(endDateExclusive)); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/CopticEra.java000066400000000000000000000074771343451174100266660ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the Coptic calendar system. *

* The Coptic calendar system has two eras. * The current era, for years from 1 onwards, is known as the 'Era of the Martyrs'. * All previous years, zero or earlier in the proleptic count or one and greater * in the year-of-era count, are part of the 'Before Era of the Martyrs' era. *

* The start of the Coptic epoch {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code CopticEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum CopticEra implements Era { /** * The singleton instance for the era before the current one, 'Before Era of the Martyrs', * which has the numeric value 0. */ BEFORE_AM, /** * The singleton instance for the current era, 'Era of the Martyrs', * which has the numeric value 1. */ AM; //----------------------------------------------------------------------- /** * Obtains an instance of {@code CopticEra} from an {@code int} value. *

* {@code CopticEra} is an enum representing the Coptic eras of BEFORE_AM/AM. * This factory allows the enum to be obtained from the {@code int} value. * * @param era the BEFORE_AM/AM value to represent, from 0 (BEFORE_AM) to 1 (AM) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static CopticEra of(int era) { switch (era) { case 0: return BEFORE_AM; case 1: return AM; default: throw new DateTimeException("Invalid era: " + era); } } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era BEFORE_AM has the value 0, while the era AM has the value 1. * * @return the era value, from 0 (BEFORE_AM) to 1 (AM) */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/DiscordianChronology.java000066400000000000000000000423311343451174100311240ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; import java.util.Map; /** * The Discordian calendar system. *

* This chronology defines the rules of the proleptic Discordian calendar system. * The Discordian differs from the Gregorian in terms of month and week lengths, with an offset year. * Dates are aligned such that each Discordian year starts with each ISO year, with an offset index. *

* This class is not proleptic. It implements Discordian rules only since YOLD 1 (ISO BCE 1166). *

* The fields are defined as follows: *

    *
  • era - There is one era, the current 'Year of Our Lady of Discord' (YOLD). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the current era. *
  • month-of-year - There are 5 months in a Discordian year, numbered from 1 to 5. *
  • day-of-week - There are 5 days in a Discordian week, numbered from 1 to 5. *
  • day-of-month - There are 73 days in each Discordian month, numbered from 1 to 73. *
  • day-of-year - There are 365 days in a standard Discordian year and 366 in a leap year. * The days are numbered from 1 to 365 or 1 to 366. *
  • leap-year - Leap years occur in sync with ISO leap-years; that is, they occur every 4 ISO years, excepting when that ISO year is divisible by 100 but not 400. *
  • St. Tib's Day - St. Tib's Day occurs each leap-year, and is inserted between the 59th and 60th day of the first month. * St. Tib's Day is not part of any month, nor is it part of the Discordian week. It is aligned with the ISO calendar leap-day (February 29th). *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class DiscordianChronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Discordian chronology. */ public static final DiscordianChronology INSTANCE = new DiscordianChronology(); /** * Serialization version. */ private static final long serialVersionUID = 1075529146344250850L; /** * Offset from ISO 0 */ static final int OFFSET_FROM_ISO_0000 = 1166; /** * Days in a regular month. */ static final int DAYS_IN_MONTH = 73; /** * Days in a regular week. */ static final int DAYS_IN_WEEK = 5; /** * Months in a regular year. */ static final int MONTHS_IN_YEAR = 5; /** * Weeks in a regular year. */ static final int WEEKS_IN_YEAR = 73; /** * Range of proleptic-year. */ static final ValueRange YEAR_RANGE = ValueRange.of(1, 999_999); /** * Range of month-of-year. */ static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(0, 1, MONTHS_IN_YEAR, MONTHS_IN_YEAR); /** * Range of day-of-month. */ static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(0, 1, 0, DAYS_IN_MONTH); /** * Range of epoch day. */ static final ValueRange EPOCH_DAY_RANGE = ValueRange.of(-1_145_400, 999_999 * 365L + 242_499); /** * Range of proleptic month. */ private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(0, (long) 999_999 * MONTHS_IN_YEAR + MONTHS_IN_YEAR - 1); /** * Range of day-of-week. */ private static final ValueRange DAY_OF_WEEK_RANGE = ValueRange.of(0, 1, 0, DAYS_IN_WEEK); /** * Range of aligned day-of-week of year. */ private static final ValueRange ALIGNED_DOW_OF_YEAR_RANGE = ValueRange.of(0, 1, DAYS_IN_WEEK, DAYS_IN_WEEK); /** * Range of week-of-month. */ private static final ValueRange WEEK_OF_MONTH_RANGE = ValueRange.of(0, 1, 0, 15); /** * Range of week-of-year. */ private static final ValueRange WEEK_OF_YEAR_RANGE = ValueRange.of(0, 1, WEEKS_IN_YEAR, WEEKS_IN_YEAR); /** * Range of eras. */ private static final ValueRange ERA_RANGE = ValueRange.of(1, 1); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public DiscordianChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Discordian'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Discordian' * @see #getCalendarType() */ @Override public String getId() { return "Discordian"; } /** * Gets the calendar type of the underlying calendar system - 'discordian'. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for the Discordian calendar, but were it to * do so, 'discordian' is highly likely to be chosen. * * @return the calendar system type - 'discordian' * @see #getId() */ @Override public String getCalendarType() { return "discordian"; } //----------------------------------------------------------------------- /** * Obtains a local date in Discordian calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Discordian era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Discordian local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code DiscordianEra} */ @Override public DiscordianDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Discordian calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Discordian local date, not null * @throws DateTimeException if unable to create the date */ @Override public DiscordianDate date(int prolepticYear, int month, int dayOfMonth) { return DiscordianDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Discordian calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Discordian era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Discordian local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code DiscordianEra} */ @Override public DiscordianDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in the Discordian calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Discordian local date, not null * @throws DateTimeException if unable to create the date */ @Override public DiscordianDate dateYearDay(int prolepticYear, int dayOfYear) { return DiscordianDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Discordian calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Discordian local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public DiscordianDate dateEpochDay(long epochDay) { return DiscordianDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Discordian local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Discordian local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public DiscordianDate dateNow() { return DiscordianDate.now(); } /** * Obtains the current Discordian local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Discordian local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public DiscordianDate dateNow(ZoneId zone) { return DiscordianDate.now(zone); } /** * Obtains the current Discordian local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Discordian local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public DiscordianDate dateNow(Clock clock) { return DiscordianDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Discordian local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Discordian local date, not null * @throws DateTimeException if unable to create the date */ @Override public DiscordianDate date(TemporalAccessor temporal) { return DiscordianDate.from(temporal); } /** * Obtains a Discordian local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Discordian local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Discordian zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Discordian zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Discordian zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Discordian zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* A Discordian proleptic-year is leap if the remainder after division by four equals zero. * There are two special cases. * If the year minus 1166 is divisible by 100 it is not a leap year, unless * it is also divisible by 400, when it is a leap year. * These rules produce leap days on the same dates as the ISO-8601 calendar system. * * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long prolepticYear) { long offsetYear = prolepticYear - OFFSET_FROM_ISO_0000; return (offsetYear % 4 == 0) && ((offsetYear % 400 == 0) || (offsetYear % 100 != 0)); } @Override public int prolepticYear(Era era, int yearOfEra) { if (!DiscordianEra.YOLD.equals(era)) { throw new ClassCastException("Era must be DiscordianEra.YOLD"); } return YEAR_RANGE.checkValidIntValue(yearOfEra, ChronoField.YEAR_OF_ERA); } @Override public DiscordianEra eraOf(int era) { return DiscordianEra.of(era); } @Override public List eras() { return Arrays.asList(DiscordianEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ALIGNED_DOW_OF_YEAR_RANGE; case ALIGNED_DAY_OF_WEEK_IN_MONTH: case DAY_OF_WEEK: return DAY_OF_WEEK_RANGE; case ALIGNED_WEEK_OF_MONTH: return WEEK_OF_MONTH_RANGE; case ALIGNED_WEEK_OF_YEAR: return WEEK_OF_YEAR_RANGE; case DAY_OF_MONTH: return DAY_OF_MONTH_RANGE; case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: case YEAR: return YEAR_RANGE; default: break; } return field.range(); } //----------------------------------------------------------------------- @Override // override for return type public DiscordianDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) { return (DiscordianDate) super.resolveDate(fieldValues, resolverStyle); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/DiscordianDate.java000066400000000000000000000745331343451174100276670ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import static org.threeten.extra.chrono.DiscordianChronology.DAYS_IN_MONTH; import static org.threeten.extra.chrono.DiscordianChronology.DAYS_IN_WEEK; import static org.threeten.extra.chrono.DiscordianChronology.MONTHS_IN_YEAR; import static org.threeten.extra.chrono.DiscordianChronology.OFFSET_FROM_ISO_0000; import static org.threeten.extra.chrono.DiscordianChronology.WEEKS_IN_YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.ValueRange; /** * A date in the Discordian calendar system. *

* This date operates using the {@linkplain DiscordianChronology Discordian calendar}. * This calendar system is used by some adherents to Discordianism. * The Discordian differs from the Gregorian in terms of the length of the week and month, and uses an offset year. * Dates are aligned such that {@code 0001-01-01 (Discordian)} is {@code -1165-01-01 (ISO)}. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class DiscordianDate extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -4340508226506164852L; /** * The difference between the Discordian and ISO epoch day count (Discordian 1167-01-01 to ISO 1970-01-01). */ private static final int DISCORDIAN_1167_TO_ISO_1970 = 719162; /** * The days per short 4 year cycle. */ private static final int DAYS_PER_SHORT_CYCLE = (365 * 4) + 1; /** * The days per 100 year cycle. */ private static final int DAYS_PER_CYCLE = (DAYS_PER_SHORT_CYCLE * 25) - 1; /** * The days per 400 year long cycle. */ private static final int DAYS_PER_LONG_CYCLE = (DAYS_PER_CYCLE * 4) + 1; /** * Offset in days from start of year to St. Tib's Day. */ private static final int ST_TIBS_OFFSET = 60; /** * The proleptic year. */ private final int prolepticYear; /** * The month. */ private final short month; /** * The day. */ private final short day; //----------------------------------------------------------------------- /** * Obtains the current {@code DiscordianDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static DiscordianDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code DiscordianDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static DiscordianDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code DiscordianDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static DiscordianDate now(Clock clock) { LocalDate now = LocalDate.now(clock); return DiscordianDate.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code DiscordianDate} representing a date in the Discordian calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code DiscordianDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. *

* St. Tib's Day is indicated by specifying 0 for both month and day-of-month. * * @param prolepticYear the Discordian proleptic-year * @param month the Discordian month-of-year, from 1 to 5 * @param dayOfMonth the Discordian day-of-month, from 1 to 73 * @return the date in Discordian calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static DiscordianDate of(int prolepticYear, int month, int dayOfMonth) { return DiscordianDate.create(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code DiscordianDate} from a temporal object. *

* This obtains a date in the Discordian calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code DiscordianDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code DiscordianDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in Discordian calendar system, not null * @throws DateTimeException if unable to convert to a {@code DiscordianDate} */ public static DiscordianDate from(TemporalAccessor temporal) { if (temporal instanceof DiscordianDate) { return (DiscordianDate) temporal; } return DiscordianDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code DiscordianDate} representing a date in the Discordian calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code DiscordianDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Discordian proleptic-year * @param dayOfYear the Discordian day-of-year, from 1 to 366 * @return the date in Discordian calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static DiscordianDate ofYearDay(int prolepticYear, int dayOfYear) { DiscordianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); DAY_OF_YEAR.checkValidValue(dayOfYear); boolean leap = DiscordianChronology.INSTANCE.isLeapYear(prolepticYear); if (dayOfYear == 366 && !leap) { throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' is not a leap year"); } if (leap) { if (dayOfYear == ST_TIBS_OFFSET) { // Take care of special case of St Tib's Day. return new DiscordianDate(prolepticYear, 0, 0); } else if (dayOfYear > ST_TIBS_OFFSET) { // Offset dayOfYear to account for added day. dayOfYear--; } } int month = (dayOfYear - 1) / DAYS_IN_MONTH + 1; int dayOfMonth = (dayOfYear - 1) % DAYS_IN_MONTH + 1; return new DiscordianDate(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code DiscordianDate} representing a date in the Discordian calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in Discordian calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static DiscordianDate ofEpochDay(final long epochDay) { DiscordianChronology.EPOCH_DAY_RANGE.checkValidValue(epochDay, EPOCH_DAY); // use of Discordian 1167 makes leap year at end of long cycle long discordianEpochDay = epochDay + DISCORDIAN_1167_TO_ISO_1970; long longCycle = Math.floorDiv(discordianEpochDay, DAYS_PER_LONG_CYCLE); long daysInLongCycle = Math.floorMod(discordianEpochDay, DAYS_PER_LONG_CYCLE); if (daysInLongCycle == DAYS_PER_LONG_CYCLE - 1) { int year = (int) (longCycle * 400) + 400; return ofYearDay(year + OFFSET_FROM_ISO_0000, 366); } int cycle = (int) daysInLongCycle / DAYS_PER_CYCLE; int dayInCycle = (int) daysInLongCycle % DAYS_PER_CYCLE; int shortCycle = dayInCycle / DAYS_PER_SHORT_CYCLE; int dayInShortCycle = dayInCycle % DAYS_PER_SHORT_CYCLE; if (dayInShortCycle == DAYS_PER_SHORT_CYCLE - 1) { int year = (int) (longCycle * 400) + (cycle * 100) + (shortCycle * 4) + 4; return ofYearDay(year + OFFSET_FROM_ISO_0000, 366); } int year = (int) (longCycle * 400) + (cycle * 100) + (shortCycle * 4) + (dayInShortCycle / 365) + 1; int dayOfYear = (dayInShortCycle % 365) + 1; return ofYearDay(year + OFFSET_FROM_ISO_0000, dayOfYear); } private static DiscordianDate resolvePreviousValid(int prolepticYear, int month, int day) { switch (month) { case 0: day = 0; if (DiscordianChronology.INSTANCE.isLeapYear(prolepticYear)) { break; } month = 1; // fall through default: if (day == 0) { day = ST_TIBS_OFFSET; } } return new DiscordianDate(prolepticYear, month, day); } private static long getLeapYearsBefore(long year) { long offsetYear = year - OFFSET_FROM_ISO_0000 - 1; return Math.floorDiv(offsetYear, 4) - Math.floorDiv(offsetYear, 100) + Math.floorDiv(offsetYear, 400); } /** * Creates a {@code DiscordianDate} validating the input. * * @param prolepticYear the Discordian proleptic-year * @param month the Discordian month-of-year, from 1 to 5 * @param dayOfMonth the Discordian day-of-month, from 1 to 73 * @return the date in Discordian calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the month-year */ static DiscordianDate create(int prolepticYear, int month, int dayOfMonth) { DiscordianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); DiscordianChronology.MONTH_OF_YEAR_RANGE.checkValidValue(month, MONTH_OF_YEAR); DiscordianChronology.DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH); if (month == 0 || dayOfMonth == 0) { if (month != 0 || dayOfMonth != 0) { throw new DateTimeException("Invalid date '" + month + " " + dayOfMonth + "' as St. Tib's Day is the only special day inserted in a nonexistant month."); } else if (!DiscordianChronology.INSTANCE.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid date 'St. Tibs Day' as '" + prolepticYear + "' is not a leap year"); } } return new DiscordianDate(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Discordian proleptic-year * @param month the Discordian month, from 1 to 5 * @param dayOfMonth the Discordian day-of-month, from 1 to 73 */ private DiscordianDate(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = (short) month; this.day = (short) dayOfMonth; } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return DiscordianDate.create(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { // St. Tib's Day isn't part of any month, but would be the 60th day of the year. if (month == 0 && day == 0) { return ST_TIBS_OFFSET; } int dayOfYear = (month - 1) * DAYS_IN_MONTH + day; // If after St. Tib's day, need to offset to account for it. return dayOfYear + (dayOfYear >= ST_TIBS_OFFSET && isLeapYear() ? 1 : 0); } @Override AbstractDate withDayOfYear(int value) { return plusDays(value - getDayOfYear()); } @Override int lengthOfWeek() { return DAYS_IN_WEEK; } @Override int lengthOfYearInMonths() { return MONTHS_IN_YEAR; } @Override ValueRange rangeAlignedWeekOfMonth() { return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, 15); } @Override DiscordianDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- @Override public ValueRange range(TemporalField field) { if (field instanceof ChronoField) { if (isSupported(field)) { ChronoField f = (ChronoField) field; switch (f) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, DAYS_IN_WEEK); case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ValueRange.of(isLeapYear() ? 0 : 1, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_YEAR: return ValueRange.of(isLeapYear() ? 0 : 1, WEEKS_IN_YEAR); case DAY_OF_MONTH: return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, DAYS_IN_MONTH); case DAY_OF_WEEK: return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, DAYS_IN_WEEK); case MONTH_OF_YEAR: return ValueRange.of(isLeapYear() ? 0 : 1, MONTHS_IN_YEAR); default: break; } } } return super.range(field); } //----------------------------------------------------------------------- @Override public long getLong(TemporalField field) { if (field instanceof ChronoField) { switch ((ChronoField) field) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: return month == 0 ? 0 : super.getLong(field); case ALIGNED_DAY_OF_WEEK_IN_YEAR: return getDayOfWeek(); case ALIGNED_WEEK_OF_MONTH: return month == 0 ? 0 : super.getLong(field); case ALIGNED_WEEK_OF_YEAR: if (month == 0) { return 0; } else { return ((getDayOfYear() - (getDayOfYear() >= ST_TIBS_OFFSET && isLeapYear() ? 1 : 0) - 1) / DAYS_IN_WEEK) + 1; } default: break; } } return super.getLong(field); } @Override int getDayOfWeek() { if (month == 0) { return 0; } // Need to offset to account for added day. int dayOfYear = getDayOfYear() - (getDayOfYear() >= ST_TIBS_OFFSET && isLeapYear() ? 1 : 0); return (dayOfYear - 1) % DAYS_IN_WEEK + 1; } @Override long getProlepticMonth() { // Consider St. Tib's day to be part of the 1st month for this count. return prolepticYear * MONTHS_IN_YEAR + (month == 0 ? 1 : month) - 1; } long getProlepticWeek() { // Consider St. Tib's day to be part of the 12th week for this count. return ((long) prolepticYear) * WEEKS_IN_YEAR + (month == 0 ? ST_TIBS_OFFSET / DAYS_IN_WEEK : getLong(ALIGNED_WEEK_OF_YEAR)) - 1; } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Discordian calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Discordian chronology, not null */ @Override public DiscordianChronology getChronology() { return DiscordianChronology.INSTANCE; } /** * Gets the era applicable at this date. *

* The Discordian calendar system has one era, 'YOLD', * defined by {@link DiscordianEra}. * * @return the era YOLD, not null */ @Override public DiscordianEra getEra() { return DiscordianEra.YOLD; } @Override public int lengthOfMonth() { return month == 0 ? 1 : DAYS_IN_MONTH; } //------------------------------------------------------------------------- @Override public DiscordianDate with(TemporalAdjuster adjuster) { return (DiscordianDate) adjuster.adjustInto(this); } @Override public DiscordianDate with(TemporalField field, long newValue) { if (field instanceof ChronoField) { ChronoField f = (ChronoField) field; DiscordianChronology.INSTANCE.range(f).checkValidValue(newValue, f); int nvalue = (int) newValue; // trying to move to St Tibs if (nvalue == 0 && isLeapYear()) { switch (f) { case DAY_OF_WEEK: case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: case ALIGNED_WEEK_OF_MONTH: case ALIGNED_WEEK_OF_YEAR: case DAY_OF_MONTH: case MONTH_OF_YEAR: if (month == 0) { return this; } return DiscordianDate.create(prolepticYear, 0, 0); default: break; } } // currently on St Tibs if (month == 0) { switch (f) { case YEAR: case YEAR_OF_ERA: if (DiscordianChronology.INSTANCE.isLeapYear(nvalue)) { return DiscordianDate.create(nvalue, 0, 0); } // fall through default: return DiscordianDate.create(prolepticYear, 1, 60).with(field, newValue); } } // validate range (generally excluding zero value) range(f).checkValidValue(newValue, f); switch (f) { case DAY_OF_WEEK: case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: if (month == 1 && day >= ST_TIBS_OFFSET - DAYS_IN_WEEK + 1 && day < ST_TIBS_OFFSET + 1 && isLeapYear()) { int currentDayOfWeek = getDayOfWeek(); // St. Tib's Day is between the 4th and 5th days of the week... if (currentDayOfWeek < DAYS_IN_WEEK && nvalue == DAYS_IN_WEEK) { return (DiscordianDate) plusDays(nvalue - currentDayOfWeek + 1); } else if (currentDayOfWeek == DAYS_IN_WEEK && nvalue < DAYS_IN_WEEK) { return (DiscordianDate) plusDays(nvalue - currentDayOfWeek - 1); } } break; case ALIGNED_WEEK_OF_MONTH: case ALIGNED_WEEK_OF_YEAR: if ((month == 1 || field == ALIGNED_WEEK_OF_YEAR) && isLeapYear()) { // St. Tib's Day is in the middle of the 12th week... int alignedWeek = (int) getLong(field); int currentDayOfWeek = getDayOfWeek(); if ((alignedWeek > ST_TIBS_OFFSET / DAYS_IN_WEEK || (alignedWeek == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek == DAYS_IN_WEEK)) && (nvalue < ST_TIBS_OFFSET / DAYS_IN_WEEK || (nvalue == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek < DAYS_IN_WEEK))) { return (DiscordianDate) plusDays((newValue - alignedWeek) * DAYS_IN_WEEK - 1); } else if ((nvalue > ST_TIBS_OFFSET / DAYS_IN_WEEK || (nvalue == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek == DAYS_IN_WEEK)) && (alignedWeek < ST_TIBS_OFFSET / DAYS_IN_WEEK || (alignedWeek == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek < DAYS_IN_WEEK))) { return (DiscordianDate) plusDays((newValue - alignedWeek) * lengthOfWeek() + 1); } } break; default: break; } } return (DiscordianDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public DiscordianDate plus(TemporalAmount amount) { return (DiscordianDate) amount.addTo(this); } @Override public DiscordianDate plus(long amountToAdd, TemporalUnit unit) { if (unit instanceof ChronoUnit) { ChronoUnit f = (ChronoUnit) unit; switch (f) { case WEEKS: return plusWeeks(amountToAdd); case MONTHS: return plusMonths(amountToAdd); default: break; } } return (DiscordianDate) super.plus(amountToAdd, unit); } @Override DiscordianDate plusMonths(long months) { if (months == 0) { return this; } long calcEm = Math.addExact(getProlepticMonth(), months); int newYear = Math.toIntExact(Math.floorDiv(calcEm, MONTHS_IN_YEAR)); int newMonth = (int) (Math.floorMod(calcEm, MONTHS_IN_YEAR) + 1); // If the starting date was St. Tib's Day, attempt to retain that status. if (month == 0 && newMonth == 1) { newMonth = 0; } return resolvePrevious(newYear, newMonth, day); } @Override DiscordianDate plusWeeks(long weeks) { if (weeks == 0) { return this; } long calcEm = Math.addExact(getProlepticWeek(), weeks); int newYear = Math.toIntExact(Math.floorDiv(calcEm, WEEKS_IN_YEAR)); // Give St. Tib's Day the same day-of-week as the day after it. int newDayOfYear = (int) (Math.floorMod(calcEm, WEEKS_IN_YEAR)) * DAYS_IN_WEEK + (month == 0 ? DAYS_IN_WEEK : get(DAY_OF_WEEK)); // Need to offset day-of-year if leap year, and not heading to St. Tib's Day again. if (DiscordianChronology.INSTANCE.isLeapYear(newYear) && (newDayOfYear > ST_TIBS_OFFSET || (newDayOfYear == ST_TIBS_OFFSET && month != 0))) { newDayOfYear++; } return ofYearDay(newYear, newDayOfYear); } @Override public DiscordianDate minus(TemporalAmount amount) { return (DiscordianDate) amount.subtractFrom(this); } @Override public DiscordianDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return until(DiscordianDate.from(endExclusive), unit); } @Override long until(AbstractDate end, TemporalUnit unit) { if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case WEEKS: return weeksUntil(DiscordianDate.from(end)); default: break; } } return super.until(end, unit); } long weeksUntil(DiscordianDate end) { long weekStart = this.getProlepticWeek() * 8L; long weekEnd = end.getProlepticWeek() * 8L; // Toggle offset for St. Tib's Day based on the direction traveled. long packed1 = weekStart + (this.month == 0 && end.month != 0 ? (weekEnd > weekStart ? DAYS_IN_WEEK : DAYS_IN_WEEK - 1) : this.getDayOfWeek()); // no overflow long packed2 = weekEnd + (end.month == 0 && this.month != 0 ? (weekStart > weekEnd ? DAYS_IN_WEEK : DAYS_IN_WEEK - 1) : end.getDayOfWeek()); // no overflow return (packed2 - packed1) / 8L; } @Override long monthsUntil(AbstractDate end) { DiscordianDate discordianEnd = DiscordianDate.from(end); long monthStart = this.getProlepticMonth() * 128L; long monthEnd = discordianEnd.getProlepticMonth() * 128L; // Toggle offset for St. Tib's Day based on the direction traveled. long packed1 = monthStart + (this.month == 0 && discordianEnd.month != 0 ? (monthEnd > monthStart ? ST_TIBS_OFFSET : ST_TIBS_OFFSET - 1) : this.getDayOfMonth()); // no overflow long packed2 = monthEnd + (discordianEnd.month == 0 && this.month != 0 ? (monthStart > monthEnd ? ST_TIBS_OFFSET : ST_TIBS_OFFSET - 1) : end.getDayOfMonth()); // no overflow return (packed2 - packed1) / 128L; } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { long monthsUntil = monthsUntil(DiscordianDate.from(endDateExclusive)); int years = Math.toIntExact(monthsUntil / MONTHS_IN_YEAR); int months = (int) (monthsUntil % MONTHS_IN_YEAR); int days = (int) this.plusMonths(monthsUntil).daysUntil(endDateExclusive); return DiscordianChronology.INSTANCE.period(years, months, days); } //----------------------------------------------------------------------- @Override public long toEpochDay() { long year = prolepticYear; long discordianEpochDay = ((year - OFFSET_FROM_ISO_0000 - 1) * 365) + getLeapYearsBefore(year) + (getDayOfYear() - 1); return discordianEpochDay - DISCORDIAN_1167_TO_ISO_1970; } //------------------------------------------------------------------------- @Override public String toString() { StringBuilder buf = new StringBuilder(30); buf.append(DiscordianChronology.INSTANCE.toString()) .append(" ") .append(DiscordianEra.YOLD) .append(" ") .append(prolepticYear); if (month == 0) { buf.append(" St. Tib's Day"); } else { buf.append("-").append(month) .append(day < 10 ? "-0" : "-").append(day); } return buf.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/DiscordianEra.java000066400000000000000000000067761343451174100275250ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the Discordian calendar system. *

* The Discordian calendar system has one era. * The current era, for years from 1 onwards, is known as the 'Year of Our Lady of Discord'. * No other eras are supported. *

* The start of the Discordian epoch {@code 0001-01-01 (Discordian)} is {@code -1165-01-01 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code DiscordianEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum DiscordianEra implements Era { /** * The singleton instance for the current era, 'Year of Our Lady of Discord', * which has the numeric value 1. */ YOLD; //----------------------------------------------------------------------- /** * Obtains an instance of {@code DiscordianEra} from an {@code int} value. *

* {@code DiscordianEra} is an enum representing the Discordian era of YOLD. * This factory allows the enum to be obtained from the {@code int} value. * Only the value 1 is ever accepted. * * @param era the YOLD value to represent, which must be 1 (YOLD) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static DiscordianEra of(int era) { switch (era) { case 1: return YOLD; default: throw new DateTimeException("Invalid era: " + era); } } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era YOLD has the value 1. * * @return the era value, 1 for YOLD */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/EthiopicChronology.java000066400000000000000000000321241343451174100306100ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.format.ResolverStyle; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Map; /** * The Ethiopic calendar system. *

* This chronology defines the rules of the Ethiopic calendar system. * This calendar system is primarily used in Ethiopia. * Dates are aligned such that {@code 0001-01-01 (Ethiopic)} is {@code 0284-08-29 (ISO)}. *

* The fields are defined as follows: *

    *
  • era - There are two eras, the current 'Incarnation Era' (INCARNATION) and the previous era (BEFORE_INCARNATION). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. * For the previous era the year increases from one as time goes backwards. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the * current era. For the previous era, years have zero, then negative values. *
  • month-of-year - There are 13 months in a Ethiopic year, numbered from 1 to 13. *
  • day-of-month - There are 30 days in each of the first 12 Ethiopic months, numbered 1 to 30. * The 13th month has 5 days, or 6 in a leap year, numbered 1 to 5 or 1 to 6. *
  • day-of-year - There are 365 days in a standard Ethiopic year and 366 in a leap year. * The days are numbered from 1 to 365 or 1 to 366. *
  • leap-year - Leap years occur every 4 years. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class EthiopicChronology extends AbstractNileChronology implements Serializable { /** * Singleton instance for the Ethiopic chronology. */ public static final EthiopicChronology INSTANCE = new EthiopicChronology(); /** * Serialization version. */ private static final long serialVersionUID = 53287687268768L; /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public EthiopicChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Ethiopic'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Ethiopic' * @see #getCalendarType() */ @Override public String getId() { return "Ethiopic"; } /** * Gets the calendar type of the underlying calendar system - 'ethiopic'. *

* The calendar type is an identifier defined by the * Unicode Locale Data Markup Language (LDML) specification. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * It can also be used as part of a locale, accessible via * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. * * @return the calendar system type - 'ethiopic' * @see #getId() */ @Override public String getCalendarType() { return "ethiopic"; } //----------------------------------------------------------------------- /** * Obtains a local date in Ethiopic calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Ethiopic era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Ethiopic local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code EthiopicEra} */ @Override public EthiopicDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Ethiopic calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Ethiopic local date, not null * @throws DateTimeException if unable to create the date */ @Override public EthiopicDate date(int prolepticYear, int month, int dayOfMonth) { return EthiopicDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Ethiopic calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Ethiopic era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Ethiopic local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code EthiopicEra} */ @Override public EthiopicDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Ethiopic calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Ethiopic local date, not null * @throws DateTimeException if unable to create the date */ @Override public EthiopicDate dateYearDay(int prolepticYear, int dayOfYear) { return EthiopicDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Ethiopic calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Ethiopic local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public EthiopicDate dateEpochDay(long epochDay) { return EthiopicDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Ethiopic local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Ethiopic local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public EthiopicDate dateNow() { return EthiopicDate.now(); } /** * Obtains the current Ethiopic local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Ethiopic local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public EthiopicDate dateNow(ZoneId zone) { return EthiopicDate.now(zone); } /** * Obtains the current Ethiopic local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Ethiopic local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public EthiopicDate dateNow(Clock clock) { return EthiopicDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Ethiopic local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Ethiopic local date, not null * @throws DateTimeException if unable to create the date */ @Override public EthiopicDate date(TemporalAccessor temporal) { return EthiopicDate.from(temporal); } /** * Obtains a Ethiopic local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Ethiopic local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Ethiopic zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Ethiopic zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Ethiopic zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Ethiopic zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- @Override public int prolepticYear(Era era, int yearOfEra) { if (era instanceof EthiopicEra == false) { throw new ClassCastException("Era must be EthiopicEra"); } return (era == EthiopicEra.INCARNATION ? yearOfEra : 1 - yearOfEra); } @Override public EthiopicEra eraOf(int eraValue) { return EthiopicEra.of(eraValue); } @Override public List eras() { return Arrays.asList(EthiopicEra.values()); } //----------------------------------------------------------------------- @Override // override for return type public EthiopicDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) { return (EthiopicDate) super.resolveDate(fieldValues, resolverStyle); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/EthiopicDate.java000066400000000000000000000370141343451174100273450ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; /** * A date in the Ethiopic calendar system. *

* This date operates using the {@linkplain EthiopicChronology Ethiopic calendar}. * This calendar system is primarily used in Ethiopia. * Dates are aligned such that {@code 0001-01-01 (Ethiopic)} is {@code 0008-08-27 (ISO)}. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class EthiopicDate extends AbstractNileDate implements ChronoLocalDate, Serializable { // TODO: Check epoch day // TODO: Check conversion year (and Coptic) /** * Serialization version. */ private static final long serialVersionUID = -268768729L; /** * The difference between the ISO and Ethiopic epoch day count. */ private static final int EPOCH_DAY_DIFFERENCE = 716367; /** * The proleptic year. */ private final int prolepticYear; /** * The month. */ private final short month; /** * The day. */ private final short day; //----------------------------------------------------------------------- /** * Obtains the current {@code EthiopicDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static EthiopicDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code EthiopicDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static EthiopicDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code EthiopicDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static EthiopicDate now(Clock clock) { LocalDate now = LocalDate.now(clock); return EthiopicDate.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code EthiopicDate} representing a date in the Ethiopic calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code EthiopicDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the Ethiopic proleptic-year * @param month the Ethiopic month-of-year, from 1 to 13 * @param dayOfMonth the Ethiopic day-of-month, from 1 to 30 * @return the date in Ethiopic calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static EthiopicDate of(int prolepticYear, int month, int dayOfMonth) { return EthiopicDate.create(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code EthiopicDate} from a temporal object. *

* This obtains a date in the Ethiopic calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code EthiopicDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code EthiopicDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in Ethiopic calendar system, not null * @throws DateTimeException if unable to convert to a {@code EthiopicDate} */ public static EthiopicDate from(TemporalAccessor temporal) { if (temporal instanceof EthiopicDate) { return (EthiopicDate) temporal; } return EthiopicDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code EthiopicDate} representing a date in the Ethiopic calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code EthiopicDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Ethiopic proleptic-year * @param dayOfYear the Ethiopic day-of-year, from 1 to 366 * @return the date in Ethiopic calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static EthiopicDate ofYearDay(int prolepticYear, int dayOfYear) { EthiopicChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); DAY_OF_YEAR.range().checkValidValue(dayOfYear, DAY_OF_YEAR); if (dayOfYear == 366 && EthiopicChronology.INSTANCE.isLeapYear(prolepticYear) == false) { throw new DateTimeException("Invalid date 'Pagumen 6' as '" + prolepticYear + "' is not a leap year"); } return new EthiopicDate(prolepticYear, (dayOfYear - 1) / 30 + 1, (dayOfYear - 1) % 30 + 1); } /** * Obtains a {@code EthiopicDate} representing a date in the Ethiopic calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in Ethiopic calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static EthiopicDate ofEpochDay(final long epochDay) { EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds long ethiopicED = epochDay + EPOCH_DAY_DIFFERENCE; int adjustment = 0; if (ethiopicED < 0) { ethiopicED = ethiopicED + (1461L * (1_000_000L / 4)); adjustment = -1_000_000; } int prolepticYear = (int) (((ethiopicED * 4) + 1463) / 1461); int startYearEpochDay = (prolepticYear - 1) * 365 + (prolepticYear / 4); int doy0 = (int) (ethiopicED - startYearEpochDay); int month = doy0 / 30 + 1; int dom = doy0 % 30 + 1; return new EthiopicDate(prolepticYear + adjustment, month, dom); } private static EthiopicDate resolvePreviousValid(int prolepticYear, int month, int day) { if (month == 13 && day > 5) { day = EthiopicChronology.INSTANCE.isLeapYear(prolepticYear) ? 6 : 5; } return new EthiopicDate(prolepticYear, month, day); } /** * Creates a {@code EthiopicDate} validating the input. * * @param prolepticYear the Ethiopic proleptic-year * @param month the Ethiopic month-of-year, from 1 to 13 * @param dayOfMonth the Ethiopic day-of-month, from 1 to 30 * @return the date in Ethiopic calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the month-year */ static EthiopicDate create(int prolepticYear, int month, int dayOfMonth) { EthiopicChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); EthiopicChronology.MOY_RANGE.checkValidValue(month, MONTH_OF_YEAR); EthiopicChronology.DOM_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH); if (month == 13 && dayOfMonth > 5) { if (EthiopicChronology.INSTANCE.isLeapYear(prolepticYear)) { if (dayOfMonth > 6) { throw new DateTimeException("Invalid date 'Pagumen " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year"); } } else { if (dayOfMonth == 6) { throw new DateTimeException("Invalid date 'Pagumen 6' as '" + prolepticYear + "' is not a leap year"); } else { throw new DateTimeException("Invalid date 'Pagumen " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year"); } } } return new EthiopicDate(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Ethiopic proleptic-year * @param month the Ethiopic month, from 1 to 13 * @param dayOfMonth the Ethiopic day-of-month, from 1 to 30 */ private EthiopicDate(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = (short) month; this.day = (short) dayOfMonth; } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return EthiopicDate.create(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getEpochDayDifference() { return EPOCH_DAY_DIFFERENCE; } @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override EthiopicDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Ethiopic calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Ethiopic chronology, not null */ @Override public EthiopicChronology getChronology() { return EthiopicChronology.INSTANCE; } /** * Gets the era applicable at this date. *

* The Ethiopic calendar system has two eras, 'INCARNATION' and 'BEFORE_INCARNATION', * defined by {@link EthiopicEra}. * * @return the era applicable at this date, not null */ @Override public EthiopicEra getEra() { return (prolepticYear >= 1 ? EthiopicEra.INCARNATION : EthiopicEra.BEFORE_INCARNATION); } //------------------------------------------------------------------------- @Override public EthiopicDate with(TemporalAdjuster adjuster) { return (EthiopicDate) adjuster.adjustInto(this); } @Override public EthiopicDate with(TemporalField field, long newValue) { return (EthiopicDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public EthiopicDate plus(TemporalAmount amount) { return (EthiopicDate) amount.addTo(this); } @Override public EthiopicDate plus(long amountToAdd, TemporalUnit unit) { return (EthiopicDate) super.plus(amountToAdd, unit); } @Override public EthiopicDate minus(TemporalAmount amount) { return (EthiopicDate) amount.subtractFrom(this); } @Override public EthiopicDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return super.until(EthiopicDate.from(endExclusive), unit); } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { return super.doUntil(EthiopicDate.from(endDateExclusive)); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/EthiopicEra.java000066400000000000000000000077051343451174100272030ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the Ethiopic calendar system. *

* The Ethiopic calendar system has two eras. * The current era, for years from 1 onwards, is known as the 'Incarnation Era'. * All previous years, zero or earlier in the proleptic count or one and greater * in the year-of-era count, are part of the 'Before Incarnation Era' era. *

* The start of the Ethiopic epoch {@code 0001-01-01 (Ethiopic)} is {@code 0284-08-29 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code EthiopicEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum EthiopicEra implements Era { /** * The singleton instance for the era before the current one, 'Before Incarnation Era', * which has the numeric value 0. */ BEFORE_INCARNATION, /** * The singleton instance for the current era, 'Incarnation Era', * which has the numeric value 1. */ INCARNATION; //----------------------------------------------------------------------- /** * Obtains an instance of {@code EthiopicEra} from an {@code int} value. *

* {@code EthiopicEra} is an enum representing the Ethiopic eras of BEFORE_INCARNATION/INCARNATION. * This factory allows the enum to be obtained from the {@code int} value. * * @param era the BEFORE_INCARNATION/INCARNATION value to represent, from 0 (BEFORE_INCARNATION) to 1 (INCARNATION) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static EthiopicEra of(int era) { switch (era) { case 0: return BEFORE_INCARNATION; case 1: return INCARNATION; default: throw new DateTimeException("Invalid era: " + era); } } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era BEFORE_INCARNATION has the value 0, while the era INCARNATION has the value 1. * * @return the era value, from 0 (BEFORE_INCARNATION) to 1 (INCARNATION) */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/InternationalFixedChronology.java000066400000000000000000000447401343451174100326420ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; /** * The International Fixed calendar system. *

* This chronology defines the rules of the International Fixed calendar system. * It shares the leap year rule with the Gregorian calendar. * Dates are aligned such that {@code 0001-01-01 (International Fixed)} is {@code 0001-01-01 (ISO)}. *

* This class is proleptic. It implements only years greater or equal to 1. *

* This class implements a calendar where January 1st is the start of the year. *

* The fields are defined as follows: *

    *
  • era - There is only one era, the current 'Common Era' (CE). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year 1. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the current era. *
  • month-of-year - There are 13 months in an International Fixed year, numbered from 1 to 13. *
  • day-of-month - There are 28 days in a standard International Fixed month, numbered from 1 to 28, * with day 29 used in months 6 and 12 (month 6 only has 29 days in a leap year). *
  • day-of-year - There are 365 days in a standard International Fixed year and 366 days in a leap year. * The days are numbered accordingly. *
  • leap-year - Leap years occur every 4 years, but skips 3 out of four centuries, i.e. when the century * is not divisible by 400. This is the same rule in use for the Gregorian calendar. *
  • Week day - every month starts on a Sunday. Leap-day and year-day are neither part of a week, nor of any month. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class InternationalFixedChronology extends AbstractChronology implements Serializable { /** * Singleton instance for the International fixed chronology. */ public static final InternationalFixedChronology INSTANCE = new InternationalFixedChronology(); /** * Serialization version UID. */ private static final long serialVersionUID = -8252657100538813526L; /** * Standard 7-day week. */ static final int DAYS_IN_WEEK = 7; /** * In all months, there are 4 complete weeks. */ static final int WEEKS_IN_MONTH = 4; /** * There are 13 months in a year. */ static final int MONTHS_IN_YEAR = 13; /** * There are 4 weeks of 7 days, or 28 total days in a month. */ static final int DAYS_IN_MONTH = WEEKS_IN_MONTH * DAYS_IN_WEEK; /** * There are 29 days in a long month. */ static final int DAYS_IN_LONG_MONTH = DAYS_IN_MONTH + 1; /** * There are 13 months of 28 days, or 365 days in a (non-leap) year. */ static final int DAYS_IN_YEAR = MONTHS_IN_YEAR * DAYS_IN_MONTH + 1; /** * There are 52 weeks in a year. */ static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK; /** * The number of days in a 400 year cycle. */ static final int DAYS_PER_CYCLE = 146097; /** * The number of days from year zero to year 1970, still the era only allows year 1 and higher. * There are five 400 year cycles from year zero to 2000. * There are 7 leap years from 1970 to 2000. */ static final long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L); /** * Range of year. */ static final ValueRange YEAR_RANGE = ValueRange.of(1, 1_000_000L); /** * Epoch day range. */ static final ValueRange EPOCH_DAY_RANGE = ValueRange.of(-DAYS_0000_TO_1970, 1_000_000L * DAYS_IN_YEAR + getLeapYearsBefore(1_000_000L) - DAYS_0000_TO_1970); /** * Range of proleptic month. */ private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(13, 1_000_000 * 13L - 1); /** * Range of day of month. */ static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_MONTH + 1); /** * Range of day of year. */ static final ValueRange DAY_OF_YEAR_NORMAL_RANGE = ValueRange.of(1, DAYS_IN_YEAR); /** * Range of day of leap year. */ static final ValueRange DAY_OF_YEAR_LEAP_RANGE = ValueRange.of(1, DAYS_IN_YEAR + 1); /** * Range of month of year. */ static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR); /** * Range of eras. */ static final ValueRange ERA_RANGE = ValueRange.of(1, 1); /** * Empty range: [0, 0]. */ static final ValueRange EMPTY_RANGE = ValueRange.of(0, 0); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public InternationalFixedChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ @SuppressWarnings("static-method") private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Ifc'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Ifc' * @see #getCalendarType() */ @Override public String getId() { return "Ifc"; } /** * Gets the calendar type of the underlying calendar system, which returns null. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for this calendar system, thus null is returned. * * @return the calendar system type, null * @see #getId() */ @Override public String getCalendarType() { return null; } //----------------------------------------------------------------------- /** * Obtains a local date in International Fixed calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the International Fixed era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the International Fixed local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code InternationalFixedEra} */ @Override public InternationalFixedDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in International Fixed calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the International Fixed local date, not null * @throws DateTimeException if unable to create the date */ @Override public InternationalFixedDate date(int prolepticYear, int month, int dayOfMonth) { return InternationalFixedDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in International Fixed calendar system from the * era, year-of-era and day-of-year fields. * * @param era the International Fixed era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the International Fixed local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code InternationalFixedEra} */ @Override public InternationalFixedDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in International Fixed calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the International Fixed local date, not null * @throws DateTimeException if unable to create the date */ @Override public InternationalFixedDate dateYearDay(int prolepticYear, int dayOfYear) { return InternationalFixedDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the International Fixed calendar system from the epoch-day. * * @param epochDay the epoch day * @return the International Fixed local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public InternationalFixedDate dateEpochDay(long epochDay) { return InternationalFixedDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current International Fixed local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current International Fixed local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public InternationalFixedDate dateNow() { return InternationalFixedDate.now(); } /** * Obtains the current International Fixed local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current International Fixed local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public InternationalFixedDate dateNow(ZoneId zone) { return InternationalFixedDate.now(zone); } /** * Obtains the current International Fixed local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current International Fixed local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public InternationalFixedDate dateNow(Clock clock) { return InternationalFixedDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a International Fixed local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the International Fixed local date, not null * @throws DateTimeException if unable to create the date */ @Override public InternationalFixedDate date(TemporalAccessor temporal) { return InternationalFixedDate.from(temporal); } /** * Obtains a International Fixed local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the International Fixed local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a International Fixed zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the International Fixed zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a International Fixed zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the International Fixed zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* A leap-year is a year of a longer length than normal. * Leap years in the calendar system match those of the ISO calendar system. * * @param year the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long year) { return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0); } //----------------------------------------------------------------------- /** * Creates the chronology era object from the numeric value. *

* Only one era is supported, CE, with the value 1. * * @param eraValue the era value * @return the calendar system era, not null * @throws DateTimeException if unable to create the era */ @Override public InternationalFixedEra eraOf(int eraValue) { return InternationalFixedEra.of(eraValue); } /** * Gets the list of eras for the chronology. *

* Only one era is supported, CE, with the value 1. * * @return the list of eras for the chronology, may be immutable, not null */ @Override public List eras() { return Arrays.asList(InternationalFixedEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case ALIGNED_DAY_OF_WEEK_IN_YEAR: case ALIGNED_DAY_OF_WEEK_IN_MONTH: case DAY_OF_WEEK: return ValueRange.of(0, 1, 0, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(0, 1, 0, WEEKS_IN_MONTH); case ALIGNED_WEEK_OF_YEAR: return ValueRange.of(0, 1, 0, WEEKS_IN_YEAR); case DAY_OF_MONTH: return DAY_OF_MONTH_RANGE; case DAY_OF_YEAR: return ChronoField.DAY_OF_YEAR.range(); case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: case YEAR: return YEAR_RANGE; default: return field.range(); } } @Override public int prolepticYear(Era era, int yearOfEra) { if (!(era instanceof InternationalFixedEra)) { throw new ClassCastException("Invalid era: " + era); } YEAR_RANGE.checkValidIntValue(yearOfEra, ChronoField.YEAR_OF_ERA); return yearOfEra; } /** * Get the count of leap years since International fixed year 1. * * @param prolepticYear The year. * @return The number of leap years since International fixed year 1. */ static long getLeapYearsBefore(long prolepticYear) { long yearBefore = prolepticYear - 1; return (yearBefore / 4) - (yearBefore / 100) + (yearBefore / 400); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/InternationalFixedDate.java000066400000000000000000000762421343451174100313760ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static org.threeten.extra.chrono.InternationalFixedChronology.DAYS_0000_TO_1970; import static org.threeten.extra.chrono.InternationalFixedChronology.DAYS_IN_LONG_MONTH; import static org.threeten.extra.chrono.InternationalFixedChronology.DAYS_IN_MONTH; import static org.threeten.extra.chrono.InternationalFixedChronology.DAYS_IN_WEEK; import static org.threeten.extra.chrono.InternationalFixedChronology.DAYS_IN_YEAR; import static org.threeten.extra.chrono.InternationalFixedChronology.DAYS_PER_CYCLE; import static org.threeten.extra.chrono.InternationalFixedChronology.DAY_OF_MONTH_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.DAY_OF_YEAR_LEAP_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.DAY_OF_YEAR_NORMAL_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.EMPTY_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.EPOCH_DAY_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.ERA_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.INSTANCE; import static org.threeten.extra.chrono.InternationalFixedChronology.MONTHS_IN_YEAR; import static org.threeten.extra.chrono.InternationalFixedChronology.MONTH_OF_YEAR_RANGE; import static org.threeten.extra.chrono.InternationalFixedChronology.WEEKS_IN_MONTH; import static org.threeten.extra.chrono.InternationalFixedChronology.WEEKS_IN_YEAR; import static org.threeten.extra.chrono.InternationalFixedChronology.YEAR_RANGE; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; /** * A date in the International fixed calendar system. *

* Implements a pure International Fixed calendar (also known as the Cotsworth plan, the Eastman plan, * the 13 Month calendar or the Equal Month calendar) a solar calendar proposal for calendar reform designed by * Moses B. Cotsworth, who presented it in 1902. *

* It provides for a year of 13 months of 28 days each. * Month 6 has 29 days in a leap year, but the additional day is not part of any week. * Month 12 always has 29 days, but the additional day is not part of any week. * It is therefore a perennial calendar, with every date fixed always on the same weekday. * Though it was never officially adopted in any country, it was the official calendar of the Eastman Kodak Company * from 1928 to 1989. *

* This date operates using the {@linkplain InternationalFixedChronology International fixed calendar}. * This calendar system is a proposed reform calendar system, and is not in common use. * The International fixed differs from the Gregorian in terms of month count and length, and the leap year rule. * Dates are aligned such that {@code 0001/01/01 (International fixed)} is {@code 0001-01-01 (ISO)}. *

* More information is available in the * International Fixed Calendar * Wikipedia article. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class InternationalFixedDate extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -5501342824322148215L; /** * Leap Day as day-of-year */ private static final int LEAP_DAY_AS_DAY_OF_YEAR = 6 * DAYS_IN_MONTH + 1; /** * The proleptic year. */ private final int prolepticYear; /** * The month of the year. */ private final int month; /** * The day of the month. */ private final int day; /** * The day of year. */ private final transient int dayOfYear; /** * Is the proleptic year a Leap year ? */ private final transient boolean isLeapYear; /** * Is the day-of-year a Leap Day ? */ private final transient boolean isLeapDay; /** * Is the day-of-year a Year Day ? */ private final transient boolean isYearDay; //----------------------------------------------------------------------- /** * Obtains the current {@code InternationalFixedDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static InternationalFixedDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code InternationalFixedDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static InternationalFixedDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code InternationalFixedDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static InternationalFixedDate now(Clock clock) { LocalDate now = LocalDate.now(clock); return InternationalFixedDate.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code InternationalFixedDate} representing a date in the International fixed calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code InternationalFixedDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the International fixed proleptic-year * @param month the International fixed month-of-year, from 1 to 13 * @param dayOfMonth the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day) * @return the date in International fixed calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static InternationalFixedDate of(int prolepticYear, int month, int dayOfMonth) { return create(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Obtains a {@code InternationalFixedDate} from a temporal object. *

* This obtains a date in the International fixed calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code InternationalFixedDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code InternationalFixedDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in the International fixed calendar system, not null * @throws DateTimeException if unable to convert to a {@code InternationalFixedDate} */ public static InternationalFixedDate from(TemporalAccessor temporal) { if (temporal instanceof InternationalFixedDate) { return (InternationalFixedDate) temporal; } return InternationalFixedDate.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code InternationalFixedDate} representing a date in the International fixed calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code InternationalFixedDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the International fixed proleptic-year * @param dayOfYear the International fixed day-of-year, from 1 to 366 * @return the date in International fixed calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static InternationalFixedDate ofYearDay(int prolepticYear, int dayOfYear) { YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA); ChronoField.DAY_OF_YEAR.checkValidValue(dayOfYear); boolean isLeapYear = INSTANCE.isLeapYear(prolepticYear); int lastDoy = (DAYS_IN_YEAR + (isLeapYear ? 1 : 0)); if (dayOfYear > lastDoy) { throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' is not a leap year"); } if (dayOfYear == lastDoy) { return new InternationalFixedDate(prolepticYear, 13, 29); } if (dayOfYear == LEAP_DAY_AS_DAY_OF_YEAR && isLeapYear) { return new InternationalFixedDate(prolepticYear, 6, 29); } int doy0 = dayOfYear - 1; if (dayOfYear >= LEAP_DAY_AS_DAY_OF_YEAR && isLeapYear) { doy0--; } int month = (doy0 / DAYS_IN_MONTH) + 1; int day = (doy0 % DAYS_IN_MONTH) + 1; return new InternationalFixedDate(prolepticYear, month, day); } /** * Obtains a {@code InternationalFixedDate} representing a date in the International fixed calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in International fixed calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static InternationalFixedDate ofEpochDay(long epochDay) { EPOCH_DAY_RANGE.checkValidValue(epochDay, ChronoField.EPOCH_DAY); long zeroDay = epochDay + DAYS_0000_TO_1970; // The two values work great for any dates, just not the first (N/01/01) or the last of the year (N/0/0). long year = (400 * zeroDay) / DAYS_PER_CYCLE; long doy = zeroDay - (DAYS_IN_YEAR * year + InternationalFixedChronology.getLeapYearsBefore(year)); boolean isLeapYear = INSTANCE.isLeapYear(year); // In some cases, N/01/01 (January 1st) results in (N-1)/0/0, i.e. -1 day off. if (doy == (DAYS_IN_YEAR + 1) && !isLeapYear) { year += 1; doy = 1; } // In some cases, N/0/0 results in (N+1)/0/0 (rubbish), in a way +1 year off. if (doy == 0) { year -= 1; doy = DAYS_IN_YEAR + (isLeapYear ? 1 : 0); } return ofYearDay((int) year, (int) doy); } /** * Consistency check for dates manipulations after calls to * {@link #plus(long, TemporalUnit)}, * {@link #minus(long, TemporalUnit)}, * {@link #until(AbstractDate, TemporalUnit)} or * {@link #with(TemporalField, long)}. * * @param prolepticYear the International fixed proleptic-year * @param month the International fixed month, from 1 to 13 * @param day the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day) * @return the resolved date */ private static InternationalFixedDate resolvePreviousValid(int prolepticYear, int month, int day) { int monthR = Math.min(month, MONTHS_IN_YEAR); int dayR = Math.min(day, (monthR == 13 || (monthR == 6 && INSTANCE.isLeapYear(prolepticYear)) ? DAYS_IN_LONG_MONTH : DAYS_IN_MONTH)); return create(prolepticYear, monthR, dayR); } //----------------------------------------------------------------------- /** * Factory method, validates the given triplet year, month and dayOfMonth. * * @param prolepticYear the International fixed proleptic-year * @param month the International fixed month, from 1 to 13 * @param dayOfMonth the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day) * @return the International fixed date * @throws DateTimeException if the date is invalid */ static InternationalFixedDate create(int prolepticYear, int month, int dayOfMonth) { YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA); MONTH_OF_YEAR_RANGE.checkValidValue(month, ChronoField.MONTH_OF_YEAR); DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, ChronoField.DAY_OF_MONTH); if (dayOfMonth == DAYS_IN_LONG_MONTH && month != 6 && month != MONTHS_IN_YEAR) { throw new DateTimeException("Invalid date: " + prolepticYear + '/' + month + '/' + dayOfMonth); } if (month == 6 && dayOfMonth == DAYS_IN_LONG_MONTH && !INSTANCE.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid Leap Day as '" + prolepticYear + "' is not a leap year"); } return new InternationalFixedDate(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the International fixed proleptic-year * @param month the International fixed month, from 1 to 13 * @param dayOfMonth the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day) */ private InternationalFixedDate(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = month; this.day = dayOfMonth; this.isLeapYear = INSTANCE.isLeapYear(prolepticYear); this.isLeapDay = this.month == 6 && this.day == 29; this.isYearDay = this.month == 13 && this.day == 29; this.dayOfYear = ((month - 1) * DAYS_IN_MONTH + day) + (month > 6 && isLeapYear ? 1 : 0); } /** * * Validates the object. * * @return InternationalFixedDate the resolved date, not null */ private Object readResolve() { return InternationalFixedDate.of(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { return dayOfYear; } @Override int lengthOfYearInMonths() { return MONTHS_IN_YEAR; } @Override int getAlignedDayOfWeekInMonth() { return getDayOfWeek(); } @Override int getAlignedDayOfWeekInYear() { return getDayOfWeek(); } @Override int getAlignedWeekOfMonth() { if (isSpecialDay()) { return 0; } return ((day - 1) / DAYS_IN_WEEK) + 1; } @Override int getAlignedWeekOfYear() { if (isSpecialDay()) { return 0; } return (month - 1) * WEEKS_IN_MONTH + ((day - 1) / DAYS_IN_WEEK) + 1; } /** * Returns the day of the week represented by this date. *

* Leap Day and Year Day are not considered week-days, thus return 0. * * @return the day of the week: between 1 and 7, or 0 (Leap Day, Year Day) */ @Override int getDayOfWeek() { if (isSpecialDay()) { return 0; } return ((day - 1) % DAYS_IN_WEEK) + 1; } long getProlepticWeek() { return getProlepticMonth() * WEEKS_IN_MONTH + ((getDayOfMonth() - 1) / DAYS_IN_WEEK) - 1; } private boolean isSpecialDay() { return day == DAYS_IN_LONG_MONTH; } //----------------------------------------------------------------------- @Override public ValueRange range(TemporalField field) { if (field instanceof ChronoField) { if (isSupported(field)) { // day 29 is treated as being outside the normal week ChronoField f = (ChronoField) field; switch (f) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: case DAY_OF_WEEK: return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_MONTH: return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, WEEKS_IN_MONTH); case ALIGNED_WEEK_OF_YEAR: return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, WEEKS_IN_YEAR); case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth()); case DAY_OF_YEAR: return isLeapYear ? DAY_OF_YEAR_LEAP_RANGE : DAY_OF_YEAR_NORMAL_RANGE; case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; default: break; } } else { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } } return super.range(field); } @Override ValueRange rangeAlignedWeekOfMonth() { // never invoked return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, WEEKS_IN_MONTH); } @Override InternationalFixedDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the International fixed calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the International fixed chronology, not null */ @Override public InternationalFixedChronology getChronology() { return INSTANCE; } /** * Gets the era applicable at this date. *

* The International fixed calendar system only has one era, 'CE', * defined by {@link InternationalFixedEra}. * * @return the era applicable at this date, not null */ @Override public InternationalFixedEra getEra() { return InternationalFixedEra.CE; } /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * Month lengths do not match those of the ISO calendar system. *

* Months have 28 days, except June which has 29 in leap years * and December (month 13) which always has 29 days. * * @return the length of the month in days */ @Override public int lengthOfMonth() { return (isLongMonth() ? DAYS_IN_LONG_MONTH : DAYS_IN_MONTH); } private boolean isLongMonth() { return month == 13 || (month == 6 && isLeapYear); } /** * Returns the length of the year represented by this date. *

* This returns the length of the year in days. * Year lengths match those of the ISO calendar system. * * @return the length of the year in days: 365 or 366 */ @Override public int lengthOfYear() { return DAYS_IN_YEAR + (isLeapYear ? 1 : 0); } //------------------------------------------------------------------------- @Override public InternationalFixedDate with(TemporalAdjuster adjuster) { return (InternationalFixedDate) adjuster.adjustInto(this); } @Override public InternationalFixedDate with(TemporalField field, long newValue) { if (field instanceof ChronoField) { if (newValue == 0 && isSpecialDay()) { return this; } ChronoField f = (ChronoField) field; getChronology().range(f).checkValidValue(newValue, f); int nval = (int) newValue; switch (f) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: case DAY_OF_WEEK: if (newValue == 0 && !isSpecialDay()) { range(f).checkValidValue(newValue, field); } int dom = isSpecialDay() ? 21 : ((getDayOfMonth() - 1) / DAYS_IN_WEEK) * DAYS_IN_WEEK; return resolvePreviousValid(prolepticYear, month, dom + nval); case ALIGNED_WEEK_OF_MONTH: if (newValue == 0 && !isSpecialDay()) { range(f).checkValidValue(newValue, field); } int d = isSpecialDay() ? 1 : day % DAYS_IN_WEEK; return resolvePreviousValid(prolepticYear, month, (nval - 1) * DAYS_IN_WEEK + d); case ALIGNED_WEEK_OF_YEAR: if (newValue == 0 && !isSpecialDay()) { range(f).checkValidValue(newValue, field); } int newMonth = 1 + ((nval - 1) / WEEKS_IN_MONTH); int newDay = ((nval - 1) % WEEKS_IN_MONTH) * DAYS_IN_WEEK + 1 + ((day - 1) % DAYS_IN_WEEK); return resolvePreviousValid(prolepticYear, newMonth, newDay); case DAY_OF_MONTH: return create(prolepticYear, month, nval); default: break; } } return (InternationalFixedDate) super.with(field, newValue); } @Override InternationalFixedDate withDayOfYear(int value) { return ofYearDay(prolepticYear, value); } //----------------------------------------------------------------------- @Override public InternationalFixedDate plus(TemporalAmount amount) { return (InternationalFixedDate) amount.addTo(this); } @Override public InternationalFixedDate plus(long amountToAdd, TemporalUnit unit) { return (InternationalFixedDate) super.plus(amountToAdd, unit); } @Override InternationalFixedDate plusWeeks(long weeks) { if (weeks == 0) { return this; } if (weeks % WEEKS_IN_MONTH == 0) { return plusMonths(weeks / WEEKS_IN_MONTH); } long calcEm = Math.addExact(getProlepticWeek(), weeks); int newYear = Math.toIntExact(Math.floorDiv(calcEm, WEEKS_IN_YEAR)); int newWeek = Math.toIntExact(Math.floorMod(calcEm, WEEKS_IN_YEAR)); int newMonth = 1 + Math.floorDiv(newWeek, WEEKS_IN_MONTH); //int newDay = 1 + ((newWeek * DAYS_IN_WEEK + ((day - 1) % DAYS_IN_WEEK)) % DAYS_IN_MONTH); int newDay = 1 + ((newWeek * DAYS_IN_WEEK + 8 + (isLeapDay ? 0 : isYearDay ? -1 : (day - 1) % DAYS_IN_WEEK) - 1) % DAYS_IN_MONTH); return create(newYear, newMonth, newDay); } @Override InternationalFixedDate plusMonths(long months) { if (months == 0) { return this; } if (months % MONTHS_IN_YEAR == 0) { return plusYears(months / MONTHS_IN_YEAR); } int newMonth = (int) Math.addExact(getProlepticMonth(), months); int newYear = newMonth / MONTHS_IN_YEAR; newMonth = 1 + (newMonth % MONTHS_IN_YEAR); return resolvePreviousValid(newYear, newMonth, day); } @Override InternationalFixedDate plusYears(long yearsToAdd) { if (yearsToAdd == 0) { return this; } int newYear = YEAR_RANGE.checkValidIntValue(Math.addExact(prolepticYear, yearsToAdd), ChronoField.YEAR); return resolvePreviousValid(newYear, month, day); } @Override public InternationalFixedDate minus(TemporalAmount amount) { return (InternationalFixedDate) amount.subtractFrom(this); } @Override public InternationalFixedDate minus(long amountToSubtract, TemporalUnit unit) { return (InternationalFixedDate) super.minus(amountToSubtract, unit); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return until(InternationalFixedDate.from(endExclusive), unit); } /** * Get the number of years from this date to the given day. * * @param end The end date. * @return The number of years from this date to the given day. */ long yearsUntil(InternationalFixedDate end) { long startYear = this.prolepticYear * 512L + this.getInternalDayOfYear(); long endYear = end.prolepticYear * 512L + end.getInternalDayOfYear(); return (endYear - startYear) / 512L; } /** * For calculation purposes in a leap year, decrement the day of the year for months 7 and higher - including Year Day. * Leave out Leap Day though! * * @return int day of the year for calculations */ private int getInternalDayOfYear() { return isLeapYear && (month > 6) ? dayOfYear - 1 : dayOfYear; } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { InternationalFixedDate end = InternationalFixedDate.from(endDateExclusive); int years = Math.toIntExact(yearsUntil(end)); // Get to the same "whole" year. InternationalFixedDate sameYearEnd = plusYears(years); int months = (int) sameYearEnd.monthsUntil(end); int days = (int) sameYearEnd.plusMonths(months).daysUntil(end); // When both Leap Day and Year Day start / end the period, the intra-month difference can be +- 28 days, // because internally day-of-month as 1 (Leap Day) or 29 (Year Day) for calculations. // Thus we have to compensate the difference accordingly. if ((!isYearDay && !isLeapDay) && !(end.isYearDay && !end.isLeapDay)) { if (days == DAYS_IN_MONTH) { days = 0; months += 1; } if (days == -DAYS_IN_MONTH) { days = 0; months -= 1; } } return getChronology().period(years, months, days); } @Override long weeksUntil(AbstractDate end) { InternationalFixedDate endDate = InternationalFixedDate.from(end); int offset = (this.day < 1 || endDate.day < 1) && (this.day != endDate.day) && this.isLeapYear && endDate.isLeapYear ? (this.isBefore(endDate) ? 1 : -1) : 0; long startWeek = this.getProlepticWeek() * 8L + this.getDayOfWeek(); long endWeek = endDate.getProlepticWeek() * 8L + end.getDayOfWeek(); return (endWeek - startWeek - offset) / 8L; } @Override long monthsUntil(AbstractDate end) { InternationalFixedDate date = InternationalFixedDate.from(end); long monthStart = this.getProlepticMonth() * 32L + this.getDayOfMonth(); long monthEnd = date.getProlepticMonth() * 32L + date.getDayOfMonth(); return (monthEnd - monthStart) / 32L; } //----------------------------------------------------------------------- @Override public long toEpochDay() { long epochDay = ((long) this.prolepticYear) * DAYS_IN_YEAR + InternationalFixedChronology.getLeapYearsBefore(this.prolepticYear) + this.dayOfYear; return epochDay - DAYS_0000_TO_1970; } /** * Display the date in human-readable format. * * @return the string representation */ @Override public String toString() { StringBuilder buf = new StringBuilder(30); return buf.append(getChronology().toString()) .append(' ') .append(getEra()) .append(' ') .append(getYearOfEra()) .append(this.month < 10 && this.month > 0 ? "/0" : '/') .append(this.month) .append(this.day < 10 ? "/0" : '/') .append(this.day) .toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/InternationalFixedEra.java000066400000000000000000000066721343451174100312300ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the International Fixed calendar system. *

* The International Fixed calendar system only has one era. * The current era, for years from 1 onwards, is known as 'Current Era'. * All previous years are invalid. *

* The start of the International Fixed epoch {@code 0001/01/01 (IFC)} is {@code 0001-01-01 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code InternationalFixedEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum InternationalFixedEra implements Era { /** * The singleton instance for the current era, 'Current Era', * which has the numeric value 1. */ CE; //----------------------------------------------------------------------- /** * Obtains an instance of {@code InternationalFixedEra} from an {@code int} value. *

* {@code InternationalFixedEra} is an enum representing the International Fixed era of CE. * This factory allows the enum to be obtained from the {@code int} value. * * @param era the CE value to represent, only 1 (CE) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static InternationalFixedEra of(final int era) { if (era == 1) { return CE; } throw new DateTimeException("Invalid era: " + era); } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era CE has the value 1. * * @return the era value, only 1 (CE) */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/JulianChronology.java000066400000000000000000000352401343451174100302700ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; import java.util.Map; /** * The Julian calendar system. *

* This chronology defines the rules of the proleptic Julian calendar system. * This calendar system is the forerunner to the modern Gregorian and ISO calendars. * The Julian differs from the Gregorian only in terms of the leap year rule. * Dates are aligned such that {@code 0001-01-01 (Julian)} is {@code 0000-12-30 (ISO)}. *

* This class is proleptic. It implements Julian rules to the entire time-line. *

* This class implements a calendar where January 1st is the start of the year. * The history of the start of the year is complex and using the current standard * is the most consistent. *

* The fields are defined as follows: *

    *
  • era - There are two eras, the current 'Anno Domini' (AD) and the previous era 'Before Christ' (BC). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. * For the previous era the year increases from one as time goes backwards. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the * current era. For the previous era, years have zero, then negative values. *
  • month-of-year - There are 12 months in a Julian year, numbered from 1 to 12. *
  • day-of-month - There are between 28 and 31 days in each Julian month, numbered from 1 to 31. * Months 4, 6, 9 and 11 have 30 days, Months 1, 3, 5, 7, 8, 10 and 12 have 31 days. * Month 2 has 28 days, or 29 in a leap year. *
  • day-of-year - There are 365 days in a standard Julian year and 366 in a leap year. * The days are numbered from 1 to 365 or 1 to 366. *
  • leap-year - Leap years occur every 4 years. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class JulianChronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Julian chronology. */ public static final JulianChronology INSTANCE = new JulianChronology(); /** * Serialization version. */ private static final long serialVersionUID = 7291205177830286973L; /** * Range of proleptic-year. */ static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999); /** * Range of year. */ static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999); /** * Range of proleptic month. */ static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998 * 12L, 999_999 * 12L + 11); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public JulianChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Julian'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Julian' * @see #getCalendarType() */ @Override public String getId() { return "Julian"; } /** * Gets the calendar type of the underlying calendar system - 'julian'. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for the Julian calendar, but were it to * do so, 'julian' is highly likely to be chosen. * * @return the calendar system type - 'julian' * @see #getId() */ @Override public String getCalendarType() { return "julian"; } //----------------------------------------------------------------------- /** * Obtains a local date in Julian calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Julian era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Julian local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code JulianEra} */ @Override public JulianDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Julian calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Julian local date, not null * @throws DateTimeException if unable to create the date */ @Override public JulianDate date(int prolepticYear, int month, int dayOfMonth) { return JulianDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Julian calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Julian era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Julian local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code JulianEra} */ @Override public JulianDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Julian calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Julian local date, not null * @throws DateTimeException if unable to create the date */ @Override public JulianDate dateYearDay(int prolepticYear, int dayOfYear) { return JulianDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Julian calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Julian local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public JulianDate dateEpochDay(long epochDay) { return JulianDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Julian local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Julian local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public JulianDate dateNow() { return JulianDate.now(); } /** * Obtains the current Julian local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Julian local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public JulianDate dateNow(ZoneId zone) { return JulianDate.now(zone); } /** * Obtains the current Julian local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Julian local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public JulianDate dateNow(Clock clock) { return JulianDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Julian local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Julian local date, not null * @throws DateTimeException if unable to create the date */ @Override public JulianDate date(TemporalAccessor temporal) { return JulianDate.from(temporal); } /** * Obtains a Julian local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Julian local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Julian zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Julian zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Julian zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Julian zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* A Julian proleptic-year is leap if the remainder after division by four equals zero. * This method does not validate the year passed in, and only has a * well-defined result for years in the supported range. * * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long prolepticYear) { return (prolepticYear % 4) == 0; } @Override public int prolepticYear(Era era, int yearOfEra) { if (era instanceof JulianEra == false) { throw new ClassCastException("Era must be JulianEra"); } return (era == JulianEra.AD ? yearOfEra : 1 - yearOfEra); } @Override public JulianEra eraOf(int eraValue) { return JulianEra.of(eraValue); } @Override public List eras() { return Arrays.asList(JulianEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: return YOE_RANGE; case YEAR: return YEAR_RANGE; default: break; } return field.range(); } //----------------------------------------------------------------------- @Override // override for return type public JulianDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) { return (JulianDate) super.resolveDate(fieldValues, resolverStyle); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/JulianDate.java000066400000000000000000000422311343451174100270200ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.Month; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.ValueRange; /** * A date in the Julian calendar system. *

* This date operates using the {@linkplain JulianChronology Julian calendar}. * This calendar system is the forerunner to the modern Gregorian and ISO calendars. * The Julian differs from the Gregorian only in terms of the leap year rule. * Dates are aligned such that {@code 0001-01-01 (Julian)} is {@code 0000-12-30 (ISO)}. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class JulianDate extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -7920528871688876868L; /** * The difference between the ISO and Julian epoch day count (Julian 0001-01-01 to ISO 1970-01-01). */ private static final int JULIAN_0001_TO_ISO_1970 = 678577 + 40587; // MJD values /** * The days per 4 year cycle. */ private static final int DAYS_PER_CYCLE = (365 * 4) + 1; /** * The proleptic year. */ private final int prolepticYear; /** * The month. */ private final short month; /** * The day. */ private final short day; //----------------------------------------------------------------------- /** * Obtains the current {@code JulianDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static JulianDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code JulianDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static JulianDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code JulianDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static JulianDate now(Clock clock) { LocalDate now = LocalDate.now(clock); return JulianDate.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code JulianDate} representing a date in the Julian calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code JulianDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the Julian proleptic-year * @param month the Julian month-of-year, from 1 to 12 * @param dayOfMonth the Julian day-of-month, from 1 to 31 * @return the date in Julian calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static JulianDate of(int prolepticYear, int month, int dayOfMonth) { return JulianDate.create(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code JulianDate} from a temporal object. *

* This obtains a date in the Julian calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code JulianDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code JulianDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in Julian calendar system, not null * @throws DateTimeException if unable to convert to a {@code JulianDate} */ public static JulianDate from(TemporalAccessor temporal) { if (temporal instanceof JulianDate) { return (JulianDate) temporal; } return JulianDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code JulianDate} representing a date in the Julian calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code JulianDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Julian proleptic-year * @param dayOfYear the Julian day-of-year, from 1 to 366 * @return the date in Julian calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static JulianDate ofYearDay(int prolepticYear, int dayOfYear) { JulianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); DAY_OF_YEAR.checkValidValue(dayOfYear); boolean leap = JulianChronology.INSTANCE.isLeapYear(prolepticYear); if (dayOfYear == 366 && leap == false) { throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' is not a leap year"); } Month moy = Month.of((dayOfYear - 1) / 31 + 1); int monthEnd = moy.firstDayOfYear(leap) + moy.length(leap) - 1; if (dayOfYear > monthEnd) { moy = moy.plus(1); } int dom = dayOfYear - moy.firstDayOfYear(leap) + 1; return new JulianDate(prolepticYear, moy.getValue(), dom); } /** * Obtains a {@code JulianDate} representing a date in the Julian calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in Julian calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static JulianDate ofEpochDay(final long epochDay) { EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds // use of Julian 0001 makes leap year at end of cycle long julianEpochDay = epochDay + JULIAN_0001_TO_ISO_1970; long cycle = Math.floorDiv(julianEpochDay, DAYS_PER_CYCLE); long daysInCycle = Math.floorMod(julianEpochDay, DAYS_PER_CYCLE); if (daysInCycle == DAYS_PER_CYCLE - 1) { int year = (int) ((cycle * 4 + 3) + 1); return ofYearDay(year, 366); } int year = (int) ((cycle * 4 + daysInCycle / 365) + 1); int doy = (int) ((daysInCycle % 365) + 1); return ofYearDay(year, doy); } private static JulianDate resolvePreviousValid(int prolepticYear, int month, int day) { switch (month) { case 2: day = Math.min(day, JulianChronology.INSTANCE.isLeapYear(prolepticYear) ? 29 : 28); break; case 4: case 6: case 9: case 11: day = Math.min(day, 30); break; default: break; } return new JulianDate(prolepticYear, month, day); } /** * Creates a {@code JulianDate} validating the input. * * @param prolepticYear the Julian proleptic-year * @param month the Julian month-of-year, from 1 to 12 * @param dayOfMonth the Julian day-of-month, from 1 to 31 * @return the date in Julian calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the month-year */ static JulianDate create(int prolepticYear, int month, int dayOfMonth) { JulianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR); MONTH_OF_YEAR.checkValidValue(month); DAY_OF_MONTH.checkValidValue(dayOfMonth); if (dayOfMonth > 28) { int dom = 31; switch (month) { case 2: dom = (JulianChronology.INSTANCE.isLeapYear(prolepticYear) ? 29 : 28); break; case 4: case 6: case 9: case 11: dom = 30; break; default: break; } if (dayOfMonth > dom) { if (dayOfMonth == 29) { throw new DateTimeException("Invalid date 'February 29' as '" + prolepticYear + "' is not a leap year"); } else { throw new DateTimeException("Invalid date '" + Month.of(month).name() + " " + dayOfMonth + "'"); } } } return new JulianDate(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Julian proleptic-year * @param month the Julian month, from 1 to 12 * @param dayOfMonth the Julian day-of-month, from 1 to 31 */ private JulianDate(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = (short) month; this.day = (short) dayOfMonth; } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return JulianDate.create(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { return Month.of(month).firstDayOfYear(isLeapYear()) + day - 1; } @Override ValueRange rangeAlignedWeekOfMonth() { return ValueRange.of(1, month == 2 && isLeapYear() == false ? 4 : 5); } @Override JulianDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Julian calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Julian chronology, not null */ @Override public JulianChronology getChronology() { return JulianChronology.INSTANCE; } /** * Gets the era applicable at this date. *

* The Julian calendar system has two eras, 'AD' and 'BC', * defined by {@link JulianEra}. * * @return the era applicable at this date, not null */ @Override public JulianEra getEra() { return (prolepticYear >= 1 ? JulianEra.AD : JulianEra.BC); } /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * Month lengths match those of the ISO calendar system. * * @return the length of the month in days, from 28 to 31 */ @Override public int lengthOfMonth() { switch (month) { case 2: return (isLeapYear() ? 29 : 28); case 4: case 6: case 9: case 11: return 30; default: return 31; } } //------------------------------------------------------------------------- @Override public JulianDate with(TemporalAdjuster adjuster) { return (JulianDate) adjuster.adjustInto(this); } @Override public JulianDate with(TemporalField field, long newValue) { return (JulianDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public JulianDate plus(TemporalAmount amount) { return (JulianDate) amount.addTo(this); } @Override public JulianDate plus(long amountToAdd, TemporalUnit unit) { return (JulianDate) super.plus(amountToAdd, unit); } @Override public JulianDate minus(TemporalAmount amount) { return (JulianDate) amount.subtractFrom(this); } @Override public JulianDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return super.until(JulianDate.from(endExclusive), unit); } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { return super.doUntil(JulianDate.from(endDateExclusive)); } //----------------------------------------------------------------------- @Override public long toEpochDay() { long year = (long) prolepticYear; long julianEpochDay = ((year - 1) * 365) + Math.floorDiv((year - 1), 4) + (getDayOfYear() - 1); return julianEpochDay - JULIAN_0001_TO_ISO_1970; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/JulianEra.java000066400000000000000000000073441343451174100266600ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the Julian calendar system. *

* The Julian calendar system has two eras. * The current era, for years from 1 onwards, is known as 'Anno Domini'. * All previous years, zero or earlier in the proleptic count or one and greater * in the year-of-era count, are part of the 'Before Christ' era. *

* The start of the Julian epoch {@code 0001-01-01 (Julian)} is {@code 0000-12-30 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code JulianEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum JulianEra implements Era { /** * The singleton instance for the era before the current one, 'Before Christ', * which has the numeric value 0. */ BC, /** * The singleton instance for the current era, 'Anno Domini', * which has the numeric value 1. */ AD; //----------------------------------------------------------------------- /** * Obtains an instance of {@code JulianEra} from an {@code int} value. *

* {@code JulianEra} is an enum representing the Julian eras of BC/AD. * This factory allows the enum to be obtained from the {@code int} value. * * @param era the BC/AD value to represent, from 0 (BC) to 1 (AD) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static JulianEra of(int era) { switch (era) { case 0: return BC; case 1: return AD; default: throw new DateTimeException("Invalid era: " + era); } } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era BC has the value 0, while the era AD has the value 1. * * @return the era value, from 0 (BC) to 1 (AD) */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/PaxChronology.java000066400000000000000000000403561343451174100276020ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; import java.util.Map; /** * The Pax calendar system. *

* This chronology defines the rules of the proleptic Pax calendar system. * This calendar system is a proposed reform calendar system, and is not in common use. * The Pax differs from the Gregorian in terms of month count and length, and the leap year rule. * Dates are aligned such that {@code 0001-01-01 (Pax)} is {@code 0000-12-31 (ISO)}. *

* This class is proleptic. It implements Pax rules to the entire time-line. *

* The fields are defined as follows: *

    *
  • era - There are two eras, the current 'Current Era' (CE) and the previous era 'Before Current Era' (BCE). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. * For the previous era the year increases from one as time goes backwards. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the * current era. For the previous era, years have zero, then negative values. *
  • month-of-year - There are 13 or 14 months in a Pax year, numbered from 1 to 13 (or 14). *
  • day-of-month - There are 28 days in each Pax month, numbered from 1 to 28. * In a leap year a one-week month ('Pax') is inserted between months 12 and 13, shifting 13 to 14. *
  • day-of-year - There are 364 days in a standard Pax year and 371 in a leap year. * The days are numbered from 1 to 364 or 1 to 371. *
  • leap-year - Leap years occur in every year whose last two digits are divisible by {@code 6}, are {@code 99}, or are {@code 00} and the year is not divisible by 400. *
*

* For more information, please read the Pax Calendar Wikipedia article. * *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class PaxChronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Pax chronology. */ public static final PaxChronology INSTANCE = new PaxChronology(); /** * Serialization version. */ private static final long serialVersionUID = -7021464635577802085L; /** * The leap-month of Pax is only one week long. */ static final int WEEKS_IN_LEAP_MONTH = 1; /** * Standard 7-day week. */ static final int DAYS_IN_WEEK = 7; /** * In all months (except Pax), there are 4 complete weeks. */ static final int WEEKS_IN_MONTH = 4; /** * There are 13 months in a (non-leap) year. */ static final int MONTHS_IN_YEAR = 13; /** * There are 4 weeks of 7 days, or 28 total days in a month. */ static final int DAYS_IN_MONTH = WEEKS_IN_MONTH * DAYS_IN_WEEK; /** * There are 13 months of 28 days, or 364 days in a (non-leap) year. */ static final int DAYS_IN_YEAR = MONTHS_IN_YEAR * DAYS_IN_MONTH; /** * There are 52 weeks in a (non-leap) year. */ static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK; /** * Range of aligned week of month. */ static final ValueRange ALIGNED_WEEK_OF_MONTH_RANGE = ValueRange.of(1, WEEKS_IN_LEAP_MONTH, WEEKS_IN_MONTH); /** * Range of aligned week of year. */ static final ValueRange ALIGNED_WEEK_OF_YEAR_RANGE = ValueRange.of(1, WEEKS_IN_YEAR, WEEKS_IN_YEAR + 1); /** * Range of day of month. */ static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_WEEK, DAYS_IN_MONTH); /** * Range of day of year. */ static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, DAYS_IN_YEAR, DAYS_IN_YEAR + DAYS_IN_WEEK); /** * Range of month of year. */ static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR, MONTHS_IN_YEAR + 1); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public PaxChronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ @SuppressWarnings("static-method") private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Pax'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Pax' * @see #getCalendarType() */ @Override public String getId() { return "Pax"; } /** * Gets the calendar type of the underlying calendar system - 'pax'. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for the Pax calendar, but were it to * do so, 'pax' is highly likely to be chosen. * * @return the calendar system type - 'pax' * @see #getId() */ @Override public String getCalendarType() { return "pax"; } //----------------------------------------------------------------------- /** * Obtains a local date in Pax calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Pax era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Pax local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code PaxEra} */ @Override public PaxDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Pax calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Pax local date, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate date(int prolepticYear, int month, int dayOfMonth) { return PaxDate.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Pax calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Pax era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Pax local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code PaxEra} */ @Override public PaxDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Pax calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Pax local date, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate dateYearDay(int prolepticYear, int dayOfYear) { return PaxDate.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Pax calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Pax local date, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate dateEpochDay(long epochDay) { return PaxDate.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Pax local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Pax local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate dateNow() { return PaxDate.now(); } /** * Obtains the current Pax local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the ZoneId to use, not null * @return the current Pax local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate dateNow(ZoneId zone) { return PaxDate.now(zone); } /** * Obtains the current Pax local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Pax local date, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate dateNow(Clock clock) { return PaxDate.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Pax local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Pax local date, not null * @throws DateTimeException if unable to create the date */ @Override public PaxDate date(TemporalAccessor temporal) { return PaxDate.from(temporal); } /** * Obtains a Pax local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Pax local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Pax zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Pax zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Pax zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Pax zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* In general, a year is a leap year if the last two digits are divisible by 6 without remainder, or are 99. * Years with the last two digits of 00 are also leap years, with the exception of years divisible by 400, which are not. *

* For example, 2012 is a leap year because the last two digits (12) are divisible by 6. * 1999 is a leap year as the last two digits are both 9's (99). * 1900 is a leap year as the last two digits are both 0's (00), however 2000 was not a leap year as it is divisible by 400. * The year 0 is not a leap year. * * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long prolepticYear) { long lastTwoDigits = prolepticYear % 100; return Math.abs(lastTwoDigits) == 99 || (prolepticYear % 400 != 0 && (lastTwoDigits == 0 || lastTwoDigits % 6 == 0)); } @Override public int prolepticYear(Era era, int yearOfEra) { if (!(era instanceof PaxEra)) { throw new ClassCastException("Era must be PaxEra"); } return (era == PaxEra.CE ? yearOfEra : 1 - yearOfEra); } @Override public PaxEra eraOf(int eraValue) { return PaxEra.of(eraValue); } @Override public List eras() { return Arrays.asList(PaxEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case ALIGNED_WEEK_OF_MONTH: return ALIGNED_WEEK_OF_MONTH_RANGE; case ALIGNED_WEEK_OF_YEAR: return ALIGNED_WEEK_OF_YEAR_RANGE; case DAY_OF_MONTH: return DAY_OF_MONTH_RANGE; case DAY_OF_YEAR: return DAY_OF_YEAR_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; default: return field.range(); } } //----------------------------------------------------------------------- @Override public PaxDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) { return (PaxDate) super.resolveDate(fieldValues, resolverStyle); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/PaxDate.java000066400000000000000000000717741343451174100263440ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import static org.threeten.extra.chrono.PaxChronology.DAYS_IN_MONTH; import static org.threeten.extra.chrono.PaxChronology.DAYS_IN_WEEK; import static org.threeten.extra.chrono.PaxChronology.DAYS_IN_YEAR; import static org.threeten.extra.chrono.PaxChronology.MONTHS_IN_YEAR; import static org.threeten.extra.chrono.PaxChronology.WEEKS_IN_LEAP_MONTH; import static org.threeten.extra.chrono.PaxChronology.WEEKS_IN_MONTH; import static org.threeten.extra.chrono.PaxChronology.WEEKS_IN_YEAR; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.ValueRange; /** * A date in the Pax calendar system. *

* This date operates using the {@linkplain PaxChronology Pax calendar}. * This calendar system is a proposed reform calendar system, and is not in common use. * The Pax differs from the Gregorian in terms of month count and length, and the leap year rule. * Dates are aligned such that {@code 0001-01-01 (Pax)} is {@code 0000-12-31 (ISO)}. *

* More information is available in the Pax Calendar Wikipedia article. * *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class PaxDate extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -2229133057743750072L; /** * The difference between the ISO and Pax epoch day count (Pax 0001-01-01 to ISO 1970-01-01). */ private static final int PAX_0001_TO_ISO_1970 = 719163; /** * The days per 400 year cycle. */ private static final int DAYS_PER_LONG_CYCLE = (DAYS_IN_YEAR * 400) + (DAYS_IN_WEEK * 71); /** * The days per 100 year cycle. */ private static final int DAYS_PER_CYCLE = (DAYS_IN_YEAR * 100) + (DAYS_IN_WEEK * 18); /** * The days per 6 year cycle. */ private static final int DAYS_PER_SIX_CYCLE = (DAYS_IN_YEAR * 6) + (DAYS_IN_WEEK * 1); /** * Number of years in a decade. */ private static final int YEARS_IN_DECADE = 10; /** * Number of years in a century. */ private static final int YEARS_IN_CENTURY = 100; /** * Number of years in a millennium. */ private static final int YEARS_IN_MILLENNIUM = 1000; /** * The proleptic year. */ private final int prolepticYear; /** * The month. */ private final short month; /** * The day. */ private final short day; //----------------------------------------------------------------------- /** * Obtains the current {@code PaxDate} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static PaxDate now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code PaxDate} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static PaxDate now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code PaxDate} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static PaxDate now(Clock clock) { LocalDate now = LocalDate.now(clock); return PaxDate.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code PaxDate} representing a date in the Pax calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code PaxDate} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the Pax proleptic-year * @param month the Pax month-of-year, from 1 to 14 * @param dayOfMonth the Pax day-of-month, from 1 to 28 * @return the date in Pax calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static PaxDate of(int prolepticYear, int month, int dayOfMonth) { YEAR.checkValidValue(prolepticYear); PaxChronology.MONTH_OF_YEAR_RANGE.checkValidValue(month, MONTH_OF_YEAR); PaxChronology.DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH); if (month == MONTHS_IN_YEAR + 1 && !PaxChronology.INSTANCE.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid month 14 as " + prolepticYear + "is not a leap year"); } if (dayOfMonth > DAYS_IN_WEEK && month == MONTHS_IN_YEAR && PaxChronology.INSTANCE.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid date during Pax as " + prolepticYear + " is a leap year"); } return new PaxDate(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code PaxDate} from a temporal object. *

* This obtains a date in the Pax calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code PaxDate}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code PaxDate::from}. * * @param temporal the temporal object to convert, not null * @return the date in the Pax calendar system, not null * @throws DateTimeException if unable to convert to a {@code PaxDate} */ public static PaxDate from(TemporalAccessor temporal) { if (temporal instanceof PaxDate) { return (PaxDate) temporal; } return PaxDate.ofEpochDay(temporal.getLong(EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code PaxDate} representing a date in the Pax calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code PaxDate} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Pax proleptic-year * @param dayOfYear the Pax day-of-year, from 1 to 371 * @return the date in Pax calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static PaxDate ofYearDay(int prolepticYear, int dayOfYear) { YEAR.checkValidValue(prolepticYear); PaxChronology.DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, DAY_OF_YEAR); boolean leap = PaxChronology.INSTANCE.isLeapYear(prolepticYear); if (dayOfYear > DAYS_IN_YEAR && !leap) { throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year"); } int month = ((dayOfYear - 1) / DAYS_IN_MONTH) + 1; // In leap years, the leap-month is shorter than the following month, so needs to be adjusted. if (leap && month == MONTHS_IN_YEAR && dayOfYear >= (DAYS_IN_YEAR + DAYS_IN_WEEK) - DAYS_IN_MONTH + 1) { month++; } // Subtract days-at-start-of-month from days in year int dayOfMonth = dayOfYear - (month - 1) * DAYS_IN_MONTH; // Adjust for shorter inserted leap-month. if (month == MONTHS_IN_YEAR + 1) { dayOfMonth += (DAYS_IN_MONTH - DAYS_IN_WEEK); } return of(prolepticYear, month, dayOfMonth); } /** * Obtains a {@code PaxDate} representing a date in the Pax calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) * @return the date in Pax calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static PaxDate ofEpochDay(long epochDay) { EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // use of Pax 0001 makes non-leap century at end of (long) cycle. long paxEpochDay = epochDay + PAX_0001_TO_ISO_1970; int longCycle = (int) Math.floorDiv(paxEpochDay, DAYS_PER_LONG_CYCLE); int cycle = (int) (paxEpochDay - longCycle * DAYS_PER_LONG_CYCLE) / DAYS_PER_CYCLE; int dayOfCycle = (int) Math.floorMod(paxEpochDay - longCycle * DAYS_PER_LONG_CYCLE, DAYS_PER_CYCLE); if (dayOfCycle >= DAYS_PER_CYCLE - DAYS_IN_YEAR - DAYS_IN_WEEK) { // Is in the century year int dayOfYear = dayOfCycle - (DAYS_PER_CYCLE - DAYS_IN_YEAR - DAYS_IN_WEEK) + 1; return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + YEARS_IN_CENTURY, dayOfYear); } // For negative years, the cycle of leap years runs the other direction for 99s and 6s. if (paxEpochDay >= 0) { if (dayOfCycle >= DAYS_PER_CYCLE - 2 * DAYS_IN_YEAR - 2 * DAYS_IN_WEEK) { // Is in the '99 year int dayOfYear = dayOfCycle - (DAYS_PER_CYCLE - 2 * DAYS_IN_YEAR - 2 * DAYS_IN_WEEK) + 1; return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + (YEARS_IN_CENTURY - 1), dayOfYear); } // Otherwise, part of the regular 6-year cycle. int sixCycle = dayOfCycle / DAYS_PER_SIX_CYCLE; int dayOfSixCycle = dayOfCycle % DAYS_PER_SIX_CYCLE; int year = dayOfSixCycle / DAYS_IN_YEAR + 1; int dayOfYear = dayOfSixCycle % DAYS_IN_YEAR + 1; if (year == 7) { year--; dayOfYear += DAYS_IN_YEAR; } return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + sixCycle * 6 + year, dayOfYear); } else { if (dayOfCycle < DAYS_IN_YEAR + DAYS_IN_WEEK) { // -'99 year is at _start_ of cycle (first year encountered). return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + 1, dayOfCycle + 1); } // Otherwise, part of the regular 6-year cycle, but offset -'96 to be end of six-year-cycle first. int offsetCycle = dayOfCycle + 2 * DAYS_IN_YEAR - DAYS_IN_WEEK; int sixCycle = offsetCycle / DAYS_PER_SIX_CYCLE; int dayOfSixCycle = offsetCycle % DAYS_PER_SIX_CYCLE; int year = dayOfSixCycle / DAYS_IN_YEAR + 1; int dayOfYear = dayOfSixCycle % DAYS_IN_YEAR + 1; if (year == 7) { year--; dayOfYear += DAYS_IN_YEAR; } return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY - 2 + (sixCycle * 6 + year), dayOfYear); } } private static PaxDate resolvePreviousValid(int prolepticYear, int month, int day) { int monthR = Math.min(month, MONTHS_IN_YEAR + (PaxChronology.INSTANCE.isLeapYear(prolepticYear) ? 1 : 0)); int dayR = Math.min(day, month == MONTHS_IN_YEAR && PaxChronology.INSTANCE.isLeapYear(prolepticYear) ? DAYS_IN_WEEK : DAYS_IN_MONTH); return PaxDate.of(prolepticYear, monthR, dayR); } /** * Get the count of leap months since proleptic month 0. *

* This number is negative if the month is prior to Pax year 0. **

* Remember that if using this for things like turning months into days, you must first subtract this number from the proleptic month count. * * @param prolepticMonth The month. * @return The number of leap months since proleptic month 0. */ private static long getLeapMonthsBefore(long prolepticMonth) { long offsetMonth = prolepticMonth - (prolepticMonth <= 0 ? 13 : 13 - 1); // First, see getLeapYearsBefore(...) for explanations. // return 18 * Math.floorDiv(offsetMonth, 100 * MONTHS_IN_YEAR + (getLeapYearsBefore(100) + 1)) // - Math.floorDiv(offsetMonth, 400 * MONTHS_IN_YEAR + (getLeapYearsBefore(400) + 1)) // + (((Math.floorMod(offsetMonth, 100 * MONTHS_IN_YEAR + (getLeapYearsBefore(100) + 1)) - (offsetMonth <= 0 ? 100 * MONTHS_IN_YEAR + getLeapYearsBefore(100) : 0)) // / (99 * MONTHS_IN_YEAR + (getLeapYearsBefore(99) + 1))) + (offsetMonth <= 0 ? 1 : 0)) // + (Math.floorMod(offsetMonth, 100 * MONTHS_IN_YEAR + (getLeapYearsBefore(100) + 1)) + (offsetMonth <= 0 ? 2 * MONTHS_IN_YEAR - 1 : 0)) / (6 * MONTHS_IN_YEAR + 1); return 18L * Math.floorDiv(offsetMonth, 1318) - Math.floorDiv(offsetMonth, 5272) + (((Math.floorMod(offsetMonth, 1318) - (offsetMonth <= 0 ? 1317 : 0)) / 1304) + (offsetMonth <= 0 ? 1 : 0)) + (Math.floorMod(offsetMonth, 1318) + (offsetMonth <= 0 ? 25 : 0)) / 79; } /** * Get the count of leap years since Pax year 0. *

* This number is negative if the year is prior to Pax year 0. * * @param prolepticYear The year. * @return The number of leap years since Pax year 0. */ private static long getLeapYearsBefore(long prolepticYear) { // Some explanations are in order: // - First, because this calculates "leap years from 0 to the start of the year", // The 'current' year must be counted when the year is negative (hence Math.floorDiv(...)). // - However, this means that there's a negative count which must be offset for the in-century years, // which still occur at the "last two digits divisible by 6" (or 99) point. // - Math.floorMod(...) returns a nicely positive result for negative years, counting 'down', but // thus needs to be offset to make sure the first leap year is -6, and not -4... // The second line, which calculates the '99 occurrences, runs "backwards", so must first be reversed, then the results flipped. return 18L * Math.floorDiv(prolepticYear - 1, YEARS_IN_CENTURY) - Math.floorDiv(prolepticYear - 1, 4 * YEARS_IN_CENTURY) + (Math.floorMod(prolepticYear - 1, 100) - (prolepticYear <= 0 ? 99 : 0)) / 99 + (prolepticYear <= 0 ? 1 : 0) + ((Math.floorMod(prolepticYear - 1, 100) + (prolepticYear <= 0 ? 2 : 0)) / 6); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Pax proleptic-year * @param month the Pax month, from 1 to 14 * @param dayOfMonth the Pax day-of-month, from 1 to 28 */ private PaxDate(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = (short) month; this.day = (short) dayOfMonth; } /** * Validates the object. * * @return the resolved date, not null */ private Object readResolve() { return PaxDate.of(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { return (month - 1) * DAYS_IN_MONTH - (month == MONTHS_IN_YEAR + 1 ? DAYS_IN_MONTH - DAYS_IN_WEEK : 0) + getDayOfMonth(); } @Override int lengthOfYearInMonths() { return MONTHS_IN_YEAR + (isLeapYear() ? 1 : 0); } @Override ValueRange rangeAlignedWeekOfMonth() { return ValueRange.of(1, month == MONTHS_IN_YEAR && isLeapYear() ? WEEKS_IN_LEAP_MONTH : WEEKS_IN_MONTH); } @Override PaxDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } @Override public ValueRange range(TemporalField field) { if (field == ChronoField.ALIGNED_WEEK_OF_YEAR) { return ValueRange.of(1, WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0)); } else if (field == ChronoField.MONTH_OF_YEAR) { return ValueRange.of(1, MONTHS_IN_YEAR + (isLeapYear() ? 1 : 0)); } return super.range(field); } /** * Get the proleptic month from the start of the epoch (Pax 0000). * * @return The proleptic month. */ @Override long getProlepticMonth() { return ((long) getProlepticYear()) * MONTHS_IN_YEAR + getLeapYearsBefore(getProlepticYear()) + month - 1; } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Pax calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Pax chronology, not null */ @Override public PaxChronology getChronology() { return PaxChronology.INSTANCE; } /** * Gets the era applicable at this date. *

* The Pax calendar system has two eras, 'CE' and 'BCE', * defined by {@link PaxEra}. * * @return the era applicable at this date, not null */ @Override public PaxEra getEra() { return (prolepticYear >= 1 ? PaxEra.CE : PaxEra.BCE); } /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * Month lengths do not match those of the ISO calendar system. * * @return the length of the month in days, from 7 to 28 */ @Override public int lengthOfMonth() { switch (month) { case 13: return (isLeapYear() ? DAYS_IN_WEEK : DAYS_IN_MONTH); default: return DAYS_IN_MONTH; } } @Override public int lengthOfYear() { return DAYS_IN_YEAR + (isLeapYear() ? DAYS_IN_WEEK : 0); } //------------------------------------------------------------------------- @Override public PaxDate with(TemporalAdjuster adjuster) { return (PaxDate) adjuster.adjustInto(this); } @Override public PaxDate with(TemporalField field, long newValue) { // Evaluate years as a special case, to deal with inserted leap months. if (field == ChronoField.YEAR) { return plusYears(Math.subtractExact(newValue, getProlepticYear())); } return (PaxDate) super.with(field, newValue); } //----------------------------------------------------------------------- @Override public PaxDate plus(TemporalAmount amount) { return (PaxDate) amount.addTo(this); } @Override public PaxDate plus(long amountToAdd, TemporalUnit unit) { return (PaxDate) super.plus(amountToAdd, unit); } /** * Returns a copy of this {@code PaxDate} with the specified period in years added. *

* This method adds the specified amount to the years field in two steps: *

    *
  1. Add the input years to the year field
  2. *
  3. If necessary, shift the index to account for the inserted/deleted leap-month.
  4. *
*

* In the Pax Calendar, the month of December is 13 in non-leap-years, and 14 in leap years. * Shifting the index of the month thus means the month would still be the same. *

* In the case of moving from the inserted leap-month (destination year is non-leap), the month index is retained. * This has the effect of retaining the same day-of-year. *

* This instance is immutable and unaffected by this method call. * * @param yearsToAdd the years to add, may be negative * @return a {@code PaxDate} based on this date with the years added, not null * @throws DateTimeException if the result exceeds the supported date range */ @Override PaxDate plusYears(long yearsToAdd) { if (yearsToAdd == 0) { return this; } int newYear = YEAR.checkValidIntValue(getProlepticYear() + yearsToAdd); // Retain actual month (not index) in the case where a leap month is to be inserted. if (month == MONTHS_IN_YEAR && !isLeapYear() && PaxChronology.INSTANCE.isLeapYear(newYear)) { return of(newYear, MONTHS_IN_YEAR + 1, getDayOfMonth()); } // Otherwise, one of the following is true: // 1 - Before the leap month, nothing to do (most common) // 2 - Both source and destination in leap-month, nothing to do // 3 - Both source and destination after leap month in leap year, nothing to do // 4 - Source in leap month, but destination year not leap. Retain month index, preserving day-of-year. // 5 - Source after leap month, but destination year not leap. Move month index back. return resolvePreviousValid(newYear, month, day); } /** * Returns a copy of this {@code PaxDate} with the specified period in months added. *

* This method adds the specified amount to the months field in three steps: *

    *
  1. Add the input months to the month-of-year field
  2. *
  3. Check if the resulting date would be invalid
  4. *
  5. Adjust the day-of-month to the last valid day if necessary
  6. *
*

* For example, 2006-12-13 plus one month would result in the invalid date 2006-13-13. * Instead of returning an invalid result, the last valid day of the month, 2006-13-07, is selected instead. *

* This instance is immutable and unaffected by this method call. * * @param monthsToAdd the months to add, may be negative * @return a {@code PaxDate} based on this date with the months added, not null * @throws DateTimeException if the result exceeds the supported date range */ @Override PaxDate plusMonths(long monthsToAdd) { if (monthsToAdd == 0) { return this; } long calcMonths = Math.addExact(getProlepticMonth(), monthsToAdd); // "Regularize" the month count, as if years were all 13 months long. long monthsRegularized = calcMonths - getLeapMonthsBefore(calcMonths); int newYear = YEAR.checkValidIntValue(Math.floorDiv(monthsRegularized, MONTHS_IN_YEAR)); int newMonth = Math.toIntExact(calcMonths - ((long) newYear * MONTHS_IN_YEAR + getLeapYearsBefore(newYear)) + 1); return resolvePreviousValid(newYear, newMonth, getDayOfMonth()); } @Override public PaxDate minus(TemporalAmount amount) { return (PaxDate) amount.subtractFrom(this); } @Override public PaxDate minus(long amountToSubtract, TemporalUnit unit) { return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return until(PaxDate.from(endExclusive), unit); } @Override long until(AbstractDate end, TemporalUnit unit) { if (unit instanceof ChronoUnit) { PaxDate paxEnd = PaxDate.from(end); switch ((ChronoUnit) unit) { case YEARS: return yearsUntil(paxEnd); case DECADES: return yearsUntil(paxEnd) / YEARS_IN_DECADE; case CENTURIES: return yearsUntil(paxEnd) / YEARS_IN_CENTURY; case MILLENNIA: return yearsUntil(paxEnd) / YEARS_IN_MILLENNIUM; default: break; } } return super.until(end, unit); } /** * Get the number of years from this date to the given day. * * @param end The end date. * @return The number of years from this date to the given day. */ long yearsUntil(PaxDate end) { // If either date is after the inserted leap month, and the other year isn't leap, simulate the effect of the inserted month. long startYear = getProlepticYear() * 512L + getDayOfYear() + (this.month == MONTHS_IN_YEAR && !this.isLeapYear() && end.isLeapYear() ? DAYS_IN_WEEK : 0); long endYear = end.getProlepticYear() * 512L + end.getDayOfYear() + (end.month == MONTHS_IN_YEAR && !end.isLeapYear() && this.isLeapYear() ? DAYS_IN_WEEK : 0); return (endYear - startYear) / 512L; } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { PaxDate end = PaxDate.from(endDateExclusive); int years = Math.toIntExact(yearsUntil(end)); // Get to the same "whole" year. PaxDate sameYearEnd = this.plusYears(years); int months = (int) sameYearEnd.monthsUntil(end); int days = (int) sameYearEnd.plusMonths(months).daysUntil(end); return getChronology().period(years, months, days); } //----------------------------------------------------------------------- @Override public long toEpochDay() { long paxEpochDay = ((long) getProlepticYear() - 1) * DAYS_IN_YEAR + getLeapYearsBefore(getProlepticYear()) * DAYS_IN_WEEK + getDayOfYear() - 1; return paxEpochDay - PAX_0001_TO_ISO_1970; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/PaxEra.java000066400000000000000000000073361343451174100261670ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.time.DateTimeException; import java.time.chrono.Era; /** * An era in the Pax calendar system. *

* The Pax calendar system has two eras. * The current era, for years from 1 onwards, is known as 'Current Era'. * All previous years, zero or earlier in the proleptic count or one and greater * in the year-of-era count, are part of the 'Before Current Era' era. *

* The start of the Pax epoch {@code 0001-01-01 (Pax)} is {@code 0000-12-31 (ISO)}. *

* Do not use {@code ordinal()} to obtain the numeric representation of {@code PaxEra}. * Use {@code getValue()} instead. * *

Implementation Requirements:

* This is an immutable and thread-safe enum. */ public enum PaxEra implements Era { /** * The singleton instance for the era before the current one, 'Before Current Era', * which has the numeric value 0. */ BCE, /** * The singleton instance for the current era, 'Current Era', * which has the numeric value 1. */ CE; //----------------------------------------------------------------------- /** * Obtains an instance of {@code PaxEra} from an {@code int} value. *

* {@code PaxEra} is an enum representing the Pax eras of BCE/CE. * This factory allows the enum to be obtained from the {@code int} value. * * @param era the BCE/CE value to represent, from 0 (BCE) to 1 (CE) * @return the era singleton, not null * @throws DateTimeException if the value is invalid */ public static PaxEra of(final int era) { switch (era) { case 0: return BCE; case 1: return CE; default: throw new DateTimeException("Invalid era: " + era); } } //----------------------------------------------------------------------- /** * Gets the numeric era {@code int} value. *

* The era BCE has the value 0, while the era CE has the value 1. * * @return the era value, from 0 (BCE) to 1 (CE) */ @Override public int getValue() { return ordinal(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/Symmetry010Chronology.java000066400000000000000000000464171343451174100311300ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; /** * The Symmetry010 calendar system. *

* This chronology defines the rules of the Symmetry010 calendar system. * Dates are aligned such that {@code 0001/01/01 (Sym010)} is {@code 0001-01-01 (ISO)}. *

* The calendar implemented by this class is proleptic, with January 1st as the start of the year. * Each month either has 30 days or 31 days, in an alternating pattern; January has 30 days, * February has 31 days and March again has 30 days. Due to this, each quarter consists of 13 weeks. *

* Normal years thus have 364 days, whereas leap years have an extra week, aptly called leap week, * added to the end, extending the year to 371 days. Thus, December in a leap year has 37 days. *

* The fields are defined as follows: *

    *
  • era - Same eras as used in the Gregorian calendar: 'Before Common Era' (BCE) and 'Common Era' (CE). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year 1. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the current era. *
  • month-of-year - There are 12 months in an Symmetry010 year, numbered from 1 to 12. *
  • day-of-month - There are 30 days in a standard Symmetry010 month, numbered from 1 to 30, except for the middle-month * in each quarter, which spans 31 days: February, May, August, November. * In leap years, December has 37 days. *
  • day-of-year - There are 364 days in a standard Symmetry010 year and 371 days in a leap year. * The days are numbered accordingly. *
  • leap-year - Leap years occur every 5 or 6 years, evenly spread over 293 years according the formula: * (52 > ((52 * year + 146) % 293)). *
  • Week day - every year and every quarter starts on a Monday. * In each quarter, the first month starts on a Monday, the second month on a Wednesday and the 3rd on a Saturday. * There are no days outside of the week or month. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class Symmetry010Chronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Symmetry010 chronology. */ public static final Symmetry010Chronology INSTANCE = new Symmetry010Chronology(); /** * Serialization version UID. */ private static final long serialVersionUID = -1287766365831162587L; /** * Standard 7 day weeks. */ static final int DAYS_IN_WEEK = 7; /** * Standard 12 month years. */ static final int MONTHS_IN_YEAR = 12; /** * Normal month is 4 weeks. */ static final int WEEKS_IN_MONTH = 4; /** * Long month is 5 weeks. */ static final int WEEKS_IN_MONTH_LONG = 5; /** * Normal month is 30 days. */ static final int DAYS_IN_MONTH = 30; /** * Long month is 31 days. */ static final int DAYS_IN_MONTH_LONG = 31; /** * Days in quarter, (30 + 31 + 30) = 91 */ static final int DAYS_IN_QUARTER = DAYS_IN_MONTH + DAYS_IN_MONTH_LONG + DAYS_IN_MONTH; /** * There are 4 quarters of 91 (30 + 31 + 30) days each, or 364 days in a (non-leap) year. */ static final int DAYS_IN_YEAR = 4 * DAYS_IN_QUARTER; /** * Leap years are 364 + 7 days. */ static final int DAYS_IN_YEAR_LONG = DAYS_IN_YEAR + DAYS_IN_WEEK; /** * 52 weeks in a normal year. */ static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK; /** * 53 weeks in a leap year. */ static final int WEEKS_IN_YEAR_LONG = DAYS_IN_YEAR_LONG / DAYS_IN_WEEK; /** * Number of years in a cycle. */ private static final int YEARS_IN_CYCLE = 293; /** * Number of days in a cycle. */ static final int DAYS_PER_CYCLE = YEARS_IN_CYCLE * DAYS_IN_YEAR + WEEKS_IN_YEAR * DAYS_IN_WEEK; // == 294 full years! /** * The number of days from year zero to CE 1970, still the era only allows CE 1 and higher. * There are 6 full 293-year cycles from CE 1 to 1758, with 6 * 52 leap years, i.e. 312. * There are 37 leap years from CE 1758 to 1970. */ //public static final long DAYS_0001_TO_1970 = (DAYS_PER_CYCLE * 6L) + 211L * DAYS_IN_YEAR + 37 * DAYS_IN_WEEK; //public static final long DAYS_0001_TO_1970_ISO = IsoChronology.INSTANCE.date(1,1,1).toEpochDay() * -1; public static final long DAYS_0001_TO_1970 = (146097 * 5L) - (31L * 365L + 7L) - 1; /** * Highest year in the range. */ private static final long MAX_YEAR = 1_000_000L; /** * Range of year. */ static final ValueRange YEAR_RANGE = ValueRange.of(-MAX_YEAR, MAX_YEAR); /** * Epoch day range. */ static final ValueRange EPOCH_DAY_RANGE = ValueRange.of( -MAX_YEAR * DAYS_IN_YEAR - getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970, MAX_YEAR * DAYS_IN_YEAR + getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970); /** * Range of proleptic month. */ private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-MAX_YEAR * MONTHS_IN_YEAR, MAX_YEAR * MONTHS_IN_YEAR - 1); /** * Range of day of month. */ static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_MONTH, DAYS_IN_MONTH_LONG + 6); /** * Range of day of year. */ static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, DAYS_IN_YEAR, DAYS_IN_YEAR_LONG); /** * Range of month of year. */ static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR); /** * Range of eras. */ static final ValueRange ERA_RANGE = ValueRange.of(0, 1); /** * Empty range: [0, 0]. */ static final ValueRange EMPTY_RANGE = ValueRange.of(0, 0); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public Symmetry010Chronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Sym010'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Sym010' * @see #getCalendarType() */ @Override public String getId() { return "Sym010"; } /** * Gets the calendar type of the underlying calendar system, which returns null. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for this calendar system, thus null is returned. * * @return the calendar system type, null * @see #getId() */ @Override public String getCalendarType() { return null; } //----------------------------------------------------------------------- /** * Obtains a local date in Symmetry010 calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Symmetry010 era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Symmetry010 local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code IsoEra} */ @Override public Symmetry010Date date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Symmetry010 calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Symmetry010 local date, not null * @throws DateTimeException if unable to create the date */ @Override public Symmetry010Date date(int prolepticYear, int month, int dayOfMonth) { return Symmetry010Date.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Symmetry010 calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Symmetry010 era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Symmetry010 local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code IsoEra} */ @Override public Symmetry010Date dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Symmetry010 calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Symmetry010 local date, not null * @throws DateTimeException if unable to create the date */ @Override public Symmetry010Date dateYearDay(int prolepticYear, int dayOfYear) { return Symmetry010Date.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Symmetry010 calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Symmetry010 local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry010Date dateEpochDay(long epochDay) { return Symmetry010Date.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Symmetry010 local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Symmetry010 local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry010Date dateNow() { return Symmetry010Date.now(); } /** * Obtains the current Symmetry010 local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Symmetry010 local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry010Date dateNow(ZoneId zone) { return Symmetry010Date.now(zone); } /** * Obtains the current Symmetry010 local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Symmetry010 local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry010Date dateNow(Clock clock) { return Symmetry010Date.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Symmetry010 local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Symmetry010 local date, not null * @throws DateTimeException if unable to create the date */ @Override public Symmetry010Date date(TemporalAccessor temporal) { return Symmetry010Date.from(temporal); } /** * Obtains a Symmetry010 local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Symmetry010 local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Symmetry010 zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Symmetry010 zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Symmetry010 zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Symmetry010 zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* A leap-year is a year of a longer length than normal. * Leap years in the calendar system match those of the ISO calendar system. * * @param year the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long year) { return WEEKS_IN_YEAR > ((WEEKS_IN_YEAR * year + 146) % YEARS_IN_CYCLE); } //----------------------------------------------------------------------- /** * Creates the chronology era object from the numeric value. *

* The list of eras is shared with {@link IsoEra}. * * @param eraValue the era value * @return the calendar system era, not null * @throws DateTimeException if unable to create the era */ @Override public IsoEra eraOf(int eraValue) { return IsoEra.of(eraValue); } /** * Gets the list of eras for the chronology. *

* The list of eras is shared with {@link IsoEra}. * * @return the list of eras for the chronology, may be immutable, not null */ @Override public List eras() { return Arrays.asList(IsoEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case ALIGNED_DAY_OF_WEEK_IN_YEAR: case ALIGNED_DAY_OF_WEEK_IN_MONTH: case DAY_OF_WEEK: return ValueRange.of(1, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, WEEKS_IN_MONTH, WEEKS_IN_MONTH_LONG); case ALIGNED_WEEK_OF_YEAR: return ValueRange.of(1, WEEKS_IN_YEAR, WEEKS_IN_YEAR + 1); case DAY_OF_MONTH: return DAY_OF_MONTH_RANGE; case DAY_OF_YEAR: return DAY_OF_YEAR_RANGE; case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: case YEAR: return YEAR_RANGE; default: return field.range(); } } @Override public int prolepticYear(Era era, int yearOfEra) { if (!(era instanceof IsoEra)) { throw new ClassCastException("Invalid era: " + era); } YEAR_RANGE.checkValidIntValue(yearOfEra, ChronoField.YEAR_OF_ERA); return yearOfEra; } /** * Get the count of leap years since CE 1. * * @param prolepticYear the year * @return the number of leap years since CE 1 */ public static long getLeapYearsBefore(long prolepticYear) { return Math.floorDiv(WEEKS_IN_YEAR * (prolepticYear - 1) + 146, YEARS_IN_CYCLE); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/Symmetry010Date.java000066400000000000000000000641661343451174100276630ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_0001_TO_1970; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_IN_MONTH; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_IN_MONTH_LONG; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_IN_QUARTER; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_IN_WEEK; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_IN_YEAR; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_IN_YEAR_LONG; import static org.threeten.extra.chrono.Symmetry010Chronology.DAYS_PER_CYCLE; import static org.threeten.extra.chrono.Symmetry010Chronology.DAY_OF_MONTH_RANGE; import static org.threeten.extra.chrono.Symmetry010Chronology.DAY_OF_YEAR_RANGE; import static org.threeten.extra.chrono.Symmetry010Chronology.EPOCH_DAY_RANGE; import static org.threeten.extra.chrono.Symmetry010Chronology.ERA_RANGE; import static org.threeten.extra.chrono.Symmetry010Chronology.INSTANCE; import static org.threeten.extra.chrono.Symmetry010Chronology.MONTHS_IN_YEAR; import static org.threeten.extra.chrono.Symmetry010Chronology.MONTH_OF_YEAR_RANGE; import static org.threeten.extra.chrono.Symmetry010Chronology.WEEKS_IN_MONTH; import static org.threeten.extra.chrono.Symmetry010Chronology.WEEKS_IN_YEAR; import static org.threeten.extra.chrono.Symmetry010Chronology.YEAR_RANGE; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.chrono.IsoEra; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; /** * A date in the Symmetry010 calendar system. *

* This date operates using the {@linkplain Symmetry010Chronology Symmetry010 calendar}. * This calendar system is a proposed reform calendar system, and is not in common use. * The Symmetry010 differs from the Gregorian in terms of month length, and the leap year rule. * Dates are aligned such that {@code 0001/01/01 (Sym010)} is {@code 0001-01-01 (ISO)}. * The alignment of January 1st happens 40 times within a 293 years cycle, skipping 5, 6, 11 or 12 years in between: * 1, 7, 18, 24, 29, 35, 46, 52, 57, 63, 74, 80, 85, 91, 103, 114, 120, 125, 131, 142, * 148, 153, 159, 170, 176, 181, 187, 198, 210, 216, 221, 227, 238, 244, 249, 255, 266, 272, 277, 283. *

* The implementation is a pure Symmetry010 calendar, as proposed by Dr. Irv Bromberg. * The year shares the 12 months with the Gregorian calendar. * The months February, May, August, November span 31 days, all other months consist of 30 days. * In leap years, December is extended with a full week, the so-called "leap week". * Thus December in a leap year has 37. * Since each month is made of full weeks, the calendar is perennial, with every date fixed always on the same weekday. * Each month starts on a Monday and ends on a Sunday; so does each year. * The 13th day of a month is always a Saturday. *

* More information is available on Wikipedia at * Symmetry010 or on the calendar's * home page. *

* *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Symmetry010Date extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -8275627894629629L; /** * The proleptic year. */ private final int prolepticYear; /** * The month of the year. */ private final int month; /** * The day of the month. */ private final int day; /** * The day of year. */ private final transient int dayOfYear; //----------------------------------------------------------------------- /** * Obtains the current {@code Symmetry010Date} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static Symmetry010Date now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code Symmetry010Date} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static Symmetry010Date now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code Symmetry010Date} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static Symmetry010Date now(Clock clock) { LocalDate now = LocalDate.now(clock); return Symmetry010Date.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code Symmetry010Date} representing a date in the Symmetry010 calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code Symmetry010Date} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the Symmetry010 proleptic-year * @param month the Symmetry010 month-of-year, from 1 to 12 * @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November, * or 1 to 37 in December in a Leap Year * @return the date in Symmetry010 calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static Symmetry010Date of(int prolepticYear, int month, int dayOfMonth) { return create(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Obtains a {@code Symmetry010Date} from a temporal object. *

* This obtains a date in the Symmetry010 calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code Symmetry010Date}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code Symmetry010Date::from}. * * @param temporal the temporal object to convert, not null * @return the date in the Symmetry010 calendar system, not null * @throws DateTimeException if unable to convert to a {@code Symmetry010Date} */ public static Symmetry010Date from(TemporalAccessor temporal) { if (temporal instanceof Symmetry010Date) { return (Symmetry010Date) temporal; } return Symmetry010Date.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code Symmetry010Date} representing a date in the Symmetry010 calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code Symmetry010Date} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Symmetry010 proleptic-year * @param dayOfYear the Symmetry010 day-of-year, from 1 to 364/371 * @return the date in Symmetry010 calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static Symmetry010Date ofYearDay(int prolepticYear, int dayOfYear) { YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA); DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, ChronoField.DAY_OF_YEAR); boolean leap = INSTANCE.isLeapYear(prolepticYear); if (dayOfYear > DAYS_IN_YEAR && !leap) { throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year"); } int offset = Math.min(dayOfYear, DAYS_IN_YEAR) - 1; int quarter = offset / DAYS_IN_QUARTER; int day = ((dayOfYear - 1) - quarter * DAYS_IN_QUARTER) + 1; int month = 1 + quarter * 3; if (day > DAYS_IN_MONTH + DAYS_IN_MONTH + 1) { month += 2; day -= DAYS_IN_MONTH + DAYS_IN_MONTH + 1; } else if (day > DAYS_IN_MONTH) { month += 1; day -= DAYS_IN_MONTH; } return new Symmetry010Date(prolepticYear, month, day); } /** * Obtains a {@code Symmetry010Date} representing a date in the Symmetry010 calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO), corresponds to 1970-01-04 (Sym010) * @return the date in Symmetry010 calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static Symmetry010Date ofEpochDay(long epochDay) { EPOCH_DAY_RANGE.checkValidValue(epochDay + 3, ChronoField.EPOCH_DAY); long zeroDay = epochDay + DAYS_0001_TO_1970 + 1; long year = 1 + ((293 * zeroDay) / DAYS_PER_CYCLE); long doy = zeroDay - (DAYS_IN_YEAR * (year - 1) + Symmetry010Chronology.getLeapYearsBefore(year) * DAYS_IN_WEEK); if (doy < 1) { year--; doy += INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR; } int diy = INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR; if (doy > diy) { doy -= diy; year++; } return ofYearDay((int) year, (int) doy); } /** * Consistency check for dates manipulations after calls to * {@link #plus(long, TemporalUnit)}, * {@link #minus(long, TemporalUnit)}, * {@link #until(AbstractDate, TemporalUnit)} or * {@link #with(TemporalField, long)}. * * @param prolepticYear the Symmetry010 proleptic-year * @param month the Symmetry010 month, from 1 to 12 * @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November, * or 1 to 37 in December in a Leap Year * @return the resolved date */ private static Symmetry010Date resolvePreviousValid(int prolepticYear, int month, int dayOfMonth) { int monthR = Math.min(month, MONTHS_IN_YEAR); int dayR = Math.min(dayOfMonth, monthR == 12 && INSTANCE.isLeapYear(prolepticYear) ? DAYS_IN_MONTH + 7 : monthR % 3 == 2 ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH); return create(prolepticYear, monthR, dayR); } //----------------------------------------------------------------------- /** * Factory method, validates the given triplet year, month and dayOfMonth. * * @param prolepticYear the Symmetry010 proleptic-year * @param month the Symmetry010 month, from 1 to 12 * @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November, * or 1 to 37 in December in a Leap Year * @return the Symmetry010 date * @throws DateTimeException if the date is invalid */ static Symmetry010Date create(int prolepticYear, int month, int dayOfMonth) { YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA); MONTH_OF_YEAR_RANGE.checkValidValue(month, ChronoField.MONTH_OF_YEAR); DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, ChronoField.DAY_OF_MONTH); if (dayOfMonth > DAYS_IN_MONTH) { if (month == MONTHS_IN_YEAR) { if (!INSTANCE.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid Leap Day as '" + prolepticYear + "' is not a leap year"); } } else if (((month % 3 == 2) && dayOfMonth > DAYS_IN_MONTH_LONG) || (month % 3 != 2)) { throw new DateTimeException("Invalid date: " + prolepticYear + '/' + month + '/' + dayOfMonth); } } return new Symmetry010Date(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Symmetry010 proleptic-year * @param month the Symmetry010 month, from 1 to 12 * @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November, * or 1 to 37 in December in a Leap Year */ private Symmetry010Date(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = month; this.day = dayOfMonth; this.dayOfYear = DAYS_IN_MONTH * (month - 1) + (month / 3) + dayOfMonth; } /** * Validates the object. * * @return Symmetry010Date the resolved date, not null */ private Object readResolve() { return Symmetry010Date.of(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { return dayOfYear; } @Override int lengthOfYearInMonths() { return MONTHS_IN_YEAR; } @Override int getDayOfWeek() { return ((dayOfYear - 1 + getDayOfMonthOffset()) % DAYS_IN_WEEK) + 1; } long getProlepticWeek() { return prolepticYear * WEEKS_IN_YEAR + Symmetry010Chronology.getLeapYearsBefore(prolepticYear) + ((dayOfYear - 1) / DAYS_IN_WEEK) - 1; } /** * Each 1st month of a quarter (month % 3 == 1) starts on a Monday, offset is 0. * Each 2nd month of a quarter (month % 3 == 2) starts on a Wednesday, offset is 2. * Each 3rd month of a quarter (month % 3 == 0) starts on a Saturday, offset is 5. */ private static final int[] dayOfMonthOffset = {5, 0, 2}; private int getDayOfMonthOffset() { return dayOfMonthOffset[month % 3]; } /** * Checks if the date is within the leap week. * * @return true if this date is in the leap week */ public boolean isLeapWeek() { return isLeapYear() && this.dayOfYear > DAYS_IN_YEAR; } //----------------------------------------------------------------------- @Override public ValueRange range(TemporalField field) { if (field instanceof ChronoField) { if (isSupported(field)) { ChronoField f = (ChronoField) field; switch (f) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: case DAY_OF_WEEK: return ValueRange.of(1, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, lengthOfMonth() / DAYS_IN_WEEK); case ALIGNED_WEEK_OF_YEAR: return ValueRange.of(1, WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0)); case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth()); case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear()); case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; default: break; } } else { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } } return super.range(field); } @Override ValueRange rangeAlignedWeekOfMonth() { // never invoked return ValueRange.of(1, WEEKS_IN_MONTH); } @Override Symmetry010Date resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Symmetry010 calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Symmetry010 chronology, not null */ @Override public Symmetry010Chronology getChronology() { return INSTANCE; } /** * Gets the era applicable at this date. *

* The Symmetry454 calendar system uses {@link IsoEra}. * * @return the era applicable at this date, not null */ @Override public IsoEra getEra() { return (prolepticYear >= 1 ? IsoEra.CE : IsoEra.BCE); } /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * Month lengths do not match those of the ISO calendar system. *

* Most months have 30 days, except for February, May, August, November each have 31 days. * December in a leap year has 37 days. * * @return the length of the month in days */ @Override public int lengthOfMonth() { return this.isLeapYear() && this.month == MONTHS_IN_YEAR ? DAYS_IN_MONTH + 7 : this.month % 3 == 2 ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH; } /** * Returns the length of the year represented by this date. *

* This returns the length of the year in days. * Year lengths do NOT match those of the ISO calendar system. * * @return the length of the year in days: 364 or 371 */ @Override public int lengthOfYear() { return isLeapYear() ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR; } //------------------------------------------------------------------------- @Override public Symmetry010Date with(TemporalAdjuster adjuster) { return (Symmetry010Date) adjuster.adjustInto(this); } @Override public Symmetry010Date with(TemporalField field, long newValue) { if (field instanceof ChronoField) { if (newValue == 0) { return this; } ChronoField f = (ChronoField) field; getChronology().range(f).checkValidValue(newValue, f); int nval = (int) newValue; switch (f) { case DAY_OF_MONTH: return create(prolepticYear, month, nval); case DAY_OF_WEEK: int week = (this.dayOfYear - 1) / 7; int yd = 7 * week + nval; return ofYearDay(prolepticYear, yd); default: break; } } return (Symmetry010Date) super.with(field, newValue); } @Override Symmetry010Date withDayOfYear(int value) { return ofYearDay(prolepticYear, value); } //----------------------------------------------------------------------- @Override public Symmetry010Date plus(TemporalAmount amount) { return (Symmetry010Date) amount.addTo(this); } @Override public Symmetry010Date plus(long amountToAdd, TemporalUnit unit) { return (Symmetry010Date) super.plus(amountToAdd, unit); } @Override public Symmetry010Date minus(TemporalAmount amount) { return (Symmetry010Date) amount.subtractFrom(this); } @Override public Symmetry010Date minus(long amountToSubtract, TemporalUnit unit) { return (Symmetry010Date) super.minus(amountToSubtract, unit); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return until(Symmetry010Date.from(endExclusive), unit); } /** * Get the number of years from this date to the given day. * * @param end The end date. * @return The number of years from this date to the given day. */ long yearsUntil(Symmetry010Date end) { long startYear = this.prolepticYear * 512L + this.getDayOfYear(); long endYear = end.prolepticYear * 512L + end.getDayOfYear(); return (endYear - startYear) / 512L; } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { Symmetry010Date end = Symmetry010Date.from(endDateExclusive); int years = Math.toIntExact(yearsUntil(end)); // Get to the same "whole" year. Symmetry010Date sameYearEnd = (Symmetry010Date) plusYears(years); int months = (int) sameYearEnd.monthsUntil(end); int days = (int) sameYearEnd.plusMonths(months).daysUntil(end); return getChronology().period(years, months, days); } @Override long weeksUntil(AbstractDate end) { Symmetry010Date endDate = Symmetry010Date.from(end); long startWeek = this.getProlepticWeek() * 8L + this.getDayOfWeek(); long endWeek = endDate.getProlepticWeek() * 8L + endDate.getDayOfWeek(); return (endWeek - startWeek) / 8L; } @Override long monthsUntil(AbstractDate end) { Symmetry010Date date = Symmetry010Date.from(end); long monthStart = this.getProlepticMonth() * 64L + this.getDayOfMonth(); long monthEnd = date.getProlepticMonth() * 64L + date.getDayOfMonth(); return (monthEnd - monthStart) / 64L; } //----------------------------------------------------------------------- @Override public long toEpochDay() { long epochDay = (long) (this.prolepticYear - 1) * DAYS_IN_YEAR + Symmetry010Chronology.getLeapYearsBefore(this.prolepticYear) * DAYS_IN_WEEK + this.dayOfYear - DAYS_0001_TO_1970 - 1; return epochDay; } /** * Display the date in human-readable format. * * @return the string representation */ @Override public String toString() { StringBuilder buf = new StringBuilder(30); return buf.append(getChronology().toString()) .append(' ') .append(getEra()) .append(' ') .append(getYearOfEra()) .append(this.month < 10 && this.month > 0 ? "/0" : '/') .append(this.month) .append(this.day < 10 ? "/0" : '/') .append(this.day) .toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/Symmetry454Chronology.java000066400000000000000000000460411343451174100311350ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.chrono.AbstractChronology; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; /** * The Symmetry454 calendar system. *

* This chronology defines the rules of the Symmetry454 calendar system. * Dates are aligned such that {@code 0001/01/01 (Sym454)} is {@code 0001-01-01 (ISO)}. *

* The calendar implemented by this class is proleptic, with January 1st as the start of the year. * Each month either has 28 days or 35 days, in an alternating pattern; January has 28 days, * February has 35 days and March again has 28 days. Due to this, each quarter consists of 13 weeks. *

* Normal years thus have 364 days, whereas leap years have an extra week, aptly called leap week, * added to the end, extending the year to 371 days. *

* The fields are defined as follows: *

    *
  • era - Same eras as used in the Gregorian calendar: 'Before Common Era' (BCE) and 'Common Era' (CE). *
  • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year 1. *
  • proleptic-year - The proleptic year is the same as the year-of-era for the current era. *
  • month-of-year - There are 12 months in an Symmetry454 year, numbered from 1 to 12. *
  • day-of-month - There are 28 days in a standard Symmetry454 month, numbered from 1 to 28, except for the middle-month * in each quarter, which spans 35 days: February, May, August, November; and December in leap years. *
  • day-of-year - There are 364 days in a standard Symmetry454 year and 371 days in a leap year. * The days are numbered accordingly. *
  • leap-year - Leap years occur every 5 or 6 years, evenly spread over 293 years according the formula: * (52 > ((52 * year + 146) % 293)). *
  • Week day - every month starts on a Monday. There are no days outside of the week or month. *
* *

Implementation Requirements

* This class is immutable and thread-safe. */ public final class Symmetry454Chronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Symmetry454 chronology. */ public static final Symmetry454Chronology INSTANCE = new Symmetry454Chronology(); /** * Serialization version. */ private static final long serialVersionUID = -1287766365831162587L; /** * Standard 7 day weeks. */ static final int DAYS_IN_WEEK = 7; /** * Standard 12 month years. */ static final int MONTHS_IN_YEAR = 12; /** * Normal month is 4 weeks. */ static final int WEEKS_IN_MONTH = 4; /** * Long month is 5 weeks. */ static final int WEEKS_IN_MONTH_LONG = 5; /** * Days in quarter, (4 + 5 + 4) * 7 = 91 */ static final int DAYS_IN_QUARTER = (WEEKS_IN_MONTH + WEEKS_IN_MONTH_LONG + WEEKS_IN_MONTH) * DAYS_IN_WEEK; /** * Days in year, 8 months of 28 days plus 4 months of 35 days, or 364 days in a normal year. */ static final int DAYS_IN_YEAR = 4 * DAYS_IN_QUARTER; /** * Leap years are 364 + 7 days. */ static final int DAYS_IN_YEAR_LONG = DAYS_IN_YEAR + DAYS_IN_WEEK; /** * Days in long month. */ static final int DAYS_IN_MONTH_LONG = WEEKS_IN_MONTH_LONG * DAYS_IN_WEEK; /** * Days in normal month. */ static final int DAYS_IN_MONTH = WEEKS_IN_MONTH * DAYS_IN_WEEK; /** * 52 weeks in a normal year. */ static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK; /** * Number of years in a cycle. */ private static final int YEARS_IN_CYCLE = 293; /** * Number of days in a cycle. */ static final int DAYS_PER_CYCLE = YEARS_IN_CYCLE * DAYS_IN_YEAR + WEEKS_IN_YEAR * DAYS_IN_WEEK; // == 294 full years! /** * The number of days from year zero to CE 1970, still the era only allows CE 1 and higher. * There are 6 full 293-year cycles from CE 1 to 1758, with 6 * 52 leap years, i.e. 312. * There are 37 leap years from CE 1758 to 1970. */ //public static final long DAYS_0001_TO_1970 = (DAYS_PER_CYCLE * 6L) + 211L * DAYS_IN_YEAR + 37 * DAYS_IN_WEEK; //public static final long DAYS_0001_TO_1970_ISO = IsoChronology.INSTANCE.date(1,1,1).toEpochDay() * -1; public static final long DAYS_0001_TO_1970 = (146097 * 5L) - (31L * 365L + 7L) - 1; /** * Highest year in the range. */ private static final long MAX_YEAR = 1_000_000L; /** * Range of year. */ static final ValueRange YEAR_RANGE = ValueRange.of(-MAX_YEAR, MAX_YEAR); /** * Epoch day range. */ static final ValueRange EPOCH_DAY_RANGE = ValueRange.of( -MAX_YEAR * DAYS_IN_YEAR - getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970, MAX_YEAR * DAYS_IN_YEAR + getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970); /** * Range of proleptic month. */ private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-MAX_YEAR * MONTHS_IN_YEAR, MAX_YEAR * MONTHS_IN_YEAR - 1); /** * Range of day of month. */ static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_MONTH, DAYS_IN_MONTH_LONG); /** * Range of day of year. */ static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, DAYS_IN_YEAR, DAYS_IN_YEAR + DAYS_IN_WEEK); /** * Range of month of year. */ static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR); /** * Range of eras. */ static final ValueRange ERA_RANGE = ValueRange.of(0, 1); /** * Empty range: [0, 0]. */ static final ValueRange EMPTY_RANGE = ValueRange.of(0, 0); /** * Private constructor, that is public to satisfy the {@code ServiceLoader}. * @deprecated Use the singleton {@link #INSTANCE} instead. */ @Deprecated public Symmetry454Chronology() { } /** * Resolve singleton. * * @return the singleton instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Gets the ID of the chronology - 'Sym454'. *

* The ID uniquely identifies the {@code Chronology}. * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}. * * @return the chronology ID - 'Sym454' * @see #getCalendarType() */ @Override public String getId() { return "Sym454"; } /** * Gets the calendar type of the underlying calendar system, which returns null. *

* The Unicode Locale Data Markup Language (LDML) specification * does not define an identifier for this calendar system, thus null is returned. * * @return the calendar system type, null * @see #getId() */ @Override public String getCalendarType() { return null; } //----------------------------------------------------------------------- /** * Obtains a local date in Symmetry454 calendar system from the * era, year-of-era, month-of-year and day-of-month fields. * * @param era the Symmetry454 era, not null * @param yearOfEra the year-of-era * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Symmetry454 local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code IsoEra} */ @Override public Symmetry454Date date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } /** * Obtains a local date in Symmetry454 calendar system from the * proleptic-year, month-of-year and day-of-month fields. * * @param prolepticYear the proleptic-year * @param month the month-of-year * @param dayOfMonth the day-of-month * @return the Symmetry454 local date, not null * @throws DateTimeException if unable to create the date */ @Override public Symmetry454Date date(int prolepticYear, int month, int dayOfMonth) { return Symmetry454Date.of(prolepticYear, month, dayOfMonth); } /** * Obtains a local date in Symmetry454 calendar system from the * era, year-of-era and day-of-year fields. * * @param era the Symmetry454 era, not null * @param yearOfEra the year-of-era * @param dayOfYear the day-of-year * @return the Symmetry454 local date, not null * @throws DateTimeException if unable to create the date * @throws ClassCastException if the {@code era} is not a {@code IsoEra} */ @Override public Symmetry454Date dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } /** * Obtains a local date in Symmetry454 calendar system from the * proleptic-year and day-of-year fields. * * @param prolepticYear the proleptic-year * @param dayOfYear the day-of-year * @return the Symmetry454 local date, not null * @throws DateTimeException if unable to create the date */ @Override public Symmetry454Date dateYearDay(int prolepticYear, int dayOfYear) { return Symmetry454Date.ofYearDay(prolepticYear, dayOfYear); } /** * Obtains a local date in the Symmetry454 calendar system from the epoch-day. * * @param epochDay the epoch day * @return the Symmetry454 local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry454Date dateEpochDay(long epochDay) { return Symmetry454Date.ofEpochDay(epochDay); } //------------------------------------------------------------------------- /** * Obtains the current Symmetry454 local date from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current Symmetry454 local date using the system clock and default time-zone, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry454Date dateNow() { return Symmetry454Date.now(); } /** * Obtains the current Symmetry454 local date from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current Symmetry454 local date using the system clock, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry454Date dateNow(ZoneId zone) { return Symmetry454Date.now(zone); } /** * Obtains the current Symmetry454 local date from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * * @param clock the clock to use, not null * @return the current Symmetry454 local date, not null * @throws DateTimeException if unable to create the date */ @Override // override with covariant return type public Symmetry454Date dateNow(Clock clock) { return Symmetry454Date.now(clock); } //------------------------------------------------------------------------- /** * Obtains a Symmetry454 local date from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Symmetry454 local date, not null * @throws DateTimeException if unable to create the date */ @Override public Symmetry454Date date(TemporalAccessor temporal) { return Symmetry454Date.from(temporal); } /** * Obtains a Symmetry454 local date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Symmetry454 local date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime) super.localDateTime(temporal); } /** * Obtains a Symmetry454 zoned date-time from another date-time object. * * @param temporal the date-time object to convert, not null * @return the Symmetry454 zoned date-time, not null * @throws DateTimeException if unable to create the date-time */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime) super.zonedDateTime(temporal); } /** * Obtains a Symmetry454 zoned date-time in this chronology from an {@code Instant}. * * @param instant the instant to create the date-time from, not null * @param zone the time-zone, not null * @return the Symmetry454 zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ @Override @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime) super.zonedDateTime(instant, zone); } //----------------------------------------------------------------------- /** * Checks if the specified year is a leap year. *

* A leap-year is a year of a longer length than normal. * Leap years in the calendar system match those of the ISO calendar system. * * @param year the proleptic-year to check, not validated for range * @return true if the year is a leap year */ @Override public boolean isLeapYear(long year) { return WEEKS_IN_YEAR > ((WEEKS_IN_YEAR * year + 146) % YEARS_IN_CYCLE); } //----------------------------------------------------------------------- /** * Creates the chronology era object from the numeric value. *

* The list of eras is shared with {@link IsoEra}. * * @param eraValue the era value * @return the calendar system era, not null * @throws DateTimeException if unable to create the era */ @Override public IsoEra eraOf(int eraValue) { return IsoEra.of(eraValue); } /** * Gets the list of eras for the chronology. *

* The list of eras is shared with {@link IsoEra}. * * @return the list of eras for the chronology, may be immutable, not null */ @Override public List eras() { return Arrays.asList(IsoEra.values()); } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { case ALIGNED_DAY_OF_WEEK_IN_YEAR: case ALIGNED_DAY_OF_WEEK_IN_MONTH: case DAY_OF_WEEK: return ValueRange.of(1, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, WEEKS_IN_MONTH, WEEKS_IN_MONTH_LONG); case ALIGNED_WEEK_OF_YEAR: return ValueRange.of(1, WEEKS_IN_YEAR, WEEKS_IN_YEAR + 1); case DAY_OF_MONTH: return DAY_OF_MONTH_RANGE; case DAY_OF_YEAR: return DAY_OF_YEAR_RANGE; case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; case PROLEPTIC_MONTH: return PROLEPTIC_MONTH_RANGE; case YEAR_OF_ERA: case YEAR: return YEAR_RANGE; default: return field.range(); } } @Override public int prolepticYear(Era era, int yearOfEra) { if (!(era instanceof IsoEra)) { throw new ClassCastException("Invalid era: " + era); } YEAR_RANGE.checkValidIntValue(yearOfEra, ChronoField.YEAR_OF_ERA); return yearOfEra; } /** * Get the count of leap years since CE 1. * * @param prolepticYear the year * @return the number of leap years since CE 1 */ public static long getLeapYearsBefore(long prolepticYear) { return Math.floorDiv(WEEKS_IN_YEAR * (prolepticYear - 1) + 146, YEARS_IN_CYCLE); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/Symmetry454Date.java000066400000000000000000000647721343451174100277020ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_0001_TO_1970; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_IN_MONTH; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_IN_MONTH_LONG; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_IN_QUARTER; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_IN_WEEK; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_IN_YEAR; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_IN_YEAR_LONG; import static org.threeten.extra.chrono.Symmetry454Chronology.DAYS_PER_CYCLE; import static org.threeten.extra.chrono.Symmetry454Chronology.DAY_OF_MONTH_RANGE; import static org.threeten.extra.chrono.Symmetry454Chronology.DAY_OF_YEAR_RANGE; import static org.threeten.extra.chrono.Symmetry454Chronology.EPOCH_DAY_RANGE; import static org.threeten.extra.chrono.Symmetry454Chronology.ERA_RANGE; import static org.threeten.extra.chrono.Symmetry454Chronology.INSTANCE; import static org.threeten.extra.chrono.Symmetry454Chronology.MONTHS_IN_YEAR; import static org.threeten.extra.chrono.Symmetry454Chronology.MONTH_OF_YEAR_RANGE; import static org.threeten.extra.chrono.Symmetry454Chronology.WEEKS_IN_MONTH; import static org.threeten.extra.chrono.Symmetry454Chronology.WEEKS_IN_YEAR; import static org.threeten.extra.chrono.Symmetry454Chronology.YEAR_RANGE; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.chrono.IsoEra; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; /** * A date in the Symmetry454 calendar system. *

* This date operates using the {@linkplain Symmetry454Chronology Symmetry454 calendar}. * This calendar system is a proposed reform calendar system, and is not in common use. * The Symmetry454 calendar differs from the Gregorian in terms of month length, and the leap year rule. * Dates are aligned such that {@code 0001/01/01 (Sym454)} is {@code 0001-01-01 (ISO)}. * The alignment of January 1st happens 40 times within a 293 years cycle, skipping 5, 6, 11 or 12 years in between: * 1, 7, 18, 24, 29, 35, 46, 52, 57, 63, 74, 80, 85, 91, 103, 114, 120, 125, 131, 142, * 148, 153, 159, 170, 176, 181, 187, 198, 210, 216, 221, 227, 238, 244, 249, 255, 266, 272, 277, 283. *

* The implementation is a pure Symmetry454 calendar, as proposed by Dr. Irv Bromberg. * The year shares the 12 months with the Gregorian calendar. * The months February, May, August, November span 35 days, all other months consist of 28 days. * In leap years, December is extended with a full week, the so-called "leap week". * Since each month is made of full weeks, the calendar is perennial, with every date fixed always on the same weekday. * Each month starts on a Monday and ends on a Sunday; so does each year. * The 13th day of a month is always a Saturday. *

* More information is available on Wikipedia at * Symmetry454 or on the calendar's * home page. *

* *

Implementation Requirements

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class Symmetry454Date extends AbstractDate implements ChronoLocalDate, Serializable { /** * Serialization version. */ private static final long serialVersionUID = -3540913335234762448L; /** * The proleptic year. */ private final int prolepticYear; /** * The month of the year. */ private final int month; /** * The day of the month. */ private final int day; /** * The day of year. */ private final transient int dayOfYear; //----------------------------------------------------------------------- /** * Obtains the current {@code Symmetry454Date} from the system clock in the default time-zone. *

* This will query the {@link Clock#systemDefaultZone() system clock} in the default * time-zone to obtain the current date. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @return the current date using the system clock and default time-zone, not null */ public static Symmetry454Date now() { return now(Clock.systemDefaultZone()); } /** * Obtains the current {@code Symmetry454Date} from the system clock in the specified time-zone. *

* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. * Specifying the time-zone avoids dependence on the default time-zone. *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * * @param zone the zone ID to use, not null * @return the current date using the system clock, not null */ public static Symmetry454Date now(ZoneId zone) { return now(Clock.system(zone)); } /** * Obtains the current {@code Symmetry454Date} from the specified clock. *

* This will query the specified clock to obtain the current date - today. * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@linkplain Clock dependency injection}. * * @param clock the clock to use, not null * @return the current date, not null * @throws DateTimeException if the current date cannot be obtained */ public static Symmetry454Date now(Clock clock) { LocalDate now = LocalDate.now(clock); return Symmetry454Date.ofEpochDay(now.toEpochDay()); } /** * Obtains a {@code Symmetry454Date} representing a date in the Symmetry454 calendar * system from the proleptic-year, month-of-year and day-of-month fields. *

* This returns a {@code Symmetry454Date} with the specified fields. * The day must be valid for the year and month, otherwise an exception will be thrown. * * @param prolepticYear the Symmetry454 proleptic-year * @param month the Symmetry454 month-of-year, from 1 to 12 * @param dayOfMonth the Symmetry454 day-of-month, from 1 to 28, or 1 to 35 in February, May, August, November * and December in a Leap Year * @return the date in Symmetry454 calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-month is invalid for the month-year */ public static Symmetry454Date of(int prolepticYear, int month, int dayOfMonth) { return create(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Obtains a {@code Symmetry454Date} from a temporal object. *

* This obtains a date in the Symmetry454 calendar system based on the specified temporal. * A {@code TemporalAccessor} represents an arbitrary set of date and time information, * which this factory converts to an instance of {@code Symmetry454Date}. *

* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} * field, which is standardized across calendar systems. *

* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code Symmetry454Date::from}. * * @param temporal the temporal object to convert, not null * @return the date in the Symmetry454 calendar system, not null * @throws DateTimeException if unable to convert to a {@code Symmetry454Date} */ public static Symmetry454Date from(TemporalAccessor temporal) { if (temporal instanceof Symmetry454Date) { return (Symmetry454Date) temporal; } return Symmetry454Date.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY)); } //----------------------------------------------------------------------- /** * Obtains a {@code Symmetry454Date} representing a date in the Symmetry454 calendar * system from the proleptic-year and day-of-year fields. *

* This returns a {@code Symmetry454Date} with the specified fields. * The day must be valid for the year, otherwise an exception will be thrown. * * @param prolepticYear the Symmetry454 proleptic-year * @param dayOfYear the Symmetry454 day-of-year, from 1 to 364/371 * @return the date in Symmetry454 calendar system, not null * @throws DateTimeException if the value of any field is out of range, * or if the day-of-year is invalid for the year */ static Symmetry454Date ofYearDay(int prolepticYear, int dayOfYear) { YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA); DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, ChronoField.DAY_OF_YEAR); boolean leap = INSTANCE.isLeapYear(prolepticYear); if (dayOfYear > DAYS_IN_YEAR && !leap) { throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year"); } int offset = Math.min(dayOfYear, DAYS_IN_YEAR) - 1; int quarter = offset / DAYS_IN_QUARTER; int day = dayOfYear - quarter * DAYS_IN_QUARTER; int month = 1 + quarter * 3; if (day > DAYS_IN_MONTH + DAYS_IN_MONTH + DAYS_IN_WEEK) { month += 2; day -= DAYS_IN_MONTH + DAYS_IN_MONTH + DAYS_IN_WEEK; } else if (day > DAYS_IN_MONTH) { month += 1; day -= DAYS_IN_MONTH; } return new Symmetry454Date(prolepticYear, month, day); } /** * Obtains a {@code Symmetry454Date} representing a date in the Symmetry454 calendar * system from the epoch-day. * * @param epochDay the epoch day to convert based on 1970-01-01 (ISO), corresponds to 1970-01-04 (Sym454) * @return the date in Symmetry454 calendar system, not null * @throws DateTimeException if the epoch-day is out of range */ static Symmetry454Date ofEpochDay(long epochDay) { EPOCH_DAY_RANGE.checkValidValue(epochDay + 3, ChronoField.EPOCH_DAY); long zeroDay = epochDay + DAYS_0001_TO_1970 + 1; long year = 1 + ((293 * zeroDay) / DAYS_PER_CYCLE); long doy = zeroDay - (DAYS_IN_YEAR * (year - 1) + Symmetry454Chronology.getLeapYearsBefore(year) * DAYS_IN_WEEK); if (doy < 1) { year--; doy += INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR; } int diy = INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR; if (doy > diy) { doy -= diy; year++; } return ofYearDay((int) year, (int) doy); } /** * Consistency check for dates manipulations after calls to * {@link #plus(long, TemporalUnit)}, * {@link #minus(long, TemporalUnit)}, * {@link #until(AbstractDate, TemporalUnit)} or * {@link #with(TemporalField, long)}. * * @param prolepticYear the Symmetry454 proleptic-year * @param month the Symmetry454 month, from 1 to 12 * @return the resolved date */ private static Symmetry454Date resolvePreviousValid(int prolepticYear, int month, int day) { int monthR = Math.min(month, MONTHS_IN_YEAR); int dayR = Math.min(day, (monthR % 3 == 2) || (monthR == 12 && INSTANCE.isLeapYear(prolepticYear)) ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH); return create(prolepticYear, monthR, dayR); } //----------------------------------------------------------------------- /** * Factory method, validates the given triplet year, month and dayOfMonth. * * @param prolepticYear the Symmetry454 proleptic-year * @param month the Symmetry454 month, from 1 to 12 * @param dayOfMonth the Symmetry454 day-of-month, from 1 to 28, or 1 to 35 in February, May, August, November * and December in a Leap Year * @return the Symmetry454 date * @throws DateTimeException if the date is invalid */ static Symmetry454Date create(int prolepticYear, int month, int dayOfMonth) { YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA); MONTH_OF_YEAR_RANGE.checkValidValue(month, ChronoField.MONTH_OF_YEAR); DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, ChronoField.DAY_OF_MONTH); if (dayOfMonth > DAYS_IN_MONTH) { if (month == MONTHS_IN_YEAR) { if (!INSTANCE.isLeapYear(prolepticYear)) { throw new DateTimeException("Invalid Leap Day as '" + prolepticYear + "' is not a leap year"); } } else if (month % 3 != 2) { throw new DateTimeException("Invalid date: " + prolepticYear + '/' + month + '/' + dayOfMonth); } } return new Symmetry454Date(prolepticYear, month, dayOfMonth); } //----------------------------------------------------------------------- /** * Creates an instance from validated data. * * @param prolepticYear the Symmetry454 proleptic-year * @param month the Symmetry454 month, from 1 to 12 * @param dayOfMonth the Symmetry454 day-of-month, from 1 to 28, or 1 to 35 in February, May, August, November * and December in a Leap Year */ private Symmetry454Date(int prolepticYear, int month, int dayOfMonth) { this.prolepticYear = prolepticYear; this.month = month; this.day = dayOfMonth; this.dayOfYear = DAYS_IN_MONTH * (month - 1) + DAYS_IN_WEEK * (month / 3) + dayOfMonth; } /** * Validates the object. * * @return Symmetry454Date the resolved date, not null */ private Object readResolve() { return Symmetry454Date.of(prolepticYear, month, day); } //----------------------------------------------------------------------- @Override int getProlepticYear() { return prolepticYear; } @Override int getMonth() { return month; } @Override int getDayOfMonth() { return day; } @Override int getDayOfYear() { return dayOfYear; } @Override int lengthOfYearInMonths() { return MONTHS_IN_YEAR; } @Override int getAlignedDayOfWeekInMonth() { return getDayOfWeek(); } @Override int getAlignedDayOfWeekInYear() { return getDayOfWeek(); } @Override int getAlignedWeekOfMonth() { return ((day - 1) / DAYS_IN_WEEK) + 1; } @Override int getAlignedWeekOfYear() { return ((dayOfYear - 1) / DAYS_IN_WEEK) + 1; } @Override int getDayOfWeek() { return ((day - 1) % DAYS_IN_WEEK) + 1; } long getProlepticWeek() { return getProlepticMonth() * WEEKS_IN_MONTH + ((getDayOfMonth() - 1) / DAYS_IN_WEEK) - 1; } /** * Checks if the date is within the leap week. * * @return true if this date is in the leap week */ public boolean isLeapWeek() { return isLeapYear() && this.dayOfYear > DAYS_IN_YEAR; } //----------------------------------------------------------------------- @Override public ValueRange range(TemporalField field) { if (field instanceof ChronoField) { if (isSupported(field)) { ChronoField f = (ChronoField) field; switch (f) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: case DAY_OF_WEEK: return ValueRange.of(1, DAYS_IN_WEEK); case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, WEEKS_IN_MONTH + (isLongMonth() ? 1 : 0)); case ALIGNED_WEEK_OF_YEAR: return ValueRange.of(1, WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0)); case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth()); case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear()); case EPOCH_DAY: return EPOCH_DAY_RANGE; case ERA: return ERA_RANGE; case MONTH_OF_YEAR: return MONTH_OF_YEAR_RANGE; default: break; } } else { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } } return super.range(field); } @Override ValueRange rangeAlignedWeekOfMonth() { // never invoked return ValueRange.of(1, WEEKS_IN_MONTH); } @Override Symmetry454Date resolvePrevious(int newYear, int newMonth, int dayOfMonth) { return resolvePreviousValid(newYear, newMonth, dayOfMonth); } //----------------------------------------------------------------------- /** * Gets the chronology of this date, which is the Symmetry454 calendar system. *

* The {@code Chronology} represents the calendar system in use. * The era and other fields in {@link ChronoField} are defined by the chronology. * * @return the Symmetry454 chronology, not null */ @Override public Symmetry454Chronology getChronology() { return INSTANCE; } /** * Gets the era applicable at this date. *

* The Symmetry454 calendar system uses {@link IsoEra}. * * @return the era applicable at this date, not null */ @Override public IsoEra getEra() { return (prolepticYear >= 1 ? IsoEra.CE : IsoEra.BCE); } /** * Returns the length of the month represented by this date. *

* This returns the length of the month in days. * Month lengths do not match those of the ISO calendar system. *

* Most months have 28 days, except for February, May, August, November, and * December in a leap year, each has 35 days. * * @return the length of the month in days */ @Override public int lengthOfMonth() { return isLongMonth() ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH; } private boolean isLongMonth() { return this.month % 3 == 2 || (isLeapYear() && this.month == MONTHS_IN_YEAR); } /** * Returns the length of the year represented by this date. *

* This returns the length of the year in days. * Year lengths do NOT match those of the ISO calendar system. * * @return the length of the year in days: 364 or 371 */ @Override public int lengthOfYear() { return DAYS_IN_YEAR + (isLeapYear() ? DAYS_IN_WEEK : 0); } //------------------------------------------------------------------------- @Override public Symmetry454Date with(TemporalAdjuster adjuster) { return (Symmetry454Date) adjuster.adjustInto(this); } @Override public Symmetry454Date with(TemporalField field, long newValue) { if (field instanceof ChronoField) { if (newValue == 0) { return this; } ChronoField f = (ChronoField) field; getChronology().range(f).checkValidValue(newValue, f); int nval = (int) newValue; switch (f) { case ALIGNED_DAY_OF_WEEK_IN_MONTH: case ALIGNED_DAY_OF_WEEK_IN_YEAR: case DAY_OF_WEEK: range(f).checkValidValue(newValue, field); int dom = ((getDayOfMonth() - 1) / DAYS_IN_WEEK) * DAYS_IN_WEEK; return resolvePreviousValid(prolepticYear, month, dom + nval); case ALIGNED_WEEK_OF_MONTH: range(f).checkValidValue(newValue, field); int d = day % DAYS_IN_WEEK; return resolvePreviousValid(prolepticYear, month, (nval - 1) * DAYS_IN_WEEK + d); case ALIGNED_WEEK_OF_YEAR: range(f).checkValidValue(newValue, field); int newMonth = 1 + ((nval - 1) / WEEKS_IN_MONTH); int newDay = ((nval - 1) % WEEKS_IN_MONTH) * DAYS_IN_WEEK + 1 + ((day - 1) % DAYS_IN_WEEK); return resolvePreviousValid(prolepticYear, newMonth, newDay); case DAY_OF_MONTH: return create(prolepticYear, month, nval); default: break; } } return (Symmetry454Date) super.with(field, newValue); } @Override Symmetry454Date withDayOfYear(int value) { return ofYearDay(prolepticYear, value); } //----------------------------------------------------------------------- @Override public Symmetry454Date plus(TemporalAmount amount) { return (Symmetry454Date) amount.addTo(this); } @Override public Symmetry454Date plus(long amountToAdd, TemporalUnit unit) { return (Symmetry454Date) super.plus(amountToAdd, unit); } @Override public Symmetry454Date minus(TemporalAmount amount) { return (Symmetry454Date) amount.subtractFrom(this); } @Override public Symmetry454Date minus(long amountToSubtract, TemporalUnit unit) { return (Symmetry454Date) super.minus(amountToSubtract, unit); } //------------------------------------------------------------------------- @Override // for covariant return type @SuppressWarnings("unchecked") public ChronoLocalDateTime atTime(LocalTime localTime) { return (ChronoLocalDateTime) super.atTime(localTime); } @Override public long until(Temporal endExclusive, TemporalUnit unit) { return until(Symmetry454Date.from(endExclusive), unit); } /** * Get the number of years from this date to the given day. * * @param end The end date. * @return The number of years from this date to the given day. */ long yearsUntil(Symmetry454Date end) { long startYear = this.prolepticYear * 512L + this.getDayOfYear(); long endYear = end.prolepticYear * 512L + end.getDayOfYear(); return (endYear - startYear) / 512L; } @Override public ChronoPeriod until(ChronoLocalDate endDateExclusive) { Symmetry454Date end = Symmetry454Date.from(endDateExclusive); int years = Math.toIntExact(yearsUntil(end)); // Get to the same "whole" year. Symmetry454Date sameYearEnd = (Symmetry454Date) plusYears(years); int months = (int) sameYearEnd.monthsUntil(end); int days = (int) sameYearEnd.plusMonths(months).daysUntil(end); return getChronology().period(years, months, days); } @Override long weeksUntil(AbstractDate end) { Symmetry454Date endDate = Symmetry454Date.from(end); long startWeek = this.getProlepticWeek() * 8L + this.getDayOfWeek(); long endWeek = endDate.getProlepticWeek() * 8L + endDate.getDayOfWeek(); return (endWeek - startWeek) / 8L; } @Override long monthsUntil(AbstractDate end) { Symmetry454Date date = Symmetry454Date.from(end); long monthStart = this.getProlepticMonth() * 64L + this.getDayOfMonth(); long monthEnd = date.getProlepticMonth() * 64L + date.getDayOfMonth(); return (monthEnd - monthStart) / 64L; } //----------------------------------------------------------------------- @Override public long toEpochDay() { long epochDay = (long) (this.prolepticYear - 1) * DAYS_IN_YEAR + Symmetry454Chronology.getLeapYearsBefore(this.prolepticYear) * DAYS_IN_WEEK + this.dayOfYear - DAYS_0001_TO_1970 - 1; return epochDay; } /** * Display the date in human-readable format. * * @return the string representation */ @Override public String toString() { StringBuilder buf = new StringBuilder(30); return buf.append(getChronology().toString()) .append(' ') .append(getEra()) .append(' ') .append(getYearOfEra()) .append(this.month < 10 && this.month > 0 ? "/0" : '/') .append(this.month) .append(this.day < 10 ? "/0" : '/') .append(this.day) .toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/package-info.java000066400000000000000000000033111343451174100273200ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Additional chronologies (calendar systems) that extend {@code java.time.*}. */ package org.threeten.extra.chrono; threeten-extra-1.5.0/src/main/java/org/threeten/extra/package-info.java000066400000000000000000000032611343451174100260340ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Value types and utilities that extend {@code java.time.*}. */ package org.threeten.extra; threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/000077500000000000000000000000001343451174100237325ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/SystemUtcRules.java000066400000000000000000000304601343451174100275530ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Serializable; import java.io.StreamCorruptedException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.temporal.JulianFields; import java.util.ArrayList; import java.util.Arrays; import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * System default UTC rules. * *

Implementation Requirements:

* This class is immutable and thread-safe. */ final class SystemUtcRules extends UtcRules implements Serializable { /** * The leap seconds config file. */ private static final String LEAP_SECONDS_TXT = "org/threeten/extra/scale/LeapSeconds.txt"; /** * Leap second file format. */ private static final Pattern LEAP_FILE_FORMAT = Pattern.compile("([0-9-]{10})[ ]+([0-9]+)"); /** * Serialization version. */ private static final long serialVersionUID = 7594178360693417218L; /** * Singleton. */ static final SystemUtcRules INSTANCE = new SystemUtcRules(); /** * The table of leap second dates. */ private AtomicReference dataRef = new AtomicReference(loadLeapSeconds()); /** Data holder. */ private static final class Data implements Serializable { /** Serialization version. */ private static final long serialVersionUID = -3655687912882817265L; /** Constructor. */ private Data(long[] dates, int[] offsets, long[] taiSeconds) { super(); this.dates = dates; this.offsets = offsets; this.taiSeconds = taiSeconds; } /** The table of leap second date when the leap second occurs. */ private final long[] dates; /** The table of TAI offset after the leap second. */ private final int[] offsets; /** The table of TAI second when the new offset starts. */ private final long[] taiSeconds; /** * @return The modified Julian Date of the newest leap second */ public long getNewestDate() { return dates[dates.length - 1]; } } //----------------------------------------------------------------------- /** * Restricted constructor. */ private SystemUtcRules() { } /** * Resolves singleton. * * @return the resolved instance, not null */ private Object readResolve() { return INSTANCE; } //----------------------------------------------------------------------- /** * Adds a new leap second to these rules. * * @param mjDay the Modified Julian Day that the leap second occurs at the end of * @param leapAdjustment the leap seconds to add/remove at the end of the day, either -1 or 1 * @throws IllegalArgumentException if the leap adjustment is invalid * @throws IllegalArgumentException if the day is before or equal the last known leap second day * and the definition does not match a previously registered leap * @throws ConcurrentModificationException if another thread updates the rules at the same time */ void register(long mjDay, int leapAdjustment) { if (leapAdjustment != -1 && leapAdjustment != 1) { throw new IllegalArgumentException("Leap adjustment must be -1 or 1"); } Data data = dataRef.get(); int pos = Arrays.binarySearch(data.dates, mjDay); int currentAdj = pos > 0 ? data.offsets[pos] - data.offsets[pos - 1] : 0; if (currentAdj == leapAdjustment) { return; // matches previous definition } if (mjDay <= data.dates[data.dates.length - 1]) { throw new IllegalArgumentException("Date must be after the last configured leap second date"); } long[] dates = Arrays.copyOf(data.dates, data.dates.length + 1); int[] offsets = Arrays.copyOf(data.offsets, data.offsets.length + 1); long[] taiSeconds = Arrays.copyOf(data.taiSeconds, data.taiSeconds.length + 1); int offset = offsets[offsets.length - 2] + leapAdjustment; dates[dates.length - 1] = mjDay; offsets[offsets.length - 1] = offset; taiSeconds[taiSeconds.length - 1] = tai(mjDay, offset); Data newData = new Data(dates, offsets, taiSeconds); if (dataRef.compareAndSet(data, newData) == false) { throw new ConcurrentModificationException("Unable to update leap second rules as they have already been updated"); } } //----------------------------------------------------------------------- @Override public String getName() { return "System"; } @Override public int getLeapSecondAdjustment(long mjDay) { Data data = dataRef.get(); int pos = Arrays.binarySearch(data.dates, mjDay); return pos > 0 ? data.offsets[pos] - data.offsets[pos - 1] : 0; } @Override public int getTaiOffset(long mjDay) { Data data = dataRef.get(); int pos = Arrays.binarySearch(data.dates, mjDay); pos = (pos < 0 ? ~pos : pos); return pos > 0 ? data.offsets[pos - 1] : 10; } @Override public long[] getLeapSecondDates() { Data data = dataRef.get(); return data.dates.clone(); } //----------------------------------------------------------------------- @Override public UtcInstant convertToUtc(TaiInstant taiInstant) { Data data = dataRef.get(); long[] mjds = data.dates; long[] tais = data.taiSeconds; int pos = Arrays.binarySearch(tais, taiInstant.getTaiSeconds()); pos = (pos >= 0 ? pos : ~pos - 1); int taiOffset = (pos >= 0 ? data.offsets[pos] : 10); long adjustedTaiSecs = taiInstant.getTaiSeconds() - taiOffset; long mjd = Math.floorDiv(adjustedTaiSecs, SECS_PER_DAY) + OFFSET_MJD_TAI; long nod = Math.floorMod(adjustedTaiSecs, SECS_PER_DAY) * NANOS_PER_SECOND + taiInstant.getNano(); long mjdNextRegionStart = (pos + 1 < mjds.length ? mjds[pos + 1] + 1 : Long.MAX_VALUE); if (mjd == mjdNextRegionStart) { // in leap second mjd--; nod = SECS_PER_DAY * NANOS_PER_SECOND + (nod / NANOS_PER_SECOND) * NANOS_PER_SECOND + nod % NANOS_PER_SECOND; } return UtcInstant.ofModifiedJulianDay(mjd, nod); } //----------------------------------------------------------------------- /** * Loads the rules from files in the class loader, often jar files. * * @return the list of loaded rules, not null * @throws Exception if an error occurs */ private static Data loadLeapSeconds() { Data bestData = null; URL url = null; try { // this is the new location of the file, working on Java 8, Java 9 class path and Java 9 module path Enumeration en = Thread.currentThread().getContextClassLoader().getResources("META-INF/" + LEAP_SECONDS_TXT); while (en.hasMoreElements()) { url = en.nextElement(); Data candidate = loadLeapSeconds(url); if (bestData == null || candidate.getNewestDate() > bestData.getNewestDate()) { bestData = candidate; } } // this location does not work on Java 9 module path because the resource is encapsulated en = Thread.currentThread().getContextClassLoader().getResources(LEAP_SECONDS_TXT); while (en.hasMoreElements()) { url = en.nextElement(); Data candidate = loadLeapSeconds(url); if (bestData == null || candidate.getNewestDate() > bestData.getNewestDate()) { bestData = candidate; } } // this location is the canonical one, and class-based loading works on Java 9 module path url = SystemUtcRules.class.getResource("/" + LEAP_SECONDS_TXT); if (url != null) { Data candidate = loadLeapSeconds(url); if (bestData == null || candidate.getNewestDate() > bestData.getNewestDate()) { bestData = candidate; } } } catch (Exception ex) { throw new RuntimeException("Unable to load time-zone rule data: " + url, ex); } if (bestData == null) { // no data on classpath, but we allow manual registration of leap seconds // setup basic known data - MJD 1972-01-01 is 41317L, where offset was 10 bestData = new Data(new long[] {41317L}, new int[] {10}, new long[] {tai(41317L, 10)}); } return bestData; } /** * Loads the leap second rules from a URL, often in a jar file. * * @param url the jar file to load, not null * @throws Exception if an error occurs */ private static Data loadLeapSeconds(URL url) throws ClassNotFoundException, IOException { List lines; try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) { lines = reader.lines().collect(Collectors.toList()); } List dates = new ArrayList<>(); List offsets = new ArrayList<>(); for (String line : lines) { line = line.trim(); if (line.isEmpty() || line.startsWith("#")) { continue; } Matcher matcher = LEAP_FILE_FORMAT.matcher(line); if (matcher.matches() == false) { throw new StreamCorruptedException("Invalid leap second file"); } dates.add(LocalDate.parse(matcher.group(1)).getLong(JulianFields.MODIFIED_JULIAN_DAY)); offsets.add(Integer.valueOf(matcher.group(2))); } long[] datesData = new long[dates.size()]; int[] offsetsData = new int[dates.size()]; long[] taiData = new long[dates.size()]; for (int i = 0; i < datesData.length; i++) { datesData[i] = dates.get(i); offsetsData[i] = offsets.get(i); taiData[i] = tai(datesData[i], offsetsData[i]); } return new Data(datesData, offsetsData, taiData); } /** * Gets the TAI seconds for the start of the day following the day passed in. * * @param changeMjd the MJD that the leap second is added to * @param offset the new offset after the leap * @return the TAI seconds */ private static long tai(long changeMjd, int offset) { return (changeMjd + 1 - OFFSET_MJD_TAI) * SECS_PER_DAY + offset; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/TaiInstant.java000066400000000000000000000465511343451174100266660ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * An instantaneous point on the time-line measured in the TAI time-scale. *

* The java.time classes use the Java time-scale for simplicity. * That scale works on the assumption that the time-line is simple, there are no leap-seconds * and there are always 24 * 60 * 60 seconds in a day. Unfortunately, the Earth's rotation * is not straightforward, and a solar day does not match this definition. *

* This class is an alternative representation based on the TAI time-scale. * TAI is a single incrementing count of SI seconds. * There are no leap seconds or other discontinuities. *

* As a result of the simple definition, this time-scale would make an excellent timestamp. * However, there are, at the time of writing, few easy ways to obtain an accurate TAI instant, * but it is relatively easy to obtain a GPS instant. * GPS and TAI differ by the fixed amount of 19 seconds. *

* The duration between two points on the TAI time-scale is calculated solely using this class. * Do not use the {@code between} method on {@code Duration} as that will lose information. * Instead use {@link #durationUntil(TaiInstant)} on this class. *

* It is intended that most applications will use the {@code Instant} class * which uses the UTC-SLS mapping from UTC to guarantee 86400 seconds per day. * Specialist applications with access to an accurate time-source may find this class useful. * *

Time-scale

*

* The TAI time-scale is a very simple well-regarded representation of time. * The scale is defined using atomic clocks counting SI seconds. * It has proceeded in a continuous uninterrupted manner since the defined * epoch of {@code 1958-01-01T00:00:00(TAI)}. * There are no leap seconds or other discontinuities. *

* This class may be used for instants in the far past and far future. * Since some instants will be prior to 1958, it is not strictly an implementation of TAI. * Instead, it is a proleptic time-scale based on TAI and equivalent to it since 1958. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class TaiInstant implements Comparable, Serializable { // does not implement Temporal as that would enable methods like // Duration.between which gives the wrong answer due to lossy conversion /** * Constant for nanos per second. */ private static final int NANOS_PER_SECOND = 1000000000; /** * Parse regex. */ private static final Pattern PARSER = Pattern.compile("([-]?[0-9]+)\\.([0-9]{9})s[(]TAI[)]"); /** * Serialization version. */ private static final long serialVersionUID = 2133469726395847026L; /** * The number of seconds from the epoch of 1958-01-01T00:00:00(TAI). */ private final long seconds; /** * The number of nanoseconds, later along the time-line, from the seconds field. * This is always positive, and never exceeds 999,999,999. */ private final int nanos; //----------------------------------------------------------------------- /** * Obtains an instance of {@code TaiInstant} from the number of seconds from * the TAI epoch of 1958-01-01T00:00:00(TAI) with a nanosecond fraction of second. *

* This method allows an arbitrary number of nanoseconds to be passed in. * The factory will alter the values of the second and nanosecond in order * to ensure that the stored nanosecond is in the range 0 to 999,999,999. * For example, the following will result in the exactly the same instant: *

     *  TaiInstant.ofTaiSeconds(3, 1);
     *  TaiInstant.ofTaiSeconds(4, -999999999);
     *  TaiInstant.ofTaiSeconds(2, 1000000001);
     * 
* * @param taiSeconds the number of seconds from the epoch of 1958-01-01T00:00:00(TAI) * @param nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative * @return the TAI instant, not null * @throws ArithmeticException if numeric overflow occurs */ public static TaiInstant ofTaiSeconds(long taiSeconds, long nanoAdjustment) { long secs = Math.addExact(taiSeconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND)); int nos = (int) Math.floorMod(nanoAdjustment, NANOS_PER_SECOND); // safe cast return new TaiInstant(secs, nos); } /** * Obtains an instance of {@code TaiInstant} from an {@code Instant}. *

* Converting a UTC-SLS instant to TAI requires leap second rules. * This method uses the latest available system rules. * The conversion first maps from UTC-SLS to UTC, then converts to TAI. *

* Conversion from an {@code Instant} will not be completely accurate near * a leap second in accordance with UTC-SLS. * * @param instant the instant to convert, not null * @return the TAI instant, not null * @throws DateTimeException if the range of {@code TaiInstant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public static TaiInstant of(Instant instant) { return UtcRules.system().convertToTai(instant); } /** * Obtains an instance of {@code TaiInstant} from a {@code UtcInstant}. *

* Converting a UTC instant to TAI requires leap second rules. * This method uses the latest available system rules. *

* The {@code TaiInstant} will represent exactly the same point on the * time-line as per the available leap-second rules. * If the leap-second rules change then conversion back to UTC may * result in a different instant. * * @param instant the instant to convert, not null * @return the TAI instant, not null * @throws DateTimeException if the range of {@code TaiInstant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public static TaiInstant of(UtcInstant instant) { return UtcRules.system().convertToTai(instant); } //------------------------------------------------------------------------- /** * Obtains an instance of {@code TaiInstant} from a text string. *

* The following format is accepted: *

    *
  • {@code {seconds}.{nanosOfSecond}s(TAI)} *
*

* The accepted format is strict. * The seconds part must contain only numbers and a possible leading negative sign. * The nanoseconds part must contain exactly nine digits. * The trailing literal must be exactly specified. * This format parses the {@code toString} format. * * @param text the text to parse such as "12345.123456789s(TAI)", not null * @return the parsed instant, not null * @throws DateTimeParseException if the text cannot be parsed */ @FromString public static TaiInstant parse(CharSequence text) { Objects.requireNonNull(text, "text"); Matcher matcher = PARSER.matcher(text); if (matcher.matches()) { try { long seconds = Long.parseLong(matcher.group(1)); long nanos = Long.parseLong(matcher.group(2)); return TaiInstant.ofTaiSeconds(seconds, nanos); } catch (NumberFormatException ex) { throw new DateTimeParseException("The text could not be parsed", text, 0, ex); } } throw new DateTimeParseException("The text could not be parsed", text, 0); } //----------------------------------------------------------------------- /** * Constructs an instance. * * @param taiSeconds the number of TAI seconds from the epoch * @param nanoOfSecond the nanoseconds within the second, from 0 to 999,999,999 */ private TaiInstant(long taiSeconds, int nanoOfSecond) { super(); this.seconds = taiSeconds; this.nanos = nanoOfSecond; } //----------------------------------------------------------------------- /** * Gets the number of seconds from the TAI epoch of 1958-01-01T00:00:00(TAI). *

* The TAI second count is a simple incrementing count of seconds where * second 0 is 1958-01-01T00:00:00(TAI). * The nanosecond part of the day is returned by {@code getNanosOfSecond}. * * @return the seconds from the epoch of 1958-01-01T00:00:00(TAI) */ public long getTaiSeconds() { return seconds; } /** * Returns a copy of this {@code TaiInstant} with the number of seconds * from the TAI epoch of 1958-01-01T00:00:00(TAI). *

* The TAI second count is a simple incrementing count of seconds where * second 0 is 1958-01-01T00:00:00(TAI). * The nanosecond part of the day is returned by {@code getNanosOfSecond}. *

* This instance is immutable and unaffected by this method call. * * @param taiSeconds the number of seconds from the epoch of 1958-01-01T00:00:00(TAI) * @return a {@code TaiInstant} based on this instant with the requested second, not null */ public TaiInstant withTaiSeconds(long taiSeconds) { return ofTaiSeconds(taiSeconds, nanos); } /** * Gets the number of nanoseconds, later along the time-line, from the start * of the second. *

* The nanosecond-of-second value measures the total number of nanoseconds from * the second returned by {@code getTaiSeconds()}. * * @return the nanoseconds within the second, from 0 to 999,999,999 */ public int getNano() { return nanos; } /** * Returns a copy of this {@code TaiInstant} with the nano-of-second value changed. *

* The nanosecond-of-second value measures the total number of nanoseconds from * the second returned by {@code getTaiSeconds()}. *

* This instance is immutable and unaffected by this method call. * * @param nanoOfSecond the nano-of-second, from 0 to 999,999,999 * @return a {@code TaiInstant} based on this instant with the requested nano-of-second, not null * @throws IllegalArgumentException if nanoOfSecond is out of range */ public TaiInstant withNano(int nanoOfSecond) { if (nanoOfSecond < 0 || nanoOfSecond >= NANOS_PER_SECOND) { throw new IllegalArgumentException("NanoOfSecond must be from 0 to 999,999,999"); } return ofTaiSeconds(seconds, nanoOfSecond); } //----------------------------------------------------------------------- /** * Returns a copy of this instant with the specified duration added. *

* The duration is added using simple addition of the seconds and nanoseconds * in the duration to the seconds and nanoseconds of this instant. * As a result, the duration is treated as being measured in TAI compatible seconds * for the purpose of this method. *

* This instance is immutable and unaffected by this method call. * * @param duration the duration to add, not null * @return a {@code TaiInstant} based on this instant with the duration added, not null * @throws ArithmeticException if the calculation exceeds the supported range */ public TaiInstant plus(Duration duration) { long secsToAdd = duration.getSeconds(); int nanosToAdd = duration.getNano(); if ((secsToAdd | nanosToAdd) == 0) { return this; } long secs = Math.addExact(seconds, secsToAdd); long nanoAdjustment = ((long) nanos) + nanosToAdd; // safe int+int return ofTaiSeconds(secs, nanoAdjustment); } //----------------------------------------------------------------------- /** * Returns a copy of this instant with the specified duration subtracted. *

* The duration is subtracted using simple subtraction of the seconds and nanoseconds * in the duration from the seconds and nanoseconds of this instant. * As a result, the duration is treated as being measured in TAI compatible seconds * for the purpose of this method. *

* This instance is immutable and unaffected by this method call. * * @param duration the duration to subtract, not null * @return a {@code TaiInstant} based on this instant with the duration subtracted, not null * @throws ArithmeticException if the calculation exceeds the supported range */ public TaiInstant minus(Duration duration) { long secsToSubtract = duration.getSeconds(); int nanosToSubtract = duration.getNano(); if ((secsToSubtract | nanosToSubtract) == 0) { return this; } long secs = Math.subtractExact(seconds, secsToSubtract); long nanoAdjustment = ((long) nanos) - nanosToSubtract; // safe int+int return ofTaiSeconds(secs, nanoAdjustment); } //----------------------------------------------------------------------- /** * Returns the duration between this instant and the specified instant. *

* This calculates the duration between this instant and another based on * the TAI time-scale. Adding the duration to this instant using {@link #plus} * will always result in an instant equal to the specified instant. * * @param otherInstant the instant to calculate the duration until, not null * @return the duration until the specified instant, may be negative, not null * @throws ArithmeticException if the calculation exceeds the supported range */ public Duration durationUntil(TaiInstant otherInstant) { long durSecs = Math.subtractExact(otherInstant.seconds, seconds); long durNanos = otherInstant.nanos - nanos; return Duration.ofSeconds(durSecs, durNanos); } //----------------------------------------------------------------------- /** * Converts this instant to an {@code Instant}. *

* Converting a TAI instant to UTC-SLS requires leap second rules. * This method uses the latest available system rules. * The conversion first maps from TAI to UTC, then converts to UTC-SLS. *

* Conversion to an {@code Instant} will not be completely accurate near * a leap second in accordance with UTC-SLS. * * @return an {@code Instant} representing the best approximation of this instant, not null * @throws DateTimeException if the range of {@code Instant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public Instant toInstant() { return UtcRules.system().convertToInstant(this); } /** * Converts this instant to a {@code UtcInstant}. *

* Converting a TAI instant to UTC requires leap second rules. * This method uses the latest available system rules. *

* The {@code UtcInstant} will represent exactly the same point on the * time-line as per the available leap-second rules. * If the leap-second rules change then conversion back to TAI may * result in a different instant. * * @return a {@code UtcInstant} representing the same instant, not null * @throws DateTimeException if the range of {@code UtcInstant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public UtcInstant toUtcInstant() { return UtcRules.system().convertToUtc(this); } //----------------------------------------------------------------------- /** * Compares this instant to another based on the time-line. * * @param otherInstant the other instant to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(TaiInstant otherInstant) { int cmp = Long.compare(seconds, otherInstant.seconds); if (cmp != 0) { return cmp; } return nanos - otherInstant.nanos; } //----------------------------------------------------------------------- /** * Checks if this instant is equal to the specified {@code TaiInstant}. * * @param otherInstant the other instant, null returns false * @return true if the other instant is equal to this one */ @Override public boolean equals(Object otherInstant) { if (this == otherInstant) { return true; } if (otherInstant instanceof TaiInstant) { TaiInstant other = (TaiInstant) otherInstant; return this.seconds == other.seconds && this.nanos == other.nanos; } return false; } /** * Returns a hash code for this instant. * * @return a suitable hash code */ @Override public int hashCode() { // TODO: Evaluate hash code return ((int) (seconds ^ (seconds >>> 32))) + 51 * nanos; } //----------------------------------------------------------------------- /** * A string representation of this instant. *

* The string is formatted as {@code {seconds).(nanosOfSecond}s(TAI)}. * At least one second digit will be present. * The nanoseconds will always be nine digits. * * @return a representation of this instant, not null */ @Override @ToString public String toString() { StringBuilder buf = new StringBuilder(); buf.append(seconds); int pos = buf.length(); buf.append(nanos + NANOS_PER_SECOND); buf.setCharAt(pos, '.'); buf.append("s(TAI)"); return buf.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/TimeSource.java000066400000000000000000000103551343451174100266600ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import java.time.Clock; import java.time.Instant; /** * A clock that provides the current UTC or TAI instant. *

* This clock differs from {@link Clock} in providing access to the current instant * in the UTC and TAI time-scales. However, there is currently no implementation that * provides accurate UTC or TAI. * *

Implementation Requirements:

* This abstract class must be implemented with care to ensure other classes in * the framework operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. *

* The principal methods are defined to allow the throwing of an exception. * In normal use, no exceptions will be thrown, however one possible implementation would be to * obtain the time from a central time server across the network. Obviously, in this case the * lookup could fail, and so the method is permitted to throw an exception. *

* Subclass implementations should implement {@code Serializable} wherever possible. * They should also be immutable and thread-safe, implementing {@code equals()}, * {@code hashCode()} and {@code toString()} based on their state. */ public interface TimeSource { /** * Gets the current {@code Instant}. *

* The instant returned is based on the Java time-scale defined in {@link Instant}. * An accurate implementation of this interface will return the correct instant * as per that definition. * * @return the current {@code Instant} from this time-source, not null * @throws RuntimeException if the instant cannot be obtained, not thrown by most implementations */ Instant instant(); /** * Gets the current {@code UtcInstant}. *

* The UTC time-scale is the current world civil time and includes leap seconds. * An accurate implementation of this interface will return the correct UTC instant. * * @return the current {@code UtcInstant} from this time-source, not null * @throws RuntimeException if the instant cannot be obtained, not thrown by most implementations */ UtcInstant utcInstant(); /** * Gets the current {@code TaiInstant}. *

* The TAI time-scale is a simple incrementing number of seconds from the TAI epoch of 1958-01-01(TAI). * It ignores all human concepts of time such as days. * An accurate implementation of this interface will return the correct TAI instant. * * @return the current {@code TaiInstant} from this time-source, not null * @throws RuntimeException if the instant cannot be obtained, not thrown by most implementations */ TaiInstant taiInstant(); } threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/UtcInstant.java000066400000000000000000000530171343451174100266770ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static org.threeten.extra.scale.UtcRules.NANOS_PER_SECOND; import static org.threeten.extra.scale.UtcRules.OFFSET_MJD_EPOCH; import static org.threeten.extra.scale.UtcRules.SECS_PER_DAY; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.JulianFields; import java.time.temporal.TemporalAccessor; import org.joda.convert.FromString; import org.joda.convert.ToString; /** * An instantaneous point on the time-line measured in the UTC time-scale * with leap seconds. *

* The java.time classes use the Java time-scale for simplicity. * That scale works on the assumption that the time-line is simple, there are no leap-seconds * and there are always 24 * 60 * 60 seconds in a day. Unfortunately, the Earth's rotation * is not straightforward, and a solar day does not match this definition. *

* This class is an alternative representation based on the UTC time-scale which * includes leap-seconds. Leap-seconds are additional seconds that are inserted into the * year-month-day-hour-minute-second time-line in order to keep UTC in line with the solar day. * When a leap second occurs, an accurate clock will show the time {@code 23:59:60} just before midnight. *

* Leap-seconds are announced in advance, typically at least six months. * The {@link UtcRules} class models which dates have leap-seconds. * All the methods on this class use the latest available system rules. *

* The system leap-second rules fix the start point of UTC as 1972. * This date was chosen as UTC was more complex before 1972. *

* The duration between two points on the UTC time-scale is calculated solely using this class. * Do not use the {@code between} method on {@code Duration} as that will lose information. * Instead use {@link #durationUntil(UtcInstant)} on this class. *

* It is intended that most applications will use the {@code Instant} class * which uses the UTC-SLS mapping from UTC to guarantee 86400 seconds per day. * Specialist applications with access to an accurate time-source may find this class useful. * *

Time-scale

*

* The length of the solar day is the standard way that humans measure time. * As the Earth's rotation changes, the length of the day varies. * In general, a solar day is slightly longer than 86400 SI seconds. * The actual length is not predictable and can only be determined by measurement. * The UT1 time-scale captures these measurements. *

* The UTC time-scale is a standard approach to bundle up all the additional fractions * of a second from UT1 into whole seconds, known as leap-seconds. * A leap-second may be added or removed depending on the Earth's rotational changes. * If it is removed, then the relevant date will have no time of {@code 23:59:59}. * If it is added, then the relevant date will have an extra second of {@code 23:59:60}. *

* The modern UTC time-scale was introduced in 1972, introducing the concept of whole leap-seconds. * Between 1958 and 1972, the definition of UTC was complex, with minor sub-second leaps and * alterations to the length of the notional second. *

* This class may be used for instants in the far past and far future. * Since some instants will be prior to 1972, it is not strictly an implementation of UTC. * Instead, it is a proleptic time-scale based on TAI and equivalent to it since 1972. * *

Implementation Requirements:

* This class is immutable and thread-safe. *

* This class must be treated as a value type. Do not synchronize, rely on the * identity hash code or use the distinction between equals() and ==. */ public final class UtcInstant implements Comparable, Serializable { // does not implement Temporal as that would enable methods like // Duration.between which gives the wrong answer due to lossy conversion /** * Serialization version. */ private static final long serialVersionUID = 2600294095511836210L; /** * The Modified Julian Day, from the epoch of 1858-11-17. */ private final long mjDay; /** * The number of nanoseconds, later along the time-line, from the MJD field. * This is always positive and includes leap seconds. */ private final long nanoOfDay; //----------------------------------------------------------------------- /** * Obtains an instance of {@code UtcInstant} from a Modified Julian Day with * a nanosecond fraction of day. *

* Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17. * Nanosecond-of-day is a simple count of nanoseconds from the start of the day * including any additional leap-second. * This method validates the nanosecond-of-day value against the Modified Julian Day. *

* The nanosecond-of-day value has a valid range from {@code 0} to * {@code 86,400,000,000,000 - 1} on most days, and a larger or smaller range * on leap-second days. *

* The nanosecond value must be positive even for negative values of Modified * Julian Day. One nanosecond before Modified Julian Day zero will be * {@code -1} days and the maximum nanosecond value. * * @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17) * @param nanoOfDay the nanoseconds within the day, including leap seconds * @return the UTC instant, not null * @throws IllegalArgumentException if nanoOfDay is out of range */ public static UtcInstant ofModifiedJulianDay(long mjDay, long nanoOfDay) { UtcRules.system().validateModifiedJulianDay(mjDay, nanoOfDay); return new UtcInstant(mjDay, nanoOfDay); } /** * Obtains an instance of {@code UtcInstant} from an {@code Instant}. *

* Converting a UTC-SLS instant to UTC requires leap second rules. * This method uses the latest available system rules. *

* Conversion from an {@code Instant} will not be completely accurate near * a leap second in accordance with UTC-SLS. * * @param instant the instant to convert, not null * @return the UTC instant, not null * @throws DateTimeException if the range of {@code UtcInstant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public static UtcInstant of(Instant instant) { return UtcRules.system().convertToUtc(instant); } /** * Obtains an instance of {@code UtcInstant} from a {@code TaiInstant}. *

* Converting a TAI instant to UTC requires leap second rules. * This method uses the latest available system rules. *

* The {@code UtcInstant} will represent exactly the same point on the * time-line as per the available leap-second rules. * If the leap-second rules change then conversion back to TAI may * result in a different instant. * * @param instant the instant to convert, not null * @return the UTC instant, not null * @throws DateTimeException if the range of {@code UtcInstant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public static UtcInstant of(TaiInstant instant) { return UtcRules.system().convertToUtc(instant); } //------------------------------------------------------------------------- /** * Obtains an instance of {@code UtcInstant} from a text string * {@code 2007-12-03T10:15:30.00Z}. *

* The string must represent a valid instant in UTC and is parsed using * {@link DateTimeFormatter#ISO_INSTANT} with leap seconds handled. * * @param text the text to parse such as "12345.123456789s(TAI)", not null * @return the parsed instant, not null * @throws DateTimeParseException if the text cannot be parsed * @throws DateTimeException if parsed text represents an invalid leap second */ @FromString public static UtcInstant parse(CharSequence text) { TemporalAccessor parsed = DateTimeFormatter.ISO_INSTANT.parse(text); long epochSecond = parsed.getLong(INSTANT_SECONDS); long nanoOfSecond = parsed.getLong(NANO_OF_SECOND); boolean leap = parsed.query(DateTimeFormatter.parsedLeapSecond()); long epochDay = Math.floorDiv(epochSecond, SECS_PER_DAY); long mjd = epochDay + OFFSET_MJD_EPOCH; long nanoOfDay = Math.floorMod(epochSecond, SECS_PER_DAY) * NANOS_PER_SECOND + nanoOfSecond; if (leap) { nanoOfDay += NANOS_PER_SECOND; } return UtcInstant.ofModifiedJulianDay(mjd, nanoOfDay); } //----------------------------------------------------------------------- /** * Constructs an instance. * * @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17) * @param nanoOfDay the nanoseconds within the day, including leap seconds */ private UtcInstant(long mjDay, long nanoOfDay) { super(); this.mjDay = mjDay; this.nanoOfDay = nanoOfDay; } //----------------------------------------------------------------------- /** * Gets the Modified Julian Day (MJD). *

* The Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17. * The nanosecond part of the day is returned by {@code getNanosOfDay}. * The day varies in length, being one second longer on a leap day. * * @return the Modified Julian Day based on the epoch 1858-11-17 */ public long getModifiedJulianDay() { return mjDay; } /** * Returns a copy of this {@code UtcInstant} with the Modified Julian Day (MJD) altered. *

* The Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17. * The nanosecond part of the day is returned by {@code getNanosOfDay}. * The day varies in length, being one second longer on a leap day. *

* This instance is immutable and unaffected by this method call. * * @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17) * @return a {@code UtcInstant} based on this instant with the requested day, not null * @throws DateTimeException if nanoOfDay becomes invalid */ public UtcInstant withModifiedJulianDay(long mjDay) { return UtcInstant.ofModifiedJulianDay(mjDay, nanoOfDay); } /** * Gets the number of nanoseconds, later along the time-line, from the start * of the Modified Julian Day. *

* The nanosecond-of-day value measures the total number of nanoseconds within * the day from the start of the day returned by {@code getModifiedJulianDay}. * This value will include any additional leap seconds. * * @return the nanoseconds within the day, including leap seconds */ public long getNanoOfDay() { return nanoOfDay; } /** * Returns a copy of this {@code UtcInstant} with the nano-of-day altered. *

* The nanosecond-of-day value measures the total number of nanoseconds within * the day from the start of the day returned by {@code getModifiedJulianDay}. * This value will include any additional leap seconds. *

* This instance is immutable and unaffected by this method call. * * @param nanoOfDay the nanoseconds within the day, including leap seconds * @return a {@code UtcInstant} based on this instant with the requested nano-of-day, not null * @throws DateTimeException if the nanoOfDay value is invalid */ public UtcInstant withNanoOfDay(long nanoOfDay) { return UtcInstant.ofModifiedJulianDay(mjDay, nanoOfDay); } //----------------------------------------------------------------------- /** * Checks if the instant is within a leap second. *

* This method returns true when an accurate clock would return a seconds * field of 60. * * @return true if this instant is within a leap second */ public boolean isLeapSecond() { return nanoOfDay > SECS_PER_DAY * NANOS_PER_SECOND; } //----------------------------------------------------------------------- /** * Returns a copy of this instant with the specified duration added. *

* The duration is added using simple addition of the seconds and nanoseconds * in the duration to the seconds and nanoseconds of this instant. * As a result, the duration is treated as being measured in TAI compatible seconds * for the purpose of this method. *

* This instance is immutable and unaffected by this method call. * * @param duration the duration to add, not null * @return a {@code UtcInstant} with the duration added, not null * @throws ArithmeticException if the calculation exceeds the supported range */ public UtcInstant plus(Duration duration) { return UtcInstant.of(toTaiInstant().plus(duration)); } //----------------------------------------------------------------------- /** * Returns a copy of this instant with the specified duration subtracted. *

* The duration is subtracted using simple subtraction of the seconds and nanoseconds * in the duration from the seconds and nanoseconds of this instant. * As a result, the duration is treated as being measured in TAI compatible seconds * for the purpose of this method. *

* This instance is immutable and unaffected by this method call. * * @param duration the duration to subtract, not null * @return a {@code UtcInstant} with the duration subtracted, not null * @throws ArithmeticException if the calculation exceeds the supported range */ public UtcInstant minus(Duration duration) { return UtcInstant.of(toTaiInstant().minus(duration)); } //----------------------------------------------------------------------- /** * Returns the duration between this instant and the specified instant. *

* This calculates the duration between this instant and another based on * the UTC time-scale. Any leap seconds that occur will be included in the duration. * Adding the duration to this instant using {@link #plus} will always result * in an instant equal to the specified instant. * * @param utcInstant the instant to calculate the duration until, not null * @return the duration until the specified instant, may be negative, not null * @throws ArithmeticException if the calculation exceeds the supported range */ public Duration durationUntil(UtcInstant utcInstant) { TaiInstant thisTAI = toTaiInstant(); TaiInstant otherTAI = utcInstant.toTaiInstant(); return thisTAI.durationUntil(otherTAI); } //----------------------------------------------------------------------- /** * Converts this instant to an {@code Instant}. *

* Converting a UTC instant to UTC-SLS requires leap second rules. * This method uses the latest available system rules. *

* Conversion to an {@code Instant} will not be completely accurate near * a leap second in accordance with UTC-SLS. * * @return an {@code Instant} representing the best approximation of this instant, not null * @throws DateTimeException if the range of {@code Instant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public Instant toInstant() { return UtcRules.system().convertToInstant(this); } /** * Converts this instant to a {@code TaiInstant}. *

* Converting a UTC instant to TAI requires leap second rules. * This method uses the latest available system rules. *

* The {@code TaiInstant} will represent exactly the same point on the * time-line as per the available leap-second rules. * If the leap-second rules change then conversion back to UTC may * result in a different instant. * * @return a {@code TaiInstant} representing the same instant, not null * @throws DateTimeException if the range of {@code TaiInstant} is exceeded * @throws ArithmeticException if numeric overflow occurs */ public TaiInstant toTaiInstant() { return UtcRules.system().convertToTai(this); } //----------------------------------------------------------------------- /** * Compares this instant to another based on the time-line. *

* The comparison is based first on the Modified Julian Day, then on the nano-of-day. * It is "consistent with equals", as defined by {@link Comparable}. * * @param otherInstant the other instant to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override public int compareTo(UtcInstant otherInstant) { int cmp = Long.compare(mjDay, otherInstant.mjDay); if (cmp != 0) { return cmp; } return Long.compare(nanoOfDay, otherInstant.nanoOfDay); } //----------------------------------------------------------------------- /** * Checks if this instant is equal to the specified {@code UtcInstant}. *

* The comparison is based on the Modified Julian Day, then on the nano-of-day. * * @param otherInstant the other instant, null returns false * @return true if the other instant is equal to this one */ @Override public boolean equals(Object otherInstant) { if (this == otherInstant) { return true; } if (otherInstant instanceof UtcInstant) { UtcInstant other = (UtcInstant) otherInstant; return this.mjDay == other.mjDay && this.nanoOfDay == other.nanoOfDay; } return false; } /** * Returns a hash code for this instant. * * @return a suitable hash code */ @Override public int hashCode() { return ((int) (mjDay ^ (mjDay >>> 32))) + 51 * ((int) (nanoOfDay ^ (nanoOfDay >>> 32))); } //----------------------------------------------------------------------- /** * A string representation of this instant. *

* The string is formatted using ISO-8601. * The output includes seconds, 9 nanosecond digits and a trailing 'Z'. * The time-of-day will be 23:59:60 during a positive leap second. * * @return a representation of this instant, not null */ @Override @ToString public String toString() { LocalDate date = LocalDate.MAX.with(JulianFields.MODIFIED_JULIAN_DAY, mjDay); // TODO: capacity/import issues StringBuilder buf = new StringBuilder(30); int sod = (int) (nanoOfDay / NANOS_PER_SECOND); int hourValue = sod / (60 * 60); int minuteValue = (sod / 60) % 60; int secondValue = sod % 60; int nanoValue = (int) (nanoOfDay % NANOS_PER_SECOND); if (hourValue == 24) { hourValue = 23; minuteValue = 59; secondValue = 60; } buf.append(date).append('T') .append(hourValue < 10 ? "0" : "").append(hourValue) .append(minuteValue < 10 ? ":0" : ":").append(minuteValue) .append(secondValue < 10 ? ":0" : ":").append(secondValue); if (nanoValue > 0) { buf.append('.'); if (nanoValue % 1000_000 == 0) { buf.append(Integer.toString((nanoValue / 1000_000) + 1000).substring(1)); } else if (nanoValue % 1000 == 0) { buf.append(Integer.toString((nanoValue / 1000) + 1000_000).substring(1)); } else { buf.append(Integer.toString((nanoValue) + 1000_000_000).substring(1)); } } buf.append('Z'); return buf.toString(); } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/UtcRules.java000066400000000000000000000364571343451174100263620ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import java.time.DateTimeException; import java.time.Instant; import java.util.ConcurrentModificationException; /** * Rules defining the UTC time-scale, notably when leap seconds occur. *

* This class defines the UTC time-scale including when leap seconds occur. * Subclasses obtain the data from a suitable source, such as a file. *

* The static methods on this class provide access to the system leap second rules. * These are used by default in {@code UtcInstant} and {@code TaiInstant}. * Using other rules requires manual use of this class. *

* The system rules can be updated using a {@code LeapSeconds.txt}} file. * You can create your own version of this file and place it in on the classpath * and it will be used. Due to Java 9 module restrictions, the file is located * under META-INF to avoid module encapsulation problems - * {@code META-INF/org/threeten/extra/scale/LeapSeconds.txt}. * *

Implementation Requirements:

* This is an abstract class and must be implemented with care * to ensure other classes in the framework operate correctly. * All implementations must be final, immutable and thread-safe. * Subclasses should be {@code Serializable} wherever possible. */ public abstract class UtcRules { /** * Constant for the offset from MJD day 0 to the Java Epoch of 1970-01-01: 40587. */ static final int OFFSET_MJD_EPOCH = 40587; /** * Constant for the offset from MJD day 0 to TAI day 0 of 1958-01-01: 36204. */ static final int OFFSET_MJD_TAI = 36204; /** * Constant for number of seconds per standard day: 86,400. */ static final long SECS_PER_DAY = 24L * 60L * 60L; /** * Constant for nanos per standard second: 1,000,000,000. */ static final long NANOS_PER_SECOND = 1000000000L; /** * Gets the system default leap second rules. *

* The system default rules are serializable, immutable and thread-safe. * They will remain up to date as new leap seconds are added. * * @return the system rules, not null */ public static UtcRules system() { return SystemUtcRules.INSTANCE; } /** * Adds a new leap second to the system default leap second rules. *

* This method registers a new leap second with the system leap second rules. * Once registered, there is no way to deregister the leap second. *

* Calling this method is thread-safe. * Its effects are immediately visible in all threads. * Where possible, only call this method from a single thread to avoid the possibility of * a {@code ConcurrentModificationException}. *

* If the leap second being added matches a previous definition, then the method returns normally. * If the date is before the last registered date and does not match a previous definition, * then an exception is thrown. * * @param mjDay the Modified Julian Day that the leap second occurs at the end of * @param leapAdjustment the leap seconds to add/remove at the end of the day, either -1 or 1 * @throws IllegalArgumentException if the leap adjustment is invalid * @throws IllegalArgumentException if the day is before or equal the last known leap second day * and the definition does not match a previously registered leap * @throws ConcurrentModificationException if another thread updates the rules at the same time */ public static void registerLeapSecond(long mjDay, int leapAdjustment) { SystemUtcRules.INSTANCE.register(mjDay, leapAdjustment); } //----------------------------------------------------------------------- /** * Creates an instance of the rules. */ protected UtcRules() { } //----------------------------------------------------------------------- /** * The name of these rules. * * @return the name, not null */ public abstract String getName(); /** * Gets the leap second adjustment on the specified date. *

* The UTC standard restricts the adjustment on any day to {@code -1} or {@code 1}. *

* Any leap seconds are added to, or removed from, the end of the specified date. *

* If the UTC specification is altered to allow multiple leap seconds at once, then * the result of this method would return a number with an absolute value greater than one. * * @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17) * @return the number of seconds added, or removed, from the date, either -1 or 1 */ public abstract int getLeapSecondAdjustment(long mjDay); /** * Gets the offset to TAI on the specified date. *

* The TAI offset starts at 10 in 1972 and varies from then on based on the * dates of leap seconds. * The offset will apply for the whole of the date. * * @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17) * @return the TAI offset in seconds */ public abstract int getTaiOffset(long mjDay); /** * Gets all known leap second dates. *

* The dates are returned as Modified Julian Day values. * The leap second is added to, or removed from, the end of the specified dates. * The dates will be sorted from earliest to latest. * * @return an array of leap second dates expressed as Modified Julian Day values, not null */ public abstract long[] getLeapSecondDates(); //----------------------------------------------------------------------- /** * Validates combination of Modified Julian Day and nanosecond-of-day. *

* Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17. * Nanosecond-of-day is a simple count of nanoseconds from the start of the day * including any additional leap-second. * This method validates the nanosecond-of-day value against the Modified Julian Day. *

* The nanosecond-of-day value has a valid range from {@code 0} to * {@code 86,400,000,000,000 - 1} on most days, and a larger or smaller range * on leap-second days. * * @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17) * @param nanoOfDay the nanoseconds within the day, including leap seconds * @throws DateTimeException if nanoOfDay is out of range */ public void validateModifiedJulianDay(long mjDay, long nanoOfDay) { long leapSecs = getLeapSecondAdjustment(mjDay); long maxNanos = (SECS_PER_DAY + leapSecs) * NANOS_PER_SECOND; if (nanoOfDay < 0 || nanoOfDay >= maxNanos) { throw new DateTimeException("Nanosecond-of-day must be between 0 and " + maxNanos + " on date " + mjDay); } } //----------------------------------------------------------------------- /** * Converts a {@code UtcInstant} to a {@code TaiInstant}. *

* This method converts from the UTC to the TAI time-scale using the * leap-second rules of the implementation. * * @param utcInstant the UTC instant to convert, not null * @return the converted TAI instant, not null * @throws DateTimeException if the valid range is exceeded * @throws ArithmeticException if numeric overflow occurs */ public TaiInstant convertToTai(UtcInstant utcInstant) { long mjd = utcInstant.getModifiedJulianDay(); long nod = utcInstant.getNanoOfDay(); long taiUtcDaySeconds = Math.multiplyExact(Math.subtractExact(mjd, OFFSET_MJD_TAI), SECS_PER_DAY); long taiSecs = Math.addExact(taiUtcDaySeconds, nod / NANOS_PER_SECOND + getTaiOffset(mjd)); int nos = (int) (nod % NANOS_PER_SECOND); return TaiInstant.ofTaiSeconds(taiSecs, nos); } /** * Converts a {@code TaiInstant} to a {@code UtcInstant}. *

* This method converts from the TAI to the UTC time-scale using the * leap-second rules of the implementation. * * @param taiInstant the TAI instant to convert, not null * @return the converted UTC instant, not null * @throws DateTimeException if the valid range is exceeded * @throws ArithmeticException if numeric overflow occurs */ public abstract UtcInstant convertToUtc(TaiInstant taiInstant); //----------------------------------------------------------------------- /** * Converts a {@code UtcInstant} to an {@code Instant}. *

* This method converts from the UTC time-scale to one with 86400 subdivisions * per day using the leap-second rules of the implementation. *

* The standard implementation uses the UTC-SLS algorithm. * Overriding this algorithm is possible, however doing so will conflict other parts * of the specification. *

* The algorithm calculates the UTC-SLS nanos-of-day {@code US} from the UTC nanos-of day {@code U}.
* Let {@code L = getLeapAdjustment(mjd)}.
* Let {@code B = 86400 + L - 1000}.
* Let {@code US = U - L * (U - B) / 1000}.
* Where the algorithm is applied while {@code U >= B}. * * @param utcInstant the UTC instant to convert, not null * @return the converted instant, not null * @throws DateTimeException if the valid range is exceeded * @throws ArithmeticException if numeric overflow occurs */ public Instant convertToInstant(UtcInstant utcInstant) { long mjd = utcInstant.getModifiedJulianDay(); long utcNanos = utcInstant.getNanoOfDay(); long epochDay = Math.subtractExact(mjd, OFFSET_MJD_EPOCH); long epochSec = Math.multiplyExact(epochDay, SECS_PER_DAY); int leapAdj = getLeapSecondAdjustment(mjd); long startSlsNanos = (SECS_PER_DAY + leapAdj - 1000) * NANOS_PER_SECOND; long slsNanos = utcNanos; if (leapAdj != 0 && utcNanos >= startSlsNanos) { slsNanos = utcNanos - leapAdj * (utcNanos - startSlsNanos) / 1000; // apply UTC-SLS mapping } return Instant.ofEpochSecond(epochSec + slsNanos / NANOS_PER_SECOND, slsNanos % NANOS_PER_SECOND); } /** * Converts an {@code Instant} to a {@code UtcInstant}. *

* This method converts from an instant with 86400 subdivisions per day * to the UTC time-scale using the leap-second rules of the implementation. *

* The standard implementation uses the UTC-SLS algorithm. * Overriding this algorithm is possible, however doing so will conflict other parts * of the specification. *

* The algorithm calculates the UTC nanos-of-day {@code U} from the UTC-SLS nanos-of day {@code US}.
* Let {@code L = getLeapAdjustment(mjd)}.
* Let {@code B = 86400 + L - 1000}.
* Let {@code U = B + ((US - B) * 1000) / (1000 - L)}.
* Where the algorithm is applied while {@code US >= B}.
* (This algorithm has been tuned for integer arithmetic from the UTC-SLS specification.) * * @param instant the instant to convert, not null * @return the converted UTC instant, not null * @throws DateTimeException if the valid range is exceeded * @throws ArithmeticException if numeric overflow occurs */ public UtcInstant convertToUtc(Instant instant) { long epochDay = Math.floorDiv(instant.getEpochSecond(), SECS_PER_DAY); long mjd = epochDay + OFFSET_MJD_EPOCH; long slsNanos = Math.floorMod(instant.getEpochSecond(), SECS_PER_DAY) * NANOS_PER_SECOND + instant.getNano(); int leapAdj = getLeapSecondAdjustment(mjd); long startSlsNanos = (SECS_PER_DAY + leapAdj - 1000) * NANOS_PER_SECOND; long utcNanos = slsNanos; if (leapAdj != 0 && slsNanos >= startSlsNanos) { utcNanos = startSlsNanos + ((slsNanos - startSlsNanos) * 1000) / (1000 - leapAdj); // apply UTC-SLS mapping } return UtcInstant.ofModifiedJulianDay(mjd, utcNanos); } //------------------------------------------------------------------------- /** * Converts a {@code TaiInstant} to an {@code Instant}. *

* This method converts from the TAI time-scale to one with 86400 subdivisions * per day using the leap-second rules of the implementation. *

* The standard implementation uses UTC-SLS. It uses * {@link #convertToUtc(TaiInstant)} and {@link #convertToInstant(UtcInstant)}. * * @param taiInstant the TAI instant to convert, not null * @return the converted instant, not null * @throws DateTimeException if the valid range is exceeded * @throws ArithmeticException if numeric overflow occurs */ public Instant convertToInstant(TaiInstant taiInstant) { return convertToInstant(convertToUtc(taiInstant)); } /** * Converts an {@code Instant} to a {@code TaiInstant}. *

* This method converts from an instant with 86400 subdivisions per day * to the TAI time-scale using the leap-second rules of the implementation. *

* The standard implementation uses the UTC-SLS algorithm. It uses * {@link #convertToUtc(TaiInstant)} and {@link #convertToInstant(UtcInstant)}. * * @param instant the instant to convert, not null * @return the converted instant, not null * @throws DateTimeException if the valid range is exceeded * @throws ArithmeticException if numeric overflow occurs */ public TaiInstant convertToTai(Instant instant) { return convertToTai(convertToUtc(instant)); } //----------------------------------------------------------------------- /** * A string representation of these rules. * * @return the string representation, not null */ @Override public String toString() { return "UtcRules[" + getName() + ']'; } } threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/package-info.java000066400000000000000000000032651343451174100271270ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Support for time scales that extend {@code java.time.*}. */ package org.threeten.extra.scale; threeten-extra-1.5.0/src/main/resources/000077500000000000000000000000001343451174100202045ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/META-INF/000077500000000000000000000000001343451174100213445ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/META-INF/services/000077500000000000000000000000001343451174100231675ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/META-INF/services/java.time.chrono.Chronology000066400000000000000000000006441343451174100304050ustar00rootroot00000000000000org.threeten.extra.chrono.BritishCutoverChronology org.threeten.extra.chrono.CopticChronology org.threeten.extra.chrono.DiscordianChronology org.threeten.extra.chrono.EthiopicChronology org.threeten.extra.chrono.InternationalFixedChronology org.threeten.extra.chrono.JulianChronology org.threeten.extra.chrono.PaxChronology org.threeten.extra.chrono.Symmetry010Chronology org.threeten.extra.chrono.Symmetry454Chronology threeten-extra-1.5.0/src/main/resources/org/000077500000000000000000000000001343451174100207735ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/org/threeten/000077500000000000000000000000001343451174100226115ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/org/threeten/extra/000077500000000000000000000000001343451174100237345ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/org/threeten/extra/scale/000077500000000000000000000000001343451174100250235ustar00rootroot00000000000000threeten-extra-1.5.0/src/main/resources/org/threeten/extra/scale/LeapSeconds.txt000066400000000000000000000007151343451174100277670ustar00rootroot00000000000000# the ISO date of the leap second and the TAI offset after that date 1972-01-01 10 1972-06-30 11 1972-12-31 12 1973-12-31 13 1974-12-31 14 1975-12-31 15 1976-12-31 16 1977-12-31 17 1978-12-31 18 1979-12-31 19 1981-06-30 20 1982-06-30 21 1983-06-30 22 1985-06-30 23 1987-12-31 24 1989-12-31 25 1990-12-31 26 1992-06-30 27 1993-06-30 28 1994-06-30 29 1995-12-31 30 1997-06-30 31 1998-12-31 32 2005-12-31 33 2008-12-31 34 2012-06-30 35 2015-06-30 36 2016-12-31 37 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased.properties000066400000000000000000000010621343451174100300230ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,and WordBased.commaspaceand=, and WordBased.commaspace=, WordBased.spaceandspace=\ and WordBased.year=\ year WordBased.years=\ years WordBased.month=\ month WordBased.months=\ months WordBased.week=\ week WordBased.weeks=\ weeks WordBased.day=\ day WordBased.days=\ days WordBased.hour=\ hour WordBased.hours=\ hours WordBased.minute=\ minute WordBased.minutes=\ minutes WordBased.second=\ second WordBased.seconds=\ seconds WordBased.millisecond=\ millisecond WordBased.milliseconds=\ milliseconds threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ca.properties000066400000000000000000000010451343451174100304670ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,i WordBased.commaspaceand=, i WordBased.commaspace=, WordBased.spaceandspace=\ i WordBased.year=\ any WordBased.years=\ anys WordBased.month=\ mes WordBased.months=\ mesos WordBased.week=\ setmana WordBased.weeks=\ setmanes WordBased.day=\ dia WordBased.days=\ dies WordBased.hour=\ hora WordBased.hours=\ hores WordBased.minute=\ minut WordBased.minutes=\ minuts WordBased.second=\ segon WordBased.seconds=\ segons WordBased.millisecond=\ milisegon WordBased.milliseconds=\ milisegons threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_cs.properties000066400000000000000000000016731343451174100305200ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,a WordBased.commaspaceand=, a WordBased.commaspace=, WordBased.spaceandspace=\ a WordBased.years.predicates=One|||End234NotTeens WordBased.years.list=\ rok||| roky||| let WordBased.months.predicates=One|||End234NotTeens WordBased.months.list=\ měsíc||| měsíce||| měsíců WordBased.weeks.predicates=One|||End234NotTeens WordBased.weeks.list=\ týden||| týdny||| týdnů WordBased.days.predicates=One|||End234NotTeens WordBased.days.list=\ den||| dny||| dnů WordBased.hours.predicates=One|||End234NotTeens WordBased.hours.list=\ hodina||| hodiny||| hodin WordBased.minutes.predicates=One|||End234NotTeens WordBased.minutes.list=\ minuta||| minuty||| minut WordBased.seconds.predicates=One|||End234NotTeens WordBased.seconds.list=\ sekunda||| sekundy||| sekund WordBased.milliseconds.predicates=One|||End234NotTeens WordBased.milliseconds.list=\ milisekunda||| milisekundy||| milisekund threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_da.properties000066400000000000000000000010771343451174100304750ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,og WordBased.commaspaceand=, og WordBased.commaspace=,\ WordBased.spaceandspace=\ og\ WordBased.year=\ \u00e5r WordBased.years=\ \u00e5r WordBased.month=\ m\u00e5ned WordBased.months=\ m\u00e5neder WordBased.week=\ uge WordBased.weeks=\ uger WordBased.day=\ dag WordBased.days=\ dage WordBased.hour=\ time WordBased.hours=\ timer WordBased.minute=\ minut WordBased.minutes=\ minutter WordBased.second=\ sekund WordBased.seconds=\ sekunder WordBased.millisecond=\ millisekund WordBased.milliseconds=\ millisekunder threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_de.properties000066400000000000000000000010741343451174100304760ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,und WordBased.commaspaceand=, und WordBased.commaspace=, WordBased.spaceandspace=\ und WordBased.year=\ Jahr WordBased.years=\ Jahre WordBased.month=\ Monat WordBased.months=\ Monate WordBased.week=\ Woche WordBased.weeks=\ Wochen WordBased.day=\ Tag WordBased.days=\ Tage WordBased.hour=\ Stunde WordBased.hours=\ Stunden WordBased.minute=\ Minute WordBased.minutes=\ Minuten WordBased.second=\ Sekunde WordBased.seconds=\ Sekunden WordBased.millisecond=\ Millisekunde WordBased.milliseconds=\ Millisekunden threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_en.properties000066400000000000000000000000011343451174100304750ustar00rootroot00000000000000 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_es.properties000066400000000000000000000011011343451174100305040ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,y WordBased.commaspaceand=, y WordBased.commaspace=, WordBased.spaceandspace=\ y WordBased.year=\ a\u00f1o WordBased.years=\ a\u00f1os WordBased.month=\ mes WordBased.months=\ meses WordBased.week=\ semana WordBased.weeks=\ semanas WordBased.day=\ d\u00eda WordBased.days=\ d\u00edas WordBased.hour=\ hora WordBased.hours=\ horas WordBased.minute=\ minuto WordBased.minutes=\ minutos WordBased.second=\ segundo WordBased.seconds=\ segundos WordBased.millisecond=\ milisegundo WordBased.milliseconds=\ milisegundos threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_fr.properties000066400000000000000000000011061343451174100305110ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,et WordBased.commaspaceand=, et WordBased.commaspace=, WordBased.spaceandspace=\ et WordBased.year=\ ann\u00e9e WordBased.years=\ ann\u00e9es WordBased.month=\ mois WordBased.months=\ mois WordBased.week=\ semaine WordBased.weeks=\ semaines WordBased.day=\ jour WordBased.days=\ jours WordBased.hour=\ heure WordBased.hours=\ heures WordBased.minute=\ minute WordBased.minutes=\ minutes WordBased.second=\ seconde WordBased.seconds=\ secondes WordBased.millisecond=\ milliseconde WordBased.milliseconds=\ millisecondes threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_it.properties000066400000000000000000000010621343451174100305170ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,e WordBased.commaspaceand=, e WordBased.commaspace=, WordBased.spaceandspace=\ e WordBased.year=\ anno WordBased.years=\ anni WordBased.month=\ mese WordBased.months=\ mesi WordBased.week=\ settimana WordBased.weeks=\ settimane WordBased.day=\ giorno WordBased.days=\ giorni WordBased.hour=\ ora WordBased.hours=\ ore WordBased.minute=\ minuto WordBased.minutes=\ minuti WordBased.second=\ secondo WordBased.seconds=\ secondi WordBased.millisecond=\ millisecondo WordBased.milliseconds=\ millisecondi threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ja.properties000066400000000000000000000010721343451174100304760ustar00rootroot00000000000000WordBased.space= WordBased.comma= WordBased.commandand= WordBased.commaspaceand= WordBased.commaspace= WordBased.spaceandspace= WordBased.year=\u5E74 WordBased.years=\u5E74 WordBased.month=\u304B\u6708 WordBased.months=\u304B\u6708 WordBased.week=\u9031\u9593 WordBased.weeks=\u9031\u9593 WordBased.day=\u65E5 WordBased.days=\u65E5 WordBased.hour=\u6642\u9593 WordBased.hours=\u6642\u9593 WordBased.minute=\u5206 WordBased.minutes=\u5206 WordBased.second=\u79D2 WordBased.seconds=\u79D2 WordBased.millisecond=\u30DF\u30EA\u79D2 WordBased.milliseconds=\u30DF\u30EA\u79D2 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_nl.properties000066400000000000000000000010611343451174100305130ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,en WordBased.commaspaceand=, en WordBased.commaspace=, WordBased.spaceandspace=\ en WordBased.year=\ jaar WordBased.years=\ jaar WordBased.month=\ maand WordBased.months=\ maanden WordBased.week=\ week WordBased.weeks=\ weken WordBased.day=\ dag WordBased.days=\ dagen WordBased.hour=\ uur WordBased.hours=\ uur WordBased.minute=\ minuut WordBased.minutes=\ minuten WordBased.second=\ seconde WordBased.seconds=\ seconden WordBased.millisecond=\ milliseconde WordBased.milliseconds=\ milliseconden threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_pl.properties000066400000000000000000000016541343451174100305250ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,i WordBased.commaspaceand=, i WordBased.commaspace=, WordBased.spaceandspace=\ i WordBased.years.predicates=One|||End234NotTeens WordBased.years.list=\ rok||| lata||| lat WordBased.months.predicates=One|||End234NotTeens WordBased.months.list=\ miesi\u0105c||| miesi\u0105ce||| miesi\u0119cy WordBased.weeks.predicates=One|||End234NotTeens WordBased.weeks.list=\ tydzie\u0144||| tygodnie||| tygodni WordBased.day=\ dzie\u0144 WordBased.days=\ dni WordBased.hours.predicates=One|||End234NotTeens WordBased.hours.list=\ godzina||| godziny||| godzin WordBased.minutes.predicates=One|||End234NotTeens WordBased.minutes.list=\ minuta||| minuty||| minut WordBased.seconds.predicates=One|||End234NotTeens WordBased.seconds.list=\ sekunda||| sekundy||| sekund WordBased.milliseconds.predicates=One|||End234NotTeens WordBased.milliseconds.list=\ milisekunda||| milisekundy||| milisekund threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_pt.properties000066400000000000000000000010641343451174100305300ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,e WordBased.commaspaceand=, e WordBased.commaspace=, WordBased.spaceandspace=\ e WordBased.year=\ ano WordBased.years=\ anos WordBased.month=\ m\u00eas WordBased.months=\ meses WordBased.week=\ semana WordBased.weeks=\ semanas WordBased.day=\ dia WordBased.days=\ dias WordBased.hour=\ hora WordBased.hours=\ horas WordBased.minute=\ minuto WordBased.minutes=\ minutos WordBased.second=\ segundo WordBased.seconds=\ segundos WordBased.millisecond=\ milissegundo WordBased.milliseconds=\ milissegundos threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ro.properties000066400000000000000000000011611343451174100305230ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,\u0219i WordBased.commaspaceand=, \u0219i WordBased.commaspace=, WordBased.spaceandspace=\ \u0219i WordBased.year=\ an WordBased.years=\ ani WordBased.month=\ lun\u0103 WordBased.months=\ luni WordBased.week=\ s\u0103pt\u0103m\u00E2n\u0103 WordBased.weeks=\ s\u0103pt\u0103m\u00E2ni WordBased.day=\ zi WordBased.days=\ zile WordBased.hour=\ or\u0103 WordBased.hours=\ ore WordBased.minute=\ minut WordBased.minutes=\ minute WordBased.second=\ secund\u0103 WordBased.seconds=\ secunde WordBased.millisecond=\ milisecund\u0103 WordBased.milliseconds=\ milisecunde threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ru.properties000066400000000000000000000032171343451174100305350ustar00rootroot00000000000000WordBased.space=\u0020 WordBased.comma=, WordBased.commandand=,\u0438 WordBased.commaspaceand=, \u0438 WordBased.commaspace=,\u0020 WordBased.spaceandspace=\ \u0438\u0020 WordBased.years.predicates=One|||End234NotTeens WordBased.years.list=\ \u0433\u043E\u0434||| \u0433\u043E\u0434\u0430||| \u043B\u0435\u0442 WordBased.months.predicates=One|||End234NotTeens WordBased.months.list=\ \u043C\u0435\u0441\u044F\u0446||| \u043C\u0435\u0441\u044F\u0446\u0430||| \u043C\u0435\u0441\u044F\u0446\u0435\u0432 WordBased.weeks.predicates=One|||End234NotTeens WordBased.weeks.list=\ \u043D\u0435\u0434\u0435\u043B\u044F||| \u043D\u0435\u0434\u0435\u043B\u0438||| \u043D\u0435\u0434\u0435\u043B\u044C WordBased.days.predicates=One|||End234NotTeens WordBased.days.list=\ \u0434\u0435\u043D\u044C||| \u0434\u043D\u044F||| \u0434\u043D\u0435\u0439 WordBased.hours.predicates=One|||End234NotTeens WordBased.hours.list=\ \u0447\u0430\u0441||| \u0447\u0430\u0441\u0430||| \u0447\u0430\u0441\u043E\u0432 WordBased.minutes.predicates=One|||End234NotTeens WordBased.minutes.list=\ \u043C\u0438\u043D\u0443\u0442\u0430||| \u043C\u0438\u043D\u0443\u0442\u044B||| \u043C\u0438\u043D\u0443\u0442 WordBased.seconds.predicates=One|||End234NotTeens WordBased.seconds.list=\ \u0441\u0435\u043A\u0443\u043D\u0434\u0430||| \u0441\u0435\u043A\u0443\u043D\u0434\u044B||| \u0441\u0435\u043A\u0443\u043D\u0434 WordBased.milliseconds.predicates=One|||End234NotTeens WordBased.milliseconds.list=\ \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434\u0430||| \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434\u044B||| \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_tr.properties000066400000000000000000000010631343451174100305310ustar00rootroot00000000000000WordBased.space=\ WordBased.comma=, WordBased.commandand=,ve WordBased.commaspaceand=, ve WordBased.commaspace=, WordBased.spaceandspace=\ ve WordBased.year=\ y\u0131l WordBased.years=\ y\u0131l WordBased.month=\ ay WordBased.months=\ ay WordBased.week=\ hafta WordBased.weeks=\ hafta WordBased.day=\ g\u00fcn WordBased.days=\ g\u00fcn WordBased.hour=\ saat WordBased.hours=\ saat WordBased.minute=\ dakika WordBased.minutes=\ dakika WordBased.second=\ saniye WordBased.seconds=\ saniye WordBased.millisecond=\ milisaniye WordBased.milliseconds=\ milisaniye threeten-extra-1.5.0/src/site/000077500000000000000000000000001343451174100162125ustar00rootroot00000000000000threeten-extra-1.5.0/src/site/markdown/000077500000000000000000000000001343451174100200345ustar00rootroot00000000000000threeten-extra-1.5.0/src/site/markdown/index.md000066400000000000000000000075371343451174100215010ustar00rootroot00000000000000## About **ThreeTen-Extra** provides additional date-time classes that complement those in [Java SE 8](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html). Not every piece of date/time logic is destined for the JDK. Some concepts are too specialized or too bulky to make it in. This project provides some of those additional classes as a well-tested and reliable jar. It is curated by the primary author of the Java 8 date and time library, [Stephen Colebourne](https://www.joda.org/). ThreeTen-Extra is licensed under the business-friendly [BSD 3-clause license](license.html). ## Features The following features are included: * [`DayOfMonth`](apidocs/org.threeten.extra/org/threeten/extra/DayOfMonth.html) - a day-of-month without month or year * [`DayOfYear`](apidocs/org.threeten.extra/org/threeten/extra/DayOfYear.html) - a day-of-year without year * [`AmPm`](apidocs/org.threeten.extra/org/threeten/extra/AmPm.html) - before or after midday * [`Quarter`](apidocs/org.threeten.extra/org/threeten/extra/Quarter.html) - the four quarters, Q1, Q2, Q3 and Q4 * [`YearQuarter`](apidocs/org.threeten.extra/org/threeten/extra/YearQuarter.html) - combines a year and quarter, 2014-Q4 * [`YearWeek`](apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html) - combines a week-based-year and a week, 2014-W06 * [`Days`](apidocs/org.threeten.extra/org/threeten/extra/Days.html), [`Weeks`](apidocs/org.threeten.extra/org/threeten/extra/Weeks.html), [`Months`](apidocs/org.threeten.extra/org/threeten/extra/Months.html) and [`Years`](apidocs/org.threeten.extra/org/threeten/extra/Years.html) - amounts of time * [`Interval`](apidocs/org.threeten.extra/org/threeten/extra/Interval.html) - an interval between two instants * [`PeriodDuration`](apidocs/org.threeten.extra/org/threeten/extra/PeriodDuration.html) - combines `Period` and `Duration` * Weekend adjusters * [Coptic](apidocs/org.threeten.extra/org/threeten/extra/chrono/CopticChronology.html) calendar system * [Ethiopic](apidocs/org.threeten.extra/org/threeten/extra/chrono/EthiopicChronology.html) calendar system * [Julian](apidocs/org.threeten.extra/org/threeten/extra/chrono/JulianChronology.html) calendar system * [Word-based](apidocs/org.threeten.extra/org/threeten/extra/AmountFormats.html) period and duration formatting * Support for the TAI and UTC [time-scales](apidocs/org.threeten.extra/org/threeten/extra/scale/package-summary.html) ## Documentation Various documentation is available: * The helpful [user guide](userguide.html) * The [Javadoc](apidocs/org.threeten.extra/module-summary.html) * The [change notes](changes-report.html) for each release * The [GitHub](https://github.com/ThreeTen/threeten-extra) source repository --- ## Releases Release 1.5.0 is the current release. This release is considered stable and worthy of the 1.x tag. ThreeTen-Extra requires Java SE 8 or later and has no [dependencies](dependencies.html). Available in [Maven Central](https://search.maven.org/search?q=g:org.threeten%20AND%20a:threeten-extra&core=gav). ```xml org.threeten threeten-extra 1.5.0 ``` --- ### Support Please use [Stack Overflow](https://stackoverflow.com/search?q=threeten-extra) for general usage questions. GitHub [issues](https://github.com/ThreeTen/threeten-extra/issues) and [pull requests](https://github.com/ThreeTen/threeten-extra/pulls) should be used when you want to help advance the project. Commercial support is available via the [Tidelift subscription](https://tidelift.com/subscription/pkg/maven-org-threeten-threeten-extra?utm_source=maven-org-threeten-threeten-extra&utm_medium=referral&utm_campaign=website). To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. threeten-extra-1.5.0/src/site/markdown/userguide.md000066400000000000000000000106171343451174100223570ustar00rootroot00000000000000## User guide ThreeTen-Extra is a small library that builds on the Java SE 8 [`java.time`](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) package. ## Value types The additional value types operate exactly as per similar classes in Java SE 8. These include: * [`DayOfMonth`](apidocs/org.threeten.extra/org/threeten/extra/DayOfMonth.html) - a day-of-month without month or year * [`DayOfYear`](apidocs/org.threeten.extra/org/threeten/extra/DayOfYear.html) - a day-of-year without year * [`AmPm`](apidocs/org.threeten.extra/org/threeten/extra/AmPm.html) - before or after midday * [`Quarter`](apidocs/org.threeten.extra/org/threeten/extra/Quarter.html) - the four quarters, Q1, Q2, Q3 and Q4 * [`YearQuarter`](apidocs/org.threeten.extra/org/threeten/extra/YearQuarter.html) - combines a year and quarter, 2014-Q4 * [`YearWeek`](apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html) - combines a week-based-year and a week, 2014-W06 * [`Days`](apidocs/org.threeten.extra/org/threeten/extra/Days.html) - an amount of time measured in days * [`Weeks`](apidocs/org.threeten.extra/org/threeten/extra/Weeks.html) - an amount of time measured in weeks * [`Months`](apidocs/org.threeten.extra/org/threeten/extra/Months.html) - an amount of time measured in months * [`Years`](apidocs/org.threeten.extra/org/threeten/extra/Years.html) - an amount of time measured in years * [`Interval`](apidocs/org.threeten.extra/org/threeten/extra/Interval.html) - an interval between two instants * [`PeriodDuration`](apidocs/org.threeten.extra/org/threeten/extra/PeriodDuration.html) - combines a `Period` and a `Duration` ## Period/Duration formatting The JDK does not provide a mechanism to format periods or durations beyond ISO-8601. A simple mechanism is provided here in [`AmountFormats`](apidocs/org.threeten.extra/org/threeten/extra/AmountFormats.html): ``` Period period = Period.of(1, 6, 5); String str = AmountFormats.wordBased(period, Locale.ENGLISH); // output: "1 year, 6 months and 5 days" ``` Translations are provided for cs, da, de, en, es, fr, it, ja, nl, pl, pt, ro, ru, tr. Feel free to raise PRs for other languages. ## Calendar systems The additional calendar systems operate exactly as per similar classes in Java SE 8. These include: * [Accounting](apidocs/org.threeten.extra/org/threeten/extra/chrono/AccountingChronology.html) calendar system * [British Cutover](apidocs/org.threeten.extra/org/threeten/extra/chrono/BritishCutoverChronology.html) calendar system * [Coptic](apidocs/org.threeten.extra/org/threeten/extra/chrono/CopticChronology.html) calendar system * [Discordian](apidocs/org.threeten.extra/org/threeten/extra/chrono/DiscordianChronology.html) calendar system * [Ethiopic](apidocs/org.threeten.extra/org/threeten/extra/chrono/EthiopicChronology.html) calendar system * [International Fixed](apidocs/org.threeten.extra/org/threeten/extra/chrono/InternationalFixedChronology.html) calendar system * [Julian](apidocs/org.threeten.extra/org/threeten/extra/chrono/JulianChronology.html) calendar system * [Pax](apidocs/org.threeten.extra/org/threeten/extra/chrono/PaxChronology.html) calendar system * [Symmetry010](apidocs/org.threeten.extra/org/threeten/extra/chrono/Symmetry010Chronology.html) calendar system * [Symmetry454](apidocs/org.threeten.extra/org/threeten/extra/chrono/Symmetry454Chronology.html) calendar system ## Time scales The JDK operates exclusively using the Java time-scale, defined in [Instant](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html). That time-scale eliminates leap seconds using UTC-SLS, although the JDK implementation is typically based on a POSIX-like clock (and thus relatively undefined around leap seconds). ThreeTen-Extra provides the additional classes that were rejected from JDK 8. They were not included as they were deemed to be too specialized for the JDK. Use `TaiInstant` if you need an instant using the TAI time-scale. Use `UtcInstant` if you need an instant using the UTC time-scale. The leap second data is provided in a text file loaded from the classpath. Only whole leap seconds are handled, and data starts from 1972 by default. To replace the built in leap seconds file, create a file `META-INF/org/threeten/extra/scale/LeapSeconds.txt`. The content should have two columns as per [this format](https://github.com/ThreeTen/threeten-extra/blob/0cf61e35fc165062eb70a66b026c54c261dce46d/src/main/resources/org/threeten/extra/scale/LeapSeconds.txt). threeten-extra-1.5.0/src/site/resources/000077500000000000000000000000001343451174100202245ustar00rootroot00000000000000threeten-extra-1.5.0/src/site/resources/css/000077500000000000000000000000001343451174100210145ustar00rootroot00000000000000threeten-extra-1.5.0/src/site/resources/css/site.css000066400000000000000000000015111343451174100224700ustar00rootroot00000000000000/* Fix broken definition that causes hyperlinks to break */ h1[id]:before, h2[id]:before, h3[id]:before, h4[id]:before, h5[id]:before, h6[id]:before, a[name]:before { height:0px; margin:0px; } /* Blacker text */ body { color: #222; } code, pre { color: #444; } .dropdown-menu>li>a { color: #666; } /* Sidebar had too much padding at the top */ .well { padding-top: 6px; padding-bottom: 36px; } /* Font Awesome icons by CSS as markdown class is stripped */ h2 i { display: inline-block; font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } h2#About i:before { content: "\f015"; } h2#Features i:before { content: "\f0d0"; } h2#Documentation i:before { content: "\f02d"; } h2#Releases i:before { content: "\f02c"; } threeten-extra-1.5.0/src/site/site.xml000066400000000000000000000061011343451174100176760ustar00rootroot00000000000000 UA-1425975-2 org.joda.external reflow-maven-skin 1.2 false true github false bootswatch-cosmo ThreeTen Extra index.html Documentation|Releases|Development|ThreeTen Documentation Releases Development Reports false false false true 3 Home false false false false ]]>

threeten-extra-1.5.0/src/test/000077500000000000000000000000001343451174100162255ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/000077500000000000000000000000001343451174100171465ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/org/000077500000000000000000000000001343451174100177355ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/org/threeten/000077500000000000000000000000001343451174100215535ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/org/threeten/extra/000077500000000000000000000000001343451174100226765ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/org/threeten/extra/MockDecadesMonths.java000066400000000000000000000054661343451174100271070ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Arrays; import java.util.List; /** * Mock class for decades and months. */ public class MockDecadesMonths implements TemporalAmount { private final int decades; private final int months; public MockDecadesMonths(int decades, int months) { this.decades = decades; this.months = months; } @Override public long get(TemporalUnit unit) { if (unit == ChronoUnit.DECADES) { return decades; } if (unit == ChronoUnit.MONTHS) { return months; } throw new UnsupportedTemporalTypeException("Unknown unit"); } @Override public List getUnits() { return Arrays.asList(ChronoUnit.DECADES, ChronoUnit.MONTHS); } @Override public Temporal addTo(Temporal temporal) { throw new UnsupportedOperationException(); } @Override public Temporal subtractFrom(Temporal temporal) { throw new UnsupportedOperationException(); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/MockWeeksDays.java000066400000000000000000000054161343451174100262600ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Arrays; import java.util.List; /** * Mock class for weeks and days. */ public class MockWeeksDays implements TemporalAmount { private final int weeks; private final int days; public MockWeeksDays(int weeks, int days) { this.weeks = weeks; this.days = days; } @Override public long get(TemporalUnit unit) { if (unit == ChronoUnit.WEEKS) { return weeks; } if (unit == ChronoUnit.DAYS) { return days; } throw new UnsupportedTemporalTypeException("Unknown unit"); } @Override public List getUnits() { return Arrays.asList(ChronoUnit.WEEKS, ChronoUnit.DAYS); } @Override public Temporal addTo(Temporal temporal) { throw new UnsupportedOperationException(); } @Override public Temporal subtractFrom(Temporal temporal) { throw new UnsupportedOperationException(); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/MockYearsMonths.java000066400000000000000000000054421343451174100266340ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Arrays; import java.util.List; /** * Mock class for years and months. */ public class MockYearsMonths implements TemporalAmount { private final int years; private final int months; public MockYearsMonths(int years, int months) { this.years = years; this.months = months; } @Override public long get(TemporalUnit unit) { if (unit == ChronoUnit.YEARS) { return years; } if (unit == ChronoUnit.MONTHS) { return months; } throw new UnsupportedTemporalTypeException("Unknown unit"); } @Override public List getUnits() { return Arrays.asList(ChronoUnit.YEARS, ChronoUnit.MONTHS); } @Override public Temporal addTo(Temporal temporal) { throw new UnsupportedOperationException(); } @Override public Temporal subtractFrom(Temporal temporal) { throw new UnsupportedOperationException(); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestAmPm.java000066400000000000000000000301541343451174100252360ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_DAY; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.HALF_DAYS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.TextStyle; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Locale; import org.junit.Test; /** * Test AmPm. */ public class TestAmPm { //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Enum.class.isAssignableFrom(AmPm.class)); assertTrue(Serializable.class.isAssignableFrom(AmPm.class)); assertTrue(Comparable.class.isAssignableFrom(AmPm.class)); assertTrue(TemporalAccessor.class.isAssignableFrom(AmPm.class)); } //----------------------------------------------------------------------- // of(int) //----------------------------------------------------------------------- @Test public void test_of_int_singleton_equals() { for (int i = 0; i <= 1; i++) { AmPm test = AmPm.of(i); assertEquals(i, test.getValue()); } } @Test(expected = DateTimeException.class) public void test_of_int_valueTooLow() { AmPm.of(-1); } @Test(expected = DateTimeException.class) public void test_of_int_valueTooHigh() { AmPm.of(2); } //----------------------------------------------------------------------- // ofHour(int) //----------------------------------------------------------------------- @Test public void test_ofHour_int_singleton() { for (int i = 0; i < 12; i++) { assertSame(AmPm.AM, AmPm.ofHour(i)); } for (int i = 12; i < 24; i++) { assertSame(AmPm.PM, AmPm.ofHour(i)); } } @Test(expected = DateTimeException.class) public void test_ofHour_int_valueTooLow() { AmPm.ofHour(-1); } @Test(expected = DateTimeException.class) public void test_ofHour_int_valueTooHigh() { AmPm.ofHour(24); } //----------------------------------------------------------------------- // from(TemporalAccessor) //----------------------------------------------------------------------- @Test public void test_from_TemporalAccessor() { assertEquals(AmPm.AM, AmPm.from(LocalTime.of(8, 30))); assertEquals(AmPm.PM, AmPm.from(LocalTime.of(17, 30))); } @Test(expected = DateTimeException.class) public void test_from_TemporalAccessor_invalid_noDerive() { AmPm.from(LocalDate.of(2007, 7, 30)); } @Test(expected = NullPointerException.class) public void test_from_TemporalAccessor_null() { AmPm.from((TemporalAccessor) null); } //----------------------------------------------------------------------- // getDisplayName() //----------------------------------------------------------------------- @Test public void test_getDisplayName() { assertEquals("AM", AmPm.AM.getDisplayName(TextStyle.SHORT, Locale.US)); } @Test(expected = NullPointerException.class) public void test_getDisplayName_nullStyle() { AmPm.AM.getDisplayName(null, Locale.US); } @Test(expected = NullPointerException.class) public void test_getDisplayName_nullLocale() { AmPm.AM.getDisplayName(TextStyle.FULL, null); } //----------------------------------------------------------------------- // isSupported() //----------------------------------------------------------------------- @Test public void test_isSupported() { AmPm test = AmPm.AM; assertEquals(false, test.isSupported(null)); assertEquals(false, test.isSupported(NANO_OF_SECOND)); assertEquals(false, test.isSupported(NANO_OF_DAY)); assertEquals(false, test.isSupported(MICRO_OF_SECOND)); assertEquals(false, test.isSupported(MICRO_OF_DAY)); assertEquals(false, test.isSupported(MILLI_OF_SECOND)); assertEquals(false, test.isSupported(MILLI_OF_DAY)); assertEquals(false, test.isSupported(SECOND_OF_MINUTE)); assertEquals(false, test.isSupported(SECOND_OF_DAY)); assertEquals(false, test.isSupported(MINUTE_OF_HOUR)); assertEquals(false, test.isSupported(MINUTE_OF_DAY)); assertEquals(false, test.isSupported(HOUR_OF_AMPM)); assertEquals(false, test.isSupported(CLOCK_HOUR_OF_AMPM)); assertEquals(false, test.isSupported(HOUR_OF_DAY)); assertEquals(false, test.isSupported(CLOCK_HOUR_OF_DAY)); assertEquals(true, test.isSupported(AMPM_OF_DAY)); assertEquals(false, test.isSupported(DAY_OF_WEEK)); assertEquals(false, test.isSupported(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(false, test.isSupported(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(false, test.isSupported(DAY_OF_MONTH)); assertEquals(false, test.isSupported(DAY_OF_YEAR)); assertEquals(false, test.isSupported(EPOCH_DAY)); assertEquals(false, test.isSupported(ALIGNED_WEEK_OF_MONTH)); assertEquals(false, test.isSupported(ALIGNED_WEEK_OF_YEAR)); assertEquals(false, test.isSupported(MONTH_OF_YEAR)); assertEquals(false, test.isSupported(PROLEPTIC_MONTH)); assertEquals(false, test.isSupported(YEAR_OF_ERA)); assertEquals(false, test.isSupported(YEAR)); assertEquals(false, test.isSupported(ERA)); assertEquals(false, test.isSupported(INSTANT_SECONDS)); assertEquals(false, test.isSupported(OFFSET_SECONDS)); } //----------------------------------------------------------------------- // range() //----------------------------------------------------------------------- @Test public void test_range() { assertEquals(AMPM_OF_DAY.range(), AmPm.AM.range(AMPM_OF_DAY)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_invalidField() { AmPm.AM.range(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_range_null() { AmPm.AM.range(null); } //----------------------------------------------------------------------- // get() //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(0, AmPm.AM.get(AMPM_OF_DAY)); assertEquals(1, AmPm.PM.get(AMPM_OF_DAY)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_get_invalidField() { AmPm.PM.get(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_get_null() { AmPm.PM.get(null); } //----------------------------------------------------------------------- // getLong() //----------------------------------------------------------------------- @Test public void test_getLong() { assertEquals(0, AmPm.AM.getLong(AMPM_OF_DAY)); assertEquals(1, AmPm.PM.getLong(AMPM_OF_DAY)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField() { AmPm.PM.getLong(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_getLong_null() { AmPm.PM.getLong(null); } //----------------------------------------------------------------------- // query() //----------------------------------------------------------------------- @Test public void test_query() { assertEquals(null, AmPm.AM.query(TemporalQueries.chronology())); assertEquals(null, AmPm.AM.query(TemporalQueries.localDate())); assertEquals(null, AmPm.AM.query(TemporalQueries.localTime())); assertEquals(null, AmPm.AM.query(TemporalQueries.offset())); assertEquals(HALF_DAYS, AmPm.AM.query(TemporalQueries.precision())); assertEquals(null, AmPm.AM.query(TemporalQueries.zone())); assertEquals(null, AmPm.AM.query(TemporalQueries.zoneId())); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { assertEquals("AM", AmPm.AM.toString()); assertEquals("PM", AmPm.PM.toString()); } //----------------------------------------------------------------------- // generated methods //----------------------------------------------------------------------- @Test public void test_enum() { assertEquals(AmPm.AM, AmPm.valueOf("AM")); assertEquals(AmPm.AM, AmPm.values()[0]); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestAmountFormats.java000066400000000000000000000537351343451174100272150ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import java.time.Duration; import java.time.Period; import java.util.Locale; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test AmountFormats. */ @RunWith(DataProviderRunner.class) public class TestAmountFormats { private static final Locale PL = new Locale("pl"); private static final Locale RU = new Locale("ru"); //----------------------------------------------------------------------- @Test public void test_iso8601() { assertEquals("P12M6DT8H30M", AmountFormats.iso8601(Period.of(0, 12, 6), Duration.ofMinutes(8 * 60 + 30))); assertEquals("PT8H30M", AmountFormats.iso8601(Period.ZERO, Duration.ofMinutes(8 * 60 + 30))); assertEquals("P12M6D", AmountFormats.iso8601(Period.of(0, 12, 6), Duration.ZERO)); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_wordBased() { return new Object[][] { {Period.ofYears(0), Locale.ROOT, "0 days"}, {Period.ofYears(1), Locale.ROOT, "1 year"}, {Period.ofYears(2), Locale.ROOT, "2 years"}, {Period.ofYears(12), Locale.ROOT, "12 years"}, {Period.ofYears(-1), Locale.ROOT, "-1 year"}, {Period.ofWeeks(0), Locale.ENGLISH, "0 days"}, {Period.ofWeeks(1), Locale.ENGLISH, "1 week"}, {Period.ofWeeks(4), Locale.ENGLISH, "4 weeks"}, {Period.ofMonths(0), Locale.ENGLISH, "0 days"}, {Period.ofMonths(1), Locale.ENGLISH, "1 month"}, {Period.ofMonths(4), Locale.ENGLISH, "4 months"}, {Period.ofMonths(14), Locale.ENGLISH, "1 year and 2 months"}, {Period.ofDays(1), Locale.ENGLISH, "1 day"}, {Period.ofDays(2), Locale.ENGLISH, "2 days"}, {Period.ofDays(5), Locale.ENGLISH, "5 days"}, {Period.ofDays(7), Locale.ENGLISH, "1 week"}, {Period.ofDays(-1), Locale.ENGLISH, "-1 day"}, {Period.ofDays(1), new Locale("ro"), "1 zi"}, {Period.ofDays(2), new Locale("ro"), "2 zile"}, {Period.ofDays(5), new Locale("ro"), "5 zile"}, {Period.ofDays(7), new Locale("ro"), "1 săptămână"}, {Period.ofWeeks(3), new Locale("ro"), "3 săptămâni"}, {Period.ofMonths(14), new Locale("ro"), "1 an și 2 luni"}, {Period.ofMonths(1), new Locale("ro"), "1 lună"}, {Period.ofYears(2), new Locale("ro"), "2 ani"}, }; } @Test @UseDataProvider("data_wordBased") public void test_wordBased(Period period, Locale locale, String expected) { assertEquals(expected, AmountFormats.wordBased(period, locale)); } @DataProvider public static Object[][] duration_wordBased() { return new Object[][] { {Duration.ofMinutes(180 + 2), Locale.ENGLISH, "3 hours and 2 minutes"}, {Duration.ofMinutes(-60 - 40), Locale.ENGLISH, "-1 hour and -40 minutes"}, {Duration.ofSeconds(180), Locale.ENGLISH, "3 minutes"}, {Duration.ofSeconds(100), Locale.ENGLISH, "1 minute and 40 seconds"}, {Duration.ofSeconds(-140), Locale.ENGLISH, "-2 minutes and -20 seconds"}, {Duration.ofSeconds(-90), Locale.ENGLISH, "-1 minute and -30 seconds"}, {Duration.ofSeconds(-40), Locale.ENGLISH, "-40 seconds"}, {Duration.ofMillis(1_000), Locale.ENGLISH, "1 second"}, {Duration.ofMillis(3_000), Locale.ENGLISH, "3 seconds"}, {Duration.ofNanos(1_000_000), Locale.ENGLISH, "1 millisecond"}, {Duration.ofNanos(1000_000_000 + 2_000_000), Locale.ENGLISH, "1 second and 2 milliseconds"}, {Duration.ofMinutes(60 + 1), new Locale("ro"), "1 oră și 1 minut"}, {Duration.ofMinutes(180 + 2), new Locale("ro"), "3 ore și 2 minute"}, {Duration.ofMinutes(-60 - 40), new Locale("ro"), "-1 oră și -40 minute"}, {Duration.ofSeconds(-90), new Locale("ro"), "-1 minut și -30 secunde"}, {Duration.ofNanos(1_000_000), new Locale("ro"), "1 milisecundă"}, {Duration.ofNanos(1000_000_000 + 2_000_000), new Locale("ro"), "1 secundă și 2 milisecunde"}, {Duration.ofHours(5).plusMinutes(6).plusSeconds(7).plusNanos(8_000_000L), PL, "5 godzin, 6 minut, 7 sekund i 8 milisekund"}, }; } @Test @UseDataProvider("duration_wordBased") public void test_wordBased(Duration duration, Locale locale, String expected) { assertEquals(expected, AmountFormats.wordBased(duration, locale)); } @DataProvider public static Object[][] period_duration_wordBased() { return new Object[][] { {Period.ofDays(1), Duration.ofMinutes(180 + 2), Locale.ROOT, "1 day, 3 hours and 2 minutes"}, {Period.ofDays(2), Duration.ofSeconds(180), Locale.ROOT, "2 days and 3 minutes"}, {Period.ofDays(7), Duration.ofMinutes(80), Locale.ROOT, "1 week, 1 hour and 20 minutes"}, {Period.ZERO, Duration.ofMillis(1_000), Locale.ROOT, "1 second"}, {Period.ofMonths(0), Duration.ofSeconds(0), Locale.ENGLISH, "0 milliseconds"}, {Period.ofMonths(0), Duration.ofHours(9), Locale.ENGLISH, "9 hours"}, {Period.ofMonths(1), Duration.ZERO, Locale.ENGLISH, "1 month"}, {Period.ofMonths(4), Duration.ZERO, Locale.ENGLISH, "4 months"}, {Period.of(1, 2, 5), Duration.ofHours(4), Locale.ENGLISH, "1 year, 2 months, 5 days and 4 hours"}, {Period.ofDays(5), Duration.ofDays(2).plusHours(6), Locale.ENGLISH, "7 days and 6 hours"}, {Period.ofDays(5), Duration.ofDays(-2).plusHours(-6), Locale.ENGLISH, "3 days and -6 hours"}, {Period.ofDays(1), Duration.ofHours(5).plusMinutes(6).plusSeconds(7).plusNanos(8_000_000L), PL, "1 dzie\u0144, 5 godzin, 6 minut, 7 sekund i 8 milisekund"}, }; } @Test @UseDataProvider("period_duration_wordBased") public void test_wordBased(Period period, Duration duration, Locale locale, String expected) { assertEquals(expected, AmountFormats.wordBased(period, duration, locale)); } //----------------------------------------------------------------------- public void test_wordBased_pl_formatStandard() { Duration p = Duration.ofDays(1).plusHours(5).plusMinutes(6).plusSeconds(7).plusNanos(8_000_000L); assertEquals("1 dzie\u0144, 5 godzin, 6 minut, 7 sekund i 8 milisekund", AmountFormats.wordBased(p, PL)); } public void test_wordBased_pl_predicatex() { assertEquals("1 rok", AmountFormats.wordBased(Period.ofYears(1), PL)); assertEquals("2 lata", AmountFormats.wordBased(Period.ofYears(2), PL)); assertEquals("5 lat", AmountFormats.wordBased(Period.ofYears(5), PL)); assertEquals("12 lat", AmountFormats.wordBased(Period.ofYears(12), PL)); assertEquals("15 lat", AmountFormats.wordBased(Period.ofYears(15), PL)); assertEquals("1112 lat", AmountFormats.wordBased(Period.ofYears(1112), PL)); assertEquals("1115 lat", AmountFormats.wordBased(Period.ofYears(1115), PL)); assertEquals("2112 lat", AmountFormats.wordBased(Period.ofYears(2112), PL)); assertEquals("2115 lat", AmountFormats.wordBased(Period.ofYears(2115), PL)); assertEquals("2212 lat", AmountFormats.wordBased(Period.ofYears(2212), PL)); assertEquals("2215 lat", AmountFormats.wordBased(Period.ofYears(2215), PL)); assertEquals("22 lata", AmountFormats.wordBased(Period.ofYears(22), PL)); assertEquals("25 lat", AmountFormats.wordBased(Period.ofYears(25), PL)); assertEquals("1122 lata", AmountFormats.wordBased(Period.ofYears(1122), PL)); assertEquals("1125 lat", AmountFormats.wordBased(Period.ofYears(1125), PL)); assertEquals("2122 lata", AmountFormats.wordBased(Period.ofYears(2122), PL)); assertEquals("2125 lat", AmountFormats.wordBased(Period.ofYears(2125), PL)); assertEquals("2222 lata", AmountFormats.wordBased(Period.ofYears(2222), PL)); assertEquals("2225 lat", AmountFormats.wordBased(Period.ofYears(2225), PL)); assertEquals("1 miesi\u0105c", AmountFormats.wordBased(Period.ofMonths(1), PL)); assertEquals("2 miesi\u0105ce", AmountFormats.wordBased(Period.ofMonths(2), PL)); assertEquals("5 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(5), PL)); assertEquals("12 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(12), PL)); assertEquals("15 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(15), PL)); assertEquals("1112 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(1112), PL)); assertEquals("1115 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(1115), PL)); assertEquals("2112 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(2112), PL)); assertEquals("2115 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(2115), PL)); assertEquals("2212 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(2212), PL)); assertEquals("2215 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(2215), PL)); assertEquals("22 miesi\u0105ce", AmountFormats.wordBased(Period.ofMonths(22), PL)); assertEquals("25 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(25), PL)); assertEquals("1122 miesi\u0105ce", AmountFormats.wordBased(Period.ofMonths(1122), PL)); assertEquals("1125 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(1125), PL)); assertEquals("2122 miesi\u0105ce", AmountFormats.wordBased(Period.ofMonths(2122), PL)); assertEquals("2125 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(2125), PL)); assertEquals("2222 miesi\u0105ce", AmountFormats.wordBased(Period.ofMonths(2222), PL)); assertEquals("2225 miesi\u0119cy", AmountFormats.wordBased(Period.ofMonths(2225), PL)); assertEquals("1 tydzie\u0144", AmountFormats.wordBased(Period.ofWeeks(1), PL)); assertEquals("2 tygodnie", AmountFormats.wordBased(Period.ofWeeks(2), PL)); assertEquals("5 tygodni", AmountFormats.wordBased(Period.ofWeeks(5), PL)); assertEquals("12 tygodni", AmountFormats.wordBased(Period.ofWeeks(12), PL)); assertEquals("15 tygodni", AmountFormats.wordBased(Period.ofWeeks(15), PL)); assertEquals("1112 tygodni", AmountFormats.wordBased(Period.ofWeeks(1112), PL)); assertEquals("1115 tygodni", AmountFormats.wordBased(Period.ofWeeks(1115), PL)); assertEquals("2112 tygodni", AmountFormats.wordBased(Period.ofWeeks(2112), PL)); assertEquals("2115 tygodni", AmountFormats.wordBased(Period.ofWeeks(2115), PL)); assertEquals("2212 tygodni", AmountFormats.wordBased(Period.ofWeeks(2212), PL)); assertEquals("2215 tygodni", AmountFormats.wordBased(Period.ofWeeks(2215), PL)); assertEquals("22 tygodnie", AmountFormats.wordBased(Period.ofWeeks(22), PL)); assertEquals("25 tygodni", AmountFormats.wordBased(Period.ofWeeks(25), PL)); assertEquals("1122 tygodnie", AmountFormats.wordBased(Period.ofWeeks(1122), PL)); assertEquals("1125 tygodni", AmountFormats.wordBased(Period.ofWeeks(1125), PL)); assertEquals("2122 tygodnie", AmountFormats.wordBased(Period.ofWeeks(2122), PL)); assertEquals("2125 tygodni", AmountFormats.wordBased(Period.ofWeeks(2125), PL)); assertEquals("2222 tygodnie", AmountFormats.wordBased(Period.ofWeeks(2222), PL)); assertEquals("2225 tygodni", AmountFormats.wordBased(Period.ofWeeks(2225), PL)); assertEquals("1 dzie\u0144", AmountFormats.wordBased(Period.ofDays(1), PL)); assertEquals("2 dni", AmountFormats.wordBased(Period.ofDays(2), PL)); assertEquals("5 dni", AmountFormats.wordBased(Period.ofDays(5), PL)); assertEquals("12 dni", AmountFormats.wordBased(Period.ofDays(12), PL)); assertEquals("15 dni", AmountFormats.wordBased(Period.ofDays(15), PL)); assertEquals("22 dni", AmountFormats.wordBased(Period.ofDays(22), PL)); assertEquals("25 dni", AmountFormats.wordBased(Period.ofDays(25), PL)); assertEquals("1 godzina", AmountFormats.wordBased(Duration.ofHours(1), PL)); assertEquals("2 godziny", AmountFormats.wordBased(Duration.ofHours(2), PL)); assertEquals("5 godzin", AmountFormats.wordBased(Duration.ofHours(5), PL)); assertEquals("12 godzin", AmountFormats.wordBased(Duration.ofHours(12), PL)); assertEquals("15 godzin", AmountFormats.wordBased(Duration.ofHours(15), PL)); assertEquals("1112 godzin", AmountFormats.wordBased(Duration.ofHours(1112), PL)); assertEquals("1115 godzin", AmountFormats.wordBased(Duration.ofHours(1115), PL)); assertEquals("2112 godzin", AmountFormats.wordBased(Duration.ofHours(2112), PL)); assertEquals("2115 godzin", AmountFormats.wordBased(Duration.ofHours(2115), PL)); assertEquals("2212 godzin", AmountFormats.wordBased(Duration.ofHours(2212), PL)); assertEquals("2215 godzin", AmountFormats.wordBased(Duration.ofHours(2215), PL)); assertEquals("22 godziny", AmountFormats.wordBased(Duration.ofHours(22), PL)); assertEquals("25 godzin", AmountFormats.wordBased(Duration.ofHours(25), PL)); assertEquals("1122 godziny", AmountFormats.wordBased(Duration.ofHours(1122), PL)); assertEquals("1125 godzin", AmountFormats.wordBased(Duration.ofHours(1125), PL)); assertEquals("2122 godziny", AmountFormats.wordBased(Duration.ofHours(2122), PL)); assertEquals("2125 godzin", AmountFormats.wordBased(Duration.ofHours(2125), PL)); assertEquals("2222 godziny", AmountFormats.wordBased(Duration.ofHours(2222), PL)); assertEquals("2225 godzin", AmountFormats.wordBased(Duration.ofHours(2225), PL)); assertEquals("1 minuta", AmountFormats.wordBased(Duration.ofMinutes(1), PL)); assertEquals("2 minuty", AmountFormats.wordBased(Duration.ofMinutes(2), PL)); assertEquals("5 minut", AmountFormats.wordBased(Duration.ofMinutes(5), PL)); assertEquals("12 minut", AmountFormats.wordBased(Duration.ofMinutes(12), PL)); assertEquals("15 minut", AmountFormats.wordBased(Duration.ofMinutes(15), PL)); assertEquals("1112 minut", AmountFormats.wordBased(Duration.ofMinutes(1112), PL)); assertEquals("1115 minut", AmountFormats.wordBased(Duration.ofMinutes(1115), PL)); assertEquals("2112 minut", AmountFormats.wordBased(Duration.ofMinutes(2112), PL)); assertEquals("2115 minut", AmountFormats.wordBased(Duration.ofMinutes(2115), PL)); assertEquals("2212 minut", AmountFormats.wordBased(Duration.ofMinutes(2212), PL)); assertEquals("2215 minut", AmountFormats.wordBased(Duration.ofMinutes(2215), PL)); assertEquals("22 minuty", AmountFormats.wordBased(Duration.ofMinutes(22), PL)); assertEquals("25 minut", AmountFormats.wordBased(Duration.ofMinutes(25), PL)); assertEquals("1122 minuty", AmountFormats.wordBased(Duration.ofMinutes(1122), PL)); assertEquals("1125 minut", AmountFormats.wordBased(Duration.ofMinutes(1125), PL)); assertEquals("2122 minuty", AmountFormats.wordBased(Duration.ofMinutes(2122), PL)); assertEquals("2125 minut", AmountFormats.wordBased(Duration.ofMinutes(2125), PL)); assertEquals("2222 minuty", AmountFormats.wordBased(Duration.ofMinutes(2222), PL)); assertEquals("2225 minut", AmountFormats.wordBased(Duration.ofMinutes(2225), PL)); assertEquals("1 sekunda", AmountFormats.wordBased(Duration.ofSeconds(1), PL)); assertEquals("2 sekundy", AmountFormats.wordBased(Duration.ofSeconds(2), PL)); assertEquals("5 sekund", AmountFormats.wordBased(Duration.ofSeconds(5), PL)); assertEquals("12 sekund", AmountFormats.wordBased(Duration.ofSeconds(12), PL)); assertEquals("15 sekund", AmountFormats.wordBased(Duration.ofSeconds(15), PL)); assertEquals("1112 sekund", AmountFormats.wordBased(Duration.ofSeconds(1112), PL)); assertEquals("1115 sekund", AmountFormats.wordBased(Duration.ofSeconds(1115), PL)); assertEquals("2112 sekund", AmountFormats.wordBased(Duration.ofSeconds(2112), PL)); assertEquals("2115 sekund", AmountFormats.wordBased(Duration.ofSeconds(2115), PL)); assertEquals("2212 sekund", AmountFormats.wordBased(Duration.ofSeconds(2212), PL)); assertEquals("2215 sekund", AmountFormats.wordBased(Duration.ofSeconds(2215), PL)); assertEquals("22 sekundy", AmountFormats.wordBased(Duration.ofSeconds(22), PL)); assertEquals("25 sekund", AmountFormats.wordBased(Duration.ofSeconds(25), PL)); assertEquals("1122 sekundy", AmountFormats.wordBased(Duration.ofSeconds(1122), PL)); assertEquals("1125 sekund", AmountFormats.wordBased(Duration.ofSeconds(1125), PL)); assertEquals("2122 sekundy", AmountFormats.wordBased(Duration.ofSeconds(2122), PL)); assertEquals("2125 sekund", AmountFormats.wordBased(Duration.ofSeconds(2125), PL)); assertEquals("2222 sekundy", AmountFormats.wordBased(Duration.ofSeconds(2222), PL)); assertEquals("2225 sekund", AmountFormats.wordBased(Duration.ofSeconds(2225), PL)); assertEquals("1 milisekunda", AmountFormats.wordBased(Duration.ofMillis(1), PL)); assertEquals("2 milisekundy", AmountFormats.wordBased(Duration.ofMillis(2), PL)); assertEquals("5 milisekund", AmountFormats.wordBased(Duration.ofMillis(5), PL)); assertEquals("12 milisekund", AmountFormats.wordBased(Duration.ofMillis(12), PL)); assertEquals("15 milisekund", AmountFormats.wordBased(Duration.ofMillis(15), PL)); assertEquals("1112 milisekund", AmountFormats.wordBased(Duration.ofMillis(1112), PL)); assertEquals("1115 milisekund", AmountFormats.wordBased(Duration.ofMillis(1115), PL)); assertEquals("2112 milisekund", AmountFormats.wordBased(Duration.ofMillis(2112), PL)); assertEquals("2115 milisekund", AmountFormats.wordBased(Duration.ofMillis(2115), PL)); assertEquals("2212 milisekund", AmountFormats.wordBased(Duration.ofMillis(2212), PL)); assertEquals("2215 milisekund", AmountFormats.wordBased(Duration.ofMillis(2215), PL)); assertEquals("22 milisekundy", AmountFormats.wordBased(Duration.ofMillis(22), PL)); assertEquals("25 milisekund", AmountFormats.wordBased(Duration.ofMillis(25), PL)); assertEquals("1122 milisekundy", AmountFormats.wordBased(Duration.ofMillis(1122), PL)); assertEquals("1125 milisekund", AmountFormats.wordBased(Duration.ofMillis(1125), PL)); assertEquals("2122 milisekundy", AmountFormats.wordBased(Duration.ofMillis(2122), PL)); assertEquals("2125 milisekund", AmountFormats.wordBased(Duration.ofMillis(2125), PL)); assertEquals("2222 milisekundy", AmountFormats.wordBased(Duration.ofMillis(2222), PL)); assertEquals("2225 milisekund", AmountFormats.wordBased(Duration.ofMillis(2225), PL)); } public void test_wordBased_ru_predicate() { assertEquals("1 \u0433\u043E\u0434", AmountFormats.wordBased(Period.ofYears(1), RU)); assertEquals("2 \u0433\u043E\u0434\u0430", AmountFormats.wordBased(Period.ofYears(2), RU)); assertEquals("3 \u0433\u043E\u0434\u0430", AmountFormats.wordBased(Period.ofYears(2), RU)); assertEquals("4 \u0433\u043E\u0434\u0430", AmountFormats.wordBased(Period.ofYears(2), RU)); assertEquals("5 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(5), RU)); assertEquals("11 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(12), RU)); assertEquals("12 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(12), RU)); assertEquals("13 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(12), RU)); assertEquals("14 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(12), RU)); assertEquals("15 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(15), RU)); assertEquals("21 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(15), RU)); assertEquals("22 \u0433\u043E\u0434\u0430", AmountFormats.wordBased(Period.ofYears(2), RU)); assertEquals("23 \u0433\u043E\u0434\u0430", AmountFormats.wordBased(Period.ofYears(2), RU)); assertEquals("24 \u0433\u043E\u0434\u0430", AmountFormats.wordBased(Period.ofYears(2), RU)); assertEquals("25 \u043B\u0435\u0442", AmountFormats.wordBased(Period.ofYears(15), RU)); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestConvert.java000066400000000000000000000070641343451174100260300ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.Period; import org.joda.convert.StringConvert; import org.junit.Test; import org.junit.runner.RunWith; import org.threeten.extra.scale.TaiInstant; import org.threeten.extra.scale.UtcInstant; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; @RunWith(DataProviderRunner.class) public class TestConvert { @DataProvider public static Object[][] data_inputs() { return new Object[][] { {Seconds.of(23), "PT23S"}, {Minutes.of(23), "PT23M"}, {Hours.of(23), "PT23H"}, {Days.of(23), "P23D"}, {Weeks.of(23), "P23W"}, {Months.of(23), "P23M"}, {Years.of(23), "P23Y"}, {YearWeek.of(2019, 3), "2019-W03"}, {YearQuarter.of(2019, 3), "2019-Q3"}, {PeriodDuration.of(Period.of(1, 2, 3), Duration.ofHours(6)), "P1Y2M3DT6H"}, {Interval.of(Instant.ofEpochSecond(60), Duration.ofHours(6)), "1970-01-01T00:01:00Z/1970-01-01T06:01:00Z"}, {LocalDateRange.of(LocalDate.of(2018, 6, 1), LocalDate.of(2018, 9, 15)), "2018-06-01/2018-09-15"}, {TaiInstant.ofTaiSeconds(123, 456), "123.000000456s(TAI)"}, {UtcInstant.ofModifiedJulianDay(0, 1123456789L), "1858-11-17T00:00:01.123456789Z"}, }; } @Test @UseDataProvider("data_inputs") public void test_convertToString(Object obj, String str) { assertEquals(str, StringConvert.INSTANCE.convertToString(obj)); } @Test @UseDataProvider("data_inputs") public void test_convertFromString(Object obj, String str) { assertEquals(obj, StringConvert.INSTANCE.convertFromString(obj.getClass(), str)); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestDayOfMonth.java000066400000000000000000001013271343451174100264150ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.Month.APRIL; import static java.time.Month.AUGUST; import static java.time.Month.DECEMBER; import static java.time.Month.FEBRUARY; import static java.time.Month.JANUARY; import static java.time.Month.JULY; import static java.time.Month.JUNE; import static java.time.Month.MARCH; import static java.time.Month.MAY; import static java.time.Month.NOVEMBER; import static java.time.Month.OCTOBER; import static java.time.Month.SEPTEMBER; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_DAY; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; import java.time.Month; import java.time.MonthDay; import java.time.YearMonth; import java.time.ZoneId; import java.time.chrono.IsoChronology; import java.time.chrono.JapaneseDate; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import org.junit.Test; /** * Test DayOfMonth. */ public class TestDayOfMonth { private static final int MAX_LENGTH = 31; private static final DayOfMonth TEST = DayOfMonth.of(12); private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); private static class TestingField implements TemporalField { public static final TestingField INSTANCE = new TestingField(); @Override public TemporalUnit getBaseUnit() { return ChronoUnit.DAYS; } @Override public TemporalUnit getRangeUnit() { return ChronoUnit.MONTHS; } @Override public ValueRange range() { return ValueRange.of(1, 28, 31); } @Override public boolean isDateBased() { return true; } @Override public boolean isTimeBased() { return false; } @Override public boolean isSupportedBy(TemporalAccessor temporal) { return temporal.isSupported(DAY_OF_MONTH); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { return range(); } @Override public long getFrom(TemporalAccessor temporal) { return temporal.getLong(DAY_OF_MONTH); } @Override @SuppressWarnings("unchecked") public R adjustInto(R temporal, long newValue) { return (R) temporal.with(DAY_OF_MONTH, newValue); } } //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(DayOfMonth.class)); assertTrue(Comparable.class.isAssignableFrom(DayOfMonth.class)); assertTrue(TemporalAdjuster.class.isAssignableFrom(DayOfMonth.class)); assertTrue(TemporalAccessor.class.isAssignableFrom(DayOfMonth.class)); } @Test public void test_serialization() throws IOException, ClassNotFoundException { DayOfMonth test = DayOfMonth.of(1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- // now() //----------------------------------------------------------------------- @Test public void test_now() { DayOfMonth test = DayOfMonth.now(); if (LocalDate.now().getDayOfMonth() != test.getValue()) { test = DayOfMonth.now(); } assertEquals(LocalDate.now().getDayOfMonth(), test.getValue()); } //----------------------------------------------------------------------- // now(ZoneId) //----------------------------------------------------------------------- @Test public void test_now_ZoneId() { ZoneId zone = ZoneId.of("Asia/Tokyo"); DayOfMonth test = DayOfMonth.now(zone); if (LocalDate.now(zone).getDayOfMonth() != test.getValue()) { test = DayOfMonth.now(zone); } assertEquals(LocalDate.now(zone).getDayOfMonth(), test.getValue()); } //----------------------------------------------------------------------- // of(int) //----------------------------------------------------------------------- @Test public void test_of_int_singleton() { for (int i = 1; i <= MAX_LENGTH; i++) { DayOfMonth test = DayOfMonth.of(i); assertEquals(i, test.getValue()); assertSame(test, DayOfMonth.of(i)); } } @Test(expected = DateTimeException.class) public void test_of_int_tooLow() { DayOfMonth.of(0); } @Test(expected = DateTimeException.class) public void test_of_int_tooHigh() { DayOfMonth.of(32); } //----------------------------------------------------------------------- // from(TemporalAccessor) //----------------------------------------------------------------------- @Test public void test_from_TemporalAccessor_notLeapYear() { LocalDate date = LocalDate.of(2007, 1, 1); for (int i = 1; i <= 31; i++) { // Jan assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 28; i++) { // Feb assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // Mar assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 30; i++) { // Apr assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // May assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 30; i++) { // Jun assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // Jul assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // Aug assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 30; i++) { // Sep assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // Oct assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 30; i++) { // Nov assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // Dec assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } } @Test public void test_from_TemporalAccessor_leapYear() { LocalDate date = LocalDate.of(2008, 1, 1); for (int i = 1; i <= 31; i++) { // Jan assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 29; i++) { // Feb assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } for (int i = 1; i <= 31; i++) { // Mar assertEquals(i, DayOfMonth.from(date).getValue()); date = date.plusDays(1); } } @Test public void test_from_TemporalAccessor_DayOfMonth() { DayOfMonth dom = DayOfMonth.of(6); assertEquals(dom, DayOfMonth.from(dom)); } @Test public void test_from_TemporalAccessor_nonIso() { LocalDate date = LocalDate.now(); assertEquals(date.getDayOfMonth(), DayOfMonth.from(JapaneseDate.from(date)).getValue()); } @Test(expected = DateTimeException.class) public void test_from_TemporalAccessor_noDerive() { DayOfMonth.from(LocalTime.NOON); } @Test(expected = NullPointerException.class) public void test_from_TemporalAccessor_null() { DayOfMonth.from((TemporalAccessor) null); } @Test public void test_from_parse_CharSequence() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d"); assertEquals(DayOfMonth.of(3), formatter.parse("3", DayOfMonth::from)); } //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- @Test public void test_isSupported() { assertEquals(false, TEST.isSupported((TemporalField) null)); assertEquals(false, TEST.isSupported(NANO_OF_SECOND)); assertEquals(false, TEST.isSupported(NANO_OF_DAY)); assertEquals(false, TEST.isSupported(MICRO_OF_SECOND)); assertEquals(false, TEST.isSupported(MICRO_OF_DAY)); assertEquals(false, TEST.isSupported(MILLI_OF_SECOND)); assertEquals(false, TEST.isSupported(MILLI_OF_DAY)); assertEquals(false, TEST.isSupported(SECOND_OF_MINUTE)); assertEquals(false, TEST.isSupported(SECOND_OF_DAY)); assertEquals(false, TEST.isSupported(MINUTE_OF_HOUR)); assertEquals(false, TEST.isSupported(MINUTE_OF_DAY)); assertEquals(false, TEST.isSupported(HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(AMPM_OF_DAY)); assertEquals(false, TEST.isSupported(DAY_OF_WEEK)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(true, TEST.isSupported(DAY_OF_MONTH)); assertEquals(false, TEST.isSupported(DAY_OF_YEAR)); assertEquals(false, TEST.isSupported(EPOCH_DAY)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_YEAR)); assertEquals(false, TEST.isSupported(MONTH_OF_YEAR)); assertEquals(false, TEST.isSupported(PROLEPTIC_MONTH)); assertEquals(false, TEST.isSupported(YEAR_OF_ERA)); assertEquals(false, TEST.isSupported(YEAR)); assertEquals(false, TEST.isSupported(ERA)); assertEquals(false, TEST.isSupported(INSTANT_SECONDS)); assertEquals(false, TEST.isSupported(OFFSET_SECONDS)); assertEquals(false, TEST.isSupported(IsoFields.DAY_OF_QUARTER)); assertEquals(true, TEST.isSupported(TestingField.INSTANCE)); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- @Test public void test_range() { assertEquals(DAY_OF_MONTH.range(), TEST.range(DAY_OF_MONTH)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_invalidField() { TEST.range(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_range_null() { TEST.range((TemporalField) null); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(12, TEST.get(DAY_OF_MONTH)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_get_invalidField() { TEST.get(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_get_null() { TEST.get((TemporalField) null); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- @Test public void test_getLong() { assertEquals(12L, TEST.getLong(DAY_OF_MONTH)); } @Test public void test_getLong_derivedField() { assertEquals(12L, TEST.getLong(TestingField.INSTANCE)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField() { TEST.getLong(MONTH_OF_YEAR); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField2() { TEST.getLong(IsoFields.DAY_OF_QUARTER); } @Test(expected = NullPointerException.class) public void test_getLong_null() { TEST.getLong((TemporalField) null); } //----------------------------------------------------------------------- // isValidYearMonth(YearMonth) //----------------------------------------------------------------------- @Test public void test_isValidYearMonth_31() { DayOfMonth test = DayOfMonth.of(31); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 1))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2012, 2))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 3))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2012, 4))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 5))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2012, 6))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 7))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 8))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2012, 9))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 10))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2012, 11))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 12))); } @Test public void test_isValidYearMonth_30() { DayOfMonth test = DayOfMonth.of(30); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 1))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2012, 2))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 3))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 4))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 5))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 6))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 7))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 8))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 9))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 10))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 11))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 12))); } @Test public void test_isValidYearMonth_29() { DayOfMonth test = DayOfMonth.of(29); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 1))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 2))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 3))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 4))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 5))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 6))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 7))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 8))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 9))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 10))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 11))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 12))); assertEquals(false, test.isValidYearMonth(YearMonth.of(2011, 2))); } @Test public void test_isValidYearMonth_28() { DayOfMonth test = DayOfMonth.of(28); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 1))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 2))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 3))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 4))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 5))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 6))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 7))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 8))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 9))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 10))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 11))); assertEquals(true, test.isValidYearMonth(YearMonth.of(2012, 12))); } @Test public void test_isValidYearMonth_null() { assertFalse(TEST.isValidYearMonth((YearMonth) null)); } //----------------------------------------------------------------------- // query(TemporalQuery) //----------------------------------------------------------------------- @Test public void test_query() { assertEquals(IsoChronology.INSTANCE, TEST.query(TemporalQueries.chronology())); assertEquals(null, TEST.query(TemporalQueries.localDate())); assertEquals(null, TEST.query(TemporalQueries.localTime())); assertEquals(null, TEST.query(TemporalQueries.offset())); assertEquals(null, TEST.query(TemporalQueries.precision())); assertEquals(null, TEST.query(TemporalQueries.zone())); assertEquals(null, TEST.query(TemporalQueries.zoneId())); } //----------------------------------------------------------------------- // adjustInto(Temporal) //----------------------------------------------------------------------- @Test public void test_adjustInto() { LocalDate base = LocalDate.of(2007, 1, 1); LocalDate expected = base; for (int i = 1; i <= MAX_LENGTH; i++) { // Jan Temporal result = DayOfMonth.of(i).adjustInto(base); assertEquals(expected, result); expected = expected.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_adjustInto_april31() { LocalDate base = LocalDate.of(2007, 4, 1); DayOfMonth test = DayOfMonth.of(31); test.adjustInto(base); } @Test(expected = DateTimeException.class) public void test_adjustInto_february29_notLeapYear() { LocalDate base = LocalDate.of(2007, 2, 1); DayOfMonth test = DayOfMonth.of(29); test.adjustInto(base); } @Test(expected = DateTimeException.class) public void test_adjustInto_nonIso() { TEST.adjustInto(JapaneseDate.now()); } @Test(expected = NullPointerException.class) public void test_adjustInto_null() { TEST.adjustInto((Temporal) null); } //----------------------------------------------------------------------- // atMonth(Month) //----------------------------------------------------------------------- @Test public void test_atMonth_Month_31() { DayOfMonth test = DayOfMonth.of(31); assertEquals(MonthDay.of(1, 31), test.atMonth(JANUARY)); assertEquals(MonthDay.of(2, 29), test.atMonth(FEBRUARY)); assertEquals(MonthDay.of(3, 31), test.atMonth(MARCH)); assertEquals(MonthDay.of(4, 30), test.atMonth(APRIL)); assertEquals(MonthDay.of(5, 31), test.atMonth(MAY)); assertEquals(MonthDay.of(6, 30), test.atMonth(JUNE)); assertEquals(MonthDay.of(7, 31), test.atMonth(JULY)); assertEquals(MonthDay.of(8, 31), test.atMonth(AUGUST)); assertEquals(MonthDay.of(9, 30), test.atMonth(SEPTEMBER)); assertEquals(MonthDay.of(10, 31), test.atMonth(OCTOBER)); assertEquals(MonthDay.of(11, 30), test.atMonth(NOVEMBER)); assertEquals(MonthDay.of(12, 31), test.atMonth(DECEMBER)); } @Test public void test_atMonth_Month_28() { DayOfMonth test = DayOfMonth.of(28); assertEquals(MonthDay.of(1, 28), test.atMonth(JANUARY)); assertEquals(MonthDay.of(2, 28), test.atMonth(FEBRUARY)); assertEquals(MonthDay.of(3, 28), test.atMonth(MARCH)); assertEquals(MonthDay.of(4, 28), test.atMonth(APRIL)); assertEquals(MonthDay.of(5, 28), test.atMonth(MAY)); assertEquals(MonthDay.of(6, 28), test.atMonth(JUNE)); assertEquals(MonthDay.of(7, 28), test.atMonth(JULY)); assertEquals(MonthDay.of(8, 28), test.atMonth(AUGUST)); assertEquals(MonthDay.of(9, 28), test.atMonth(SEPTEMBER)); assertEquals(MonthDay.of(10, 28), test.atMonth(OCTOBER)); assertEquals(MonthDay.of(11, 28), test.atMonth(NOVEMBER)); assertEquals(MonthDay.of(12, 28), test.atMonth(DECEMBER)); } @Test(expected = NullPointerException.class) public void test_atMonth_null() { TEST.atMonth((Month) null); } //----------------------------------------------------------------------- // atMonth(int) //----------------------------------------------------------------------- @Test public void test_atMonth_int_31() { DayOfMonth test = DayOfMonth.of(31); assertEquals(MonthDay.of(1, 31), test.atMonth(1)); assertEquals(MonthDay.of(2, 29), test.atMonth(2)); assertEquals(MonthDay.of(3, 31), test.atMonth(3)); assertEquals(MonthDay.of(4, 30), test.atMonth(4)); assertEquals(MonthDay.of(5, 31), test.atMonth(5)); assertEquals(MonthDay.of(6, 30), test.atMonth(6)); assertEquals(MonthDay.of(7, 31), test.atMonth(7)); assertEquals(MonthDay.of(8, 31), test.atMonth(8)); assertEquals(MonthDay.of(9, 30), test.atMonth(9)); assertEquals(MonthDay.of(10, 31), test.atMonth(10)); assertEquals(MonthDay.of(11, 30), test.atMonth(11)); assertEquals(MonthDay.of(12, 31), test.atMonth(12)); } @Test public void test_atMonth_int_28() { DayOfMonth test = DayOfMonth.of(28); assertEquals(MonthDay.of(1, 28), test.atMonth(1)); assertEquals(MonthDay.of(2, 28), test.atMonth(2)); assertEquals(MonthDay.of(3, 28), test.atMonth(3)); assertEquals(MonthDay.of(4, 28), test.atMonth(4)); assertEquals(MonthDay.of(5, 28), test.atMonth(5)); assertEquals(MonthDay.of(6, 28), test.atMonth(6)); assertEquals(MonthDay.of(7, 28), test.atMonth(7)); assertEquals(MonthDay.of(8, 28), test.atMonth(8)); assertEquals(MonthDay.of(9, 28), test.atMonth(9)); assertEquals(MonthDay.of(10, 28), test.atMonth(10)); assertEquals(MonthDay.of(11, 28), test.atMonth(11)); assertEquals(MonthDay.of(12, 28), test.atMonth(12)); } @Test(expected = DateTimeException.class) public void test_atMonth_tooLow() { TEST.atMonth(0); } @Test(expected = DateTimeException.class) public void test_atMonth_tooHigh() { TEST.atMonth(13); } //----------------------------------------------------------------------- // atYearMonth(YearMonth) //----------------------------------------------------------------------- @Test public void test_atYearMonth_31() { DayOfMonth test = DayOfMonth.of(31); assertEquals(LocalDate.of(2012, 1, 31), test.atYearMonth(YearMonth.of(2012, 1))); assertEquals(LocalDate.of(2012, 2, 29), test.atYearMonth(YearMonth.of(2012, 2))); assertEquals(LocalDate.of(2012, 3, 31), test.atYearMonth(YearMonth.of(2012, 3))); assertEquals(LocalDate.of(2012, 4, 30), test.atYearMonth(YearMonth.of(2012, 4))); assertEquals(LocalDate.of(2012, 5, 31), test.atYearMonth(YearMonth.of(2012, 5))); assertEquals(LocalDate.of(2012, 6, 30), test.atYearMonth(YearMonth.of(2012, 6))); assertEquals(LocalDate.of(2012, 7, 31), test.atYearMonth(YearMonth.of(2012, 7))); assertEquals(LocalDate.of(2012, 8, 31), test.atYearMonth(YearMonth.of(2012, 8))); assertEquals(LocalDate.of(2012, 9, 30), test.atYearMonth(YearMonth.of(2012, 9))); assertEquals(LocalDate.of(2012, 10, 31), test.atYearMonth(YearMonth.of(2012, 10))); assertEquals(LocalDate.of(2012, 11, 30), test.atYearMonth(YearMonth.of(2012, 11))); assertEquals(LocalDate.of(2012, 12, 31), test.atYearMonth(YearMonth.of(2012, 12))); assertEquals(LocalDate.of(2011, 2, 28), test.atYearMonth(YearMonth.of(2011, 2))); } @Test public void test_atYearMonth_28() { DayOfMonth test = DayOfMonth.of(28); assertEquals(LocalDate.of(2012, 1, 28), test.atYearMonth(YearMonth.of(2012, 1))); assertEquals(LocalDate.of(2012, 2, 28), test.atYearMonth(YearMonth.of(2012, 2))); assertEquals(LocalDate.of(2012, 3, 28), test.atYearMonth(YearMonth.of(2012, 3))); assertEquals(LocalDate.of(2012, 4, 28), test.atYearMonth(YearMonth.of(2012, 4))); assertEquals(LocalDate.of(2012, 5, 28), test.atYearMonth(YearMonth.of(2012, 5))); assertEquals(LocalDate.of(2012, 6, 28), test.atYearMonth(YearMonth.of(2012, 6))); assertEquals(LocalDate.of(2012, 7, 28), test.atYearMonth(YearMonth.of(2012, 7))); assertEquals(LocalDate.of(2012, 8, 28), test.atYearMonth(YearMonth.of(2012, 8))); assertEquals(LocalDate.of(2012, 9, 28), test.atYearMonth(YearMonth.of(2012, 9))); assertEquals(LocalDate.of(2012, 10, 28), test.atYearMonth(YearMonth.of(2012, 10))); assertEquals(LocalDate.of(2012, 11, 28), test.atYearMonth(YearMonth.of(2012, 11))); assertEquals(LocalDate.of(2012, 12, 28), test.atYearMonth(YearMonth.of(2012, 12))); } @Test(expected = NullPointerException.class) public void test_atYearMonth_null() { TEST.atYearMonth((YearMonth) null); } //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test public void test_compareTo() { for (int i = 1; i <= MAX_LENGTH; i++) { DayOfMonth a = DayOfMonth.of(i); for (int j = 1; j <= MAX_LENGTH; j++) { DayOfMonth b = DayOfMonth.of(j); if (i < j) { assertEquals(true, a.compareTo(b) < 0); assertEquals(true, b.compareTo(a) > 0); } else if (i > j) { assertEquals(true, a.compareTo(b) > 0); assertEquals(true, b.compareTo(a) < 0); } else { assertEquals(0, a.compareTo(b)); assertEquals(0, b.compareTo(a)); } } } } @Test(expected = NullPointerException.class) public void test_compareTo_nullDayOfMonth() { DayOfMonth doy = null; DayOfMonth test = DayOfMonth.of(1); test.compareTo(doy); } //----------------------------------------------------------------------- // equals() / hashCode() //----------------------------------------------------------------------- @Test public void test_equals() { for (int i = 1; i <= MAX_LENGTH; i++) { DayOfMonth a = DayOfMonth.of(i); for (int j = 1; j <= MAX_LENGTH; j++) { DayOfMonth b = DayOfMonth.of(j); assertEquals(i == j, a.equals(b)); assertEquals(i == j, a.hashCode() == b.hashCode()); } } } @Test public void test_equals_nullDayOfMonth() { DayOfMonth doy = null; DayOfMonth test = DayOfMonth.of(1); assertEquals(false, test.equals(doy)); } @Test public void test_equals_incorrectType() { DayOfMonth test = DayOfMonth.of(1); Object obj = "Incorrect type"; assertEquals(false, test.equals(obj)); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { for (int i = 1; i <= MAX_LENGTH; i++) { DayOfMonth a = DayOfMonth.of(i); assertEquals("DayOfMonth:" + i, a.toString()); } } //----------------------------------------------------------------------- // now(Clock) //----------------------------------------------------------------------- @Test public void test_now_clock() { for (int i = 1; i <= 31; i++) { // Jan Instant instant = LocalDate.of(2008, 1, i).atStartOfDay(PARIS).toInstant(); Clock clock = Clock.fixed(instant, PARIS); assertEquals(i, DayOfMonth.now(clock).getValue()); } } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestDayOfYear.java000066400000000000000000000602071343451174100262310ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_DAY; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; import java.time.Year; import java.time.ZoneId; import java.time.chrono.IsoChronology; import java.time.chrono.JapaneseDate; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import org.junit.Test; /** * Test DayOfYear. */ public class TestDayOfYear { private static final Year YEAR_STANDARD = Year.of(2007); private static final Year YEAR_LEAP = Year.of(2008); private static final int STANDARD_YEAR_LENGTH = 365; private static final int LEAP_YEAR_LENGTH = 366; private static final DayOfYear TEST = DayOfYear.of(12); private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); private static class TestingField implements TemporalField { public static final TestingField INSTANCE = new TestingField(); @Override public TemporalUnit getBaseUnit() { return ChronoUnit.DAYS; } @Override public TemporalUnit getRangeUnit() { return ChronoUnit.YEARS; } @Override public ValueRange range() { return ValueRange.of(1, 365, 366); } @Override public boolean isDateBased() { return true; } @Override public boolean isTimeBased() { return false; } @Override public boolean isSupportedBy(TemporalAccessor temporal) { return temporal.isSupported(DAY_OF_YEAR); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { return range(); } @Override public long getFrom(TemporalAccessor temporal) { return temporal.getLong(DAY_OF_YEAR); } @Override @SuppressWarnings("unchecked") public R adjustInto(R temporal, long newValue) { return (R) temporal.with(DAY_OF_YEAR, newValue); } } //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(DayOfYear.class)); assertTrue(Comparable.class.isAssignableFrom(DayOfYear.class)); assertTrue(TemporalAdjuster.class.isAssignableFrom(DayOfYear.class)); assertTrue(TemporalAccessor.class.isAssignableFrom(DayOfYear.class)); } @Test public void test_serialization() throws IOException, ClassNotFoundException { DayOfYear test = DayOfYear.of(1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- // now() //----------------------------------------------------------------------- @Test public void test_now() { DayOfYear test = DayOfYear.now(); if (LocalDate.now().getDayOfYear() != test.getValue()) { test = DayOfYear.now(); } assertEquals(LocalDate.now().getDayOfYear(), test.getValue()); } //----------------------------------------------------------------------- // now(ZoneId) //----------------------------------------------------------------------- @Test public void test_now_ZoneId() { ZoneId zone = ZoneId.of("Asia/Tokyo"); DayOfYear test = DayOfYear.now(zone); if (LocalDate.now(zone).getDayOfYear() != test.getValue()) { test = DayOfYear.now(zone); } assertEquals(LocalDate.now(zone).getDayOfYear(), test.getValue()); } //----------------------------------------------------------------------- // of(int) //----------------------------------------------------------------------- @Test public void test_of_int() { for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(i, test.getValue()); assertEquals(test, DayOfYear.of(i)); } } @Test(expected = DateTimeException.class) public void test_of_int_tooLow() { DayOfYear.of(0); } @Test(expected = DateTimeException.class) public void test_of_int_tooHigh() { DayOfYear.of(367); } //----------------------------------------------------------------------- // from(TemporalAccessor) //----------------------------------------------------------------------- @Test public void test_from_TemporalAccessor_notLeapYear() { LocalDate date = LocalDate.of(2007, 1, 1); for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.from(date); assertEquals(i, test.getValue()); date = date.plusDays(1); } DayOfYear test = DayOfYear.from(date); assertEquals(1, test.getValue()); } @Test public void test_from_TemporalAccessor_leapYear() { LocalDate date = LocalDate.of(2008, 1, 1); for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.from(date); assertEquals(i, test.getValue()); date = date.plusDays(1); } } @Test public void test_from_TemporalAccessor_DayOfYear() { DayOfYear dom = DayOfYear.of(6); assertEquals(dom, DayOfYear.from(dom)); } @Test public void test_from_TemporalAccessor_nonIso() { LocalDate date = LocalDate.now(); assertEquals(date.getDayOfYear(), DayOfYear.from(JapaneseDate.from(date)).getValue()); } @Test(expected = DateTimeException.class) public void test_from_TemporalAccessor_noDerive() { DayOfYear.from(LocalTime.NOON); } @Test(expected = NullPointerException.class) public void test_from_TemporalAccessor_null() { DayOfYear.from((TemporalAccessor) null); } @Test public void test_from_parse_CharSequence() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("D"); assertEquals(DayOfYear.of(76), formatter.parse("76", DayOfYear::from)); } //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- @Test public void test_isSupported() { assertEquals(false, TEST.isSupported((TemporalField) null)); assertEquals(false, TEST.isSupported(NANO_OF_SECOND)); assertEquals(false, TEST.isSupported(NANO_OF_DAY)); assertEquals(false, TEST.isSupported(MICRO_OF_SECOND)); assertEquals(false, TEST.isSupported(MICRO_OF_DAY)); assertEquals(false, TEST.isSupported(MILLI_OF_SECOND)); assertEquals(false, TEST.isSupported(MILLI_OF_DAY)); assertEquals(false, TEST.isSupported(SECOND_OF_MINUTE)); assertEquals(false, TEST.isSupported(SECOND_OF_DAY)); assertEquals(false, TEST.isSupported(MINUTE_OF_HOUR)); assertEquals(false, TEST.isSupported(MINUTE_OF_DAY)); assertEquals(false, TEST.isSupported(HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(AMPM_OF_DAY)); assertEquals(false, TEST.isSupported(DAY_OF_WEEK)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(false, TEST.isSupported(DAY_OF_MONTH)); assertEquals(true, TEST.isSupported(DAY_OF_YEAR)); assertEquals(false, TEST.isSupported(EPOCH_DAY)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_YEAR)); assertEquals(false, TEST.isSupported(MONTH_OF_YEAR)); assertEquals(false, TEST.isSupported(PROLEPTIC_MONTH)); assertEquals(false, TEST.isSupported(YEAR_OF_ERA)); assertEquals(false, TEST.isSupported(YEAR)); assertEquals(false, TEST.isSupported(ERA)); assertEquals(false, TEST.isSupported(INSTANT_SECONDS)); assertEquals(false, TEST.isSupported(OFFSET_SECONDS)); assertEquals(true, TEST.isSupported(TestingField.INSTANCE)); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- @Test public void test_range() { assertEquals(DAY_OF_YEAR.range(), TEST.range(DAY_OF_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_invalidField() { TEST.range(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_range_null() { TEST.range((TemporalField) null); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(12, TEST.get(DAY_OF_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_get_invalidField() { TEST.get(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_get_null() { TEST.get((TemporalField) null); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- @Test public void test_getLong() { assertEquals(12L, TEST.getLong(DAY_OF_YEAR)); } @Test public void test_getLong_derivedField() { assertEquals(12L, TEST.getLong(TestingField.INSTANCE)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField() { TEST.getLong(MONTH_OF_YEAR); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField2() { TEST.getLong(IsoFields.DAY_OF_QUARTER); } @Test(expected = NullPointerException.class) public void test_getLong_null() { TEST.getLong((TemporalField) null); } //----------------------------------------------------------------------- // isValidYear(int) //----------------------------------------------------------------------- @Test public void test_isValidYear_366() { DayOfYear test = DayOfYear.of(366); assertEquals(false, test.isValidYear(2011)); assertEquals(true, test.isValidYear(2012)); assertEquals(false, test.isValidYear(2013)); } @Test public void test_isValidYear_365() { DayOfYear test = DayOfYear.of(365); assertEquals(true, test.isValidYear(2011)); assertEquals(true, test.isValidYear(2012)); assertEquals(true, test.isValidYear(2013)); } //----------------------------------------------------------------------- // query(TemporalQuery) //----------------------------------------------------------------------- @Test public void test_query() { assertEquals(IsoChronology.INSTANCE, TEST.query(TemporalQueries.chronology())); assertEquals(null, TEST.query(TemporalQueries.localDate())); assertEquals(null, TEST.query(TemporalQueries.localTime())); assertEquals(null, TEST.query(TemporalQueries.offset())); assertEquals(null, TEST.query(TemporalQueries.precision())); assertEquals(null, TEST.query(TemporalQueries.zone())); assertEquals(null, TEST.query(TemporalQueries.zoneId())); } //----------------------------------------------------------------------- // adjustInto(Temporal) //----------------------------------------------------------------------- @Test public void test_adjustInto_fromStartOfYear_notLeapYear() { LocalDate base = LocalDate.of(2007, 1, 1); LocalDate expected = base; for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.adjustInto(base)); expected = expected.plusDays(1); } } @Test public void test_adjustInto_fromEndOfYear_notLeapYear() { LocalDate base = LocalDate.of(2007, 12, 31); LocalDate expected = LocalDate.of(2007, 1, 1); for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.adjustInto(base)); expected = expected.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_adjustInto_fromStartOfYear_notLeapYear_day366() { LocalDate base = LocalDate.of(2007, 1, 1); DayOfYear test = DayOfYear.of(LEAP_YEAR_LENGTH); test.adjustInto(base); } @Test(expected = DateTimeException.class) public void test_adjustInto_fromEndOfYear_notLeapYear_day366() { LocalDate base = LocalDate.of(2007, 12, 31); DayOfYear test = DayOfYear.of(LEAP_YEAR_LENGTH); test.adjustInto(base); } @Test public void test_adjustInto_fromStartOfYear_leapYear() { LocalDate base = LocalDate.of(2008, 1, 1); LocalDate expected = base; for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.adjustInto(base)); expected = expected.plusDays(1); } } @Test public void test_adjustInto_fromEndOfYear_leapYear() { LocalDate base = LocalDate.of(2008, 12, 31); LocalDate expected = LocalDate.of(2008, 1, 1); for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.adjustInto(base)); expected = expected.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_adjustInto_nonIso() { TEST.adjustInto(JapaneseDate.now()); } @Test(expected = NullPointerException.class) public void test_adjustInto_null() { TEST.adjustInto((Temporal) null); } //----------------------------------------------------------------------- // atYear(Year) //----------------------------------------------------------------------- @Test public void test_atYear_Year_notLeapYear() { LocalDate expected = LocalDate.of(2007, 1, 1); for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.atYear(YEAR_STANDARD)); expected = expected.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_atYear_fromStartOfYear_notLeapYear_day366() { DayOfYear test = DayOfYear.of(LEAP_YEAR_LENGTH); test.atYear(YEAR_STANDARD); } @Test public void test_atYear_Year_leapYear() { LocalDate expected = LocalDate.of(2008, 1, 1); for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.atYear(YEAR_LEAP)); expected = expected.plusDays(1); } } @Test(expected = NullPointerException.class) public void test_atYear_Year_nullYear() { TEST.atYear((Year) null); } //----------------------------------------------------------------------- // atYear(int) //----------------------------------------------------------------------- @Test public void test_atYear_int_notLeapYear() { LocalDate expected = LocalDate.of(2007, 1, 1); for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.atYear(2007)); expected = expected.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_atYear_int_fromStartOfYear_notLeapYear_day366() { DayOfYear test = DayOfYear.of(LEAP_YEAR_LENGTH); test.atYear(2007); } @Test public void test_atYear_int_leapYear() { LocalDate expected = LocalDate.of(2008, 1, 1); for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear test = DayOfYear.of(i); assertEquals(expected, test.atYear(2008)); expected = expected.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_atYear_int_invalidDay() { TEST.atYear(Year.MIN_VALUE - 1); } //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test public void test_compareTo() { for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear a = DayOfYear.of(i); for (int j = 1; j <= LEAP_YEAR_LENGTH; j++) { DayOfYear b = DayOfYear.of(j); if (i < j) { assertEquals(true, a.compareTo(b) < 0); assertEquals(true, b.compareTo(a) > 0); } else if (i > j) { assertEquals(true, a.compareTo(b) > 0); assertEquals(true, b.compareTo(a) < 0); } else { assertEquals(0, a.compareTo(b)); assertEquals(0, b.compareTo(a)); } } } } @Test(expected = NullPointerException.class) public void test_compareTo_nullDayOfYear() { DayOfYear doy = null; DayOfYear test = DayOfYear.of(1); test.compareTo(doy); } //----------------------------------------------------------------------- // equals() / hashCode() //----------------------------------------------------------------------- @Test public void test_equals() { for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear a = DayOfYear.of(i); for (int j = 1; j <= LEAP_YEAR_LENGTH; j++) { DayOfYear b = DayOfYear.of(j); assertEquals(i == j, a.equals(b)); assertEquals(i == j, a.hashCode() == b.hashCode()); } } } @Test public void test_equals_nullDayOfYear() { DayOfYear doy = null; DayOfYear test = DayOfYear.of(1); assertEquals(false, test.equals(doy)); } @Test public void test_equals_incorrectType() { DayOfYear test = DayOfYear.of(1); Object obj = "Incorrect type"; assertEquals(false, test.equals(obj)); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { DayOfYear a = DayOfYear.of(i); assertEquals("DayOfYear:" + i, a.toString()); } } //----------------------------------------------------------------------- // now(Clock) //----------------------------------------------------------------------- @Test public void test_now_clock_notLeapYear() { LocalDate date = LocalDate.of(2007, 1, 1); for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { Instant instant = date.atStartOfDay(PARIS).toInstant(); Clock clock = Clock.fixed(instant, PARIS); DayOfYear test = DayOfYear.now(clock); assertEquals(i, test.getValue()); date = date.plusDays(1); } } @Test public void test_now_clock_leapYear() { LocalDate date = LocalDate.of(2008, 1, 1); for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { Instant instant = date.atStartOfDay(PARIS).toInstant(); Clock clock = Clock.fixed(instant, PARIS); DayOfYear test = DayOfYear.now(clock); assertEquals(i, test.getValue()); date = date.plusDays(1); } } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestDays.java000066400000000000000000000471151343451174100253110ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestDays { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Days.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Days test = Days.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Days.of(0), Days.ZERO); assertEquals(Days.of(0), Days.ZERO); assertEquals(0, Days.ZERO.getAmount()); } @Test public void test_ONE() { assertSame(Days.of(1), Days.ONE); assertEquals(Days.of(1), Days.ONE); assertEquals(1, Days.ONE.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(0, Days.of(0).getAmount()); assertEquals(1, Days.of(1).getAmount()); assertEquals(2, Days.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Days.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Days.of(-1).getAmount()); assertEquals(-2, Days.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Days.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @Test public void test_ofWeeks() { assertEquals(0, Days.ofWeeks(0).getAmount()); assertEquals(7, Days.ofWeeks(1).getAmount()); assertEquals(14, Days.ofWeeks(2).getAmount()); assertEquals((Integer.MAX_VALUE / 7) * 7, Days.ofWeeks(Integer.MAX_VALUE / 7).getAmount()); assertEquals(-7, Days.ofWeeks(-1).getAmount()); assertEquals(-14, Days.ofWeeks(-2).getAmount()); assertEquals((Integer.MIN_VALUE / 7) * 7, Days.ofWeeks(Integer.MIN_VALUE / 7).getAmount()); } @Test(expected = ArithmeticException.class) public void test_ofWeeks_overflow() { Days.ofWeeks((Integer.MAX_VALUE / 7) + 7); } //----------------------------------------------------------------------- @Test public void test_from_Period_P0D() { assertEquals(Days.of(0), Days.from(Period.ofDays(0))); } @Test public void test_from_Period_P2D() { assertEquals(Days.of(2), Days.from(Period.ofDays(2))); } @Test public void test_from_P2W() { assertEquals(Days.of(14), Days.from(new MockWeeksDays(2, 0))); } @Test public void test_from_P2W3D() { assertEquals(Days.of(17), Days.from(new MockWeeksDays(2, 3))); } @Test public void test_from_Duration() { assertEquals(Days.of(2), Days.from(Duration.ofDays(2))); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_remainder() { Days.from(Duration.ofHours(3)); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_noConversion() { Days.from(Period.ofMonths(2)); } @Test(expected = NullPointerException.class) public void test_from_null() { Days.from((TemporalAmount) null); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_valid() { return new Object[][] { {"P0D", 0}, {"P1D", 1}, {"P2D", 2}, {"P123456789D", 123456789}, {"P+0D", 0}, {"P+2D", 2}, {"P-0D", 0}, {"P-2D", -2}, {"P0W", 0}, {"P1W", 7}, {"P2W", 14}, {"P12345678W", 12345678 * 7}, {"P+0W", 0}, {"P+2W", 14}, {"P-0W", 0}, {"P-2W", -14}, {"P0W0D", 0}, {"P2W3D", 17}, {"P+2W3D", 17}, {"P2W+3D", 17}, {"P-2W3D", -11}, {"P2W-3D", 11}, {"P-2W-3D", -17}, }; } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid(String str, int expectedDays) { assertEquals(Days.of(expectedDays), Days.parse(str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialPlus(String str, int expectedDays) { assertEquals(Days.of(expectedDays), Days.parse("+" + str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialMinus(String str, int expectedDays) { assertEquals(Days.of(-expectedDays), Days.parse("-" + str)); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3Y"}, {"P3M"}, {"P3Q"}, {"P1D2W"}, {"3"}, {"-3"}, {"3D"}, {"-3D"}, {"P3"}, {"P-3"}, {"P"}, {"PD"}, {"PW"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Days.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Days.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(6, Days.of(6).get(ChronoUnit.DAYS)); } @Test(expected = DateTimeException.class) public void test_get_invalidType() { Days.of(6).get(IsoFields.QUARTER_YEARS); } //----------------------------------------------------------------------- @Test public void test_between() { assertEquals(Days.of(365 + 366), Days.between(LocalDate.of(2019, 1, 1), LocalDate.of(2021, 1, 1))); } @Test(expected = NullPointerException.class) public void test_between_date_null() { Days.between(LocalDate.now(), (Temporal) null); } @Test(expected = NullPointerException.class) public void test_between_null_date() { Days.between((Temporal) null, LocalDate.now()); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Days() { Days test5 = Days.of(5); assertEquals(Days.of(5), test5.plus(Days.of(0))); assertEquals(Days.of(7), test5.plus(Days.of(2))); assertEquals(Days.of(3), test5.plus(Days.of(-2))); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE - 1).plus(Days.of(1))); assertEquals(Days.of(Integer.MIN_VALUE), Days.of(Integer.MIN_VALUE + 1).plus(Days.of(-1))); } @Test public void test_plus_TemporalAmount_Period() { Days test5 = Days.of(5); assertEquals(Days.of(5), test5.plus(Period.ofDays(0))); assertEquals(Days.of(7), test5.plus(Period.ofDays(2))); assertEquals(Days.of(3), test5.plus(Period.ofDays(-2))); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE - 1).plus(Period.ofDays(1))); assertEquals(Days.of(Integer.MIN_VALUE), Days.of(Integer.MIN_VALUE + 1).plus(Period.ofDays(-1))); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_PeriodYears() { Days.of(1).plus(Period.ofYears(2)); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_Duration() { Days.of(1).plus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Days.of(Integer.MAX_VALUE - 1).plus(Days.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Days.of(Integer.MIN_VALUE + 1).plus(Days.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Days.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Days test5 = Days.of(5); assertEquals(Days.of(5), test5.plus(0)); assertEquals(Days.of(7), test5.plus(2)); assertEquals(Days.of(3), test5.plus(-2)); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Days.of(Integer.MIN_VALUE), Days.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Days.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Days.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Days() { Days test5 = Days.of(5); assertEquals(Days.of(5), test5.minus(Days.of(0))); assertEquals(Days.of(3), test5.minus(Days.of(2))); assertEquals(Days.of(7), test5.minus(Days.of(-2))); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE - 1).minus(Days.of(-1))); assertEquals(Days.of(Integer.MIN_VALUE), Days.of(Integer.MIN_VALUE + 1).minus(Days.of(1))); } @Test public void test_minus_TemporalAmount_Period() { Days test5 = Days.of(5); assertEquals(Days.of(5), test5.minus(Period.ofDays(0))); assertEquals(Days.of(3), test5.minus(Period.ofDays(2))); assertEquals(Days.of(7), test5.minus(Period.ofDays(-2))); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE - 1).minus(Period.ofDays(-1))); assertEquals(Days.of(Integer.MIN_VALUE), Days.of(Integer.MIN_VALUE + 1).minus(Period.ofDays(1))); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_PeriodYears() { Days.of(1).minus(Period.ofYears(2)); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_Duration() { Days.of(1).minus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Days.of(Integer.MAX_VALUE - 1).minus(Days.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Days.of(Integer.MIN_VALUE + 1).minus(Days.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Days.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Days test5 = Days.of(5); assertEquals(Days.of(5), test5.minus(0)); assertEquals(Days.of(3), test5.minus(2)); assertEquals(Days.of(7), test5.minus(-2)); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Days.of(Integer.MIN_VALUE), Days.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Days.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Days.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Days test5 = Days.of(5); assertEquals(Days.of(0), test5.multipliedBy(0)); assertEquals(Days.of(5), test5.multipliedBy(1)); assertEquals(Days.of(10), test5.multipliedBy(2)); assertEquals(Days.of(15), test5.multipliedBy(3)); assertEquals(Days.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Days test5 = Days.of(5); assertEquals(Days.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Days.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Days.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Days test12 = Days.of(12); assertEquals(Days.of(12), test12.dividedBy(1)); assertEquals(Days.of(6), test12.dividedBy(2)); assertEquals(Days.of(4), test12.dividedBy(3)); assertEquals(Days.of(3), test12.dividedBy(4)); assertEquals(Days.of(2), test12.dividedBy(5)); assertEquals(Days.of(2), test12.dividedBy(6)); assertEquals(Days.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Days test12 = Days.of(12); assertEquals(Days.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Days.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Days.of(0), Days.of(0).negated()); assertEquals(Days.of(-12), Days.of(12).negated()); assertEquals(Days.of(12), Days.of(-12).negated()); assertEquals(Days.of(-Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Days.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Days.of(0), Days.of(0).abs()); assertEquals(Days.of(12), Days.of(12).abs()); assertEquals(Days.of(12), Days.of(-12).abs()); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(Integer.MAX_VALUE).abs()); assertEquals(Days.of(Integer.MAX_VALUE), Days.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Days.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { assertEquals(LocalDate.of(2019, 1, 10), Days.of(0).addTo(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2019, 1, 15), Days.of(5).addTo(LocalDate.of(2019, 1, 10))); } @Test public void test_subtractFrom() { assertEquals(LocalDate.of(2019, 1, 10), Days.of(0).subtractFrom(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2019, 1, 5), Days.of(5).subtractFrom(LocalDate.of(2019, 1, 10))); } //----------------------------------------------------------------------- @Test public void test_toPeriod() { for (int i = -20; i < 20; i++) { assertEquals(Period.ofDays(i), Days.of(i).toPeriod()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Days test5 = Days.of(5); Days test6 = Days.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Days test5 = Days.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Days test5 = Days.of(5); Days test6 = Days.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Days test5 = Days.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Days test5 = Days.of(5); Object obj = ""; assertEquals(false, test5.equals(obj)); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Days test5 = Days.of(5); Days test6 = Days.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Days test5 = Days.of(5); assertEquals("P5D", test5.toString()); Days testM1 = Days.of(-1); assertEquals("P-1D", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestHours.java000066400000000000000000000403221343451174100255020ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Duration; import java.time.LocalTime; import java.time.format.DateTimeParseException; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestHours { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Hours.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Hours test = Hours.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Hours.of(0), Hours.ZERO); assertEquals(Hours.of(0), Hours.ZERO); assertEquals(0, Hours.ZERO.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(0, Hours.of(0).getAmount()); assertEquals(1, Hours.of(1).getAmount()); assertEquals(2, Hours.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Hours.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Hours.of(-1).getAmount()); assertEquals(-2, Hours.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Hours.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_valid() { return new Object[][] { {"PT0H", 0}, {"PT1H", 1}, {"PT2H", 2}, {"PT123456789H", 123456789}, {"PT+0H", 0}, {"PT+2H", 2}, {"PT-0H", 0}, {"PT-2H", -2}, {"P0D", 0 * 24}, {"P1D", 1 * 24}, {"P2D", 2 * 24}, {"P1234567D", 1234567 * 24}, {"P+0D", 0 * 24}, {"P+2D", 2 * 24}, {"P-0D", 0 * 24}, {"P-2D", -2 * 24}, {"P0DT0H", 0}, {"P1DT2H", 1 * 24+ 2}, }; } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid(String str, int expectedDays) { assertEquals(Hours.of(expectedDays), Hours.parse(str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialPlus(String str, int expectedDays) { assertEquals(Hours.of(expectedDays), Hours.parse("+" + str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialMinus(String str, int expectedDays) { assertEquals(Hours.of(-expectedDays), Hours.parse("-" + str)); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3W"}, {"P3Q"}, {"P1M2Y"}, {"3"}, {"-3"}, {"3H"}, {"-3H"}, {"P3H"}, {"P3"}, {"P-3"}, {"PH"}, {"T"}, {"T3H"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Hours.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Hours.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Hours() { Hours test5 = Hours.of(5); assertEquals(Hours.of(5), test5.plus(Hours.of(0))); assertEquals(Hours.of(7), test5.plus(Hours.of(2))); assertEquals(Hours.of(3), test5.plus(Hours.of(-2))); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE - 1).plus(Hours.of(1))); assertEquals(Hours.of(Integer.MIN_VALUE), Hours.of(Integer.MIN_VALUE + 1).plus(Hours.of(-1))); } @Test public void test_plus_TemporalAmount_Period() { Hours test5 = Hours.of(5); assertEquals(Hours.of(5), test5.plus(Duration.ofHours(0))); assertEquals(Hours.of(7), test5.plus(Duration.ofHours(2))); assertEquals(Hours.of(3), test5.plus(Duration.ofHours(-2))); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE - 1).plus(Duration.ofHours(1))); assertEquals(Hours.of(Integer.MIN_VALUE), Hours.of(Integer.MIN_VALUE + 1).plus(Duration.ofHours(-1))); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Hours.of(Integer.MAX_VALUE - 1).plus(Hours.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Hours.of(Integer.MIN_VALUE + 1).plus(Hours.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Hours.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Hours test5 = Hours.of(5); assertEquals(Hours.of(5), test5.plus(0)); assertEquals(Hours.of(7), test5.plus(2)); assertEquals(Hours.of(3), test5.plus(-2)); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Hours.of(Integer.MIN_VALUE), Hours.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Hours.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Hours.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Hours() { Hours test5 = Hours.of(5); assertEquals(Hours.of(5), test5.minus(Hours.of(0))); assertEquals(Hours.of(3), test5.minus(Hours.of(2))); assertEquals(Hours.of(7), test5.minus(Hours.of(-2))); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE - 1).minus(Hours.of(-1))); assertEquals(Hours.of(Integer.MIN_VALUE), Hours.of(Integer.MIN_VALUE + 1).minus(Hours.of(1))); } @Test public void test_minus_TemporalAmount_Duration() { Hours test5 = Hours.of(5); assertEquals(Hours.of(5), test5.minus(Duration.ofHours(0))); assertEquals(Hours.of(3), test5.minus(Duration.ofHours(2))); assertEquals(Hours.of(7), test5.minus(Duration.ofHours(-2))); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE - 1).minus(Duration.ofHours(-1))); assertEquals(Hours.of(Integer.MIN_VALUE), Hours.of(Integer.MIN_VALUE + 1).minus(Duration.ofHours(1))); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Hours.of(Integer.MAX_VALUE - 1).minus(Hours.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Hours.of(Integer.MIN_VALUE + 1).minus(Hours.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Hours.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Hours test5 = Hours.of(5); assertEquals(Hours.of(5), test5.minus(0)); assertEquals(Hours.of(3), test5.minus(2)); assertEquals(Hours.of(7), test5.minus(-2)); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Hours.of(Integer.MIN_VALUE), Hours.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Hours.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Hours.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Hours test5 = Hours.of(5); assertEquals(Hours.of(0), test5.multipliedBy(0)); assertEquals(Hours.of(5), test5.multipliedBy(1)); assertEquals(Hours.of(10), test5.multipliedBy(2)); assertEquals(Hours.of(15), test5.multipliedBy(3)); assertEquals(Hours.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Hours test5 = Hours.of(5); assertEquals(Hours.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Hours.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Hours.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Hours test12 = Hours.of(12); assertEquals(Hours.of(12), test12.dividedBy(1)); assertEquals(Hours.of(6), test12.dividedBy(2)); assertEquals(Hours.of(4), test12.dividedBy(3)); assertEquals(Hours.of(3), test12.dividedBy(4)); assertEquals(Hours.of(2), test12.dividedBy(5)); assertEquals(Hours.of(2), test12.dividedBy(6)); assertEquals(Hours.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Hours test12 = Hours.of(12); assertEquals(Hours.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Hours.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Hours.of(0), Hours.of(0).negated()); assertEquals(Hours.of(-12), Hours.of(12).negated()); assertEquals(Hours.of(12), Hours.of(-12).negated()); assertEquals(Hours.of(-Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Hours.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Hours.of(0), Hours.of(0).abs()); assertEquals(Hours.of(12), Hours.of(12).abs()); assertEquals(Hours.of(12), Hours.of(-12).abs()); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(Integer.MAX_VALUE).abs()); assertEquals(Hours.of(Integer.MAX_VALUE), Hours.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Hours.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { LocalTime base = LocalTime.of(11, 30); assertEquals(LocalTime.of(11, 30), Hours.of(0).addTo(base)); assertEquals(LocalTime.of(17, 30), Hours.of(6).addTo(base)); } //----------------------------------------------------------------------- @Test public void test_subtractFrom() { LocalTime base = LocalTime.of(11, 30); assertEquals(LocalTime.of(11, 30), Hours.of(0).subtractFrom(base)); assertEquals(LocalTime.of(5, 30), Hours.of(6).subtractFrom(base)); } //----------------------------------------------------------------------- @SuppressWarnings("deprecation") public void test_toDuration() { for (int i = -20; i < 20; i++) { assertEquals(Duration.ofHours(i), Hours.of(i).toPeriod()); assertEquals(Duration.ofHours(i), Hours.of(i).toDuration()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Hours test5 = Hours.of(5); Hours test6 = Hours.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Hours test5 = Hours.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Hours test5 = Hours.of(5); Hours test6 = Hours.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Hours test5 = Hours.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Hours test5 = Hours.of(5); assertEquals(false, test5.equals("")); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Hours test5 = Hours.of(5); Hours test6 = Hours.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Hours test5 = Hours.of(5); assertEquals("PT5H", test5.toString()); Hours testM1 = Hours.of(-1); assertEquals("PT-1H", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestInterval.java000066400000000000000000000752101343451174100261720ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.HOURS; import static java.time.temporal.ChronoUnit.MONTHS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestInterval { static Instant NOW1 = ZonedDateTime.of(2014, 12, 1, 1, 0, 0, 0, ZoneOffset.UTC).toInstant(); static Instant NOW2 = NOW1.plusSeconds(60); static Instant NOW3 = NOW2.plusSeconds(60); static Instant NOW4 = NOW3.plusSeconds(60); //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Interval.class)); } //----------------------------------------------------------------------- @Test public void test_serialization() throws Exception { Interval test = Interval.of(Instant.EPOCH, NOW1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ALL() { Interval test = Interval.ALL; assertEquals(Instant.MIN, test.getStart()); assertEquals(Instant.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); } //----------------------------------------------------------------------- @Test public void test_of_Instant_Instant() { Interval test = Interval.of(NOW1, NOW2); assertEquals(NOW1, test.getStart()); assertEquals(NOW2, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); } @Test public void test_of_Instant_Instant_empty() { Interval test = Interval.of(NOW1, NOW1); assertEquals(NOW1, test.getStart()); assertEquals(NOW1, test.getEnd()); assertEquals(true, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); } @Test(expected = DateTimeException.class) public void test_of_Instant_Instant_badOrder() { Interval.of(NOW2, NOW1); } @Test(expected = NullPointerException.class) public void test_of_Instant_Instant_nullStart() { Interval.of(null, NOW2); } @Test(expected = NullPointerException.class) public void test_of_Instant_Instant_nullEnd() { Interval.of(NOW1, (Instant) null); } //----------------------------------------------------------------------- @Test public void test_of_Instant_Duration() { Interval test = Interval.of(NOW1, Duration.ofSeconds(60)); assertEquals(NOW1, test.getStart()); assertEquals(NOW2, test.getEnd()); } @Test public void test_of_Instant_Duration_zero() { Interval test = Interval.of(NOW1, Duration.ZERO); assertEquals(NOW1, test.getStart()); assertEquals(NOW1, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_of_Instant_Duration_negative() { Interval.of(NOW2, Duration.ofSeconds(-1)); } @Test(expected = NullPointerException.class) public void test_of_Instant_Duration_nullInstant() { Interval.of(null, Duration.ZERO); } @Test(expected = NullPointerException.class) public void test_of_Instant_Duration_nullDuration() { Interval.of(NOW1, (Duration) null); } /* Lower and upper bound for Intervals */ private static final Instant MIN_OFFSET_DATE_TIME = OffsetDateTime.MIN.plusDays(1L).toInstant(); private static final Instant MAX_OFFSET_DATE_TIME = OffsetDateTime.MAX.minusDays(1L).toInstant(); //----------------------------------------------------------------------- @DataProvider public static Object[][] data_parseValid() { Instant minPlusOneDay = Instant.MIN.plus(Duration.ofDays(1)); Instant maxMinusOneDay = Instant.MAX.minus(Duration.ofDays(1)); return new Object[][] { {NOW1 + "/" + NOW2, NOW1, NOW2}, {Duration.ofHours(6) + "/" + NOW2, NOW2.minus(6, HOURS), NOW2}, {"P6MT5H/" + NOW2, NOW2.atZone(ZoneOffset.UTC).minus(6, MONTHS).minus(5, HOURS).toInstant(), NOW2}, {"pt6h/" + NOW2, NOW2.minus(6, HOURS), NOW2}, {"pt6h/" + Instant.MAX, Instant.MAX.minus(6, HOURS), Instant.MAX}, {"pt6h/" + minPlusOneDay, minPlusOneDay.minus(6, HOURS), minPlusOneDay}, {NOW1 + "/" + Duration.ofHours(6), NOW1, NOW1.plus(6, HOURS)}, {NOW1 + "/pt6h", NOW1, NOW1.plus(6, HOURS)}, {Instant.MIN + "/pt6h", Instant.MIN, Instant.MIN.plus(6, HOURS)}, {maxMinusOneDay + "/Pt6h", maxMinusOneDay, maxMinusOneDay.plus(6, HOURS)}, {NOW1 + "/" + NOW1, NOW1, NOW1}, {NOW1.atOffset(ZoneOffset.ofHours(2)) + "/" + NOW2.atOffset(ZoneOffset.ofHours(2)), NOW1, NOW2}, {NOW1.atOffset(ZoneOffset.ofHours(2)) + "/" + NOW2.atOffset(ZoneOffset.ofHours(3)), NOW1, NOW2}, {NOW1.atOffset(ZoneOffset.ofHours(2)) + "/" + NOW2.atOffset(ZoneOffset.ofHours(2)).toLocalDateTime(), NOW1, NOW2}, {MIN_OFFSET_DATE_TIME.toString() + "/" + MAX_OFFSET_DATE_TIME, MIN_OFFSET_DATE_TIME, MAX_OFFSET_DATE_TIME}, {NOW1 + "/" + Instant.MAX, NOW1, Instant.MAX}, {Instant.MIN.toString() + "/" + NOW2, Instant.MIN, NOW2}, {Instant.MIN.toString() + "/" + Instant.MAX, Instant.MIN, Instant.MAX} }; } @Test @UseDataProvider("data_parseValid") public void test_parse_CharSequence(String input, Instant start, Instant end) { Interval test = Interval.parse(input); assertEquals(start, test.getStart()); assertEquals(end, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_parse_CharSequence_badOrder() { Interval.parse(NOW2 + "/" + NOW1); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequence_badFormat() { Interval.parse(NOW2 + "-" + NOW1); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Interval.parse(null); } //----------------------------------------------------------------------- @Test public void test_withStart() { Interval base = Interval.of(NOW1, NOW3); Interval test = base.withStart(NOW2); assertEquals(NOW2, test.getStart()); assertEquals(NOW3, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_withStart_badOrder() { Interval base = Interval.of(NOW1, NOW2); base.withStart(NOW3); } @Test(expected = NullPointerException.class) public void test_withStart_null() { Interval base = Interval.of(NOW1, NOW2); base.withStart(null); } //----------------------------------------------------------------------- @Test public void test_withEnd() { Interval base = Interval.of(NOW1, NOW3); Interval test = base.withEnd(NOW2); assertEquals(NOW1, test.getStart()); assertEquals(NOW2, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_withEnd_badOrder() { Interval base = Interval.of(NOW2, NOW3); base.withEnd(NOW1); } @Test(expected = NullPointerException.class) public void test_withEnd_null() { Interval base = Interval.of(NOW1, NOW2); base.withEnd(null); } //----------------------------------------------------------------------- @Test public void test_contains_Instant() { Interval test = Interval.of(NOW1, NOW2); assertEquals(false, test.contains(NOW1.minusSeconds(1))); assertEquals(true, test.contains(NOW1)); assertEquals(true, test.contains(NOW1.plusSeconds(1))); assertEquals(true, test.contains(NOW2.minusSeconds(1))); assertEquals(false, test.contains(NOW2)); } @Test public void test_contains_Instant_baseEmpty() { Interval test = Interval.of(NOW1, NOW1); assertEquals(false, test.contains(NOW1.minusSeconds(1))); assertEquals(false, test.contains(NOW1)); assertEquals(false, test.contains(NOW1.plusSeconds(1))); } @Test public void test_contains_max() { Interval test = Interval.of(NOW2, Instant.MAX); assertEquals(false, test.contains(Instant.MIN)); assertEquals(false, test.contains(NOW1)); assertEquals(true, test.contains(NOW2)); assertEquals(true, test.contains(NOW3)); assertEquals(true, test.contains(Instant.MAX)); } @Test(expected = NullPointerException.class) public void test_contains_Instant_null() { Interval base = Interval.of(NOW1, NOW2); base.contains((Instant) null); } //----------------------------------------------------------------------- @Test public void test_encloses_Interval() { Interval test = Interval.of(NOW1, NOW2); // completely before assertEquals(false, test.encloses(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(false, test.encloses(Interval.of(NOW1.minusSeconds(1), NOW1))); // partly before assertEquals(false, test.encloses(Interval.of(NOW1.minusSeconds(1), NOW2))); assertEquals(false, test.encloses(Interval.of(NOW1.minusSeconds(1), NOW2.minusSeconds(1)))); // contained assertEquals(true, test.encloses(Interval.of(NOW1, NOW2.minusSeconds(1)))); assertEquals(true, test.encloses(Interval.of(NOW1, NOW2))); assertEquals(true, test.encloses(Interval.of(NOW1.plusSeconds(1), NOW2))); // partly after assertEquals(false, test.encloses(Interval.of(NOW1, NOW2.plusSeconds(1)))); assertEquals(false, test.encloses(Interval.of(NOW1.plusSeconds(1), NOW2.plusSeconds(1)))); // completely after assertEquals(false, test.encloses(Interval.of(NOW2, NOW2.plusSeconds(1)))); assertEquals(false, test.encloses(Interval.of(NOW2.plusSeconds(1), NOW2.plusSeconds(2)))); } @Test public void test_encloses_Interval_empty() { Interval test = Interval.of(NOW1, NOW1); // completely before assertEquals(false, test.encloses(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); // partly before assertEquals(false, test.encloses(Interval.of(NOW1.minusSeconds(1), NOW1))); // equal assertEquals(true, test.encloses(Interval.of(NOW1, NOW1))); // completely after assertEquals(false, test.encloses(Interval.of(NOW1, NOW1.plusSeconds(1)))); assertEquals(false, test.encloses(Interval.of(NOW1.plusSeconds(1), NOW1.plusSeconds(2)))); } @Test(expected = NullPointerException.class) public void test_encloses_Interval_null() { Interval base = Interval.of(NOW1, NOW2); base.encloses((Interval) null); } //----------------------------------------------------------------------- @Test public void test_abuts_Interval() { Interval test = Interval.of(NOW1, NOW2); // completely before assertEquals(false, test.abuts(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(true, test.abuts(Interval.of(NOW1.minusSeconds(1), NOW1))); // partly before assertEquals(false, test.abuts(Interval.of(NOW1.minusSeconds(1), NOW2))); assertEquals(false, test.abuts(Interval.of(NOW1.minusSeconds(1), NOW2.minusSeconds(1)))); // contained assertEquals(false, test.abuts(Interval.of(NOW1, NOW2.minusSeconds(1)))); assertEquals(false, test.abuts(Interval.of(NOW1, NOW2))); assertEquals(false, test.abuts(Interval.of(NOW1.plusSeconds(1), NOW2))); // partly after assertEquals(false, test.abuts(Interval.of(NOW1, NOW2.plusSeconds(1)))); assertEquals(false, test.abuts(Interval.of(NOW1.plusSeconds(1), NOW2.plusSeconds(1)))); // completely after assertEquals(true, test.abuts(Interval.of(NOW2, NOW2.plusSeconds(1)))); assertEquals(false, test.abuts(Interval.of(NOW2.plusSeconds(1), NOW2.plusSeconds(2)))); } @Test public void test_abuts_Interval_empty() { Interval test = Interval.of(NOW1, NOW1); // completely before assertEquals(false, test.abuts(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(true, test.abuts(Interval.of(NOW1.minusSeconds(1), NOW1))); // equal assertEquals(false, test.abuts(Interval.of(NOW1, NOW1))); // completely after assertEquals(true, test.abuts(Interval.of(NOW1, NOW1.plusSeconds(1)))); assertEquals(false, test.abuts(Interval.of(NOW1.plusSeconds(1), NOW1.plusSeconds(2)))); } @Test(expected = NullPointerException.class) public void test_abuts_Interval_null() { Interval base = Interval.of(NOW1, NOW2); base.abuts((Interval) null); } //----------------------------------------------------------------------- @Test public void test_isConnected_Interval() { Interval test = Interval.of(NOW1, NOW2); // completely before assertEquals(false, test.isConnected(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(true, test.isConnected(Interval.of(NOW1.minusSeconds(1), NOW1))); // partly before assertEquals(true, test.isConnected(Interval.of(NOW1.minusSeconds(1), NOW2))); assertEquals(true, test.isConnected(Interval.of(NOW1.minusSeconds(1), NOW2.minusSeconds(1)))); // contained assertEquals(true, test.isConnected(Interval.of(NOW1, NOW2.minusSeconds(1)))); assertEquals(true, test.isConnected(Interval.of(NOW1, NOW2))); assertEquals(true, test.isConnected(Interval.of(NOW1.plusSeconds(1), NOW2))); // partly after assertEquals(true, test.isConnected(Interval.of(NOW1, NOW2.plusSeconds(1)))); assertEquals(true, test.isConnected(Interval.of(NOW1.plusSeconds(1), NOW2.plusSeconds(1)))); // completely after assertEquals(true, test.isConnected(Interval.of(NOW2, NOW2.plusSeconds(1)))); assertEquals(false, test.isConnected(Interval.of(NOW2.plusSeconds(1), NOW2.plusSeconds(2)))); } @Test public void test_isConnected_Interval_empty() { Interval test = Interval.of(NOW1, NOW1); // completely before assertEquals(false, test.isConnected(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(true, test.isConnected(Interval.of(NOW1.minusSeconds(1), NOW1))); // equal assertEquals(true, test.isConnected(Interval.of(NOW1, NOW1))); // completely after assertEquals(true, test.isConnected(Interval.of(NOW1, NOW1.plusSeconds(1)))); assertEquals(false, test.isConnected(Interval.of(NOW1.plusSeconds(1), NOW1.plusSeconds(2)))); } @Test(expected = NullPointerException.class) public void test_isConnected_Interval_null() { Interval base = Interval.of(NOW1, NOW2); base.isConnected((Interval) null); } //----------------------------------------------------------------------- @Test public void test_overlaps_Interval() { Interval test = Interval.of(NOW1, NOW2); // completely before assertEquals(false, test.overlaps(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(false, test.overlaps(Interval.of(NOW1.minusSeconds(1), NOW1))); // partly before assertEquals(true, test.overlaps(Interval.of(NOW1.minusSeconds(1), NOW2))); assertEquals(true, test.overlaps(Interval.of(NOW1.minusSeconds(1), NOW2.minusSeconds(1)))); // contained assertEquals(true, test.overlaps(Interval.of(NOW1, NOW2.minusSeconds(1)))); assertEquals(true, test.overlaps(Interval.of(NOW1, NOW2))); assertEquals(true, test.overlaps(Interval.of(NOW1.plusSeconds(1), NOW2))); // partly after assertEquals(true, test.overlaps(Interval.of(NOW1, NOW2.plusSeconds(1)))); assertEquals(true, test.overlaps(Interval.of(NOW1.plusSeconds(1), NOW2.plusSeconds(1)))); // completely after assertEquals(false, test.overlaps(Interval.of(NOW2, NOW2.plusSeconds(1)))); assertEquals(false, test.overlaps(Interval.of(NOW2.plusSeconds(1), NOW2.plusSeconds(2)))); } @Test public void test_overlaps_Interval_empty() { Interval test = Interval.of(NOW1, NOW1); // completely before assertEquals(false, test.overlaps(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(false, test.overlaps(Interval.of(NOW1.minusSeconds(1), NOW1))); // equal assertEquals(true, test.overlaps(Interval.of(NOW1, NOW1))); // completely after assertEquals(false, test.overlaps(Interval.of(NOW1, NOW1.plusSeconds(1)))); assertEquals(false, test.overlaps(Interval.of(NOW1.plusSeconds(1), NOW1.plusSeconds(2)))); } @Test(expected = NullPointerException.class) public void test_overlaps_Interval_null() { Interval base = Interval.of(NOW1, NOW2); base.overlaps((Interval) null); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_intersection() { return new Object[][] { // adjacent { NOW1, NOW2, NOW2, NOW4, NOW2, NOW2 }, // adjacent empty { NOW1, NOW4, NOW4, NOW4, NOW4, NOW4 }, // overlap { NOW1, NOW3, NOW2, NOW4, NOW2, NOW3 }, // encloses { NOW1, NOW4, NOW2, NOW3, NOW2, NOW3 }, // encloses empty { NOW1, NOW4, NOW2, NOW2, NOW2, NOW2 }, }; } @Test @UseDataProvider("data_intersection") public void test_intersection( Instant start1, Instant end1, Instant start2, Instant end2, Instant expStart, Instant expEnd) { Interval test1 = Interval.of(start1, end1); Interval test2 = Interval.of(start2, end2); Interval expected = Interval.of(expStart, expEnd); assertTrue(test1.isConnected(test2)); assertEquals(expected, test1.intersection(test2)); } @Test @UseDataProvider("data_intersection") public void test_intersection_reverse( Instant start1, Instant end1, Instant start2, Instant end2, Instant expStart, Instant expEnd) { Interval test1 = Interval.of(start1, end1); Interval test2 = Interval.of(start2, end2); Interval expected = Interval.of(expStart, expEnd); assertTrue(test2.isConnected(test1)); assertEquals(expected, test2.intersection(test1)); } @Test(expected = DateTimeException.class) public void test_intersectionBad() { Interval test1 = Interval.of(NOW1, NOW2); Interval test2 = Interval.of(NOW3, NOW4); assertEquals(false, test1.isConnected(test2)); test1.intersection(test2); } @Test public void test_intersection_same() { Interval test = Interval.of(NOW2, NOW4); assertEquals(test, test.intersection(test)); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_union() { return new Object[][] { // adjacent { NOW1, NOW2, NOW2, NOW4, NOW1, NOW4 }, // adjacent empty { NOW1, NOW4, NOW4, NOW4, NOW1, NOW4 }, // overlap { NOW1, NOW3, NOW2, NOW4, NOW1, NOW4 }, // encloses { NOW1, NOW4, NOW2, NOW3, NOW1, NOW4 }, // encloses empty { NOW1, NOW4, NOW2, NOW2, NOW1, NOW4 }, }; } @Test @UseDataProvider("data_union") public void test_unionAndSpan( Instant start1, Instant end1, Instant start2, Instant end2, Instant expStart, Instant expEnd) { Interval test1 = Interval.of(start1, end1); Interval test2 = Interval.of(start2, end2); Interval expected = Interval.of(expStart, expEnd); assertTrue(test1.isConnected(test2)); assertEquals(expected, test1.union(test2)); assertEquals(expected, test1.span(test2)); } @Test @UseDataProvider("data_union") public void test_unionAndSpan_reverse( Instant start1, Instant end1, Instant start2, Instant end2, Instant expStart, Instant expEnd) { Interval test1 = Interval.of(start1, end1); Interval test2 = Interval.of(start2, end2); Interval expected = Interval.of(expStart, expEnd); assertTrue(test2.isConnected(test1)); assertEquals(expected, test2.union(test1)); assertEquals(expected, test2.span(test1)); } @Test @UseDataProvider("data_union") public void test_span_enclosesInputs( Instant start1, Instant end1, Instant start2, Instant end2, Instant expStart, Instant expEnd) { Interval test1 = Interval.of(start1, end1); Interval test2 = Interval.of(start2, end2); Interval expected = Interval.of(expStart, expEnd); assertEquals(true, expected.encloses(test1)); assertEquals(true, expected.encloses(test2)); } @Test(expected = DateTimeException.class) public void test_union_disconnected() { Interval test1 = Interval.of(NOW1, NOW2); Interval test2 = Interval.of(NOW3, NOW4); assertFalse(test1.isConnected(test2)); test1.union(test2); } @Test public void test_span_disconnected() { Interval test1 = Interval.of(NOW1, NOW2); Interval test2 = Interval.of(NOW3, NOW4); assertFalse(test1.isConnected(test2)); assertEquals(Interval.of(NOW1, NOW4), test1.span(test2)); } @Test public void test_unionAndSpan_same() { Interval test = Interval.of(NOW2, NOW4); assertEquals(test, test.union(test)); assertEquals(test, test.span(test)); } //----------------------------------------------------------------------- @Test public void test_isAfter_Instant() { Interval test = Interval.of(NOW1, NOW2); assertEquals(true, test.isAfter(NOW1.minusSeconds(2))); assertEquals(true, test.isAfter(NOW1.minusSeconds(1))); assertEquals(false, test.isAfter(NOW1)); assertEquals(false, test.isAfter(NOW2)); assertEquals(false, test.isAfter(NOW2.plusSeconds(1))); } @Test public void test_isAfter_Instant_empty() { Interval test = Interval.of(NOW1, NOW1); assertEquals(true, test.isAfter(NOW1.minusSeconds(2))); assertEquals(true, test.isAfter(NOW1.minusSeconds(1))); assertEquals(false, test.isAfter(NOW1)); assertEquals(false, test.isAfter(NOW1.plusSeconds(1))); } //----------------------------------------------------------------------- @Test public void test_isBefore_Instant() { Interval test = Interval.of(NOW1, NOW2); assertEquals(false, test.isBefore(NOW1.minusSeconds(1))); assertEquals(false, test.isBefore(NOW1)); assertEquals(true, test.isBefore(NOW2)); assertEquals(true, test.isBefore(NOW2.plusSeconds(1))); } @Test public void test_isBefore_Instant_empty() { Interval test = Interval.of(NOW1, NOW1); assertEquals(false, test.isBefore(NOW1.minusSeconds(1))); assertEquals(false, test.isBefore(NOW1)); assertEquals(true, test.isBefore(NOW1.plusSeconds(1))); } //----------------------------------------------------------------------- @Test public void test_isAfter_Interval() { Interval test = Interval.of(NOW1, NOW2); // completely before assertEquals(true, test.isAfter(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(true, test.isAfter(Interval.of(NOW1.minusSeconds(1), NOW1))); // partly before assertEquals(false, test.isAfter(Interval.of(NOW1.minusSeconds(1), NOW2))); assertEquals(false, test.isAfter(Interval.of(NOW1.minusSeconds(1), NOW2.minusSeconds(1)))); // contained assertEquals(false, test.isAfter(Interval.of(NOW1, NOW2.minusSeconds(1)))); assertEquals(false, test.isAfter(Interval.of(NOW1, NOW2))); assertEquals(false, test.isAfter(Interval.of(NOW1.plusSeconds(1), NOW2))); // partly after assertEquals(false, test.isAfter(Interval.of(NOW1, NOW2.plusSeconds(1)))); assertEquals(false, test.isAfter(Interval.of(NOW1.plusSeconds(1), NOW2.plusSeconds(1)))); // completely after assertEquals(false, test.isAfter(Interval.of(NOW2, NOW2.plusSeconds(1)))); assertEquals(false, test.isAfter(Interval.of(NOW2.plusSeconds(1), NOW2.plusSeconds(2)))); } @Test public void test_isAfter_Interval_empty() { Interval test = Interval.of(NOW1, NOW1); // completely before assertEquals(true, test.isAfter(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(true, test.isAfter(Interval.of(NOW1.minusSeconds(1), NOW1))); // equal assertEquals(false, test.isAfter(Interval.of(NOW1, NOW1))); // completely after assertEquals(false, test.isAfter(Interval.of(NOW1, NOW1.plusSeconds(1)))); assertEquals(false, test.isAfter(Interval.of(NOW1.plusSeconds(1), NOW1.plusSeconds(2)))); } //----------------------------------------------------------------------- @Test public void test_isBefore_Interval() { Interval test = Interval.of(NOW1, NOW2); // completely before assertEquals(false, test.isBefore(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(false, test.isBefore(Interval.of(NOW1.minusSeconds(1), NOW1))); // partly before assertEquals(false, test.isBefore(Interval.of(NOW1.minusSeconds(1), NOW2))); assertEquals(false, test.isBefore(Interval.of(NOW1.minusSeconds(1), NOW2.minusSeconds(1)))); // contained assertEquals(false, test.isBefore(Interval.of(NOW1, NOW2.minusSeconds(1)))); assertEquals(false, test.isBefore(Interval.of(NOW1, NOW2))); assertEquals(false, test.isBefore(Interval.of(NOW1.plusSeconds(1), NOW2))); // partly after assertEquals(false, test.isBefore(Interval.of(NOW1, NOW2.plusSeconds(1)))); assertEquals(false, test.isBefore(Interval.of(NOW1.plusSeconds(1), NOW2.plusSeconds(1)))); // completely after assertEquals(true, test.isBefore(Interval.of(NOW2, NOW2.plusSeconds(1)))); assertEquals(true, test.isBefore(Interval.of(NOW2.plusSeconds(1), NOW2.plusSeconds(2)))); } @Test public void test_isBefore_Interval_empty() { Interval test = Interval.of(NOW1, NOW1); // completely before assertEquals(false, test.isBefore(Interval.of(NOW1.minusSeconds(2), NOW1.minusSeconds(1)))); assertEquals(false, test.isBefore(Interval.of(NOW1.minusSeconds(1), NOW1))); // equal assertEquals(false, test.isBefore(Interval.of(NOW1, NOW1))); // completely after assertEquals(true, test.isBefore(Interval.of(NOW1, NOW1.plusSeconds(1)))); assertEquals(true, test.isBefore(Interval.of(NOW1.plusSeconds(1), NOW1.plusSeconds(2)))); } //----------------------------------------------------------------------- @Test public void test_toDuration() { Interval test = Interval.of(NOW1, NOW2); assertEquals(Duration.between(NOW1, NOW2), test.toDuration()); } //----------------------------------------------------------------------- @Test public void test_equals() { Interval a = Interval.of(NOW1, NOW2); Interval a2 = Interval.of(NOW1, NOW2); Interval b = Interval.of(NOW1, NOW3); Interval c = Interval.of(NOW2, NOW2); assertEquals(true, a.equals(a)); assertEquals(true, a.equals(a2)); assertEquals(false, a.equals(b)); assertEquals(false, a.equals(c)); assertEquals(false, a.equals(null)); assertEquals(false, a.equals("")); assertEquals(true, a.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Interval test = Interval.of(NOW1, NOW2); assertEquals(NOW1 + "/" + NOW2, test.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestLocalDateRange.java000066400000000000000000001602271343451174100272160ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.junit.Test; import org.junit.runner.RunWith; import com.google.common.collect.Range; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test date range. */ @RunWith(DataProviderRunner.class) public class TestLocalDateRange { private static final LocalDate MINP1 = LocalDate.MIN.plusDays(1); private static final LocalDate MINP2 = LocalDate.MIN.plusDays(2); private static final LocalDate MINP3 = LocalDate.MIN.plusDays(3); private static final LocalDate MAXM1 = LocalDate.MAX.minusDays(1); private static final LocalDate MAXM2 = LocalDate.MAX.minusDays(2); private static final LocalDate DATE_2012_07_01 = LocalDate.of(2012, 7, 1); private static final LocalDate DATE_2012_07_27 = LocalDate.of(2012, 7, 27); private static final LocalDate DATE_2012_07_28 = LocalDate.of(2012, 7, 28); private static final LocalDate DATE_2012_07_29 = LocalDate.of(2012, 7, 29); private static final LocalDate DATE_2012_07_30 = LocalDate.of(2012, 7, 30); private static final LocalDate DATE_2012_07_31 = LocalDate.of(2012, 7, 31); private static final LocalDate DATE_2012_08_01 = LocalDate.of(2012, 8, 1); private static final LocalDate DATE_2012_08_31 = LocalDate.of(2012, 8, 31); //----------------------------------------------------------------------- @Test public void test_ALL() { LocalDateRange test = LocalDateRange.ALL; assertEquals(LocalDate.MIN, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals("-999999999-01-01/+999999999-12-31", test.toString()); } //----------------------------------------------------------------------- @Test public void test_of() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(3, test.lengthInDays()); assertEquals("2012-07-28/2012-07-31", test.toString()); } @Test public void test_of_MIN() { LocalDateRange test = LocalDateRange.of(LocalDate.MIN, DATE_2012_07_31); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/2012-07-31", test.toString()); } @Test public void test_of_MAX() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, LocalDate.MAX); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("2012-07-28/+999999999-12-31", test.toString()); } @Test public void test_of_MIN_MAX() { LocalDateRange test = LocalDateRange.of(LocalDate.MIN, LocalDate.MAX); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/+999999999-12-31", test.toString()); } @Test(expected = DateTimeException.class) public void test_of_MIN_MIN() { LocalDateRange.of(LocalDate.MIN, LocalDate.MIN); } @Test(expected = DateTimeException.class) public void test_of_MIN_MINP1() { LocalDateRange.of(LocalDate.MIN, MINP1); } @Test(expected = DateTimeException.class) public void test_of_MINP1_MINP1() { LocalDateRange.of(MINP1, MINP1); } @Test public void test_of_MIN_MINP2() { LocalDateRange test = LocalDateRange.of(LocalDate.MIN, MINP2); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(MINP1, test.getEndInclusive()); assertEquals(MINP2, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/-999999999-01-03", test.toString()); } @Test public void test_of_MINP1_MINP2() { LocalDateRange test = LocalDateRange.of(MINP1, MINP2); assertEquals(MINP1, test.getStart()); assertEquals(MINP1, test.getEndInclusive()); assertEquals(MINP2, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(1, test.lengthInDays()); assertEquals("-999999999-01-02/-999999999-01-03", test.toString()); } @Test public void test_of_MINP2_MINP2() { LocalDateRange test = LocalDateRange.of(MINP2, MINP2); assertEquals(MINP2, test.getStart()); assertEquals(MINP1, test.getEndInclusive()); assertEquals(MINP2, test.getEnd()); assertEquals(true, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(0, test.lengthInDays()); assertEquals("-999999999-01-03/-999999999-01-03", test.toString()); } @Test(expected = DateTimeException.class) public void test_of_MAX_MAX() { LocalDateRange.of(LocalDate.MAX, LocalDate.MAX); } @Test(expected = DateTimeException.class) public void test_of_MAXM1_MAX() { LocalDateRange.of(MAXM1, LocalDate.MAX); } @Test(expected = DateTimeException.class) public void test_of_MAXM1_MAXM1() { LocalDateRange.of(MAXM1, MAXM1); } @Test public void test_of_empty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_30); assertEquals(DATE_2012_07_30, test.getStart()); assertEquals(DATE_2012_07_29, test.getEndInclusive()); assertEquals(DATE_2012_07_30, test.getEnd()); assertEquals(true, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(0, test.lengthInDays()); assertEquals("2012-07-30/2012-07-30", test.toString()); } @Test(expected = DateTimeException.class) public void test_of_badOrder() { LocalDateRange.of(DATE_2012_07_31, DATE_2012_07_30); } //----------------------------------------------------------------------- @Test public void test_ofClosed() { LocalDateRange test = LocalDateRange.ofClosed(DATE_2012_07_28, DATE_2012_07_30); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(3, test.lengthInDays()); assertEquals("2012-07-28/2012-07-31", test.toString()); } @Test public void test_ofClosed_MIN() { LocalDateRange test = LocalDateRange.ofClosed(LocalDate.MIN, DATE_2012_07_30); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/2012-07-31", test.toString()); } @Test public void test_ofClosed_MAX() { LocalDateRange test = LocalDateRange.ofClosed(DATE_2012_07_28, LocalDate.MAX); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("2012-07-28/+999999999-12-31", test.toString()); } @Test public void test_ofClosed_MIN_MAX() { LocalDateRange test = LocalDateRange.ofClosed(LocalDate.MIN, LocalDate.MAX); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/+999999999-12-31", test.toString()); } @Test(expected = DateTimeException.class) public void test_ofClosed_MIN_MIN() { LocalDateRange.ofClosed(LocalDate.MIN, LocalDate.MIN); } @Test public void test_ofClosed_MIN_MINP1() { LocalDateRange test = LocalDateRange.ofClosed(LocalDate.MIN, MINP1); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(MINP1, test.getEndInclusive()); assertEquals(MINP2, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/-999999999-01-03", test.toString()); } @Test public void test_ofClosed_MINP1_MINP1() { LocalDateRange test = LocalDateRange.ofClosed(MINP1, MINP1); assertEquals(MINP1, test.getStart()); assertEquals(MINP1, test.getEndInclusive()); assertEquals(MINP2, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(1, test.lengthInDays()); assertEquals("-999999999-01-02/-999999999-01-03", test.toString()); } @Test public void test_ofClosed_MIN_MINP2() { LocalDateRange test = LocalDateRange.ofClosed(LocalDate.MIN, MINP2); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(MINP2, test.getEndInclusive()); assertEquals(MINP3, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(Integer.MAX_VALUE, test.lengthInDays()); assertEquals("-999999999-01-01/-999999999-01-04", test.toString()); } @Test public void test_ofClosed_MINP1_MINP2() { LocalDateRange test = LocalDateRange.ofClosed(MINP1, MINP2); assertEquals(MINP1, test.getStart()); assertEquals(MINP2, test.getEndInclusive()); assertEquals(MINP3, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(2, test.lengthInDays()); assertEquals("-999999999-01-02/-999999999-01-04", test.toString()); } @Test public void test_ofClosed_MINP2_MINP2() { LocalDateRange test = LocalDateRange.ofClosed(MINP2, MINP2); assertEquals(MINP2, test.getStart()); assertEquals(MINP2, test.getEndInclusive()); assertEquals(MINP3, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals(1, test.lengthInDays()); assertEquals("-999999999-01-03/-999999999-01-04", test.toString()); } @Test(expected = DateTimeException.class) public void test_ofClosed_MAX_MAX() { LocalDateRange.ofClosed(LocalDate.MAX, LocalDate.MAX); } @Test(expected = DateTimeException.class) public void test_ofClosed_MAXM1_MAX() { LocalDateRange.ofClosed(MAXM1, LocalDate.MAX); } @Test(expected = DateTimeException.class) public void test_ofClosed_MAXM1_MAXM1() { LocalDateRange.ofClosed(MAXM1, MAXM1); } @Test(expected = DateTimeException.class) public void test_ofClosed_badOrder() { LocalDateRange.ofClosed(DATE_2012_07_31, DATE_2012_07_30); } //----------------------------------------------------------------------- @Test public void test_ofEmpty() { LocalDateRange test = LocalDateRange.ofEmpty(DATE_2012_07_30); assertEquals(DATE_2012_07_30, test.getStart()); assertEquals(DATE_2012_07_29, test.getEndInclusive()); assertEquals(DATE_2012_07_30, test.getEnd()); assertEquals(true, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals("2012-07-30/2012-07-30", test.toString()); } @Test(expected = DateTimeException.class) public void test_ofEmpty_MIN() { LocalDateRange.ofEmpty(LocalDate.MIN); } @Test(expected = DateTimeException.class) public void test_ofEmpty_MINP1() { LocalDateRange.ofEmpty(MINP1); } @Test(expected = DateTimeException.class) public void test_ofEmpty_MAX() { LocalDateRange.ofEmpty(LocalDate.MAX); } @Test(expected = DateTimeException.class) public void test_ofEmpty_MAXM1() { LocalDateRange.ofEmpty(MAXM1); } //----------------------------------------------------------------------- @Test public void test_ofUnbounded() { LocalDateRange test = LocalDateRange.ofUnbounded(); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals("-999999999-01-01/+999999999-12-31", test.toString()); } //----------------------------------------------------------------------- @Test public void test_ofUnboundedStart() { LocalDateRange test = LocalDateRange.ofUnboundedStart(DATE_2012_07_30); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(DATE_2012_07_29, test.getEndInclusive()); assertEquals(DATE_2012_07_30, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(true, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals("-999999999-01-01/2012-07-30", test.toString()); } @Test(expected = DateTimeException.class) public void test_ofUnboundedStart_MIN() { LocalDateRange.ofUnboundedStart(LocalDate.MIN); } @Test(expected = DateTimeException.class) public void test_ofUnboundedStart_MINP1() { LocalDateRange.ofUnboundedStart(MINP1); } //----------------------------------------------------------------------- @Test public void test_ofUnboundedEnd() { LocalDateRange test = LocalDateRange.ofUnboundedEnd(DATE_2012_07_30); assertEquals(DATE_2012_07_30, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); assertEquals(false, test.isEmpty()); assertEquals(false, test.isUnboundedStart()); assertEquals(true, test.isUnboundedEnd()); assertEquals("2012-07-30/+999999999-12-31", test.toString()); } @Test(expected = DateTimeException.class) public void test_ofUnboundedEnd_MAX() { LocalDateRange.ofUnboundedEnd(LocalDate.MAX); } @Test(expected = DateTimeException.class) public void test_ofUnboundedEnd_MAXM1() { LocalDateRange.ofUnboundedEnd(MAXM1); } //----------------------------------------------------------------------- @Test public void test_of_period() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, Period.ofDays(3)); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); assertEquals(false, test.isUnboundedStart()); assertEquals(false, test.isUnboundedEnd()); assertEquals("2012-07-28/2012-07-31", test.toString()); } @Test(expected = DateTimeException.class) public void test_of_period_negative() { LocalDateRange.of(DATE_2012_07_31, Period.ofDays(-1)); } @Test(expected = DateTimeException.class) public void test_of_period_atMIN() { LocalDateRange.of(LocalDate.MIN, Period.ofDays(0)); } @Test(expected = DateTimeException.class) public void test_of_period_atMAX() { LocalDateRange.of(LocalDate.MAX, Period.ofDays(0)); } @Test(expected = DateTimeException.class) public void test_of_period_atMAXM1_0D() { LocalDateRange.of(MAXM1, Period.ofDays(0)); } @Test(expected = DateTimeException.class) public void test_of_period_atMAXM1_1D() { LocalDateRange.of(MAXM1, Period.ofDays(1)); } //----------------------------------------------------------------------- @Test public void test_parse_CharSequence() { LocalDateRange test = LocalDateRange.parse(DATE_2012_07_27 + "/" + DATE_2012_07_29); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_29, test.getEnd()); } @Test public void test_parse_CharSequence_PeriodLocalDate() { LocalDateRange test = LocalDateRange.parse("P2D/" + DATE_2012_07_29); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_29, test.getEnd()); } @Test public void test_parse_CharSequence_PeriodLocalDate_case() { LocalDateRange test = LocalDateRange.parse("p2d/" + DATE_2012_07_29); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_29, test.getEnd()); } @Test public void test_parse_CharSequence_LocalDatePeriod() { LocalDateRange test = LocalDateRange.parse(DATE_2012_07_27 + "/P2D"); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_29, test.getEnd()); } @Test public void test_parse_CharSequence_LocalDatePeriod_case() { LocalDateRange test = LocalDateRange.parse(DATE_2012_07_27 + "/p2d"); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_29, test.getEnd()); } @Test public void test_parse_CharSequence_empty() { LocalDateRange test = LocalDateRange.parse(DATE_2012_07_27 + "/" + DATE_2012_07_27); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_27, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_parse_CharSequence_badOrder() { LocalDateRange.parse(DATE_2012_07_29 + "/" + DATE_2012_07_27); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequence_badFormat() { LocalDateRange.parse(DATE_2012_07_29 + "-" + DATE_2012_07_27); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { LocalDateRange.parse(null); } //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(LocalDateRange.class)); } @Test public void test_serialization() throws Exception { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_withStart() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withStart(DATE_2012_07_27); assertEquals(DATE_2012_07_27, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); } @Test public void test_withStart_adjuster() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withStart(date -> date.minus(1, ChronoUnit.WEEKS)); assertEquals(DATE_2012_07_28.minusWeeks(1), test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); } @Test public void test_withStart_min() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withStart(LocalDate.MIN); assertEquals(LocalDate.MIN, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); } @Test public void test_withStart_empty() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withStart(DATE_2012_07_31); assertEquals(DATE_2012_07_31, test.getStart()); assertEquals(DATE_2012_07_30, test.getEndInclusive()); assertEquals(DATE_2012_07_31, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_withStart_invalid() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_30); base.withStart(DATE_2012_07_31); } //----------------------------------------------------------------------- @Test public void test_withEnd() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withEnd(DATE_2012_07_30); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(DATE_2012_07_29, test.getEndInclusive()); assertEquals(DATE_2012_07_30, test.getEnd()); } @Test public void test_withEnd_adjuster() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withEnd(date -> date.plus(1, ChronoUnit.WEEKS)); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(DATE_2012_07_30.plusWeeks(1), test.getEndInclusive()); assertEquals(DATE_2012_07_31.plusWeeks(1), test.getEnd()); } @Test public void test_withEnd_max() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange test = base.withEnd(LocalDate.MAX); assertEquals(DATE_2012_07_28, test.getStart()); assertEquals(LocalDate.MAX, test.getEndInclusive()); assertEquals(LocalDate.MAX, test.getEnd()); } @Test public void test_withEnd_empty() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_31); LocalDateRange test = base.withEnd(DATE_2012_07_30); assertEquals(DATE_2012_07_30, test.getStart()); assertEquals(DATE_2012_07_29, test.getEndInclusive()); assertEquals(DATE_2012_07_30, test.getEnd()); } @Test(expected = DateTimeException.class) public void test_withEnd_invalid() { LocalDateRange base = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); base.withEnd(DATE_2012_07_27); } //----------------------------------------------------------------------- @Test public void test_contains() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(false, test.contains(LocalDate.MIN)); assertEquals(false, test.contains(DATE_2012_07_27)); assertEquals(true, test.contains(DATE_2012_07_28)); assertEquals(true, test.contains(DATE_2012_07_29)); assertEquals(true, test.contains(DATE_2012_07_30)); assertEquals(false, test.contains(DATE_2012_07_31)); assertEquals(false, test.contains(DATE_2012_08_01)); assertEquals(false, test.contains(LocalDate.MAX)); } @Test public void test_contains_baseEmpty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28); assertEquals(false, test.contains(LocalDate.MIN)); assertEquals(false, test.contains(DATE_2012_07_27)); assertEquals(false, test.contains(DATE_2012_07_28)); assertEquals(false, test.contains(DATE_2012_07_29)); assertEquals(false, test.contains(LocalDate.MAX)); } @Test public void test_contains_max() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, LocalDate.MAX); assertEquals(false, test.contains(LocalDate.MIN)); assertEquals(false, test.contains(DATE_2012_07_27)); assertEquals(true, test.contains(DATE_2012_07_28)); assertEquals(true, test.contains(DATE_2012_07_29)); assertEquals(true, test.contains(DATE_2012_07_30)); assertEquals(true, test.contains(DATE_2012_07_31)); assertEquals(true, test.contains(DATE_2012_08_01)); assertEquals(true, test.contains(LocalDate.MAX)); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_queries() { return new Object[][] { // before start { DATE_2012_07_01, DATE_2012_07_27, false, false, false, false }, { DATE_2012_07_01, DATE_2012_07_28, false, true, true, false }, // before end { DATE_2012_07_27, DATE_2012_07_30, false, false, true, true }, { DATE_2012_07_28, DATE_2012_07_30, true, false, true, true }, { DATE_2012_07_29, DATE_2012_07_30, true, false, true, true }, // same end { DATE_2012_07_27, DATE_2012_07_31, false, false, true, true }, { DATE_2012_07_28, DATE_2012_07_31, true, false, true, true }, { DATE_2012_07_29, DATE_2012_07_31, true, false, true, true }, { DATE_2012_07_30, DATE_2012_07_31, true, false, true , true}, // past end { DATE_2012_07_27, DATE_2012_08_01, false, false, true, true }, { DATE_2012_07_28, DATE_2012_08_01, false, false, true, true }, { DATE_2012_07_29, DATE_2012_08_01, false, false, true, true }, { DATE_2012_07_30, DATE_2012_08_01, false, false, true, true }, // start past end { DATE_2012_07_31, DATE_2012_08_01, false, true, true, false }, { DATE_2012_07_31, DATE_2012_08_31, false, true, true, false }, { DATE_2012_08_01, DATE_2012_08_31, false, false, false, false }, // empty { DATE_2012_07_27, DATE_2012_07_27, false, false, false, false }, { DATE_2012_07_28, DATE_2012_07_28, true, true, true, false }, { DATE_2012_07_29, DATE_2012_07_29, true, false, true, true }, { DATE_2012_07_30, DATE_2012_07_30, true, false, true, true }, { DATE_2012_07_31, DATE_2012_07_31, true, true, true, false }, { DATE_2012_08_31, DATE_2012_08_31, false, false, false, false }, // min { LocalDate.MIN, DATE_2012_07_27, false, false, false, false }, { LocalDate.MIN, DATE_2012_07_28, false, true, true, false }, { LocalDate.MIN, DATE_2012_07_29, false, false, true, true }, { LocalDate.MIN, DATE_2012_07_30, false, false, true, true }, { LocalDate.MIN, DATE_2012_07_31, false, false, true, true }, { LocalDate.MIN, DATE_2012_08_01, false, false, true, true }, { LocalDate.MIN, LocalDate.MAX, false, false, true, true }, // max { DATE_2012_07_27, LocalDate.MAX, false, false, true, true }, { DATE_2012_07_28, LocalDate.MAX, false, false, true, true }, { DATE_2012_07_29, LocalDate.MAX, false, false, true, true }, { DATE_2012_07_30, LocalDate.MAX, false, false, true, true }, { DATE_2012_07_31, LocalDate.MAX, false, true, true, false }, { DATE_2012_08_01, LocalDate.MAX, false, false, false, false }, }; } @Test @UseDataProvider("data_queries") public void test_encloses( LocalDate start, LocalDate end, boolean isEnclosedBy, boolean abuts, boolean isConnected, boolean overlaps) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(isEnclosedBy, test.encloses(LocalDateRange.of(start, end))); } @Test @UseDataProvider("data_queries") public void test_abuts( LocalDate start, LocalDate end, boolean isEnclosedBy, boolean abuts, boolean isConnected, boolean overlaps) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(abuts, test.abuts(LocalDateRange.of(start, end))); } @Test @UseDataProvider("data_queries") public void test_isConnected( LocalDate start, LocalDate end, boolean isEnclosedBy, boolean abuts, boolean isConnected, boolean overlaps) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(isConnected, test.isConnected(LocalDateRange.of(start, end))); } @Test @UseDataProvider("data_queries") public void test_overlaps( LocalDate start, LocalDate end, boolean isEnclosedBy, boolean abuts, boolean isConnected, boolean overlaps) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(overlaps, test.overlaps(LocalDateRange.of(start, end))); } @Test @UseDataProvider("data_queries") public void test_crossCheck( LocalDate start, LocalDate end, boolean isEnclosedBy, boolean abuts, boolean isConnected, boolean overlaps) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); LocalDateRange input = LocalDateRange.of(start, end); assertEquals(test.overlaps(input) || test.abuts(input), test.isConnected(input)); assertEquals(test.isConnected(input) && !test.abuts(input), test.overlaps(input)); } @Test public void test_encloses_max() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, LocalDate.MAX); assertEquals(true, test.encloses(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28))); assertEquals(true, test.encloses(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_29))); assertEquals(true, test.encloses(LocalDateRange.of(DATE_2012_07_28, LocalDate.MAX))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_01, DATE_2012_07_27))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_27, LocalDate.MAX))); } @Test public void test_encloses_baseEmpty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_27))); assertEquals(true, test.encloses(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_27))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_28))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_29))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_27, LocalDate.MAX))); assertEquals(false, test.encloses(LocalDateRange.of(DATE_2012_07_28, LocalDate.MAX))); } @Test public void test_abuts_baseEmpty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28); assertEquals(false, test.abuts(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_27))); assertEquals(false, test.abuts(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28))); assertEquals(false, test.abuts(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29))); assertEquals(true, test.abuts(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_28))); assertEquals(true, test.abuts(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_29))); } @Test public void test_isConnected_baseEmpty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28); assertEquals(false, test.isConnected(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_27))); assertEquals(true, test.isConnected(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28))); assertEquals(false, test.isConnected(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29))); } @Test public void test_overlaps_baseEmpty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28); assertEquals(false, test.overlaps(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_27))); assertEquals(true, test.overlaps(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28))); assertEquals(false, test.overlaps(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29))); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_intersection() { return new Object[][] { // adjacent { DATE_2012_07_01, DATE_2012_07_28, DATE_2012_07_28, DATE_2012_07_30, DATE_2012_07_28, DATE_2012_07_28 }, // adjacent empty { DATE_2012_07_01, DATE_2012_07_30, DATE_2012_07_30, DATE_2012_07_30, DATE_2012_07_30, DATE_2012_07_30 }, // overlap { DATE_2012_07_01, DATE_2012_07_29, DATE_2012_07_28, DATE_2012_07_30, DATE_2012_07_28, DATE_2012_07_29 }, // encloses { DATE_2012_07_01, DATE_2012_07_30, DATE_2012_07_28, DATE_2012_07_29, DATE_2012_07_28, DATE_2012_07_29 }, // encloses empty { DATE_2012_07_01, DATE_2012_07_30, DATE_2012_07_28, DATE_2012_07_28, DATE_2012_07_28, DATE_2012_07_28 }, }; } @Test @UseDataProvider("data_intersection") public void test_intersection( LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2, LocalDate expStart, LocalDate expEnd) { LocalDateRange test1 = LocalDateRange.of(start1, end1); LocalDateRange test2 = LocalDateRange.of(start2, end2); LocalDateRange expected = LocalDateRange.of(expStart, expEnd); assertTrue(test1.isConnected(test2)); assertEquals(expected, test1.intersection(test2)); } @Test @UseDataProvider("data_intersection") public void test_intersection_reverse( LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2, LocalDate expStart, LocalDate expEnd) { LocalDateRange test1 = LocalDateRange.of(start1, end1); LocalDateRange test2 = LocalDateRange.of(start2, end2); LocalDateRange expected = LocalDateRange.of(expStart, expEnd); assertTrue(test2.isConnected(test1)); assertEquals(expected, test2.intersection(test1)); } @Test(expected = DateTimeException.class) public void test_intersectionBad() { LocalDateRange test1 = LocalDateRange.of(DATE_2012_07_01, DATE_2012_07_28); LocalDateRange test2 = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_30); assertEquals(false, test1.isConnected(test2)); test1.intersection(test2); } @Test public void test_intersection_same() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(test, test.intersection(test)); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_union() { return new Object[][] { // adjacent { DATE_2012_07_01, DATE_2012_07_28, DATE_2012_07_28, DATE_2012_07_30, DATE_2012_07_01, DATE_2012_07_30 }, // adjacent empty { DATE_2012_07_01, DATE_2012_07_30, DATE_2012_07_30, DATE_2012_07_30, DATE_2012_07_01, DATE_2012_07_30 }, // overlap { DATE_2012_07_01, DATE_2012_07_29, DATE_2012_07_28, DATE_2012_07_30, DATE_2012_07_01, DATE_2012_07_30 }, // encloses { DATE_2012_07_01, DATE_2012_07_30, DATE_2012_07_28, DATE_2012_07_29, DATE_2012_07_01, DATE_2012_07_30 }, // encloses empty { DATE_2012_07_01, DATE_2012_07_30, DATE_2012_07_28, DATE_2012_07_28, DATE_2012_07_01, DATE_2012_07_30 }, }; } @Test @UseDataProvider("data_union") public void test_unionAndSpan( LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2, LocalDate expStart, LocalDate expEnd) { LocalDateRange test1 = LocalDateRange.of(start1, end1); LocalDateRange test2 = LocalDateRange.of(start2, end2); LocalDateRange expected = LocalDateRange.of(expStart, expEnd); assertTrue(test1.isConnected(test2)); assertEquals(expected, test1.union(test2)); assertEquals(expected, test1.span(test2)); } @Test @UseDataProvider("data_union") public void test_unionAndSpan_reverse( LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2, LocalDate expStart, LocalDate expEnd) { LocalDateRange test1 = LocalDateRange.of(start1, end1); LocalDateRange test2 = LocalDateRange.of(start2, end2); LocalDateRange expected = LocalDateRange.of(expStart, expEnd); assertTrue(test2.isConnected(test1)); assertEquals(expected, test2.union(test1)); assertEquals(expected, test2.span(test1)); } @Test @UseDataProvider("data_union") public void test_span_enclosesInputs( LocalDate start1, LocalDate end1, LocalDate start2, LocalDate end2, LocalDate expStart, LocalDate expEnd) { LocalDateRange test1 = LocalDateRange.of(start1, end1); LocalDateRange test2 = LocalDateRange.of(start2, end2); LocalDateRange expected = LocalDateRange.of(expStart, expEnd); assertEquals(true, expected.encloses(test1)); assertEquals(true, expected.encloses(test2)); } @Test(expected = DateTimeException.class) public void test_union_disconnected() { LocalDateRange test1 = LocalDateRange.of(DATE_2012_07_01, DATE_2012_07_28); LocalDateRange test2 = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_30); assertFalse(test1.isConnected(test2)); test1.union(test2); } @Test public void test_span_disconnected() { LocalDateRange test1 = LocalDateRange.of(DATE_2012_07_01, DATE_2012_07_28); LocalDateRange test2 = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_30); assertFalse(test1.isConnected(test2)); assertEquals(LocalDateRange.of(DATE_2012_07_01, DATE_2012_07_30), test1.span(test2)); } @Test public void test_unionAndSpan_same() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(test, test.union(test)); assertEquals(test, test.span(test)); } //----------------------------------------------------------------------- @Test public void test_stream() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); List result = test.stream().collect(Collectors.toList()); assertEquals(3, result.size()); assertEquals(DATE_2012_07_28, result.get(0)); assertEquals(DATE_2012_07_29, result.get(1)); assertEquals(DATE_2012_07_30, result.get(2)); } @Test public void test_stream_MIN_MINP3() { LocalDateRange test = LocalDateRange.of(LocalDate.MIN, MINP3); List result = test.stream().collect(Collectors.toList()); assertEquals(3, result.size()); assertEquals(LocalDate.MIN, result.get(0)); assertEquals(MINP1, result.get(1)); assertEquals(MINP2, result.get(2)); } @Test public void test_stream_MAXM2_MAX() { LocalDateRange test = LocalDateRange.of(MAXM2, LocalDate.MAX); List result = test.stream().collect(Collectors.toList()); assertEquals(3, result.size()); assertEquals(MAXM2, result.get(0)); assertEquals(MAXM1, result.get(1)); assertEquals(LocalDate.MAX, result.get(2)); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_isBefore() { return new Object[][] { // before start { DATE_2012_07_01, DATE_2012_07_27, false }, // before end { DATE_2012_07_27, DATE_2012_07_30, false }, { DATE_2012_07_28, DATE_2012_07_30, false }, { DATE_2012_07_29, DATE_2012_07_30, false }, // same end { DATE_2012_07_27, DATE_2012_07_31, false }, { DATE_2012_07_28, DATE_2012_07_31, false }, { DATE_2012_07_29, DATE_2012_07_31, false }, { DATE_2012_07_30, DATE_2012_07_31, false }, // past end { DATE_2012_07_27, DATE_2012_08_01, false }, { DATE_2012_07_28, DATE_2012_08_01, false }, { DATE_2012_07_29, DATE_2012_08_01, false }, { DATE_2012_07_30, DATE_2012_08_01, false }, // start past end { DATE_2012_07_31, DATE_2012_08_01, true }, { DATE_2012_07_31, DATE_2012_08_31, true }, // empty { DATE_2012_07_30, DATE_2012_07_30, false }, { DATE_2012_07_31, DATE_2012_07_31, true }, // min { LocalDate.MIN, DATE_2012_07_27, false }, { LocalDate.MIN, DATE_2012_07_28, false }, { LocalDate.MIN, DATE_2012_07_29, false }, { LocalDate.MIN, DATE_2012_07_30, false }, { LocalDate.MIN, DATE_2012_07_31, false }, { LocalDate.MIN, DATE_2012_08_01, false }, { LocalDate.MIN, LocalDate.MAX, false }, // max { DATE_2012_07_27, LocalDate.MAX, false }, { DATE_2012_07_28, LocalDate.MAX, false }, { DATE_2012_07_29, LocalDate.MAX, false }, { DATE_2012_07_30, LocalDate.MAX, false }, { DATE_2012_07_31, LocalDate.MAX, true }, { DATE_2012_08_01, LocalDate.MAX, true }, }; } @Test @UseDataProvider("data_isBefore") public void test_isBefore_range(LocalDate start, LocalDate end, boolean before) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(before, test.isBefore(LocalDateRange.of(start, end))); } @Test @UseDataProvider("data_isBefore") public void test_isBefore_date(LocalDate start, LocalDate end, boolean before) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(before, test.isBefore(start)); } @Test public void test_isBefore_range_empty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29); assertEquals(false, test.isBefore(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_28))); assertEquals(false, test.isBefore(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29))); assertEquals(false, test.isBefore(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29))); assertEquals(true, test.isBefore(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_30))); assertEquals(true, test.isBefore(LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_30))); assertEquals(true, test.isBefore(LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_31))); } @Test public void test_isBefore_date_empty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29); assertEquals(false, test.isBefore(DATE_2012_07_28)); assertEquals(false, test.isBefore(DATE_2012_07_29)); assertEquals(true, test.isBefore(DATE_2012_07_30)); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_isAfter() { return new Object[][] { // before start { DATE_2012_07_01, DATE_2012_07_27, true }, // to start { DATE_2012_07_01, DATE_2012_07_28, true }, // before end { DATE_2012_07_01, DATE_2012_07_29, false }, { DATE_2012_07_27, DATE_2012_07_30, false }, { DATE_2012_07_28, DATE_2012_07_30, false }, { DATE_2012_07_29, DATE_2012_07_30, false }, // same end { DATE_2012_07_27, DATE_2012_07_31, false }, { DATE_2012_07_28, DATE_2012_07_31, false }, { DATE_2012_07_29, DATE_2012_07_31, false }, { DATE_2012_07_30, DATE_2012_07_31, false }, // past end { DATE_2012_07_27, DATE_2012_08_01, false }, { DATE_2012_07_28, DATE_2012_08_01, false }, { DATE_2012_07_29, DATE_2012_08_01, false }, { DATE_2012_07_30, DATE_2012_08_01, false }, // start past end { DATE_2012_07_31, DATE_2012_08_01, false }, { DATE_2012_07_31, DATE_2012_08_31, false }, // empty { DATE_2012_07_28, DATE_2012_07_28, true }, { DATE_2012_07_29, DATE_2012_07_29, false }, // min { LocalDate.MIN, DATE_2012_07_27, true }, { LocalDate.MIN, DATE_2012_07_28, true }, { LocalDate.MIN, DATE_2012_07_29, false }, { LocalDate.MIN, DATE_2012_07_30, false }, { LocalDate.MIN, DATE_2012_07_31, false }, { LocalDate.MIN, DATE_2012_08_01, false }, { LocalDate.MIN, LocalDate.MAX, false }, // max { DATE_2012_07_27, LocalDate.MAX, false }, { DATE_2012_07_28, LocalDate.MAX, false }, { DATE_2012_07_29, LocalDate.MAX, false }, { DATE_2012_07_30, LocalDate.MAX, false }, { DATE_2012_07_31, LocalDate.MAX, false }, { DATE_2012_08_01, LocalDate.MAX, false }, }; } @Test @UseDataProvider("data_isAfter") public void test_isAfter_range(LocalDate start, LocalDate end, boolean before) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(before, test.isAfter(LocalDateRange.of(start, end))); } @Test @UseDataProvider("data_isAfter") public void test_isAfter_date(LocalDate start, LocalDate end, boolean before) { LocalDateRange test = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_31); assertEquals(before, test.isAfter(end.minusDays(1))); } @Test public void test_isAfter_range_empty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29); assertEquals(true, test.isAfter(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_28))); assertEquals(true, test.isAfter(LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29))); assertEquals(true, test.isAfter(LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_28))); assertEquals(false, test.isAfter(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29))); assertEquals(false, test.isAfter(LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_30))); assertEquals(false, test.isAfter(LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_30))); assertEquals(false, test.isAfter(LocalDateRange.of(DATE_2012_07_30, DATE_2012_07_31))); } @Test public void test_isAfter_date_empty() { LocalDateRange test = LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29); assertEquals(true, test.isAfter(DATE_2012_07_28)); assertEquals(false, test.isAfter(DATE_2012_07_29)); assertEquals(false, test.isAfter(DATE_2012_07_30)); } //----------------------------------------------------------------------- @Test public void test_lengthInDays() { assertEquals(2, LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29).lengthInDays()); assertEquals(1, LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_29).lengthInDays()); assertEquals(0, LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29).lengthInDays()); assertEquals(Integer.MAX_VALUE, LocalDateRange.of(LocalDate.MIN, DATE_2012_07_29).lengthInDays()); assertEquals(Integer.MAX_VALUE, LocalDateRange.of(DATE_2012_07_29, LocalDate.MAX).lengthInDays()); assertEquals(Integer.MAX_VALUE, LocalDateRange.of(MINP1, MAXM1).lengthInDays()); } @Test public void test_toPeriod() { assertEquals(Period.ofDays(2), LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29).toPeriod()); assertEquals(Period.ofDays(1), LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_29).toPeriod()); assertEquals(Period.ofDays(0), LocalDateRange.of(DATE_2012_07_29, DATE_2012_07_29).toPeriod()); } @Test(expected = ArithmeticException.class) public void test_toPeriod_unbounded_MIN() { LocalDateRange.of(LocalDate.MIN, DATE_2012_07_29).toPeriod(); } @Test(expected = ArithmeticException.class) public void test_toPeriod_unbounded_MAX() { LocalDateRange.of(DATE_2012_07_29, LocalDate.MAX).toPeriod(); } //----------------------------------------------------------------------- @Test public void test_equals() { LocalDateRange a = LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29); LocalDateRange a2 = LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_29); LocalDateRange b = LocalDateRange.of(DATE_2012_07_27, DATE_2012_07_30); LocalDateRange c = LocalDateRange.of(DATE_2012_07_28, DATE_2012_07_29); assertEquals(true, a.equals(a)); assertEquals(true, a.equals(a2)); assertEquals(false, a.equals(b)); assertEquals(false, a.equals(c)); assertEquals(false, a.equals(null)); assertEquals(false, a.equals("")); assertEquals(true, a.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- @DataProvider public static List> data_crossCheckGuava() { List> list = new ArrayList<>(); for (int i1 = 1; i1 < 5; i1++) { for (int j1 = i1; j1 < 5; j1++) { LocalDate date11 = LocalDate.of(2016, 1, i1); LocalDate date12 = LocalDate.of(2016, 1, j1); LocalDateRange extraRange1 = LocalDateRange.of(date11, date12); Range guavaRange1 = Range.closedOpen(date11, date12); for (int i2 = 1; i2 < 5; i2++) { for (int j2 = i2; j2 < 5; j2++) { LocalDate date21 = LocalDate.of(2016, 1, i2); LocalDate date22 = LocalDate.of(2016, 1, j2); LocalDateRange extraRange2 = LocalDateRange.of(date21, date22); Range guavaRange2 = Range.closedOpen(date21, date22); list.add(Arrays.asList(extraRange1, extraRange2, guavaRange1, guavaRange2)); } } } } return list; } @Test @UseDataProvider("data_crossCheckGuava") public void crossCheckGuava_encloses( LocalDateRange extraRange1, LocalDateRange extraRange2, Range guavaRange1, Range guavaRange2) { boolean extra = extraRange1.encloses(extraRange2); boolean guava = guavaRange1.encloses(guavaRange2); assertEquals(guava, extra); } @Test @UseDataProvider("data_crossCheckGuava") public void crossCheckGuava_isConnected( LocalDateRange extraRange1, LocalDateRange extraRange2, Range guavaRange1, Range guavaRange2) { boolean extra = extraRange1.isConnected(extraRange2); boolean guava = guavaRange1.isConnected(guavaRange2); assertEquals(guava, extra); } @Test @UseDataProvider("data_crossCheckGuava") public void crossCheckGuava_intersection( LocalDateRange extraRange1, LocalDateRange extraRange2, Range guavaRange1, Range guavaRange2) { LocalDateRange extra = null; try { extra = extraRange1.intersection(extraRange2); } catch (DateTimeException ex) { // continue } Range guava = null; try { guava = guavaRange1.intersection(guavaRange2); } catch (IllegalArgumentException ex) { // continue } if (extra == null) { assertEquals(guava, extra); } else if (guava != null) { assertEquals(guava.lowerEndpoint(), extra.getStart()); assertEquals(guava.upperEndpoint(), extra.getEnd()); } } @Test @UseDataProvider("data_crossCheckGuava") public void crossCheckGuava_span( LocalDateRange extraRange1, LocalDateRange extraRange2, Range guavaRange1, Range guavaRange2) { LocalDateRange extra = extraRange1.span(extraRange2); Range guava = guavaRange1.span(guavaRange2); assertEquals(guava.lowerEndpoint(), extra.getStart()); assertEquals(guava.upperEndpoint(), extra.getEnd()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestMinutes.java000066400000000000000000000413121343451174100260260ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Duration; import java.time.LocalTime; import java.time.format.DateTimeParseException; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestMinutes { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Minutes.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Minutes test = Minutes.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Minutes.of(0), Minutes.ZERO); assertEquals(Minutes.of(0), Minutes.ZERO); assertEquals(0, Minutes.ZERO.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(0, Minutes.of(0).getAmount()); assertEquals(1, Minutes.of(1).getAmount()); assertEquals(2, Minutes.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Minutes.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Minutes.of(-1).getAmount()); assertEquals(-2, Minutes.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Minutes.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @Test public void test_ofHours() { assertEquals(0, Minutes.ofHours(0).getAmount()); assertEquals(60, Minutes.ofHours(1).getAmount()); assertEquals(120, Minutes.ofHours(2).getAmount()); assertEquals((Integer.MAX_VALUE / 60) * 60, Minutes.ofHours(Integer.MAX_VALUE / 60).getAmount()); assertEquals(-60, Minutes.ofHours(-1).getAmount()); assertEquals(-120, Minutes.ofHours(-2).getAmount()); assertEquals((Integer.MIN_VALUE / 60) * 60, Minutes.ofHours(Integer.MIN_VALUE / 60).getAmount()); } @Test(expected = ArithmeticException.class) public void test_ofHours_overflow() { Minutes.ofHours((Integer.MAX_VALUE / 60) + 60); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_valid() { return new Object[][] { {"PT0M", 0}, {"PT1M", 1}, {"PT2M", 2}, {"PT123456789M", 123456789}, {"PT+0M", 0}, {"PT+2M", 2}, {"PT-0M", 0}, {"PT-2M", -2}, {"PT0H", 0}, {"PT1H", 60}, {"PT2H", 120}, {"PT1234H", 1234 * 60}, {"PT+0H", 0}, {"PT+2H", 120}, {"PT-0H", 0}, {"PT-2H", -120}, {"P0D", 0}, {"P1D", 1 * 24 * 60}, {"P2D", 2 * 24 * 60}, {"P1234D", 1234 * 24 * 60}, {"P+0D", 0}, {"P+2D", 2 * 24 * 60}, {"P-0D", 0}, {"P-2D", -2 * 24 * 60}, {"PT0H0M", 0}, {"PT2H3M", 123}, {"PT+2H3M", 123}, {"PT2H+3M", 123}, {"PT-2H3M", -117}, {"PT2H-3M", 117}, {"PT-2H-3M", -123}, {"P0DT0H0M", 0}, {"P5DT2H4M", 5 * 24 * 60 + 2 * 60 + 4}, }; } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid(String str, int expectedMinutes) { assertEquals(Minutes.of(expectedMinutes), Minutes.parse(str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialPlus(String str, int expectedMinutes) { assertEquals(Minutes.of(expectedMinutes), Minutes.parse("+" + str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialMinus(String str, int expectedMinutes) { assertEquals(Minutes.of(-expectedMinutes), Minutes.parse("-" + str)); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3W"}, {"P3Q"}, {"P1M2Y"}, {"3"}, {"-3"}, {"3M"}, {"-3M"}, {"P3M"}, {"P3"}, {"P-3"}, {"PM"}, {"T3"}, {"P3M"}, {"PT3S"}, {"PT3"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Minutes.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Minutes.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Minutes() { Minutes test5 = Minutes.of(5); assertEquals(Minutes.of(5), test5.plus(Minutes.of(0))); assertEquals(Minutes.of(7), test5.plus(Minutes.of(2))); assertEquals(Minutes.of(3), test5.plus(Minutes.of(-2))); assertEquals(Minutes.of(Integer.MAX_VALUE), Minutes.of(Integer.MAX_VALUE - 1).plus(Minutes.of(1))); assertEquals(Minutes.of(Integer.MIN_VALUE), Minutes.of(Integer.MIN_VALUE + 1).plus(Minutes.of(-1))); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Minutes.of(Integer.MAX_VALUE - 1).plus(Minutes.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Minutes.of(Integer.MIN_VALUE + 1).plus(Minutes.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Minutes.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Minutes test5 = Minutes.of(5); assertEquals(Minutes.of(5), test5.plus(0)); assertEquals(Minutes.of(7), test5.plus(2)); assertEquals(Minutes.of(3), test5.plus(-2)); assertEquals(Minutes.of(Integer.MAX_VALUE), Minutes.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Minutes.of(Integer.MIN_VALUE), Minutes.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Minutes.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Minutes.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Minutes() { Minutes test5 = Minutes.of(5); assertEquals(Minutes.of(5), test5.minus(Minutes.of(0))); assertEquals(Minutes.of(3), test5.minus(Minutes.of(2))); assertEquals(Minutes.of(7), test5.minus(Minutes.of(-2))); assertEquals(Minutes.of(Integer.MAX_VALUE), Minutes.of(Integer.MAX_VALUE - 1).minus(Minutes.of(-1))); assertEquals(Minutes.of(Integer.MIN_VALUE), Minutes.of(Integer.MIN_VALUE + 1).minus(Minutes.of(1))); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Minutes.of(Integer.MAX_VALUE - 1).minus(Minutes.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Minutes.of(Integer.MIN_VALUE + 1).minus(Minutes.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Minutes.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Minutes test5 = Minutes.of(5); assertEquals(Minutes.of(5), test5.minus(0)); assertEquals(Minutes.of(3), test5.minus(2)); assertEquals(Minutes.of(7), test5.minus(-2)); assertEquals(Minutes.of(Integer.MAX_VALUE), Minutes.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Minutes.of(Integer.MIN_VALUE), Minutes.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Minutes.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Minutes.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Minutes test5 = Minutes.of(5); assertEquals(Minutes.of(0), test5.multipliedBy(0)); assertEquals(Minutes.of(5), test5.multipliedBy(1)); assertEquals(Minutes.of(10), test5.multipliedBy(2)); assertEquals(Minutes.of(15), test5.multipliedBy(3)); assertEquals(Minutes.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Minutes test5 = Minutes.of(5); assertEquals(Minutes.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Minutes.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Minutes.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Minutes test12 = Minutes.of(12); assertEquals(Minutes.of(12), test12.dividedBy(1)); assertEquals(Minutes.of(6), test12.dividedBy(2)); assertEquals(Minutes.of(4), test12.dividedBy(3)); assertEquals(Minutes.of(3), test12.dividedBy(4)); assertEquals(Minutes.of(2), test12.dividedBy(5)); assertEquals(Minutes.of(2), test12.dividedBy(6)); assertEquals(Minutes.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Minutes test12 = Minutes.of(12); assertEquals(Minutes.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Minutes.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Minutes.of(0), Minutes.of(0).negated()); assertEquals(Minutes.of(-12), Minutes.of(12).negated()); assertEquals(Minutes.of(12), Minutes.of(-12).negated()); assertEquals(Minutes.of(-Integer.MAX_VALUE), Minutes.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Minutes.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Minutes.of(0), Minutes.of(0).abs()); assertEquals(Minutes.of(12), Minutes.of(12).abs()); assertEquals(Minutes.of(12), Minutes.of(-12).abs()); assertEquals(Minutes.of(Integer.MAX_VALUE), Minutes.of(Integer.MAX_VALUE).abs()); assertEquals(Minutes.of(Integer.MAX_VALUE), Minutes.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Minutes.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { LocalTime base = LocalTime.of(11, 30); assertEquals(LocalTime.of(11, 30), Minutes.of(0).addTo(base)); assertEquals(LocalTime.of(11, 36), Minutes.of(6).addTo(base)); } //----------------------------------------------------------------------- @Test public void test_subtractFrom() { LocalTime base = LocalTime.of(11, 30); assertEquals(LocalTime.of(11, 30), Minutes.of(0).subtractFrom(base)); assertEquals(LocalTime.of(11, 24), Minutes.of(6).subtractFrom(base)); } //----------------------------------------------------------------------- @Test public void test_toDuration() { for (int i = -20; i < 20; i++) { assertEquals(Duration.ofMinutes(i), Minutes.of(i).toDuration()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Minutes test5 = Minutes.of(5); Minutes test6 = Minutes.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Minutes test5 = Minutes.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Minutes test5 = Minutes.of(5); Minutes test6 = Minutes.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Minutes test5 = Minutes.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Minutes test5 = Minutes.of(5); assertEquals(false, test5.equals("")); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Minutes test5 = Minutes.of(5); Minutes test6 = Minutes.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Minutes test5 = Minutes.of(5); assertEquals("PT5M", test5.toString()); Minutes testM1 = Minutes.of(-1); assertEquals("PT-1M", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestMonths.java000066400000000000000000000475421343451174100256650ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestMonths { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Months.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Months test = Months.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Months.of(0), Months.ZERO); assertEquals(Months.of(0), Months.ZERO); assertEquals(0, Months.ZERO.getAmount()); } @Test public void test_ONE() { assertSame(Months.of(1), Months.ONE); assertEquals(Months.of(1), Months.ONE); assertEquals(1, Months.ONE.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(0, Months.of(0).getAmount()); assertEquals(1, Months.of(1).getAmount()); assertEquals(2, Months.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Months.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Months.of(-1).getAmount()); assertEquals(-2, Months.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Months.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @Test public void test_ofYears() { assertEquals(0, Months.ofYears(0).getAmount()); assertEquals(12, Months.ofYears(1).getAmount()); assertEquals(24, Months.ofYears(2).getAmount()); assertEquals((Integer.MAX_VALUE / 12) * 12, Months.ofYears(Integer.MAX_VALUE / 12).getAmount()); assertEquals(-12, Months.ofYears(-1).getAmount()); assertEquals(-24, Months.ofYears(-2).getAmount()); assertEquals((Integer.MIN_VALUE / 12) * 12, Months.ofYears(Integer.MIN_VALUE / 12).getAmount()); } @Test(expected = ArithmeticException.class) public void test_ofYears_overflow() { Months.ofYears((Integer.MAX_VALUE / 12) + 12); } //----------------------------------------------------------------------- @Test public void test_from_Period_P0M() { assertEquals(Months.of(0), Months.from(Period.ofMonths(0))); } @Test public void test_from_Period_P2M() { assertEquals(Months.of(2), Months.from(Period.ofMonths(2))); } @Test public void test_from_P2Y() { assertEquals(Months.of(24), Months.from(new MockYearsMonths(2, 0))); } @Test public void test_from_P2Y3M() { assertEquals(Months.of(27), Months.from(new MockYearsMonths(2, 3))); } @Test public void test_from_yearsAndMonths() { assertEquals(Months.of(41), Months.from(Period.of(3, 5, 0))); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_noConversion() { Months.from(Period.ofDays(2)); } @Test(expected = NullPointerException.class) public void test_from_null() { Months.from((TemporalAmount) null); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_valid() { return new Object[][] { {"P0M", 0}, {"P1M", 1}, {"P2M", 2}, {"P123456789M", 123456789}, {"P+0M", 0}, {"P+2M", 2}, {"P-0M", 0}, {"P-2M", -2}, {"P0Y", 0}, {"P1Y", 12}, {"P2Y", 24}, {"P1234567Y", 1234567 * 12}, {"P+0Y", 0}, {"P+2Y", 24}, {"P-0Y", 0}, {"P-2Y", -24}, {"P0Y0M", 0}, {"P2Y3M", 27}, {"P+2Y3M", 27}, {"P2Y+3M", 27}, {"P-2Y3M", -21}, {"P2Y-3M", 21}, {"P-2Y-3M", -27}, }; } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid(String str, int expectedDays) { assertEquals(Months.of(expectedDays), Months.parse(str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialPlus(String str, int expectedDays) { assertEquals(Months.of(expectedDays), Months.parse("+" + str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialMinus(String str, int expectedDays) { assertEquals(Months.of(-expectedDays), Months.parse("-" + str)); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3W"}, {"P3D"}, {"P3Q"}, {"P1M2Y"}, {"3"}, {"-3"}, {"3M"}, {"-3M"}, {"P3"}, {"P-3"}, {"PM"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Months.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Months.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_between() { assertEquals(Months.of(24), Months.between(LocalDate.of(2019, 1, 1), LocalDate.of(2021, 1, 1))); } @Test(expected = NullPointerException.class) public void test_between_date_null() { Months.between(LocalDate.now(), (Temporal) null); } @Test(expected = NullPointerException.class) public void test_between_null_date() { Months.between((Temporal) null, LocalDate.now()); } //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(6, Months.of(6).get(ChronoUnit.MONTHS)); } @Test(expected = DateTimeException.class) public void test_get_invalidType() { Months.of(6).get(IsoFields.QUARTER_YEARS); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Months() { Months test5 = Months.of(5); assertEquals(Months.of(5), test5.plus(Months.of(0))); assertEquals(Months.of(7), test5.plus(Months.of(2))); assertEquals(Months.of(3), test5.plus(Months.of(-2))); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE - 1).plus(Months.of(1))); assertEquals(Months.of(Integer.MIN_VALUE), Months.of(Integer.MIN_VALUE + 1).plus(Months.of(-1))); } @Test public void test_plus_TemporalAmount_Period() { Months test5 = Months.of(5); assertEquals(Months.of(5), test5.plus(Period.ofMonths(0))); assertEquals(Months.of(7), test5.plus(Period.ofMonths(2))); assertEquals(Months.of(3), test5.plus(Period.ofMonths(-2))); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE - 1).plus(Period.ofMonths(1))); assertEquals(Months.of(Integer.MIN_VALUE), Months.of(Integer.MIN_VALUE + 1).plus(Period.ofMonths(-1))); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_PeriodDays() { Months.of(1).plus(Period.ofDays(2)); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_Duration() { Months.of(1).plus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Months.of(Integer.MAX_VALUE - 1).plus(Months.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Months.of(Integer.MIN_VALUE + 1).plus(Months.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Months.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Months test5 = Months.of(5); assertEquals(Months.of(5), test5.plus(0)); assertEquals(Months.of(7), test5.plus(2)); assertEquals(Months.of(3), test5.plus(-2)); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Months.of(Integer.MIN_VALUE), Months.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Months.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Months.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Months() { Months test5 = Months.of(5); assertEquals(Months.of(5), test5.minus(Months.of(0))); assertEquals(Months.of(3), test5.minus(Months.of(2))); assertEquals(Months.of(7), test5.minus(Months.of(-2))); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE - 1).minus(Months.of(-1))); assertEquals(Months.of(Integer.MIN_VALUE), Months.of(Integer.MIN_VALUE + 1).minus(Months.of(1))); } @Test public void test_minus_TemporalAmount_Period() { Months test5 = Months.of(5); assertEquals(Months.of(5), test5.minus(Period.ofMonths(0))); assertEquals(Months.of(3), test5.minus(Period.ofMonths(2))); assertEquals(Months.of(7), test5.minus(Period.ofMonths(-2))); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE - 1).minus(Period.ofMonths(-1))); assertEquals(Months.of(Integer.MIN_VALUE), Months.of(Integer.MIN_VALUE + 1).minus(Period.ofMonths(1))); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_PeriodDays() { Months.of(1).minus(Period.ofDays(2)); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_Duration() { Months.of(1).minus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Months.of(Integer.MAX_VALUE - 1).minus(Months.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Months.of(Integer.MIN_VALUE + 1).minus(Months.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Months.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Months test5 = Months.of(5); assertEquals(Months.of(5), test5.minus(0)); assertEquals(Months.of(3), test5.minus(2)); assertEquals(Months.of(7), test5.minus(-2)); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Months.of(Integer.MIN_VALUE), Months.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Months.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Months.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Months test5 = Months.of(5); assertEquals(Months.of(0), test5.multipliedBy(0)); assertEquals(Months.of(5), test5.multipliedBy(1)); assertEquals(Months.of(10), test5.multipliedBy(2)); assertEquals(Months.of(15), test5.multipliedBy(3)); assertEquals(Months.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Months test5 = Months.of(5); assertEquals(Months.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Months.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Months.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Months test12 = Months.of(12); assertEquals(Months.of(12), test12.dividedBy(1)); assertEquals(Months.of(6), test12.dividedBy(2)); assertEquals(Months.of(4), test12.dividedBy(3)); assertEquals(Months.of(3), test12.dividedBy(4)); assertEquals(Months.of(2), test12.dividedBy(5)); assertEquals(Months.of(2), test12.dividedBy(6)); assertEquals(Months.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Months test12 = Months.of(12); assertEquals(Months.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Months.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Months.of(0), Months.of(0).negated()); assertEquals(Months.of(-12), Months.of(12).negated()); assertEquals(Months.of(12), Months.of(-12).negated()); assertEquals(Months.of(-Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Months.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Months.of(0), Months.of(0).abs()); assertEquals(Months.of(12), Months.of(12).abs()); assertEquals(Months.of(12), Months.of(-12).abs()); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(Integer.MAX_VALUE).abs()); assertEquals(Months.of(Integer.MAX_VALUE), Months.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Months.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { assertEquals(LocalDate.of(2019, 1, 10), Months.of(0).addTo(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2019, 6, 10), Months.of(5).addTo(LocalDate.of(2019, 1, 10))); } @Test public void test_subtractFrom() { assertEquals(LocalDate.of(2019, 1, 10), Months.of(0).subtractFrom(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2018, 8, 10), Months.of(5).subtractFrom(LocalDate.of(2019, 1, 10))); } //----------------------------------------------------------------------- @Test public void test_toPeriod() { for (int i = -20; i < 20; i++) { assertEquals(Period.ofMonths(i), Months.of(i).toPeriod()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Months test5 = Months.of(5); Months test6 = Months.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Months test5 = Months.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Months test5 = Months.of(5); Months test6 = Months.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Months test5 = Months.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Months test5 = Months.of(5); Object obj = ""; assertEquals(false, test5.equals(obj)); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Months test5 = Months.of(5); Months test6 = Months.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Months test5 = Months.of(5); assertEquals("P5M", test5.toString()); Months testM1 = Months.of(-1); assertEquals("P-1M", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestMutableClock.java000066400000000000000000000320441343451174100267510ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DayOfWeek; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Period; import java.time.Year; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAdjusters; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.Test; /** * Test class. */ public class TestMutableClock { @Test public void test_of() { assertEquals( Instant.EPOCH, MutableClock.of(Instant.EPOCH, ZoneOffset.UTC).instant()); assertEquals( Instant.MIN, MutableClock.of(Instant.MIN, ZoneOffset.UTC).instant()); assertEquals( Instant.MAX, MutableClock.of(Instant.MAX, ZoneOffset.UTC).instant()); assertEquals( ZoneOffset.UTC, MutableClock.of(Instant.EPOCH, ZoneOffset.UTC).getZone()); assertEquals( ZoneOffset.MIN, MutableClock.of(Instant.EPOCH, ZoneOffset.MIN).getZone()); assertEquals( ZoneOffset.MAX, MutableClock.of(Instant.EPOCH, ZoneOffset.MAX).getZone()); } @Test(expected = NullPointerException.class) public void test_of_nullInstant() { MutableClock.of(null, ZoneOffset.UTC); } @Test(expected = NullPointerException.class) public void test_of_nullZone() { MutableClock.of(Instant.EPOCH, null); } @Test public void test_epochUTC() { assertEquals(Instant.EPOCH, MutableClock.epochUTC().instant()); assertEquals(ZoneOffset.UTC, MutableClock.epochUTC().getZone()); } @Test public void test_setInstant() { MutableClock clock = MutableClock.epochUTC(); assertEquals(Instant.EPOCH, clock.instant()); clock.setInstant(Instant.MIN); assertEquals(Instant.MIN, clock.instant()); clock.setInstant(Instant.MAX); assertEquals(Instant.MAX, clock.instant()); clock.setInstant(Instant.EPOCH.plusSeconds(10)); assertEquals(Instant.EPOCH.plusSeconds(10), clock.instant()); } @Test(expected = NullPointerException.class) public void test_setInstant_null() { MutableClock.epochUTC().setInstant(null); } @Test public void test_add_amountOnly() { MutableClock clock = MutableClock.epochUTC(); clock.add(Duration.ofNanos(3)); clock.add(Period.ofMonths(2)); clock.add(Duration.ofSeconds(-5)); clock.add(Period.ofWeeks(-7)); clock.add(Duration.ZERO); clock.add(Period.ZERO); assertEquals( ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC) .plusNanos(3) .plusMonths(2) .minusSeconds(5) .minusWeeks(7) .toInstant(), clock.instant()); } @Test(expected = NullPointerException.class) public void test_add_amountOnly_null() { MutableClock.epochUTC().add(null); } @Test public void test_add_amountAndUnit() { MutableClock clock = MutableClock.epochUTC(); clock.add(3, ChronoUnit.NANOS); clock.add(2, ChronoUnit.MONTHS); clock.add(-5, ChronoUnit.SECONDS); clock.add(-7, ChronoUnit.WEEKS); clock.add(0, ChronoUnit.MILLIS); clock.add(0, ChronoUnit.YEARS); assertEquals( ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC) .plusNanos(3) .plusMonths(2) .minusSeconds(5) .minusWeeks(7) .toInstant(), clock.instant()); } @Test(expected = NullPointerException.class) public void test_add_amountAndUnit_nullUnit() { MutableClock.epochUTC().add(0, null); } @Test public void test_set_adjuster() { MutableClock clock = MutableClock.epochUTC(); clock.set(LocalDate.of(0, 1, 2)); clock.set(LocalTime.of(3, 4, 5)); clock.set(TemporalAdjusters.firstDayOfNextMonth()); clock.set(TemporalAdjusters.next(DayOfWeek.WEDNESDAY)); assertEquals( LocalDateTime.of(0, 1, 2, 3, 4, 5) .with(TemporalAdjusters.firstDayOfNextMonth()) .with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY)) .atZone(ZoneOffset.UTC) .toInstant(), clock.instant()); clock.set(Instant.EPOCH); assertEquals(Instant.EPOCH, clock.instant()); } @Test(expected = NullPointerException.class) public void test_set_adjuster_null() { MutableClock.epochUTC().set(null); } @Test public void test_set_fieldAndValue() { MutableClock clock = MutableClock.epochUTC(); clock.set(ChronoField.YEAR, 0); clock.set(ChronoField.MONTH_OF_YEAR, 1); clock.set(ChronoField.DAY_OF_MONTH, 2); clock.set(ChronoField.HOUR_OF_DAY, 3); clock.set(ChronoField.MINUTE_OF_HOUR, 4); clock.set(ChronoField.SECOND_OF_MINUTE, 5); assertEquals( LocalDateTime.of(0, 1, 2, 3, 4, 5) .atZone(ZoneOffset.UTC) .toInstant(), clock.instant()); } @Test(expected = NullPointerException.class) public void test_set_fieldAndValue_nullField() { MutableClock.epochUTC().set(null, 0); } @Test public void test_getZone() { MutableClock clock = MutableClock.epochUTC(); MutableClock withOtherZone = clock.withZone(ZoneOffset.MIN); MutableClock ofOtherZone = MutableClock.of(Instant.EPOCH, ZoneOffset.MAX); assertEquals(ZoneOffset.UTC, clock.getZone()); assertEquals(ZoneOffset.MIN, withOtherZone.getZone()); assertEquals(ZoneOffset.MAX, ofOtherZone.getZone()); } @Test public void test_withZone() { MutableClock clock = MutableClock.epochUTC(); MutableClock withOtherZone = clock.withZone(ZoneOffset.MIN); MutableClock withSameZone = withOtherZone.withZone(ZoneOffset.UTC); clock.setInstant(Instant.MIN); assertEquals(Instant.MIN, withOtherZone.instant()); assertEquals(Instant.MIN, withSameZone.instant()); assertEquals(ZoneOffset.MIN, withOtherZone.getZone()); assertEquals(ZoneOffset.UTC, withSameZone.getZone()); assertNotEquals(clock, withOtherZone); assertEquals(clock, withSameZone); } @Test(expected = NullPointerException.class) public void test_withZone_null() { MutableClock.epochUTC().withZone(null); } @Test public void test_instant() { MutableClock clock = MutableClock.epochUTC(); MutableClock withOtherZone = clock.withZone(ZoneOffset.MIN); assertEquals(Instant.EPOCH, clock.instant()); clock.add(Duration.ofSeconds(5)); assertEquals(Instant.EPOCH.plusSeconds(5), clock.instant()); clock.setInstant(Instant.MIN); assertEquals(Instant.MIN, clock.instant()); assertEquals(Instant.MIN, withOtherZone.instant()); } @Test public void test_equals() { MutableClock clock = MutableClock.epochUTC(); MutableClock withOtherZone = clock.withZone(ZoneOffset.MIN); MutableClock withSameZone = withOtherZone.withZone(ZoneOffset.UTC); MutableClock independent = MutableClock.epochUTC(); assertEquals(clock, clock); assertNotEquals(null, clock); assertNotEquals("", clock); assertNotEquals(withOtherZone, clock); assertEquals(clock, withSameZone); assertNotEquals(clock, independent); } @Test public void test_hashCode_isConstant() { MutableClock clock = MutableClock.epochUTC(); int hash = clock.hashCode(); clock.add(Period.ofMonths(1)); assertEquals(hash, clock.hashCode()); clock.add(1, ChronoUnit.DAYS); assertEquals(hash, clock.hashCode()); clock.set(Year.of(2000)); assertEquals(hash, clock.hashCode()); clock.set(ChronoField.INSTANT_SECONDS, -1); assertEquals(hash, clock.hashCode()); } @Test public void test_hashCode_sameWhenSharedUpdates() { MutableClock clock = MutableClock.epochUTC(); MutableClock withOtherZone = clock.withZone(ZoneOffset.MIN); MutableClock withSameZone = withOtherZone.withZone(ZoneOffset.UTC); assertEquals(clock.hashCode(), withSameZone.hashCode()); } @Test public void test_toString() { MutableClock clock = MutableClock.epochUTC(); assertEquals( "MutableClock[1970-01-01T00:00:00Z,Z]", clock.toString()); clock.add(Period.ofYears(30)); assertEquals( "MutableClock[2000-01-01T00:00:00Z,Z]", clock.toString()); MutableClock withOtherZone = clock.withZone(ZoneOffset.MIN); assertEquals( "MutableClock[2000-01-01T00:00:00Z,-18:00]", withOtherZone.toString()); } @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(MutableClock.class)); } @Test public void test_serialization() throws Exception { MutableClock test = MutableClock.epochUTC(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { MutableClock ser = (MutableClock) ois.readObject(); assertEquals(test.instant(), ser.instant()); assertEquals(test.getZone(), ser.getZone()); // no shared updates assertNotEquals(ser, test); test.add(Duration.ofSeconds(1)); assertNotEquals(ser.instant(), test.instant()); } } @Test public void test_updatesAreAtomic() throws Exception { MutableClock clock = MutableClock.epochUTC(); Duration increment = Duration.ofSeconds(1); Callable applyOneUpdate = () -> { clock.add(increment); return null; }; int updateCount = 10000; List> tasks = Collections.nCopies(updateCount, applyOneUpdate); int threads = Runtime.getRuntime().availableProcessors() * 4; ExecutorService service = Executors.newFixedThreadPool(threads); try { service.invokeAll(tasks); service.shutdown(); service.awaitTermination(1, TimeUnit.MINUTES); } finally { if (!service.isTerminated()) { service.shutdownNow(); } } assertEquals( Instant.EPOCH.plus(increment.multipliedBy(updateCount)), clock.instant()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestPackedFields.java000066400000000000000000000241441343451174100267240ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.SECONDS; import static org.junit.Assert.assertEquals; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.format.ResolverStyle; import org.junit.Test; /** * Test PackedFields. */ public class TestPackedFields { //----------------------------------------------------------------------- // packedDate() //----------------------------------------------------------------------- @Test public void test_date_basics() { assertEquals("PackedDate", PackedFields.PACKED_DATE.toString()); assertEquals(DAYS, PackedFields.PACKED_DATE.getBaseUnit()); assertEquals(FOREVER, PackedFields.PACKED_DATE.getRangeUnit()); assertEquals(true, PackedFields.PACKED_DATE.isDateBased()); assertEquals(false, PackedFields.PACKED_DATE.isTimeBased()); assertEquals(true, PackedFields.PACKED_DATE.isSupportedBy(LocalDate.of(2015, 3, 12))); assertEquals(false, PackedFields.PACKED_DATE.isSupportedBy(LocalTime.of(11, 30))); assertEquals(10000101, PackedFields.PACKED_DATE.range().getMinimum()); assertEquals(99991231, PackedFields.PACKED_DATE.range().getMaximum()); } @Test(expected = DateTimeException.class) public void test_date_rangeRefinedBy_time() { PackedFields.PACKED_DATE.rangeRefinedBy(LocalTime.of(11, 30)); } @Test public void test_date_getFrom() { assertEquals(20151203, LocalDate.of(2015, 12, 3).get(PackedFields.PACKED_DATE)); assertEquals(10000101, LocalDate.of(1000, 1, 1).get(PackedFields.PACKED_DATE)); assertEquals(99991231, LocalDate.of(9999, 12, 31).get(PackedFields.PACKED_DATE)); } @Test(expected = DateTimeException.class) public void test_date_getFrom_rangeLow() { PackedFields.PACKED_DATE.getFrom(LocalDate.of(999, 12, 31)); } @Test(expected = DateTimeException.class) public void test_date_getFrom_rangeHigh() { PackedFields.PACKED_DATE.getFrom(LocalDate.of(10000, 1, 1)); } @Test public void test_date_adjustInto() { assertEquals(LocalDate.of(2015, 12, 3), LocalDate.MIN.with(PackedFields.PACKED_DATE, 20151203)); } @Test(expected = DateTimeException.class) public void test_date_adjustInto_range() { LocalDate.MIN.with(PackedFields.PACKED_DATE, 1230101); } @Test public void test_date_resolve() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_DATE).toFormatter(); assertEquals(LocalDate.of(2015, 12, 3), LocalDate.parse("20151203", f)); } @Test(expected = DateTimeParseException.class) public void test_date_resolve_invalid_smart() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_DATE).toFormatter(); LocalDate.parse("20151403", f.withResolverStyle(ResolverStyle.SMART)); } @Test public void test_date_resolve_invalid_lenient() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_DATE).toFormatter(); assertEquals(LocalDate.of(2016, 2, 3), LocalDate.parse("20151403", f.withResolverStyle(ResolverStyle.LENIENT))); } //----------------------------------------------------------------------- // packedHourMin() //----------------------------------------------------------------------- @Test public void test_hourMin_basics() { assertEquals("PackedHourMin", PackedFields.PACKED_HOUR_MIN.toString()); assertEquals(MINUTES, PackedFields.PACKED_HOUR_MIN.getBaseUnit()); assertEquals(DAYS, PackedFields.PACKED_HOUR_MIN.getRangeUnit()); assertEquals(false, PackedFields.PACKED_HOUR_MIN.isDateBased()); assertEquals(true, PackedFields.PACKED_HOUR_MIN.isTimeBased()); assertEquals(true, PackedFields.PACKED_HOUR_MIN.isSupportedBy(LocalTime.of(11, 30))); assertEquals(false, PackedFields.PACKED_HOUR_MIN.isSupportedBy(LocalDate.of(2015, 3, 12))); assertEquals(0, PackedFields.PACKED_HOUR_MIN.range().getMinimum()); assertEquals(2359, PackedFields.PACKED_HOUR_MIN.range().getMaximum()); } @Test(expected = DateTimeException.class) public void test_hourMin_rangeRefinedBy_time() { PackedFields.PACKED_HOUR_MIN.rangeRefinedBy(LocalDate.of(2015, 12, 3)); } @Test public void test_hourMin_getFrom() { assertEquals(1130, LocalTime.of(11, 30).get(PackedFields.PACKED_HOUR_MIN)); assertEquals(121, LocalTime.of(1, 21).get(PackedFields.PACKED_HOUR_MIN)); } @Test public void test_hourMin_adjustInto() { assertEquals(LocalTime.of(11, 30), LocalTime.MIDNIGHT.with(PackedFields.PACKED_HOUR_MIN, 1130)); } @Test(expected = DateTimeException.class) public void test_hourMin_adjustInto_value() { LocalDate.MIN.with(PackedFields.PACKED_HOUR_MIN, 1273); } @Test public void test_hourMin_resolve() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_HOUR_MIN).toFormatter(); assertEquals(LocalTime.of(11, 30), LocalTime.parse("1130", f)); } @Test(expected = DateTimeParseException.class) public void test_hourMin_resolve_invalid_smart() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_HOUR_MIN).toFormatter(); LocalTime.parse("1173", f.withResolverStyle(ResolverStyle.SMART)); } @Test public void test_hourMin_resolve_invalid_lenient() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_HOUR_MIN).toFormatter(); assertEquals(LocalTime.of(12, 13), LocalTime.parse("1173", f.withResolverStyle(ResolverStyle.LENIENT))); } //----------------------------------------------------------------------- // packedTime() //----------------------------------------------------------------------- @Test public void test_time_basics() { assertEquals("PackedTime", PackedFields.PACKED_TIME.toString()); assertEquals(SECONDS, PackedFields.PACKED_TIME.getBaseUnit()); assertEquals(DAYS, PackedFields.PACKED_TIME.getRangeUnit()); assertEquals(false, PackedFields.PACKED_TIME.isDateBased()); assertEquals(true, PackedFields.PACKED_TIME.isTimeBased()); assertEquals(true, PackedFields.PACKED_TIME.isSupportedBy(LocalTime.of(11, 30))); assertEquals(false, PackedFields.PACKED_TIME.isSupportedBy(LocalDate.of(2015, 3, 12))); assertEquals(0, PackedFields.PACKED_TIME.range().getMinimum()); assertEquals(235959, PackedFields.PACKED_TIME.range().getMaximum()); } @Test(expected = DateTimeException.class) public void test_time_rangeRefinedBy_time() { PackedFields.PACKED_TIME.rangeRefinedBy(LocalDate.of(2015, 12, 3)); } @Test public void test_time_getFrom() { assertEquals(113052, LocalTime.of(11, 30, 52).get(PackedFields.PACKED_TIME)); assertEquals(113000, LocalTime.of(11, 30).get(PackedFields.PACKED_TIME)); assertEquals(12100, LocalTime.of(1, 21).get(PackedFields.PACKED_TIME)); } @Test public void test_time_adjustInto() { assertEquals(LocalTime.of(11, 30, 52), LocalTime.MIDNIGHT.with(PackedFields.PACKED_TIME, 113052)); } @Test(expected = DateTimeException.class) public void test_time_adjustInto_value() { LocalDate.MIN.with(PackedFields.PACKED_TIME, 127310); } @Test public void test_time_resolve() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_TIME).toFormatter(); assertEquals(LocalTime.of(11, 30, 52), LocalTime.parse("113052", f)); } @Test(expected = DateTimeParseException.class) public void test_time_resolve_invalid_smart() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_TIME).toFormatter(); LocalTime.parse("117361", f.withResolverStyle(ResolverStyle.SMART)); } @Test public void test_time_resolve_invalid_lenient() { DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(PackedFields.PACKED_TIME).toFormatter(); assertEquals(LocalTime.of(12, 14, 1), LocalTime.parse("117361", f.withResolverStyle(ResolverStyle.LENIENT))); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestPeriodDuration.java000066400000000000000000000434231343451174100273370ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.SECONDS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Period; import java.time.format.DateTimeParseException; import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestPeriodDuration { private static final Period P1Y2M3D = Period.of(1, 2, 3); private static final Duration DUR_5 = Duration.ofSeconds(5); private static final Duration DUR_6 = Duration.ofSeconds(6); //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(PeriodDuration.class)); } //----------------------------------------------------------------------- @Test public void test_serialization() throws Exception { PeriodDuration test = PeriodDuration.of(P1Y2M3D, DUR_5); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertEquals(PeriodDuration.ZERO, PeriodDuration.of(Period.ZERO, Duration.ZERO)); assertEquals(PeriodDuration.ZERO, PeriodDuration.of(Period.ZERO)); assertEquals(PeriodDuration.ZERO, PeriodDuration.of(Duration.ZERO)); assertEquals(Period.ZERO, PeriodDuration.ZERO.getPeriod()); assertEquals(Duration.ZERO, PeriodDuration.ZERO.getDuration()); assertEquals(true, PeriodDuration.ZERO.isZero()); assertEquals(Arrays.asList(YEARS, MONTHS, DAYS, SECONDS, NANOS), PeriodDuration.ZERO.getUnits()); assertEquals(0, PeriodDuration.ZERO.get(YEARS)); assertEquals(0, PeriodDuration.ZERO.get(MONTHS)); assertEquals(0, PeriodDuration.ZERO.get(DAYS)); assertEquals(0, PeriodDuration.ZERO.get(SECONDS)); assertEquals(0, PeriodDuration.ZERO.get(NANOS)); } @Test(expected = DateTimeException.class) public void test_ZERO_getEra() { PeriodDuration.ZERO.get(ERAS); } //----------------------------------------------------------------------- @Test public void test_of_both() { PeriodDuration test = PeriodDuration.of(P1Y2M3D, Duration.ofSeconds(4)); assertEquals(P1Y2M3D, test.getPeriod()); assertEquals(Duration.ofSeconds(4), test.getDuration()); assertEquals(false, test.isZero()); assertEquals(1, test.get(YEARS)); assertEquals(2, test.get(MONTHS)); assertEquals(3, test.get(DAYS)); assertEquals(4, test.get(SECONDS)); assertEquals(0, test.get(NANOS)); } @Test public void test_of_period() { PeriodDuration test = PeriodDuration.of(P1Y2M3D); assertEquals(P1Y2M3D, test.getPeriod()); assertEquals(Duration.ZERO, test.getDuration()); assertEquals(false, test.isZero()); assertEquals(1, test.get(YEARS)); assertEquals(2, test.get(MONTHS)); assertEquals(3, test.get(DAYS)); assertEquals(0, test.get(SECONDS)); assertEquals(0, test.get(NANOS)); } @Test public void test_of_duration() { PeriodDuration test = PeriodDuration.of(Duration.ofSeconds(4)); assertEquals(Period.ZERO, test.getPeriod()); assertEquals(Duration.ofSeconds(4), test.getDuration()); assertEquals(false, test.isZero()); assertEquals(0, test.get(YEARS)); assertEquals(0, test.get(MONTHS)); assertEquals(0, test.get(DAYS)); assertEquals(4, test.get(SECONDS)); assertEquals(0, test.get(NANOS)); } //----------------------------------------------------------------------- @Test public void test_between_dates() { PeriodDuration test = PeriodDuration.between(LocalDate.of(2012, 6, 20), LocalDate.of(2012, 8, 25)); assertEquals(Period.between(LocalDate.of(2012, 6, 20), LocalDate.of(2012, 8, 25)), test.getPeriod()); assertEquals(Duration.ZERO, test.getDuration()); } @Test public void test_between_times() { PeriodDuration test = PeriodDuration.between(LocalTime.of(11, 20), LocalTime.of(12, 25)); assertEquals(Period.ZERO, test.getPeriod()); assertEquals(Duration.between(LocalTime.of(11, 20), LocalTime.of(12, 25)), test.getDuration()); } @Test public void test_between_mixed1() { PeriodDuration test = PeriodDuration.between(LocalDate.of(2012, 6, 20), LocalTime.of(11, 25)); assertEquals(Period.ZERO, test.getPeriod()); assertEquals(Duration.ofHours(11).plusMinutes(25), test.getDuration()); } @Test public void test_between_mixed2() { PeriodDuration test = PeriodDuration.between(LocalDate.of(2012, 6, 20), LocalDateTime.of(2012, 7, 22, 11, 25)); assertEquals(Period.of(0, 1, 2), test.getPeriod()); assertEquals(Duration.ofHours(11).plusMinutes(25), test.getDuration()); } //----------------------------------------------------------------------- @Test public void test_from() { assertEquals(PeriodDuration.from(PeriodDuration.of(P1Y2M3D)), PeriodDuration.from(PeriodDuration.of(P1Y2M3D))); assertEquals(PeriodDuration.of(Period.ofYears(2)), PeriodDuration.from(Period.ofYears(2))); assertEquals(PeriodDuration.of(Duration.ofSeconds(2)), PeriodDuration.from(Duration.ofSeconds(2))); assertEquals(PeriodDuration.of(Period.ofYears(2)), PeriodDuration.from(Years.of(2))); assertEquals(PeriodDuration.of(Period.ofMonths(2)), PeriodDuration.from(Months.of(2))); assertEquals(PeriodDuration.of(Period.ofWeeks(2)), PeriodDuration.from(Weeks.of(2))); assertEquals(PeriodDuration.of(Period.ofDays(2)), PeriodDuration.from(Days.of(2))); assertEquals(PeriodDuration.of(Duration.ofHours(2)), PeriodDuration.from(Hours.of(2))); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_valid() { return new Object[][] { {"P1Y2M3W4DT5H6M7S", Period.of(1, 2, 3 * 7 + 4), Duration.ofHours(5).plusMinutes(6).plusSeconds(7)}, {"P3Y", Period.ofYears(3), Duration.ZERO}, {"P3M", Period.ofMonths(3), Duration.ZERO}, {"P3W", Period.ofWeeks(3), Duration.ZERO}, {"P3D", Period.ofDays(3), Duration.ZERO}, {"PT0S", Period.of(0, 0, 0), Duration.ofSeconds(0)}, {"PT1S", Period.of(0, 0, 0), Duration.ofSeconds(1)}, {"PT2S", Period.of(0, 0, 0), Duration.ofSeconds(2)}, {"PT123456789S", Period.of(0, 0, 0), Duration.ofSeconds(123456789)}, {"PT+0S", Period.of(0, 0, 0), Duration.ofSeconds(0)}, {"PT+2S", Period.of(0, 0, 0), Duration.ofSeconds(2)}, {"PT-0S", Period.of(0, 0, 0), Duration.ofSeconds(0)}, {"PT-2S", Period.of(0, 0, 0), Duration.ofSeconds(-2)}, {"P+0M", Period.of(0, 0, 0), Duration.ZERO}, {"P+2M", Period.of(0, 2, 0), Duration.ZERO}, {"P-0M", Period.of(0, 0, 0), Duration.ZERO}, {"P-2M", Period.of(0, -2, 0), Duration.ZERO}, }; } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid(String str, Period period, Duration duration) { assertEquals(PeriodDuration.of(period, duration), PeriodDuration.parse(str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialPlus(String str, Period period, Duration duration) { assertEquals(PeriodDuration.of(period, duration), PeriodDuration.parse("+" + str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialMinus(String str, Period period, Duration duration) { assertEquals(PeriodDuration.of(period, duration).negated(), PeriodDuration.parse("-" + str)); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3Q"}, {"P1M2Y"}, {"3"}, {"-3"}, {"3H"}, {"-3H"}, {"P3"}, {"P-3"}, {"PH"}, {"T"}, {"T3H"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { PeriodDuration.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { PeriodDuration.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_PeriodDuration() { PeriodDuration test = PeriodDuration.of(P1Y2M3D, DUR_5); assertEquals(PeriodDuration.of(Period.of(4, 4, 4), DUR_5), test.plus(Period.of(3, 2, 1))); assertEquals(PeriodDuration.of(P1Y2M3D, Duration.ofSeconds(9)), test.plus(Duration.ofSeconds(4))); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { PeriodDuration.of(Period.of(Integer.MAX_VALUE - 1, 0, 0)).plus(PeriodDuration.of(Period.ofYears(2))); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { PeriodDuration.of(Period.of(Integer.MIN_VALUE + 1, 0, 0)).plus(PeriodDuration.of(Period.ofYears(-2))); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { P1Y2M3D.plus(null); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_PeriodDuration() { PeriodDuration test = PeriodDuration.of(P1Y2M3D, DUR_5); assertEquals(PeriodDuration.of(Period.of(0, 1, 2), DUR_5), test.minus(Period.of(1, 1, 1))); assertEquals(PeriodDuration.of(P1Y2M3D, Duration.ofSeconds(1)), test.minus(Duration.ofSeconds(4))); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { PeriodDuration.of(Period.of(Integer.MAX_VALUE - 1, 0, 0)).minus(PeriodDuration.of(Period.ofYears(-2))); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { PeriodDuration.of(Period.of(Integer.MIN_VALUE + 1, 0, 0)).minus(PeriodDuration.of(Period.ofYears(2))); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { P1Y2M3D.minus(null); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { PeriodDuration test = PeriodDuration.of(P1Y2M3D, DUR_5); assertEquals(PeriodDuration.ZERO, test.multipliedBy(0)); assertEquals(test, test.multipliedBy(1)); assertEquals(PeriodDuration.of(Period.of(5, 10, 15), Duration.ofSeconds(25)), test.multipliedBy(5)); assertEquals(PeriodDuration.of(Period.of(-3, -6, -9), Duration.ofSeconds(-15)), test.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { PeriodDuration.of(Period.ofYears(Integer.MAX_VALUE / 2 + 1)).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { PeriodDuration.of(Period.ofYears(Integer.MIN_VALUE / 2 - 1)).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(PeriodDuration.of(P1Y2M3D.negated(), DUR_5.negated()), PeriodDuration.of(P1Y2M3D, DUR_5).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { PeriodDuration.of(Duration.ofSeconds(Long.MIN_VALUE)).negated(); } //----------------------------------------------------------------------- @Test public void test_normalizedYears() { assertEquals(PeriodDuration.of(P1Y2M3D.normalized(), DUR_5), PeriodDuration.of(P1Y2M3D, DUR_5).normalizedYears()); } //----------------------------------------------------------------------- @Test public void test_normalizedStandardDays() { assertEquals( PeriodDuration.of(P1Y2M3D, Duration.ofHours(5)), PeriodDuration.of(P1Y2M3D, Duration.ofHours(5)).normalizedStandardDays()); assertEquals( PeriodDuration.of(P1Y2M3D.plusDays(1), Duration.ofHours(1)), PeriodDuration.of(P1Y2M3D, Duration.ofHours(25)).normalizedStandardDays()); assertEquals( PeriodDuration.of(P1Y2M3D.plusDays(-3), Duration.ofHours(-1)), PeriodDuration.of(P1Y2M3D, Duration.ofHours(-73)).normalizedStandardDays()); } @Test(expected = ArithmeticException.class) public void test_normalizedStandardDaysn_overflow() { PeriodDuration.of(Duration.ofSeconds(Long.MIN_VALUE)).normalizedStandardDays(); } //----------------------------------------------------------------------- @Test public void test_addTo() { LocalDateTime base = LocalDateTime.of(2012, 6, 20, 11, 30, 0); assertEquals(LocalDateTime.of(2013, 8, 23, 11, 30, 5), PeriodDuration.of(P1Y2M3D, DUR_5).addTo(base)); } //----------------------------------------------------------------------- @Test public void test_subtractFrom() { LocalDateTime base = LocalDateTime.of(2012, 6, 20, 11, 30, 0); assertEquals(LocalDateTime.of(2011, 4, 17, 11, 29, 55), PeriodDuration.of(P1Y2M3D, DUR_5).subtractFrom(base)); } //----------------------------------------------------------------------- @Test public void test_equals() { PeriodDuration test5 = PeriodDuration.of(P1Y2M3D, DUR_5); PeriodDuration test6 = PeriodDuration.of(P1Y2M3D, DUR_6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { PeriodDuration test = PeriodDuration.of(P1Y2M3D, DUR_5); assertEquals(false, test.equals(null)); } @Test public void test_equals_otherClass() { PeriodDuration test = PeriodDuration.of(P1Y2M3D, DUR_5); assertEquals(false, test.equals("")); } //----------------------------------------------------------------------- @Test public void test_hashCode() { PeriodDuration test5 = PeriodDuration.of(P1Y2M3D, DUR_5); PeriodDuration test6 = PeriodDuration.of(P1Y2M3D, DUR_6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { assertEquals("P1Y2M3DT5S", PeriodDuration.of(P1Y2M3D, DUR_5).toString()); assertEquals("P1Y2M3D", PeriodDuration.of(P1Y2M3D, Duration.ZERO).toString()); assertEquals("PT5S", PeriodDuration.of(Period.ZERO, DUR_5).toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestQuarter.java000066400000000000000000000422661343451174100260360ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_DAY; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.IsoFields.QUARTER_OF_YEAR; import static java.time.temporal.IsoFields.QUARTER_YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.threeten.extra.Quarter.Q3; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.TextStyle; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.Locale; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test Quarter. */ @RunWith(DataProviderRunner.class) public class TestQuarter { //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Enum.class.isAssignableFrom(Quarter.class)); assertTrue(Serializable.class.isAssignableFrom(Quarter.class)); assertTrue(Comparable.class.isAssignableFrom(Quarter.class)); assertTrue(TemporalAccessor.class.isAssignableFrom(Quarter.class)); } //----------------------------------------------------------------------- // of(int) //----------------------------------------------------------------------- @Test public void test_of_int_singleton() { for (int i = 1; i <= 4; i++) { Quarter test = Quarter.of(i); assertEquals(i, test.getValue()); } } @Test(expected = DateTimeException.class) public void test_of_int_valueTooLow() { Quarter.of(0); } @Test(expected = DateTimeException.class) public void test_of_int_valueTooHigh() { Quarter.of(5); } //----------------------------------------------------------------------- // ofMonth(int) //----------------------------------------------------------------------- @Test public void test_ofMonth_int_singleton() { assertSame(Quarter.Q1, Quarter.ofMonth(1)); assertSame(Quarter.Q1, Quarter.ofMonth(2)); assertSame(Quarter.Q1, Quarter.ofMonth(3)); assertSame(Quarter.Q2, Quarter.ofMonth(4)); assertSame(Quarter.Q2, Quarter.ofMonth(5)); assertSame(Quarter.Q2, Quarter.ofMonth(6)); assertSame(Quarter.Q3, Quarter.ofMonth(7)); assertSame(Quarter.Q3, Quarter.ofMonth(8)); assertSame(Quarter.Q3, Quarter.ofMonth(9)); assertSame(Quarter.Q4, Quarter.ofMonth(10)); assertSame(Quarter.Q4, Quarter.ofMonth(11)); assertSame(Quarter.Q4, Quarter.ofMonth(12)); } @Test(expected = DateTimeException.class) public void test_ofMonth_int_valueTooLow() { Quarter.ofMonth(0); } @Test(expected = DateTimeException.class) public void test_ofMonth_int_valueTooHigh() { Quarter.ofMonth(13); } //----------------------------------------------------------------------- // from(TemporalAccessor) //----------------------------------------------------------------------- @Test public void test_from_TemporalAccessor() { assertEquals(Quarter.Q2, Quarter.from(LocalDate.of(2011, 6, 6))); assertEquals(Quarter.Q1, Quarter.from(LocalDateTime.of(2012, 2, 3, 12, 30))); } @Test public void test_from_TemporalAccessor_Month() { assertEquals(Quarter.Q1, Quarter.from(Month.JANUARY)); assertEquals(Quarter.Q1, Quarter.from(Month.FEBRUARY)); assertEquals(Quarter.Q1, Quarter.from(Month.MARCH)); assertEquals(Quarter.Q2, Quarter.from(Month.APRIL)); assertEquals(Quarter.Q2, Quarter.from(Month.MAY)); assertEquals(Quarter.Q2, Quarter.from(Month.JUNE)); assertEquals(Quarter.Q3, Quarter.from(Month.JULY)); assertEquals(Quarter.Q3, Quarter.from(Month.AUGUST)); assertEquals(Quarter.Q3, Quarter.from(Month.SEPTEMBER)); assertEquals(Quarter.Q4, Quarter.from(Month.OCTOBER)); assertEquals(Quarter.Q4, Quarter.from(Month.NOVEMBER)); assertEquals(Quarter.Q4, Quarter.from(Month.DECEMBER)); } @Test(expected = DateTimeException.class) public void test_from_TemporalAccessorl_invalid_noDerive() { Quarter.from(LocalTime.of(12, 30)); } @Test(expected = NullPointerException.class) public void test_from_TemporalAccessor_null() { Quarter.from((TemporalAccessor) null); } @Test public void test_from_parse_CharSequence() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("'Q'Q"); assertEquals(Q3, formatter.parse("Q3", Quarter::from)); } //----------------------------------------------------------------------- // getDisplayName() //----------------------------------------------------------------------- @Test public void test_getDisplayName() { assertEquals("Q1", Quarter.Q1.getDisplayName(TextStyle.SHORT, Locale.US)); } @Test(expected = NullPointerException.class) public void test_getDisplayName_nullStyle() { Quarter.Q1.getDisplayName(null, Locale.US); } @Test(expected = NullPointerException.class) public void test_getDisplayName_nullLocale() { Quarter.Q1.getDisplayName(TextStyle.FULL, null); } //----------------------------------------------------------------------- // isSupported() //----------------------------------------------------------------------- @Test public void test_isSupported() { Quarter test = Quarter.Q1; assertEquals(false, test.isSupported(null)); assertEquals(false, test.isSupported(NANO_OF_SECOND)); assertEquals(false, test.isSupported(NANO_OF_DAY)); assertEquals(false, test.isSupported(MICRO_OF_SECOND)); assertEquals(false, test.isSupported(MICRO_OF_DAY)); assertEquals(false, test.isSupported(MILLI_OF_SECOND)); assertEquals(false, test.isSupported(MILLI_OF_DAY)); assertEquals(false, test.isSupported(SECOND_OF_MINUTE)); assertEquals(false, test.isSupported(SECOND_OF_DAY)); assertEquals(false, test.isSupported(MINUTE_OF_HOUR)); assertEquals(false, test.isSupported(MINUTE_OF_DAY)); assertEquals(false, test.isSupported(HOUR_OF_AMPM)); assertEquals(false, test.isSupported(CLOCK_HOUR_OF_AMPM)); assertEquals(false, test.isSupported(HOUR_OF_DAY)); assertEquals(false, test.isSupported(CLOCK_HOUR_OF_DAY)); assertEquals(false, test.isSupported(AMPM_OF_DAY)); assertEquals(false, test.isSupported(DAY_OF_WEEK)); assertEquals(false, test.isSupported(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(false, test.isSupported(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(false, test.isSupported(DAY_OF_MONTH)); assertEquals(false, test.isSupported(DAY_OF_YEAR)); assertEquals(false, test.isSupported(EPOCH_DAY)); assertEquals(false, test.isSupported(ALIGNED_WEEK_OF_MONTH)); assertEquals(false, test.isSupported(ALIGNED_WEEK_OF_YEAR)); assertEquals(false, test.isSupported(MONTH_OF_YEAR)); assertEquals(false, test.isSupported(PROLEPTIC_MONTH)); assertEquals(false, test.isSupported(YEAR_OF_ERA)); assertEquals(false, test.isSupported(YEAR)); assertEquals(false, test.isSupported(ERA)); assertEquals(false, test.isSupported(INSTANT_SECONDS)); assertEquals(false, test.isSupported(OFFSET_SECONDS)); assertEquals(true, test.isSupported(QUARTER_OF_YEAR)); } //----------------------------------------------------------------------- // range() //----------------------------------------------------------------------- @Test public void test_range() { assertEquals(QUARTER_OF_YEAR.range(), Quarter.Q1.range(QUARTER_OF_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_invalidField() { Quarter.Q1.range(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_range_null() { Quarter.Q1.range(null); } //----------------------------------------------------------------------- // get() //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(1, Quarter.Q1.get(QUARTER_OF_YEAR)); assertEquals(2, Quarter.Q2.get(QUARTER_OF_YEAR)); assertEquals(3, Quarter.Q3.get(QUARTER_OF_YEAR)); assertEquals(4, Quarter.Q4.get(QUARTER_OF_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_get_invalidField() { Quarter.Q2.get(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_get_null() { Quarter.Q2.get(null); } //----------------------------------------------------------------------- // getLong() //----------------------------------------------------------------------- @Test public void test_getLong() { assertEquals(1, Quarter.Q1.getLong(QUARTER_OF_YEAR)); assertEquals(2, Quarter.Q2.getLong(QUARTER_OF_YEAR)); assertEquals(3, Quarter.Q3.getLong(QUARTER_OF_YEAR)); assertEquals(4, Quarter.Q4.getLong(QUARTER_OF_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField() { Quarter.Q2.getLong(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_getLong_null() { Quarter.Q2.getLong(null); } //----------------------------------------------------------------------- // plus(long), plus(long,unit) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {1, -5, 4}, {1, -4, 1}, {1, -3, 2}, {1, -2, 3}, {1, -1, 4}, {1, 0, 1}, {1, 1, 2}, {1, 2, 3}, {1, 3, 4}, {1, 4, 1}, {1, 5, 2}, }; } @Test @UseDataProvider("data_plus") public void test_plus_long(int base, long amount, int expected) { assertEquals(Quarter.of(expected), Quarter.of(base).plus(amount)); } //----------------------------------------------------------------------- // minus(long), minus(long,unit) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_minus() { return new Object[][] { {1, -5, 2}, {1, -4, 1}, {1, -3, 4}, {1, -2, 3}, {1, -1, 2}, {1, 0, 1}, {1, 1, 4}, {1, 2, 3}, {1, 3, 2}, {1, 4, 1}, {1, 5, 4}, }; } @Test @UseDataProvider("data_minus") public void test_minus_long(int base, long amount, int expected) { assertEquals(Quarter.of(expected), Quarter.of(base).minus(amount)); } //----------------------------------------------------------------------- // length(boolean) //----------------------------------------------------------------------- @Test public void test_length_boolean() { assertEquals(91, Quarter.Q1.length(true)); assertEquals(90, Quarter.Q1.length(false)); assertEquals(91, Quarter.Q2.length(true)); assertEquals(91, Quarter.Q2.length(false)); assertEquals(92, Quarter.Q3.length(true)); assertEquals(92, Quarter.Q3.length(false)); assertEquals(92, Quarter.Q4.length(true)); assertEquals(92, Quarter.Q4.length(false)); } //----------------------------------------------------------------------- // firstMonth() //----------------------------------------------------------------------- @Test public void test_firstMonth() { assertEquals(Month.JANUARY, Quarter.Q1.firstMonth()); assertEquals(Month.APRIL, Quarter.Q2.firstMonth()); assertEquals(Month.JULY, Quarter.Q3.firstMonth()); assertEquals(Month.OCTOBER, Quarter.Q4.firstMonth()); } //----------------------------------------------------------------------- // query() //----------------------------------------------------------------------- @Test public void test_query() { assertEquals(IsoChronology.INSTANCE, Quarter.Q1.query(TemporalQueries.chronology())); assertEquals(null, Quarter.Q1.query(TemporalQueries.localDate())); assertEquals(null, Quarter.Q1.query(TemporalQueries.localTime())); assertEquals(null, Quarter.Q1.query(TemporalQueries.offset())); assertEquals(QUARTER_YEARS, Quarter.Q1.query(TemporalQueries.precision())); assertEquals(null, Quarter.Q1.query(TemporalQueries.zone())); assertEquals(null, Quarter.Q1.query(TemporalQueries.zoneId())); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { assertEquals("Q1", Quarter.Q1.toString()); assertEquals("Q2", Quarter.Q2.toString()); assertEquals("Q3", Quarter.Q3.toString()); assertEquals("Q4", Quarter.Q4.toString()); } //----------------------------------------------------------------------- // generated methods //----------------------------------------------------------------------- @Test public void test_enum() { assertEquals(Quarter.Q4, Quarter.valueOf("Q4")); assertEquals(Quarter.Q1, Quarter.values()[0]); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestSeconds.java000066400000000000000000000440601343451174100260030ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Duration; import java.time.LocalTime; import java.time.format.DateTimeParseException; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestSeconds { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Seconds.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Seconds test = Seconds.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Seconds.of(0), Seconds.ZERO); assertEquals(Seconds.of(0), Seconds.ZERO); assertEquals(0, Seconds.ZERO.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(0, Seconds.of(0).getAmount()); assertEquals(1, Seconds.of(1).getAmount()); assertEquals(2, Seconds.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Seconds.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Seconds.of(-1).getAmount()); assertEquals(-2, Seconds.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Seconds.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @Test public void test_ofHours() { assertEquals(0, Seconds.ofHours(0).getAmount()); assertEquals(3600, Seconds.ofHours(1).getAmount()); assertEquals(7200, Seconds.ofHours(2).getAmount()); assertEquals((Integer.MAX_VALUE / 3600) * 3600, Seconds.ofHours(Integer.MAX_VALUE / 3600).getAmount()); assertEquals(-3600, Seconds.ofHours(-1).getAmount()); assertEquals(-7200, Seconds.ofHours(-2).getAmount()); assertEquals((Integer.MIN_VALUE / 3600) * 3600, Seconds.ofHours(Integer.MIN_VALUE / 3600).getAmount()); } @Test(expected = ArithmeticException.class) public void test_ofHours_overflow() { Seconds.ofHours((Integer.MAX_VALUE / 3600) + 3600); } //----------------------------------------------------------------------- @Test public void test_ofMinutes() { assertEquals(0, Seconds.ofMinutes(0).getAmount()); assertEquals(60, Seconds.ofMinutes(1).getAmount()); assertEquals(120, Seconds.ofMinutes(2).getAmount()); assertEquals((Integer.MAX_VALUE / 60) * 60, Seconds.ofMinutes(Integer.MAX_VALUE / 60).getAmount()); assertEquals(-60, Seconds.ofMinutes(-1).getAmount()); assertEquals(-120, Seconds.ofMinutes(-2).getAmount()); assertEquals((Integer.MIN_VALUE / 60) * 60, Seconds.ofMinutes(Integer.MIN_VALUE / 60).getAmount()); } @Test(expected = ArithmeticException.class) public void test_ofMinutes_overflow() { Seconds.ofMinutes((Integer.MAX_VALUE / 60) + 60); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_valid() { return new Object[][] { {"PT0S", 0}, {"PT1S", 1}, {"PT2S", 2}, {"PT123456789S", 123456789}, {"PT+0S", 0}, {"PT+2S", 2}, {"PT-0S", 0}, {"PT-2S", -2}, {"PT0M", 0}, {"PT1M", 60}, {"PT2M", 120}, {"PT1234M", 1234 * 60}, {"PT+0M", 0}, {"PT+2M", 120}, {"PT-0M", 0}, {"PT-2M", -120}, {"PT0H", 0}, {"PT1H", 60 * 60}, {"PT2H", 120 * 60}, {"PT1234H", 1234 * 60 * 60}, {"PT+0H", 0}, {"PT+2H", 120 * 60}, {"PT-0H", 0}, {"PT-2H", -120 * 60}, {"P0D", 0}, {"P1D", 60 * 60 * 24}, {"P2D", 120 * 60 * 24}, {"P1234D", 1234 * 60 * 60 * 24}, {"P+0D", 0}, {"P+2D", 120 * 60 * 24}, {"P-0D", 0}, {"P-2D", -120 * 60 * 24}, {"PT0M0S", 0}, {"PT2M3S", 2 * 60 + 3}, {"PT+2M3S", 2 * 60 + 3}, {"PT2M+3S", 2 * 60 + 3}, {"PT-2M3S", -2 * 60 + 3}, {"PT2M-3S", 2 * 60 - 3}, {"PT-2M-3S", -2 * 60 - 3}, {"PT0H0S", 0}, {"PT2H3S", 2 * 3600 + 3}, {"PT+2H3S", 2 * 3600 + 3}, {"PT2H+3S", 2 * 3600 + 3}, {"PT-2H3S", -2 * 3600 + 3}, {"PT2H-3S", 2 * 3600 - 3}, {"PT-2H-3S", -2 * 3600 - 3}, {"P0DT0H0M0S", 0}, {"P5DT2H4M3S", 5 * 86400 + 2 * 3600 + 4 * 60 + 3}, }; } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid(String str, int expectedSeconds) { assertEquals(Seconds.of(expectedSeconds), Seconds.parse(str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialPlus(String str, int expectedSeconds) { assertEquals(Seconds.of(expectedSeconds), Seconds.parse("+" + str)); } @Test @UseDataProvider("data_valid") public void test_parse_CharSequence_valid_initialMinus(String str, int expectedSeconds) { assertEquals(Seconds.of(-expectedSeconds), Seconds.parse("-" + str)); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3W"}, {"P3Q"}, {"P1M2Y"}, {"3"}, {"-3"}, {"3S"}, {"-3S"}, {"P3S"}, {"P3"}, {"P-3"}, {"PS"}, {"T3"}, {"PT3"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Seconds.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Seconds.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Seconds() { Seconds test5 = Seconds.of(5); assertEquals(Seconds.of(5), test5.plus(Seconds.of(0))); assertEquals(Seconds.of(7), test5.plus(Seconds.of(2))); assertEquals(Seconds.of(3), test5.plus(Seconds.of(-2))); assertEquals(Seconds.of(Integer.MAX_VALUE), Seconds.of(Integer.MAX_VALUE - 1).plus(Seconds.of(1))); assertEquals(Seconds.of(Integer.MIN_VALUE), Seconds.of(Integer.MIN_VALUE + 1).plus(Seconds.of(-1))); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Seconds.of(Integer.MAX_VALUE - 1).plus(Seconds.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Seconds.of(Integer.MIN_VALUE + 1).plus(Seconds.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Seconds.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Seconds test5 = Seconds.of(5); assertEquals(Seconds.of(5), test5.plus(0)); assertEquals(Seconds.of(7), test5.plus(2)); assertEquals(Seconds.of(3), test5.plus(-2)); assertEquals(Seconds.of(Integer.MAX_VALUE), Seconds.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Seconds.of(Integer.MIN_VALUE), Seconds.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Seconds.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Seconds.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Seconds() { Seconds test5 = Seconds.of(5); assertEquals(Seconds.of(5), test5.minus(Seconds.of(0))); assertEquals(Seconds.of(3), test5.minus(Seconds.of(2))); assertEquals(Seconds.of(7), test5.minus(Seconds.of(-2))); assertEquals(Seconds.of(Integer.MAX_VALUE), Seconds.of(Integer.MAX_VALUE - 1).minus(Seconds.of(-1))); assertEquals(Seconds.of(Integer.MIN_VALUE), Seconds.of(Integer.MIN_VALUE + 1).minus(Seconds.of(1))); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Seconds.of(Integer.MAX_VALUE - 1).minus(Seconds.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Seconds.of(Integer.MIN_VALUE + 1).minus(Seconds.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Seconds.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Seconds test5 = Seconds.of(5); assertEquals(Seconds.of(5), test5.minus(0)); assertEquals(Seconds.of(3), test5.minus(2)); assertEquals(Seconds.of(7), test5.minus(-2)); assertEquals(Seconds.of(Integer.MAX_VALUE), Seconds.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Seconds.of(Integer.MIN_VALUE), Seconds.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Seconds.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Seconds.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Seconds test5 = Seconds.of(5); assertEquals(Seconds.of(0), test5.multipliedBy(0)); assertEquals(Seconds.of(5), test5.multipliedBy(1)); assertEquals(Seconds.of(10), test5.multipliedBy(2)); assertEquals(Seconds.of(15), test5.multipliedBy(3)); assertEquals(Seconds.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Seconds test5 = Seconds.of(5); assertEquals(Seconds.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Seconds.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Seconds.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Seconds test12 = Seconds.of(12); assertEquals(Seconds.of(12), test12.dividedBy(1)); assertEquals(Seconds.of(6), test12.dividedBy(2)); assertEquals(Seconds.of(4), test12.dividedBy(3)); assertEquals(Seconds.of(3), test12.dividedBy(4)); assertEquals(Seconds.of(2), test12.dividedBy(5)); assertEquals(Seconds.of(2), test12.dividedBy(6)); assertEquals(Seconds.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Seconds test12 = Seconds.of(12); assertEquals(Seconds.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Seconds.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Seconds.of(0), Seconds.of(0).negated()); assertEquals(Seconds.of(-12), Seconds.of(12).negated()); assertEquals(Seconds.of(12), Seconds.of(-12).negated()); assertEquals(Seconds.of(-Integer.MAX_VALUE), Seconds.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Seconds.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Seconds.of(0), Seconds.of(0).abs()); assertEquals(Seconds.of(12), Seconds.of(12).abs()); assertEquals(Seconds.of(12), Seconds.of(-12).abs()); assertEquals(Seconds.of(Integer.MAX_VALUE), Seconds.of(Integer.MAX_VALUE).abs()); assertEquals(Seconds.of(Integer.MAX_VALUE), Seconds.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Seconds.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { LocalTime base = LocalTime.of(11, 30); assertEquals(LocalTime.of(11, 30), Seconds.of(0).addTo(base)); assertEquals(LocalTime.of(11, 30, 6), Seconds.of(6).addTo(base)); } //----------------------------------------------------------------------- @Test public void test_subtractFrom() { LocalTime base = LocalTime.of(11, 30); assertEquals(LocalTime.of(11, 30), Seconds.of(0).subtractFrom(base)); assertEquals(LocalTime.of(11, 29, 54), Seconds.of(6).subtractFrom(base)); } //----------------------------------------------------------------------- @Test public void test_toDuration() { for (int i = -20; i < 20; i++) { assertEquals(Duration.ofSeconds(i), Seconds.of(i).toDuration()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Seconds test5 = Seconds.of(5); Seconds test6 = Seconds.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Seconds test5 = Seconds.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Seconds test5 = Seconds.of(5); Seconds test6 = Seconds.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Seconds test5 = Seconds.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Seconds test5 = Seconds.of(5); assertEquals(false, test5.equals("")); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Seconds test5 = Seconds.of(5); Seconds test6 = Seconds.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Seconds test5 = Seconds.of(5); assertEquals("PT5S", test5.toString()); Seconds testM1 = Seconds.of(-1); assertEquals("PT-1S", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestTemporals.java000066400000000000000000000644121343451174100263560ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.DayOfWeek.FRIDAY; import static java.time.DayOfWeek.MONDAY; import static java.time.DayOfWeek.SATURDAY; import static java.time.DayOfWeek.SUNDAY; import static java.time.Month.DECEMBER; import static java.time.Month.JANUARY; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.HALF_DAYS; import static java.time.temporal.ChronoUnit.HOURS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.SECONDS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static java.time.temporal.IsoFields.QUARTER_YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; import java.time.Month; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test Temporals. */ @RunWith(DataProviderRunner.class) public class TestTemporals { //----------------------------------------------------------------------- // nextWorkingDay() //----------------------------------------------------------------------- @Test public void test_nextWorkingDay_serialization() throws IOException, ClassNotFoundException { TemporalAdjuster test = Temporals.nextWorkingDay(); assertTrue(test instanceof Serializable); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } @Test public void test_nextWorkingDay() { for (Month month : Month.values()) { for (int i = 1; i <= month.length(false); i++) { LocalDate date = LocalDate.of(2007, month, i); LocalDate test = (LocalDate) Temporals.nextWorkingDay().adjustInto(date); assertTrue(test.isAfter(date)); assertFalse(test.getDayOfWeek().equals(SATURDAY)); assertFalse(test.getDayOfWeek().equals(SUNDAY)); switch (date.getDayOfWeek()) { case FRIDAY: case SATURDAY: assertEquals(MONDAY, test.getDayOfWeek()); break; default: assertEquals(date.getDayOfWeek().plus(1), test.getDayOfWeek()); } if (test.getYear() == 2007) { int dayDiff = test.getDayOfYear() - date.getDayOfYear(); switch (date.getDayOfWeek()) { case FRIDAY: assertEquals(3, dayDiff); break; case SATURDAY: assertEquals(2, dayDiff); break; default: assertEquals(1, dayDiff); } } else { assertEquals(2008, test.getYear()); assertEquals(JANUARY, test.getMonth()); assertEquals(1, test.getDayOfMonth()); } } } } @Test public void test_nextWorkingDay_yearChange() { LocalDate friday = LocalDate.of(2010, DECEMBER, 31); Temporal test = Temporals.nextWorkingDay().adjustInto(friday); assertEquals(LocalDate.of(2011, JANUARY, 3), test); LocalDate saturday = LocalDate.of(2011, DECEMBER, 31); test = Temporals.nextWorkingDay().adjustInto(saturday); assertEquals(LocalDate.of(2012, JANUARY, 2), test); } //----------------------------------------------------------------------- // nextWorkingDayOrSame() //----------------------------------------------------------------------- @Test public void test_nextWorkingDayOrSame_serialization() throws IOException, ClassNotFoundException { TemporalAdjuster test = Temporals.nextWorkingDayOrSame(); assertTrue(test instanceof Serializable); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(ois.readObject(), test); } } @Test public void test_nextWorkingDayOrSame() { for (Month month : Month.values()) { for (int i = 1; i <= month.length(false); i++) { LocalDate date = LocalDate.of(2007, month, i); LocalDate test = (LocalDate) Temporals.nextWorkingDayOrSame().adjustInto(date); assertFalse(test.getDayOfWeek().equals(SATURDAY)); assertFalse(test.getDayOfWeek().equals(SUNDAY)); switch (date.getDayOfWeek()) { case SATURDAY: case SUNDAY: assertEquals(test.getDayOfWeek(), MONDAY); break; default: assertEquals(date.getDayOfWeek(), test.getDayOfWeek()); } if (test.getYear() == 2007) { int dayDiff = test.getDayOfYear() - date.getDayOfYear(); switch (date.getDayOfWeek()) { case SATURDAY: assertEquals(dayDiff, 2); break; case SUNDAY: assertEquals(dayDiff, 1); break; default: assertEquals(dayDiff, 0); } } else { assertEquals(test.getYear(), 2008); assertEquals(test.getMonth(), JANUARY); assertEquals(test.getDayOfMonth(), 1); } } } } @Test public void test_nextWorkingDayOrSame_yearChange() { LocalDate saturday = LocalDate.of(2016, DECEMBER, 31); Temporal test = Temporals.nextWorkingDayOrSame().adjustInto(saturday); assertEquals(LocalDate.of(2017, JANUARY, 2), test); LocalDate sunday = LocalDate.of(2017, DECEMBER, 31); test = Temporals.nextWorkingDayOrSame().adjustInto(sunday); assertEquals(LocalDate.of(2018, JANUARY, 1), test); } //----------------------------------------------------------------------- // previousWorkingDay() //----------------------------------------------------------------------- @Test public void test_previousWorkingDay_serialization() throws IOException, ClassNotFoundException { TemporalAdjuster test = Temporals.previousWorkingDay(); assertTrue(test instanceof Serializable); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } @Test public void test_previousWorkingDay() { for (Month month : Month.values()) { for (int i = 1; i <= month.length(false); i++) { LocalDate date = LocalDate.of(2007, month, i); LocalDate test = (LocalDate) Temporals.previousWorkingDay().adjustInto(date); assertTrue(test.isBefore(date)); assertFalse(test.getDayOfWeek().equals(SATURDAY)); assertFalse(test.getDayOfWeek().equals(SUNDAY)); switch (date.getDayOfWeek()) { case MONDAY: case SUNDAY: assertEquals(FRIDAY, test.getDayOfWeek()); break; default: assertEquals(date.getDayOfWeek().minus(1), test.getDayOfWeek()); } if (test.getYear() == 2007) { int dayDiff = test.getDayOfYear() - date.getDayOfYear(); switch (date.getDayOfWeek()) { case MONDAY: assertEquals(-3, dayDiff); break; case SUNDAY: assertEquals(-2, dayDiff); break; default: assertEquals(-1, dayDiff); } } else { assertEquals(2006, test.getYear()); assertEquals(DECEMBER, test.getMonth()); assertEquals(29, test.getDayOfMonth()); } } } } @Test public void test_previousWorkingDay_yearChange() { LocalDate monday = LocalDate.of(2011, JANUARY, 3); Temporal test = Temporals.previousWorkingDay().adjustInto(monday); assertEquals(LocalDate.of(2010, DECEMBER, 31), test); LocalDate sunday = LocalDate.of(2011, JANUARY, 2); test = Temporals.previousWorkingDay().adjustInto(sunday); assertEquals(LocalDate.of(2010, DECEMBER, 31), test); } //----------------------------------------------------------------------- // previousWorkingDayOrSame() //----------------------------------------------------------------------- @Test public void test_previousWorkingDayOrSame_serialization() throws IOException, ClassNotFoundException { TemporalAdjuster test = Temporals.previousWorkingDayOrSame(); assertTrue(test instanceof Serializable); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(ois.readObject(), test); } } @Test public void test_previousWorkingDayOrSame() { for (Month month : Month.values()) { for (int i = 1; i <= month.length(false); i++) { LocalDate date = LocalDate.of(2007, month, i); LocalDate test = (LocalDate) Temporals.previousWorkingDayOrSame().adjustInto(date); assertFalse(test.getDayOfWeek().equals(SATURDAY)); assertFalse(test.getDayOfWeek().equals(SUNDAY)); switch (date.getDayOfWeek()) { case SATURDAY: case SUNDAY: assertEquals(test.getDayOfWeek(), FRIDAY); break; default: assertEquals(date.getDayOfWeek(), test.getDayOfWeek()); } if (test.getYear() == 2007) { int dayDiff = test.getDayOfYear() - date.getDayOfYear(); switch (date.getDayOfWeek()) { case SATURDAY: assertEquals(dayDiff, -1); break; case SUNDAY: assertEquals(dayDiff, -2); break; default: assertEquals(dayDiff, 0); } } else { assertEquals(test.getYear(), 2006); assertEquals(test.getMonth(), DECEMBER); assertEquals(test.getDayOfMonth(), 29); } } } } @Test public void test_previousWorkingDayOrSame_yearChange() { LocalDate sunday = LocalDate.of(2011, JANUARY, 2); Temporal test = Temporals.previousWorkingDayOrSame().adjustInto(sunday); assertEquals(test, LocalDate.of(2010, DECEMBER, 31)); LocalDate saturday = LocalDate.of(2011, JANUARY, 1); test = Temporals.previousWorkingDayOrSame().adjustInto(saturday); assertEquals(test, LocalDate.of(2010, DECEMBER, 31)); } //----------------------------------------------------------------------- // parseFirstMatching() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_parseFirstMatching() { return new Object[][] { {"2016-09-06", DateTimeFormatter.ISO_LOCAL_DATE, DateTimeFormatter.BASIC_ISO_DATE}, {"20160906", DateTimeFormatter.ISO_LOCAL_DATE, DateTimeFormatter.BASIC_ISO_DATE}, }; } @Test @UseDataProvider("data_parseFirstMatching") public void test_parseFirstMatching(String text, DateTimeFormatter fmt1, DateTimeFormatter fmt2) { assertEquals(LocalDate.of(2016, 9, 6), Temporals.parseFirstMatching(text, LocalDate::from, fmt1, fmt2)); } @Test(expected = DateTimeParseException.class) public void test_parseFirstMatching_zero() { Temporals.parseFirstMatching("2016-09-06", LocalDate::from); } @Test public void test_parseFirstMatching_one() { assertEquals(LocalDate.of(2016, 9, 6), Temporals.parseFirstMatching("2016-09-06", LocalDate::from, DateTimeFormatter.ISO_LOCAL_DATE)); } @Test(expected = DateTimeParseException.class) public void test_parseFirstMatching_twoNoMatch() { Temporals.parseFirstMatching("2016", LocalDate::from, DateTimeFormatter.ISO_LOCAL_DATE, DateTimeFormatter.BASIC_ISO_DATE); } //----------------------------------------------------------------------- // chronoUnit() / timeUnit() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_timeUnitConversion() { return new Object[][] { {ChronoUnit.NANOS, TimeUnit.NANOSECONDS}, {ChronoUnit.MICROS, TimeUnit.MICROSECONDS}, {ChronoUnit.MILLIS, TimeUnit.MILLISECONDS}, {ChronoUnit.SECONDS, TimeUnit.SECONDS}, {ChronoUnit.MINUTES, TimeUnit.MINUTES}, {ChronoUnit.HOURS, TimeUnit.HOURS}, {ChronoUnit.DAYS, TimeUnit.DAYS}, }; } @Test @UseDataProvider("data_timeUnitConversion") public void test_timeUnit(ChronoUnit chronoUnit, TimeUnit timeUnit) { assertEquals(timeUnit, Temporals.timeUnit(chronoUnit)); } @Test(expected = IllegalArgumentException.class) public void test_timeUnit_unknown() { Temporals.timeUnit(ChronoUnit.MONTHS); } @Test(expected = NullPointerException.class) public void test_timeUnit_null() { Temporals.timeUnit(null); } @Test @UseDataProvider("data_timeUnitConversion") public void test_chronoUnit(ChronoUnit chronoUnit, TimeUnit timeUnit) { assertEquals(chronoUnit, Temporals.chronoUnit(timeUnit)); } @Test(expected = NullPointerException.class) public void test_chronoUnit_null() { Temporals.chronoUnit(null); } //----------------------------------------------------------------------- // convertAmount() //------------------------------------------------------------------------- @DataProvider public static Object[][] data_convertAmount() { return new Object[][] { {2L, NANOS, SECONDS, 0L, 2L}, {999_999_999L, NANOS, SECONDS, 0L, 999_999_999L}, {1_000_000_000L, NANOS, SECONDS, 1L, 0L}, {1_000_000_001L, NANOS, SECONDS, 1L, 1L}, {2L, NANOS, MINUTES, 0L, 2L}, {59_999_999_999L, NANOS, MINUTES, 0L, 59_999_999_999L}, {60_000_000_000L, NANOS, MINUTES, 1L, 0L}, {60_000_000_001L, NANOS, MINUTES, 1L, 1L}, {2L, NANOS, HOURS, 0L, 2L}, {3599_999_999_999L, NANOS, HOURS, 0L, 3599_999_999_999L}, {3600_000_000_000L, NANOS, HOURS, 1L, 0L}, {3600_000_000_001L, NANOS, HOURS, 1L, 1L}, {2L, NANOS, HALF_DAYS, 0L, 2L}, {3600_000_000_000L * 12 * 3, NANOS, HALF_DAYS, 3L, 0L}, {2L, NANOS, DAYS, 0L, 2L}, {3600_000_000_000L * 24 * 3, NANOS, DAYS, 3L, 0L}, {2L, NANOS, WEEKS, 0L, 2L}, {3600_000_000_000L * 24 * 7 * 3, NANOS, WEEKS, 3L, 0L}, {2L, SECONDS, MINUTES, 0L, 2L}, {59L, SECONDS, MINUTES, 0L, 59L}, {60L, SECONDS, MINUTES, 1L, 0L}, {61L, SECONDS, MINUTES, 1L, 1L}, {2L, SECONDS, HOURS, 0L, 2L}, {3599L, SECONDS, HOURS, 0L, 3599L}, {3600L, SECONDS, HOURS, 1L, 0L}, {3601L, SECONDS, HOURS, 1L, 1L}, {2L, SECONDS, HALF_DAYS, 0L, 2L}, {3600L * 12 * 3, SECONDS, HALF_DAYS, 3L, 0L}, {2L, SECONDS, DAYS, 0L, 2L}, {3600L * 24 * 3, SECONDS, DAYS, 3L, 0L}, {2L, SECONDS, WEEKS, 0L, 2L}, {3600L * 24 * 7 * 3, SECONDS, WEEKS, 3L, 0L}, {2L, MINUTES, HOURS, 0L, 2L}, {59L, MINUTES, HOURS, 0L, 59L}, {60L, MINUTES, HOURS, 1L, 0L}, {61L, MINUTES, HOURS, 1L, 1L}, {2L, MINUTES, HALF_DAYS, 0L, 2L}, {60L * 12 * 3 + 1, MINUTES, HALF_DAYS, 3L, 1L}, {2L, MINUTES, DAYS, 0L, 2L}, {60L * 24 * 3 + 1, MINUTES, DAYS, 3L, 1L}, {2L, MINUTES, WEEKS, 0L, 2L}, {60L * 24 * 7 * 3 + 1, MINUTES, WEEKS, 3L, 1L}, {2L, HOURS, HALF_DAYS, 0L, 2L}, {12L * 3 + 1, HOURS, HALF_DAYS, 3L, 1L}, {2L, HOURS, DAYS, 0L, 2L}, {24L * 3 + 1, HOURS, DAYS, 3L, 1L}, {2L, HOURS, WEEKS, 0L, 2L}, {24L * 7 * 3 + 1, HOURS, WEEKS, 3L, 1L}, {1L, HALF_DAYS, DAYS, 0L, 1L}, {2L * 3 + 1, HALF_DAYS, DAYS, 3L, 1L}, {1L, HALF_DAYS, WEEKS, 0L, 1L}, {2L * 7 * 3 + 1, HALF_DAYS, WEEKS, 3L, 1L}, {1L, DAYS, WEEKS, 0L, 1L}, {7L * 3 + 1, DAYS, WEEKS, 3L, 1L}, {2L, SECONDS, NANOS, 2_000_000_000L, 0L}, {2L, MINUTES, NANOS, 2_000_000_000L * 60, 0L}, {2L, HOURS, NANOS, 2_000_000_000L * 3600, 0L}, {2L, HALF_DAYS, NANOS, 2_000_000_000L * 3600 * 12, 0L}, {2L, DAYS, NANOS, 2_000_000_000L * 3600 * 24, 0L}, {2L, WEEKS, NANOS, 2_000_000_000L * 3600 * 24 * 7, 0L}, {2L, MINUTES, SECONDS, 2L * 60, 0L}, {2L, HOURS, SECONDS, 2L * 3600, 0L}, {2L, HALF_DAYS, SECONDS, 2L * 3600 * 12, 0L}, {2L, DAYS, SECONDS, 2L * 3600 * 24, 0L}, {2L, WEEKS, SECONDS, 2L * 3600 * 24 * 7, 0L}, {2L, HOURS, MINUTES, 2L * 60, 0L}, {2L, HALF_DAYS, MINUTES, 2L * 60 * 12, 0L}, {2L, DAYS, MINUTES, 2L * 60 * 24, 0L}, {2L, WEEKS, MINUTES, 2L * 60 * 24 * 7, 0L}, {2L, HALF_DAYS, HOURS, 2L * 12, 0L}, {2L, DAYS, HOURS, 2L * 24, 0L}, {2L, WEEKS, HOURS, 2L * 24 * 7, 0L}, {2L, DAYS, HALF_DAYS, 2L * 2, 0L}, {2L, WEEKS, HALF_DAYS, 2L * 2 * 7, 0L}, {2L, WEEKS, DAYS, 2L * 7, 0L}, {2L * 3 + 1, MONTHS, QUARTER_YEARS, 2L, 1L}, {2L * 12 + 1, MONTHS, YEARS, 2L, 1L}, {2L * 120 + 1, MONTHS, DECADES, 2L, 1L}, {2L * 1200 + 1, MONTHS, CENTURIES, 2L, 1L}, {2L * 12000 + 1, MONTHS, MILLENNIA, 2L, 1L}, {2L * 4 + 1, QUARTER_YEARS, YEARS, 2L, 1L}, {2L * 40 + 1, QUARTER_YEARS, DECADES, 2L, 1L}, {2L * 400 + 1, QUARTER_YEARS, CENTURIES, 2L, 1L}, {2L * 4000 + 1, QUARTER_YEARS, MILLENNIA, 2L, 1L}, {2L * 10 + 1, YEARS, DECADES, 2L, 1L}, {2L * 100 + 1, YEARS, CENTURIES, 2L, 1L}, {2L * 1000 + 1, YEARS, MILLENNIA, 2L, 1L}, {2L * 10 + 1, DECADES, CENTURIES, 2L, 1L}, {2L * 100 + 1, DECADES, MILLENNIA, 2L, 1L}, {2L * 10 + 1, CENTURIES, MILLENNIA, 2L, 1L}, {2L, QUARTER_YEARS, MONTHS, 2L * 3, 0L}, {2L, YEARS, MONTHS, 2L * 12, 0L}, {2L, DECADES, MONTHS, 2L * 120, 0L}, {2L, CENTURIES, MONTHS, 2L * 1200, 0L}, {2L, MILLENNIA, MONTHS, 2L * 12000, 0L}, {2L, YEARS, QUARTER_YEARS, 2L * 4, 0L}, {2L, DECADES, QUARTER_YEARS, 2L * 40, 0L}, {2L, CENTURIES, QUARTER_YEARS, 2L * 400, 0L}, {2L, MILLENNIA, QUARTER_YEARS, 2L * 4000, 0L}, {2L, DECADES, YEARS, 2L * 10, 0L}, {2L, CENTURIES, YEARS, 2L * 100, 0L}, {2L, MILLENNIA, YEARS, 2L * 1000, 0L}, {2L, CENTURIES, DECADES, 2L * 10, 0L}, {2L, MILLENNIA, DECADES, 2L * 100, 0L}, {2L, MILLENNIA, CENTURIES, 2L * 10, 0L}, }; } @Test @UseDataProvider("data_convertAmount") public void test_convertAmount( long fromAmount, TemporalUnit fromUnit, TemporalUnit resultUnit, long resultWhole, long resultRemainder) { long[] result = Temporals.convertAmount(fromAmount, fromUnit, resultUnit); assertEquals(resultWhole, result[0]); assertEquals(resultRemainder, result[1]); } @Test @UseDataProvider("data_convertAmount") public void test_convertAmount_negative( long fromAmount, TemporalUnit fromUnit, TemporalUnit resultUnit, long resultWhole, long resultRemainder) { long[] result = Temporals.convertAmount(-fromAmount, fromUnit, resultUnit); assertEquals(-resultWhole, result[0]); assertEquals(-resultRemainder, result[1]); } @Test public void test_convertAmountSameUnit_zero() { for (ChronoUnit unit : ChronoUnit.values()) { if (unit != ERAS && unit != FOREVER) { long[] result = Temporals.convertAmount(0, unit, unit); assertEquals(0, result[0]); assertEquals(0, result[1]); } } } @Test public void test_convertAmountSameUnit_nonZero() { for (ChronoUnit unit : ChronoUnit.values()) { if (unit != ERAS && unit != FOREVER) { long[] result = Temporals.convertAmount(2, unit, unit); assertEquals(2, result[0]); assertEquals(0, result[1]); } } } @DataProvider public static Object[][] data_convertAmountInvalid() { return new Object[][] { {SECONDS, MONTHS}, {SECONDS, QUARTER_YEARS}, {SECONDS, YEARS}, {SECONDS, DECADES}, {SECONDS, CENTURIES}, {SECONDS, MILLENNIA}, {MONTHS, SECONDS}, {QUARTER_YEARS, SECONDS}, {YEARS, SECONDS}, {DECADES, SECONDS}, {CENTURIES, SECONDS}, {MILLENNIA, SECONDS}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_convertAmountInvalid") public void test_convertAmountInvalid(TemporalUnit fromUnit, TemporalUnit resultUnit) { Temporals.convertAmount(1, fromUnit, resultUnit); } @DataProvider public static Object[][] data_convertAmountInvalidUnsupported() { return new Object[][] { {SECONDS, ERAS}, {ERAS, SECONDS}, {YEARS, ERAS}, {ERAS, YEARS}, {SECONDS, FOREVER}, {FOREVER, SECONDS}, {YEARS, FOREVER}, {FOREVER, YEARS}, {FOREVER, ERAS}, {ERAS, FOREVER}, }; } @Test(expected = UnsupportedTemporalTypeException.class) @UseDataProvider("data_convertAmountInvalidUnsupported") public void test_convertAmountInvalidUnsupported(TemporalUnit fromUnit, TemporalUnit resultUnit) { Temporals.convertAmount(1, fromUnit, resultUnit); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestWeeks.java000066400000000000000000000436651343451174100254750ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestWeeks { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Weeks.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Weeks test = Weeks.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Weeks.of(0), Weeks.ZERO); assertEquals(Weeks.of(0), Weeks.ZERO); assertEquals(0, Weeks.ZERO.getAmount()); } @Test public void test_ONE() { assertSame(Weeks.of(1), Weeks.ONE); assertEquals(Weeks.of(1), Weeks.ONE); assertEquals(1, Weeks.ONE.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(1, Weeks.of(1).getAmount()); assertEquals(2, Weeks.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Weeks.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Weeks.of(-1).getAmount()); assertEquals(-2, Weeks.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Weeks.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @Test public void test_from_P0W() { assertEquals(Weeks.of(0), Weeks.from(Period.ofWeeks(0))); } @Test public void test_from_P2W() { assertEquals(Weeks.of(2), Weeks.from(Period.ofWeeks(2))); } @Test public void test_from_P14D() { assertEquals(Weeks.of(2), Weeks.from(Period.ofDays(14))); } @Test public void test_from_Duration() { assertEquals(Weeks.of(2), Weeks.from(Duration.ofDays(14))); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_remainder() { Weeks.from(Period.ofDays(3)); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_noConversion() { Weeks.from(Period.ofMonths(2)); } @Test(expected = NullPointerException.class) public void test_from_null() { Weeks.from((TemporalAmount) null); } //----------------------------------------------------------------------- @Test public void test_parse_CharSequence() { assertEquals(Weeks.of(0), Weeks.parse("P0W")); assertEquals(Weeks.of(1), Weeks.parse("P1W")); assertEquals(Weeks.of(2), Weeks.parse("P2W")); assertEquals(Weeks.of(123456789), Weeks.parse("P123456789W")); assertEquals(Weeks.of(-2), Weeks.parse("P-2W")); assertEquals(Weeks.of(-2), Weeks.parse("-P2W")); assertEquals(Weeks.of(2), Weeks.parse("-P-2W")); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3Y"}, {"P3M"}, {"P3D"}, {"3"}, {"-3"}, {"3Y"}, {"-3Y"}, {"P3"}, {"P-3"}, {"PY"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Weeks.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Weeks.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_between() { assertEquals(Weeks.of(104), Weeks.between(LocalDate.of(2019, 1, 1), LocalDate.of(2021, 1, 1))); } @Test(expected = NullPointerException.class) public void test_between_date_null() { Weeks.between(LocalDate.now(), (Temporal) null); } @Test(expected = NullPointerException.class) public void test_between_null_date() { Weeks.between((Temporal) null, LocalDate.now()); } //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(6, Weeks.of(6).get(ChronoUnit.WEEKS)); } @Test(expected = DateTimeException.class) public void test_get_invalidType() { Weeks.of(6).get(IsoFields.QUARTER_YEARS); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Weeks() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(5), test5.plus(Weeks.of(0))); assertEquals(Weeks.of(7), test5.plus(Weeks.of(2))); assertEquals(Weeks.of(3), test5.plus(Weeks.of(-2))); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE - 1).plus(Weeks.of(1))); assertEquals(Weeks.of(Integer.MIN_VALUE), Weeks.of(Integer.MIN_VALUE + 1).plus(Weeks.of(-1))); } @Test public void test_plus_TemporalAmount_Period() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(5), test5.plus(Period.ofWeeks(0))); assertEquals(Weeks.of(7), test5.plus(Period.ofWeeks(2))); assertEquals(Weeks.of(3), test5.plus(Period.ofWeeks(-2))); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE - 1).plus(Period.ofWeeks(1))); assertEquals(Weeks.of(Integer.MIN_VALUE), Weeks.of(Integer.MIN_VALUE + 1).plus(Period.ofWeeks(-1))); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_PeriodMonths() { Weeks.of(1).plus(Period.ofMonths(2)); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_Duration() { Weeks.of(1).plus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Weeks.of(Integer.MAX_VALUE - 1).plus(Weeks.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Weeks.of(Integer.MIN_VALUE + 1).plus(Weeks.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Weeks.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(5), test5.plus(0)); assertEquals(Weeks.of(7), test5.plus(2)); assertEquals(Weeks.of(3), test5.plus(-2)); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Weeks.of(Integer.MIN_VALUE), Weeks.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Weeks.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Weeks.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Weeks() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(5), test5.minus(Weeks.of(0))); assertEquals(Weeks.of(3), test5.minus(Weeks.of(2))); assertEquals(Weeks.of(7), test5.minus(Weeks.of(-2))); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE - 1).minus(Weeks.of(-1))); assertEquals(Weeks.of(Integer.MIN_VALUE), Weeks.of(Integer.MIN_VALUE + 1).minus(Weeks.of(1))); } @Test public void test_minus_TemporalAmount_Period() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(5), test5.minus(Period.ofWeeks(0))); assertEquals(Weeks.of(3), test5.minus(Period.ofWeeks(2))); assertEquals(Weeks.of(7), test5.minus(Period.ofWeeks(-2))); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE - 1).minus(Period.ofWeeks(-1))); assertEquals(Weeks.of(Integer.MIN_VALUE), Weeks.of(Integer.MIN_VALUE + 1).minus(Period.ofWeeks(1))); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_PeriodMonths() { Weeks.of(1).minus(Period.ofMonths(2)); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_Duration() { Weeks.of(1).minus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Weeks.of(Integer.MAX_VALUE - 1).minus(Weeks.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Weeks.of(Integer.MIN_VALUE + 1).minus(Weeks.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Weeks.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(5), test5.minus(0)); assertEquals(Weeks.of(3), test5.minus(2)); assertEquals(Weeks.of(7), test5.minus(-2)); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Weeks.of(Integer.MIN_VALUE), Weeks.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Weeks.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Weeks.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(0), test5.multipliedBy(0)); assertEquals(Weeks.of(5), test5.multipliedBy(1)); assertEquals(Weeks.of(10), test5.multipliedBy(2)); assertEquals(Weeks.of(15), test5.multipliedBy(3)); assertEquals(Weeks.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Weeks test5 = Weeks.of(5); assertEquals(Weeks.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Weeks.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Weeks.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Weeks test12 = Weeks.of(12); assertEquals(Weeks.of(12), test12.dividedBy(1)); assertEquals(Weeks.of(6), test12.dividedBy(2)); assertEquals(Weeks.of(4), test12.dividedBy(3)); assertEquals(Weeks.of(3), test12.dividedBy(4)); assertEquals(Weeks.of(2), test12.dividedBy(5)); assertEquals(Weeks.of(2), test12.dividedBy(6)); assertEquals(Weeks.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Weeks test12 = Weeks.of(12); assertEquals(Weeks.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Weeks.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Weeks.of(0), Weeks.of(0).negated()); assertEquals(Weeks.of(-12), Weeks.of(12).negated()); assertEquals(Weeks.of(12), Weeks.of(-12).negated()); assertEquals(Weeks.of(-Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Weeks.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Weeks.of(0), Weeks.of(0).abs()); assertEquals(Weeks.of(12), Weeks.of(12).abs()); assertEquals(Weeks.of(12), Weeks.of(-12).abs()); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(Integer.MAX_VALUE).abs()); assertEquals(Weeks.of(Integer.MAX_VALUE), Weeks.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Weeks.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { assertEquals(LocalDate.of(2019, 1, 10), Weeks.of(0).addTo(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2019, 2, 14), Weeks.of(5).addTo(LocalDate.of(2019, 1, 10))); } @Test public void test_subtractFrom() { assertEquals(LocalDate.of(2019, 1, 10), Weeks.of(0).subtractFrom(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2018, 12, 6), Weeks.of(5).subtractFrom(LocalDate.of(2019, 1, 10))); } //----------------------------------------------------------------------- @Test public void test_toPeriod() { for (int i = -20; i < 20; i++) { assertEquals(Period.ofWeeks(i), Weeks.of(i).toPeriod()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Weeks test5 = Weeks.of(5); Weeks test6 = Weeks.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Weeks test5 = Weeks.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Weeks test5 = Weeks.of(5); Weeks test6 = Weeks.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Weeks test5 = Weeks.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Weeks test5 = Weeks.of(5); Object obj = ""; assertEquals(false, test5.equals(obj)); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Weeks test5 = Weeks.of(5); Weeks test6 = Weeks.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Weeks test5 = Weeks.of(5); assertEquals("P5W", test5.toString()); Weeks testM1 = Weeks.of(-1); assertEquals("P-1W", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestYearQuarter.java000066400000000000000000001243421343451174100266530ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_DAY; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.HOURS; import static java.time.temporal.ChronoUnit.MICROS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MILLIS; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.SECONDS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static java.time.temporal.IsoFields.DAY_OF_QUARTER; import static java.time.temporal.IsoFields.QUARTER_OF_YEAR; import static java.time.temporal.IsoFields.QUARTER_YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.threeten.extra.Quarter.Q1; import static org.threeten.extra.Quarter.Q2; import static org.threeten.extra.Quarter.Q3; import static org.threeten.extra.Quarter.Q4; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; import java.time.Year; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; /** * Test YearQuarter. */ public class TestYearQuarter { private static final YearQuarter TEST = YearQuarter.of(2012, Q2); private static final int STANDARD_YEAR_LENGTH = 365; private static final int LEAP_YEAR_LENGTH = 366; //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(YearQuarter.class)); assertTrue(Comparable.class.isAssignableFrom(YearQuarter.class)); assertTrue(TemporalAdjuster.class.isAssignableFrom(YearQuarter.class)); assertTrue(TemporalAccessor.class.isAssignableFrom(YearQuarter.class)); } @Test public void test_serialization() throws IOException, ClassNotFoundException { YearQuarter test = YearQuarter.of(2012, 1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- // of(int,Quarter) //----------------------------------------------------------------------- @Test public void test_of_int_Quarter() { for (int year = -100; year <= 100; year++) { for (Quarter quarter : Quarter.values()) { YearQuarter test = YearQuarter.of(year, quarter); assertEquals(year, test.getYear()); assertEquals(quarter.getValue(), test.getQuarterValue()); assertEquals(quarter, test.getQuarter()); } } } @Test(expected = DateTimeException.class) public void test_of_int_Quarter_yearTooLow() { YearQuarter.of(Year.MIN_VALUE - 1, Quarter.Q2); } @Test(expected = DateTimeException.class) public void test_of_int_Quarter_yearTooHigh() { YearQuarter.of(Year.MAX_VALUE + 1, Quarter.Q2); } @Test(expected = NullPointerException.class) public void test_of_int_Quarter_null() { YearQuarter.of(2012, (Quarter) null); } //----------------------------------------------------------------------- // of(int,int) //----------------------------------------------------------------------- @Test public void test_of_int_int() { for (int year = -100; year <= 100; year++) { for (int quarter = 1; quarter <= 4; quarter++) { YearQuarter test = YearQuarter.of(year, quarter); assertEquals(year, test.getYear()); assertEquals(quarter, test.getQuarterValue()); assertEquals(Quarter.of(quarter), test.getQuarter()); assertEquals(test, YearQuarter.of(year, quarter)); assertEquals(test.hashCode(), YearQuarter.of(year, quarter).hashCode()); } } } @Test(expected = DateTimeException.class) public void test_of_int_int_yearTooLow() { YearQuarter.of(Year.MIN_VALUE - 1, 1); } @Test(expected = DateTimeException.class) public void test_of_int_int_yearTooHigh() { YearQuarter.of(Year.MAX_VALUE + 1, 1); } @Test(expected = DateTimeException.class) public void test_of_int_int_quarterTooLow() { YearQuarter.of(2012, 0); } @Test(expected = DateTimeException.class) public void test_of_int_int_quarterTooHigh() { YearQuarter.of(2012, 5); } //----------------------------------------------------------------------- // from(TemporalAccessor) //----------------------------------------------------------------------- @Test public void test_from_TemporalAccessor_notLeapYear() { LocalDate date = LocalDate.of(2007, 1, 1); for (int i = 1; i <= STANDARD_YEAR_LENGTH; i++) { YearQuarter test = YearQuarter.from(date); int expected = ((date.getMonthValue() - 1) / 3) + 1; assertEquals(YearQuarter.of(2007, expected), test); date = date.plusDays(1); } } @Test public void test_from_TemporalAccessor_leapYear() { LocalDate date = LocalDate.of(2008, 1, 1); for (int i = 1; i <= LEAP_YEAR_LENGTH; i++) { YearQuarter test = YearQuarter.from(date); int expected = ((date.getMonthValue() - 1) / 3) + 1; assertEquals(YearQuarter.of(2008, expected), test); date = date.plusDays(1); } } @Test(expected = DateTimeException.class) public void test_from_TemporalAccessor_noDerive() { YearQuarter.from(LocalTime.NOON); } @Test(expected = NullPointerException.class) public void test_from_TemporalAccessor_null() { YearQuarter.from((TemporalAccessor) null); } //----------------------------------------------------------------------- // parse(CharSequence) //----------------------------------------------------------------------- @Test public void test_parse_CharSequence() { assertEquals(YearQuarter.of(2012, Q3), YearQuarter.parse("2012-Q3")); } @Test public void test_parse_CharSequence_caseInsensitive() { assertEquals(YearQuarter.of(2012, Q3), YearQuarter.parse("2012-q3")); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequenceDate_invalidYear() { YearQuarter.parse("12345-Q3"); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequenceDate_invalidQuarter() { YearQuarter.parse("2012-Q0"); } @Test(expected = NullPointerException.class) public void test_parse_CharSequenceDate_nullCharSequence() { YearQuarter.parse((CharSequence) null); } //----------------------------------------------------------------------- // parse(CharSequence,DateTimeFormatter) //----------------------------------------------------------------------- @Test public void test_parse_CharSequenceDateTimeFormatter() { DateTimeFormatter f = DateTimeFormatter.ofPattern("'Q'Q uuuu"); assertEquals(YearQuarter.of(2012, Q3), YearQuarter.parse("Q3 2012", f)); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequenceDateDateTimeFormatter_invalidQuarter() { DateTimeFormatter f = DateTimeFormatter.ofPattern("'Q'Q uuuu"); YearQuarter.parse("Q0 2012", f); } @Test(expected = NullPointerException.class) public void test_parse_CharSequenceDateTimeFormatter_nullCharSequence() { DateTimeFormatter f = DateTimeFormatter.ofPattern("'Q'Q uuuu"); YearQuarter.parse((CharSequence) null, f); } @Test(expected = NullPointerException.class) public void test_parse_CharSequenceDateTimeFormatter_nullDateTimeFormatter() { YearQuarter.parse("", (DateTimeFormatter) null); } //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- @Test public void test_isSupported_TemporalField() { assertEquals(false, TEST.isSupported((TemporalField) null)); assertEquals(false, TEST.isSupported(NANO_OF_SECOND)); assertEquals(false, TEST.isSupported(NANO_OF_DAY)); assertEquals(false, TEST.isSupported(MICRO_OF_SECOND)); assertEquals(false, TEST.isSupported(MICRO_OF_DAY)); assertEquals(false, TEST.isSupported(MILLI_OF_SECOND)); assertEquals(false, TEST.isSupported(MILLI_OF_DAY)); assertEquals(false, TEST.isSupported(SECOND_OF_MINUTE)); assertEquals(false, TEST.isSupported(SECOND_OF_DAY)); assertEquals(false, TEST.isSupported(MINUTE_OF_HOUR)); assertEquals(false, TEST.isSupported(MINUTE_OF_DAY)); assertEquals(false, TEST.isSupported(HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(AMPM_OF_DAY)); assertEquals(false, TEST.isSupported(DAY_OF_WEEK)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(false, TEST.isSupported(DAY_OF_MONTH)); assertEquals(false, TEST.isSupported(DAY_OF_YEAR)); assertEquals(false, TEST.isSupported(EPOCH_DAY)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_YEAR)); assertEquals(false, TEST.isSupported(MONTH_OF_YEAR)); assertEquals(false, TEST.isSupported(PROLEPTIC_MONTH)); assertEquals(true, TEST.isSupported(YEAR_OF_ERA)); assertEquals(true, TEST.isSupported(YEAR)); assertEquals(true, TEST.isSupported(ERA)); assertEquals(false, TEST.isSupported(INSTANT_SECONDS)); assertEquals(false, TEST.isSupported(OFFSET_SECONDS)); assertEquals(true, TEST.isSupported(QUARTER_OF_YEAR)); assertEquals(false, TEST.isSupported(DAY_OF_QUARTER)); } //----------------------------------------------------------------------- // isSupported(TemporalUnit) //----------------------------------------------------------------------- @Test public void test_isSupported_TemporalUnit() { assertEquals(false, TEST.isSupported((TemporalUnit) null)); assertEquals(false, TEST.isSupported(NANOS)); assertEquals(false, TEST.isSupported(MICROS)); assertEquals(false, TEST.isSupported(MILLIS)); assertEquals(false, TEST.isSupported(SECONDS)); assertEquals(false, TEST.isSupported(MINUTES)); assertEquals(false, TEST.isSupported(HOURS)); assertEquals(false, TEST.isSupported(DAYS)); assertEquals(false, TEST.isSupported(WEEKS)); assertEquals(false, TEST.isSupported(MONTHS)); assertEquals(true, TEST.isSupported(YEARS)); assertEquals(true, TEST.isSupported(DECADES)); assertEquals(true, TEST.isSupported(CENTURIES)); assertEquals(true, TEST.isSupported(MILLENNIA)); assertEquals(true, TEST.isSupported(ERA)); assertEquals(false, TEST.isSupported(FOREVER)); assertEquals(true, TEST.isSupported(QUARTER_YEARS)); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- @Test public void test_range() { assertEquals(QUARTER_OF_YEAR.range(), TEST.range(QUARTER_OF_YEAR)); assertEquals(YEAR.range(), TEST.range(YEAR)); assertEquals(ValueRange.of(1, Year.MAX_VALUE), TEST.range(YEAR_OF_ERA)); assertEquals(ERA.range(), TEST.range(ERA)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_invalidField() { TEST.range(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_range_null() { TEST.range((TemporalField) null); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(2, TEST.get(QUARTER_OF_YEAR)); assertEquals(2012, TEST.get(YEAR)); assertEquals(2012, TEST.get(YEAR_OF_ERA)); assertEquals(1, TEST.get(ERA)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_get_invalidField() { TEST.get(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_get_null() { TEST.get((TemporalField) null); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- @Test public void test_getLong() { assertEquals(2L, TEST.getLong(QUARTER_OF_YEAR)); assertEquals(2012L, TEST.getLong(YEAR)); assertEquals(2012L, TEST.getLong(YEAR_OF_ERA)); assertEquals(1L, TEST.getLong(ERA)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField() { TEST.getLong(MONTH_OF_YEAR); } @Test(expected = NullPointerException.class) public void test_getLong_null() { TEST.getLong((TemporalField) null); } //----------------------------------------------------------------------- // isLeapYear(int) //----------------------------------------------------------------------- @Test public void test_isLeapYear_int() { for (int year = -500; year <= 500; year++) { for (Quarter quarter : Quarter.values()) { YearQuarter test = YearQuarter.of(year, quarter); assertEquals(Year.isLeap(year), test.isLeapYear()); } } } //----------------------------------------------------------------------- // isValidDay(int) //----------------------------------------------------------------------- @Test public void test_isValidDay_int_nonLeap() { assertEquals(true, YearQuarter.of(2011, Q1).isValidDay(90)); assertEquals(false, YearQuarter.of(2011, Q1).isValidDay(91)); assertEquals(false, YearQuarter.of(2011, Q1).isValidDay(92)); assertEquals(true, YearQuarter.of(2011, Q2).isValidDay(90)); assertEquals(true, YearQuarter.of(2011, Q2).isValidDay(91)); assertEquals(false, YearQuarter.of(2011, Q2).isValidDay(92)); assertEquals(true, YearQuarter.of(2011, Q3).isValidDay(90)); assertEquals(true, YearQuarter.of(2011, Q3).isValidDay(91)); assertEquals(true, YearQuarter.of(2011, Q4).isValidDay(90)); assertEquals(true, YearQuarter.of(2011, Q3).isValidDay(92)); assertEquals(true, YearQuarter.of(2011, Q4).isValidDay(91)); assertEquals(true, YearQuarter.of(2011, Q4).isValidDay(92)); } @Test public void test_isValidDay_int_leap() { assertEquals(true, YearQuarter.of(2012, Q1).isValidDay(90)); assertEquals(true, YearQuarter.of(2012, Q1).isValidDay(91)); assertEquals(false, YearQuarter.of(2012, Q1).isValidDay(92)); assertEquals(true, YearQuarter.of(2012, Q2).isValidDay(90)); assertEquals(true, YearQuarter.of(2012, Q2).isValidDay(91)); assertEquals(false, YearQuarter.of(2012, Q2).isValidDay(92)); assertEquals(true, YearQuarter.of(2012, Q3).isValidDay(90)); assertEquals(true, YearQuarter.of(2012, Q3).isValidDay(91)); assertEquals(true, YearQuarter.of(2012, Q3).isValidDay(92)); assertEquals(true, YearQuarter.of(2012, Q4).isValidDay(90)); assertEquals(true, YearQuarter.of(2012, Q4).isValidDay(91)); assertEquals(true, YearQuarter.of(2012, Q4).isValidDay(92)); } @Test public void test_isValidDay_int_outOfRange() { assertEquals(false, YearQuarter.of(2011, Q1).isValidDay(93)); assertEquals(false, YearQuarter.of(2011, Q2).isValidDay(93)); assertEquals(false, YearQuarter.of(2011, Q3).isValidDay(93)); assertEquals(false, YearQuarter.of(2011, Q4).isValidDay(93)); assertEquals(false, YearQuarter.of(2011, Q1).isValidDay(0)); assertEquals(false, YearQuarter.of(2011, Q2).isValidDay(0)); assertEquals(false, YearQuarter.of(2011, Q3).isValidDay(0)); assertEquals(false, YearQuarter.of(2011, Q4).isValidDay(0)); } //----------------------------------------------------------------------- // lengthOfQuarter() //----------------------------------------------------------------------- @Test public void test_lengthOfQuarter() { for (int year = -500; year <= 500; year++) { assertEquals(Year.isLeap(year) ? 91 : 90, YearQuarter.of(year, Q1).lengthOfQuarter()); assertEquals(91, YearQuarter.of(year, Q2).lengthOfQuarter()); assertEquals(92, YearQuarter.of(year, Q3).lengthOfQuarter()); assertEquals(92, YearQuarter.of(year, Q4).lengthOfQuarter()); } } //----------------------------------------------------------------------- // with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_with_TemporalAdjuster_Quarter() { assertEquals(YearQuarter.of(2007, Q1), YearQuarter.of(2007, Q2).with(Q1)); } @Test public void test_with_TemporalAdjuster_Year() { assertEquals(YearQuarter.of(2012, Q2), YearQuarter.of(2007, Q2).with(Year.of(2012))); } @Test public void test_with_TemporalAdjuster_YearQuarter() { assertEquals(YearQuarter.of(2012, Q3), YearQuarter.of(2007, Q2).with(YearQuarter.of(2012, Q3))); } @Test(expected = DateTimeException.class) public void test_with_TemporalAdjuster_LocalDate() { YearQuarter.of(2007, Q2).with(LocalDate.of(2012, 6, 30)); } @Test(expected = NullPointerException.class) public void test_with_TemporalAdjuster_null() { YearQuarter.of(2007, Q2).with((TemporalAdjuster) null); } //----------------------------------------------------------------------- // withYear(int) //----------------------------------------------------------------------- @Test public void test_withYear() { assertEquals(YearQuarter.of(2012, Q2), YearQuarter.of(2007, Q2).withYear(2012)); } @Test(expected = DateTimeException.class) public void test_withYear_int_quarterTooLow() { TEST.withYear(Year.MIN_VALUE - 1); } @Test(expected = DateTimeException.class) public void test_withYear_int_quarterTooHigh() { TEST.withYear(Year.MAX_VALUE + 1); } //----------------------------------------------------------------------- // withQuarter(int) //----------------------------------------------------------------------- @Test public void test_withQuarter_int() { assertEquals(YearQuarter.of(2007, Q1), YearQuarter.of(2007, Q2).withQuarter(1)); } @Test(expected = DateTimeException.class) public void test_withQuarter_int_quarterTooLow() { TEST.withQuarter(0); } @Test(expected = DateTimeException.class) public void test_withQuarter_int_quarterTooHigh() { TEST.withQuarter(5); } //----------------------------------------------------------------------- // plus(long,TemporalUnit) //----------------------------------------------------------------------- @Test public void test_plus_longTemporalUnit() { assertEquals(YearQuarter.of(2012, Q2), YearQuarter.of(2007, Q2).plus(5, YEARS)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).plus(0, YEARS)); assertEquals(YearQuarter.of(2002, Q2), YearQuarter.of(2007, Q2).plus(-5, YEARS)); assertEquals(YearQuarter.of(2008, Q3), YearQuarter.of(2007, Q2).plus(5, QUARTER_YEARS)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).plus(0, QUARTER_YEARS)); assertEquals(YearQuarter.of(2006, Q1), YearQuarter.of(2007, Q2).plus(-5, QUARTER_YEARS)); } //----------------------------------------------------------------------- // plusYears(int) //----------------------------------------------------------------------- @Test public void test_plusYears() { assertEquals(YearQuarter.of(2012, Q2), YearQuarter.of(2007, Q2).plusYears(5)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).plusYears(0)); assertEquals(YearQuarter.of(2002, Q2), YearQuarter.of(2007, Q2).plusYears(-5)); } //----------------------------------------------------------------------- // plusQuarters(int) //----------------------------------------------------------------------- @Test public void test_plusQuarters() { assertEquals(YearQuarter.of(2008, Q3), YearQuarter.of(2007, Q2).plusQuarters(5)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).plusQuarters(0)); assertEquals(YearQuarter.of(2006, Q1), YearQuarter.of(2007, Q2).plusQuarters(-5)); } //----------------------------------------------------------------------- // minus(long,TemporalUnit) //----------------------------------------------------------------------- @Test public void test_minus_longTemporalUnit() { assertEquals(YearQuarter.of(2002, Q2), YearQuarter.of(2007, Q2).minus(5, YEARS)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).minus(0, YEARS)); assertEquals(YearQuarter.of(2012, Q2), YearQuarter.of(2007, Q2).minus(-5, YEARS)); assertEquals(YearQuarter.of(2006, Q1), YearQuarter.of(2007, Q2).minus(5, QUARTER_YEARS)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).minus(0, QUARTER_YEARS)); assertEquals(YearQuarter.of(2008, Q3), YearQuarter.of(2007, Q2).minus(-5, QUARTER_YEARS)); } //----------------------------------------------------------------------- // minusYears(int) //----------------------------------------------------------------------- @Test public void test_minusYears() { assertEquals(YearQuarter.of(2002, Q2), YearQuarter.of(2007, Q2).minusYears(5)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).minusYears(0)); assertEquals(YearQuarter.of(2012, Q2), YearQuarter.of(2007, Q2).minusYears(-5)); } //----------------------------------------------------------------------- // minusQuarters(int) //----------------------------------------------------------------------- @Test public void test_minusQuarters() { assertEquals(YearQuarter.of(2006, Q1), YearQuarter.of(2007, Q2).minusQuarters(5)); assertEquals(YearQuarter.of(2007, Q2), YearQuarter.of(2007, Q2).minusQuarters(0)); assertEquals(YearQuarter.of(2008, Q3), YearQuarter.of(2007, Q2).minusQuarters(-5)); } //----------------------------------------------------------------------- // lengthOfYear() //----------------------------------------------------------------------- @Test public void test_lengthOfYear() { for (int year = -500; year <= 500; year++) { for (Quarter quarter : Quarter.values()) { YearQuarter test = YearQuarter.of(year, quarter); assertEquals(Year.isLeap(year) ? 366 : 365, test.lengthOfYear()); } } } //----------------------------------------------------------------------- // query(TemporalQuery) //----------------------------------------------------------------------- @Test public void test_query() { assertEquals(IsoChronology.INSTANCE, TEST.query(TemporalQueries.chronology())); assertEquals(null, TEST.query(TemporalQueries.localDate())); assertEquals(null, TEST.query(TemporalQueries.localTime())); assertEquals(null, TEST.query(TemporalQueries.offset())); assertEquals(QUARTER_YEARS, TEST.query(TemporalQueries.precision())); assertEquals(null, TEST.query(TemporalQueries.zone())); assertEquals(null, TEST.query(TemporalQueries.zoneId())); } //----------------------------------------------------------------------- // adjustInto(Temporal) //----------------------------------------------------------------------- @Test public void test_adjustInto_Temporal() { for (int month = 1; month < 12; month++) { for (int dom = 1; dom < 28; dom++) { LocalDate base = LocalDate.of(2007, month, dom); LocalDate expected = LocalDate.of(2012, 4 + ((month - 1) % 3), dom); assertEquals(expected, YearQuarter.of(2012, Q2).adjustInto(base)); } } } @Test public void test_adjustInto_Temporal_lastValidDay_nonLeap() { LocalDate base = LocalDate.of(2007, 5, 31); LocalDate expected = LocalDate.of(2011, 2, 28); assertEquals(expected, YearQuarter.of(2011, Q1).adjustInto(base)); } @Test public void test_adjustInto_Temporal_lastValidDay_leap() { LocalDate base = LocalDate.of(2007, 5, 31); LocalDate expected = LocalDate.of(2012, 2, 29); assertEquals(expected, YearQuarter.of(2012, Q1).adjustInto(base)); } @Test(expected = NullPointerException.class) public void test_adjustInto_Temporal_null() { TEST.adjustInto((Temporal) null); } //----------------------------------------------------------------------- // until(Temporal,TemporalUnit) //----------------------------------------------------------------------- @Test public void test_until_TemporalTemporalUnit_QUARTER_YEARS() { assertEquals(-2, YearQuarter.of(2012, Q2).until(YearQuarter.of(2011, Q4), QUARTER_YEARS)); assertEquals(-1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q1), QUARTER_YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q2), QUARTER_YEARS)); assertEquals(1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q3), QUARTER_YEARS)); assertEquals(2, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q4), QUARTER_YEARS)); assertEquals(3, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q1), QUARTER_YEARS)); assertEquals(4, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q2), QUARTER_YEARS)); assertEquals(5, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q3), QUARTER_YEARS)); } @Test public void test_until_TemporalTemporalUnit_YEARS() { assertEquals(-2, YearQuarter.of(2012, Q2).until(YearQuarter.of(2010, Q2), YEARS)); assertEquals(-1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2010, Q3), YEARS)); assertEquals(-1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2010, Q4), YEARS)); assertEquals(-1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2011, Q1), YEARS)); assertEquals(-1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2011, Q2), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2011, Q3), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2011, Q4), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q1), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q2), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q3), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q4), YEARS)); assertEquals(0, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q1), YEARS)); assertEquals(1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q2), YEARS)); assertEquals(1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q3), YEARS)); assertEquals(1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2013, Q4), YEARS)); assertEquals(1, YearQuarter.of(2012, Q2).until(YearQuarter.of(2014, Q1), YEARS)); assertEquals(2, YearQuarter.of(2012, Q2).until(YearQuarter.of(2014, Q2), YEARS)); } @Test(expected = NullPointerException.class) public void test_until_TemporalTemporalUnit_nullTemporal() { YearQuarter.of(2012, Q2).until(null, QUARTER_YEARS); } @Test(expected = NullPointerException.class) public void test_until_TemporalTemporalUnit_nullTemporalUnit() { YearQuarter.of(2012, Q2).until(YearQuarter.of(2012, Q3), null); } //----------------------------------------------------------------------- // quartersUntil(YearQuarter) //----------------------------------------------------------------------- @Test(expected = NullPointerException.class) public void test_quartersUntil_null() { YearQuarter.of(2012, Q2).quartersUntil(null); } @Test(expected = IllegalArgumentException.class) public void test_quartersUntil_IllegalArgument() { YearQuarter.of(2012, Q2).quartersUntil(YearQuarter.of(2012, Q1)); } @Test public void test_quartersUntil() { assertEquals(2, YearQuarter.of(2012, Q2).quartersUntil(YearQuarter.of(2012, Q4)).count()); assertEquals(10, YearQuarter.of(2012, Q2).quartersUntil(YearQuarter.of(2014, Q4)).count()); YearQuarter start = YearQuarter.of(2012, Q1); YearQuarter end = YearQuarter.of(2013, Q3); Stream stream = start.quartersUntil(end); List expects = Arrays.asList( YearQuarter.of(start.getYear(), Q1), YearQuarter.of(start.getYear(), Q2), YearQuarter.of(start.getYear(), Q3), YearQuarter.of(start.getYear(), Q4), YearQuarter.of(end.getYear(), Q1), YearQuarter.of(end.getYear(), Q2)); assertEquals(expects, stream.collect(Collectors.toList())); } //----------------------------------------------------------------------- // format(DateTimeFormatter) //----------------------------------------------------------------------- @Test public void test_format() { DateTimeFormatter f = DateTimeFormatter.ofPattern("'Q'Q uuuu"); assertEquals("Q1 2012", YearQuarter.of(2012, Q1).format(f)); } @Test(expected = NullPointerException.class) public void test_format_null() { TEST.format((DateTimeFormatter) null); } //----------------------------------------------------------------------- // atDay(int) //----------------------------------------------------------------------- @Test public void test_atDay() { for (int i = 1; i <= 90; i++) { LocalDate expected = LocalDate.of(2012, 1, 1).plusDays(i - 1); assertEquals(expected, YearQuarter.of(2012, Q1).atDay(i)); } for (int i = 1; i <= 91; i++) { LocalDate expected = LocalDate.of(2012, 4, 1).plusDays(i - 1); assertEquals(expected, YearQuarter.of(2012, Q2).atDay(i)); } for (int i = 1; i <= 92; i++) { LocalDate expected = LocalDate.of(2012, 7, 1).plusDays(i - 1); assertEquals(expected, YearQuarter.of(2012, Q3).atDay(i)); } for (int i = 1; i <= 92; i++) { LocalDate expected = LocalDate.of(2012, 10, 1).plusDays(i - 1); assertEquals(expected, YearQuarter.of(2012, Q4).atDay(i)); } } @Test public void test_atDay_Q1_91_leap() { assertEquals(LocalDate.of(2012, 3, 31), YearQuarter.of(2012, Q1).atDay(91)); } @Test(expected = DateTimeException.class) public void test_atDay_Q1_91_notLeap() { YearQuarter.of(2011, Q1).atDay(91); } @Test(expected = DateTimeException.class) public void test_atDay_Q1_92() { YearQuarter.of(2012, Q1).atDay(92); } @Test(expected = DateTimeException.class) public void test_atDay_Q2_92() { YearQuarter.of(2012, Q2).atDay(92); } @Test(expected = DateTimeException.class) public void test_atDay_tooLow() { TEST.atDay(0); } @Test(expected = DateTimeException.class) public void test_atDay_tooHigh() { TEST.atDay(93); } //----------------------------------------------------------------------- // atEndOfQuarter(int) //----------------------------------------------------------------------- @Test public void test_atEndOfQuarter() { assertEquals(LocalDate.of(2011, 3, 31), YearQuarter.of(2011, Q1).atEndOfQuarter()); assertEquals(LocalDate.of(2011, 6, 30), YearQuarter.of(2011, Q2).atEndOfQuarter()); assertEquals(LocalDate.of(2011, 9, 30), YearQuarter.of(2011, Q3).atEndOfQuarter()); assertEquals(LocalDate.of(2011, 12, 31), YearQuarter.of(2011, Q4).atEndOfQuarter()); assertEquals(LocalDate.of(2012, 3, 31), YearQuarter.of(2012, Q1).atEndOfQuarter()); assertEquals(LocalDate.of(2012, 6, 30), YearQuarter.of(2012, Q2).atEndOfQuarter()); assertEquals(LocalDate.of(2012, 9, 30), YearQuarter.of(2012, Q3).atEndOfQuarter()); assertEquals(LocalDate.of(2012, 12, 31), YearQuarter.of(2012, Q4).atEndOfQuarter()); } //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test public void test_compareTo() { for (int year1 = -100; year1 < 100; year1++) { for (Quarter quarter1 : Quarter.values()) { YearQuarter a = YearQuarter.of(year1, quarter1); for (int year2 = -100; year2 < 100; year2++) { for (Quarter quarter2 : Quarter.values()) { YearQuarter b = YearQuarter.of(year2, quarter2); if (year1 < year2) { assertEquals(true, a.compareTo(b) < 0); assertEquals(true, b.compareTo(a) > 0); assertEquals(false, a.isAfter(b)); assertEquals(false, b.isBefore(a)); assertEquals(true, b.isAfter(a)); assertEquals(true, a.isBefore(b)); } else if (year1 > year2) { assertEquals(true, a.compareTo(b) > 0); assertEquals(true, b.compareTo(a) < 0); assertEquals(true, a.isAfter(b)); assertEquals(true, b.isBefore(a)); assertEquals(false, b.isAfter(a)); assertEquals(false, a.isBefore(b)); } else { if (quarter1.getValue() < quarter2.getValue()) { assertEquals(true, a.compareTo(b) < 0); assertEquals(true, b.compareTo(a) > 0); assertEquals(false, a.isAfter(b)); assertEquals(false, b.isBefore(a)); assertEquals(true, b.isAfter(a)); assertEquals(true, a.isBefore(b)); } else if (quarter1.getValue() > quarter2.getValue()) { assertEquals(true, a.compareTo(b) > 0); assertEquals(true, b.compareTo(a) < 0); assertEquals(true, a.isAfter(b)); assertEquals(true, b.isBefore(a)); assertEquals(false, b.isAfter(a)); assertEquals(false, a.isBefore(b)); } else { assertEquals(0, a.compareTo(b)); assertEquals(0, b.compareTo(a)); assertEquals(false, a.isAfter(b)); assertEquals(false, b.isBefore(a)); assertEquals(false, b.isAfter(a)); assertEquals(false, a.isBefore(b)); } } } } } } } @Test(expected = NullPointerException.class) public void test_compareTo_nullYearQuarter() { TEST.compareTo(null); } //----------------------------------------------------------------------- // equals() / hashCode() //----------------------------------------------------------------------- @Test public void test_equals() { for (int year1 = -100; year1 < 100; year1++) { for (Quarter quarter1 : Quarter.values()) { YearQuarter a = YearQuarter.of(year1, quarter1); for (int year2 = -100; year2 < 100; year2++) { for (Quarter quarter2 : Quarter.values()) { YearQuarter b = YearQuarter.of(year2, quarter2); if (year1 == year2 && quarter1 == quarter2) { assertEquals(a, b); assertEquals(a.hashCode(), b.hashCode()); } } } } } } @Test public void test_equals_nullYearQuarter() { assertEquals(false, TEST.equals(null)); } @Test public void test_equals_incorrectType() { assertEquals(false, TEST.equals("Incorrect type")); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { assertEquals("2012-Q2", YearQuarter.of(2012, Q2).toString()); } @Test public void test_toString_bigYear() { assertEquals("+10000-Q2", YearQuarter.of(10000, Q2).toString()); } @Test public void test_toString_negativeYear() { assertEquals("-0001-Q2", YearQuarter.of(-1, Q2).toString()); } @Test public void test_toString_negativeBigYear() { assertEquals("-10000-Q2", YearQuarter.of(-10000, Q2).toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestYearWeek.java000066400000000000000000001112601343451174100261160ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static java.time.DayOfWeek.FRIDAY; import static java.time.DayOfWeek.MONDAY; import static java.time.DayOfWeek.SATURDAY; import static java.time.DayOfWeek.SUNDAY; import static java.time.DayOfWeek.THURSDAY; import static java.time.DayOfWeek.TUESDAY; import static java.time.DayOfWeek.WEDNESDAY; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_AMPM; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MICRO_OF_DAY; import static java.time.temporal.ChronoField.MICRO_OF_SECOND; import static java.time.temporal.ChronoField.MILLI_OF_DAY; import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_DAY; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.IsoFields.DAY_OF_QUARTER; import static java.time.temporal.IsoFields.QUARTER_OF_YEAR; import static java.time.temporal.IsoFields.WEEK_BASED_YEAR; import static java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Clock; import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.chrono.IsoChronology; import java.time.chrono.ThaiBuddhistDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQueries; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.Locale; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; @RunWith(DataProviderRunner.class) public class TestYearWeek { private static final YearWeek TEST_NON_LEAP = YearWeek.of(2014, 1); private static final YearWeek TEST = YearWeek.of(2015, 1); @DataProvider public static Object[][] data_sampleYearWeeks() { return new Object[][]{ {2015, 1}, {2015, 2}, {2015, 3}, {2015, 4}, {2015, 5}, {2015, 6}, {2015, 7}, {2015, 8}, {2015, 9}, {2015, 10}, {2015, 11}, {2015, 12}, {2015, 13}, {2015, 14}, {2015, 15}, {2015, 16}, {2015, 17}, {2015, 18}, {2015, 19}, {2015, 20}, {2015, 21}, {2015, 22}, {2015, 21}, {2015, 22}, {2015, 23}, {2015, 23}, {2015, 24}, {2015, 25}, {2015, 26}, {2015, 27}, {2015, 28}, {2015, 29}, {2015, 30}, {2015, 31}, {2015, 32}, {2015, 33}, {2015, 34}, {2015, 35}, {2015, 36}, {2015, 37}, {2015, 38}, {2015, 39}, {2015, 40}, {2015, 41}, {2015, 42}, {2015, 43}, {2015, 44}, {2015, 45}, {2015, 46}, {2015, 47}, {2015, 48}, {2015, 49}, {2015, 50}, {2015, 51}, {2015, 52}, {2015, 53} }; } @DataProvider public static Object[][] data_53WeekYear() { return new Object[][]{ {4}, {9}, {15}, {20}, {26}, {32}, {37}, {43}, {48}, {54}, {60}, {65}, {71}, {76}, {82}, {88}, {93}, {99}, {105}, {111}, {116}, {122}, {128}, {133}, {139}, {144}, {150}, {156}, {161}, {167}, {172}, {178}, {184}, {189}, {195}, {201}, {207}, {212}, {218}, {224}, {229}, {235}, {240}, {246}, {252}, {257}, {263}, {268}, {274}, {280}, {285}, {291}, {296}, {303}, {308}, {314}, {320}, {325}, {331}, {336}, {342}, {348}, {353}, {359}, {364}, {370}, {376}, {381}, {387}, {392}, {398} }; } @DataProvider public static Object[][] data_sampleAtDay() { return new Object[][]{ {2014, 52, MONDAY, 2014, 12, 22}, {2014, 52, TUESDAY, 2014, 12, 23}, {2014, 52, WEDNESDAY, 2014, 12, 24}, {2014, 52, THURSDAY, 2014, 12, 25}, {2014, 52, FRIDAY, 2014, 12, 26}, {2014, 52, SATURDAY, 2014, 12, 27}, {2014, 52, SUNDAY, 2014, 12, 28}, {2015, 1, MONDAY, 2014, 12, 29}, {2015, 1, TUESDAY, 2014, 12, 30}, {2015, 1, WEDNESDAY, 2014, 12, 31}, {2015, 1, THURSDAY, 2015, 1, 1}, {2015, 1, FRIDAY, 2015, 1, 2}, {2015, 1, SATURDAY, 2015, 1, 3}, {2015, 1, SUNDAY, 2015, 1, 4}, {2015, 53, FRIDAY, 2016, 1, 1}, {2015, 53, SATURDAY, 2016, 1, 2}, {2015, 53, SUNDAY, 2016, 1, 3}, {2016, 1, MONDAY, 2016, 1, 4}, {2016, 52, SUNDAY, 2017, 1, 1}, {2017, 1, MONDAY, 2017, 1, 2}, {2017, 1, TUESDAY, 2017, 1, 3}, {2017, 1, WEDNESDAY, 2017, 1, 4}, {2017, 1, THURSDAY, 2017, 1, 5}, {2017, 1, FRIDAY, 2017, 1, 6}, {2017, 1, SATURDAY, 2017, 1, 7}, {2017, 1, SUNDAY, 2017, 1, 8}, {2025, 1, MONDAY, 2024, 12, 30}, }; } //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(YearWeek.class)); assertTrue(Comparable.class.isAssignableFrom(YearWeek.class)); assertTrue(TemporalAdjuster.class.isAssignableFrom(YearWeek.class)); assertTrue(TemporalAccessor.class.isAssignableFrom(YearWeek.class)); } @Test public void test_serialization() throws IOException, ClassNotFoundException { YearWeek test = YearWeek.of(2015, 1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( baos.toByteArray())); assertEquals(test, ois.readObject()); } //----------------------------------------------------------------------- // now() //----------------------------------------------------------------------- @Test public void test_now() { YearWeek expected = YearWeek.now(Clock.systemDefaultZone()); YearWeek test = YearWeek.now(); for (int i = 0; i < 100; i++) { if (expected.equals(test)) { return; } expected = YearWeek.now(Clock.systemDefaultZone()); test = YearWeek.now(); } assertEquals(expected, test); } //----------------------------------------------------------------------- // now(ZoneId) //----------------------------------------------------------------------- @Test(expected = NullPointerException.class) public void now_ZoneId_nullZoneId() { YearWeek.now((ZoneId) null); } @Test public void now_ZoneId() { ZoneId zone = ZoneId.of("UTC+01:02:03"); YearWeek expected = YearWeek.now(Clock.system(zone)); YearWeek test = YearWeek.now(zone); for (int i = 0; i < 100; i++) { if (expected.equals(test)) { return; } expected = YearWeek.now(Clock.system(zone)); test = YearWeek.now(zone); } assertEquals(expected, test); } //----------------------------------------------------------------------- // now(Clock) //----------------------------------------------------------------------- @Test public void now_Clock() { Instant instant = LocalDateTime.of(2010, 12, 31, 0, 0).toInstant(ZoneOffset.UTC); Clock clock = Clock.fixed(instant, ZoneOffset.UTC); YearWeek test = YearWeek.now(clock); assertEquals(2010, test.getYear()); assertEquals(52, test.getWeek()); } @Test(expected = NullPointerException.class) public void now_Clock_nullClock() { YearWeek.now((Clock) null); } //----------------------------------------------------------------------- // of(int, int) //----------------------------------------------------------------------- @Test @UseDataProvider("data_sampleYearWeeks") public void test_of(int year, int week) { YearWeek yearWeek = YearWeek.of(year, week); assertEquals(year, yearWeek.getYear()); assertEquals(week, yearWeek.getWeek()); } @Test public void test_carry() { assertTrue(YearWeek.of(2014, 53).equals(TEST)); } @Test(expected = DateTimeException.class) public void test_of_year_tooLow() { YearWeek.of(Integer.MIN_VALUE, 1); } @Test(expected = DateTimeException.class) public void test_of_year_tooHigh() { YearWeek.of(Integer.MAX_VALUE, 1); } @Test(expected = DateTimeException.class) public void test_of_invalidWeekValue() { YearWeek.of(2015, 54); } @Test(expected = DateTimeException.class) public void test_of_invalidWeekValueZero() { YearWeek.of(2015, 0); } //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- @Test public void test_isSupported_TemporalField() { assertEquals(false, TEST.isSupported((TemporalField) null)); assertEquals(false, TEST.isSupported(NANO_OF_SECOND)); assertEquals(false, TEST.isSupported(NANO_OF_DAY)); assertEquals(false, TEST.isSupported(MICRO_OF_SECOND)); assertEquals(false, TEST.isSupported(MICRO_OF_DAY)); assertEquals(false, TEST.isSupported(MILLI_OF_SECOND)); assertEquals(false, TEST.isSupported(MILLI_OF_DAY)); assertEquals(false, TEST.isSupported(SECOND_OF_MINUTE)); assertEquals(false, TEST.isSupported(SECOND_OF_DAY)); assertEquals(false, TEST.isSupported(MINUTE_OF_HOUR)); assertEquals(false, TEST.isSupported(MINUTE_OF_DAY)); assertEquals(false, TEST.isSupported(HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_AMPM)); assertEquals(false, TEST.isSupported(HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(CLOCK_HOUR_OF_DAY)); assertEquals(false, TEST.isSupported(AMPM_OF_DAY)); assertEquals(false, TEST.isSupported(DAY_OF_WEEK)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(false, TEST.isSupported(DAY_OF_MONTH)); assertEquals(false, TEST.isSupported(DAY_OF_YEAR)); assertEquals(false, TEST.isSupported(EPOCH_DAY)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_MONTH)); assertEquals(false, TEST.isSupported(ALIGNED_WEEK_OF_YEAR)); assertEquals(false, TEST.isSupported(MONTH_OF_YEAR)); assertEquals(false, TEST.isSupported(PROLEPTIC_MONTH)); assertEquals(false, TEST.isSupported(YEAR_OF_ERA)); assertEquals(false, TEST.isSupported(YEAR)); assertEquals(false, TEST.isSupported(ERA)); assertEquals(false, TEST.isSupported(INSTANT_SECONDS)); assertEquals(false, TEST.isSupported(OFFSET_SECONDS)); assertEquals(false, TEST.isSupported(QUARTER_OF_YEAR)); assertEquals(false, TEST.isSupported(DAY_OF_QUARTER)); assertEquals(true, TEST.isSupported(WEEK_BASED_YEAR)); assertEquals(true, TEST.isSupported(WEEK_OF_WEEK_BASED_YEAR)); } //----------------------------------------------------------------------- // atDay(DayOfWeek) //----------------------------------------------------------------------- @Test @UseDataProvider("data_sampleAtDay") public void test_atDay(int weekBasedYear, int weekOfWeekBasedYear, DayOfWeek dayOfWeek, int year, int month, int dayOfMonth) { YearWeek yearWeek = YearWeek.of(weekBasedYear, weekOfWeekBasedYear); LocalDate expected = LocalDate.of(year, month, dayOfMonth); LocalDate actual = yearWeek.atDay(dayOfWeek); assertEquals(expected, actual); } @Test public void test_atDay_loop20years() { YearWeek yearWeek = YearWeek.of(1998, 51); LocalDate expected = LocalDate.of(1998, 12, 14); for (int i = 0; i < (20 * 53); i++) { for (int j = 1; j <= 7; j++) { DayOfWeek dow = DayOfWeek.of(j); LocalDate actual = yearWeek.atDay(dow); assertEquals(expected, actual); expected = expected.plusDays(1); } yearWeek = yearWeek.plusWeeks(1); } } @Test(expected = NullPointerException.class) public void test_atDay_null() { TEST.atDay(null); } //----------------------------------------------------------------------- // is53WeekYear() //----------------------------------------------------------------------- @Test @UseDataProvider("data_53WeekYear") public void test_is53WeekYear(int year) { YearWeek yearWeek = YearWeek.of(year, 1); assertTrue(yearWeek.is53WeekYear()); } //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test public void test_compareTo() { for (int year1 = -100; year1 < 100; year1++) { for (int week1 = 1; week1 < 53; week1++) { YearWeek a = YearWeek.of(year1, week1); for (int year2 = -100; year2 < 100; year2++) { for (int week2 = 1; week2 < 53; week2++) { YearWeek b = YearWeek.of(year2, week2); if (year1 < year2) { assertEquals(true, a.compareTo(b) < 0); assertEquals(true, b.compareTo(a) > 0); assertEquals(false, a.isAfter(b)); assertEquals(false, b.isBefore(a)); assertEquals(true, b.isAfter(a)); assertEquals(true, a.isBefore(b)); } else if (year1 > year2) { assertEquals(true, a.compareTo(b) > 0); assertEquals(true, b.compareTo(a) < 0); assertEquals(true, a.isAfter(b)); assertEquals(true, b.isBefore(a)); assertEquals(false, b.isAfter(a)); assertEquals(false, a.isBefore(b)); } else { if (week1 < week2) { assertEquals(true, a.compareTo(b) < 0); assertEquals(true, b.compareTo(a) > 0); assertEquals(false, a.isAfter(b)); assertEquals(false, b.isBefore(a)); assertEquals(true, b.isAfter(a)); assertEquals(true, a.isBefore(b)); } else if (week1 > week2) { assertEquals(true, a.compareTo(b) > 0); assertEquals(true, b.compareTo(a) < 0); assertEquals(true, a.isAfter(b)); assertEquals(true, b.isBefore(a)); assertEquals(false, b.isAfter(a)); assertEquals(false, a.isBefore(b)); } else { assertEquals(0, a.compareTo(b)); assertEquals(0, b.compareTo(a)); assertEquals(false, a.isAfter(b)); assertEquals(false, b.isBefore(a)); assertEquals(false, b.isAfter(a)); assertEquals(false, a.isBefore(b)); } } } } } } } @Test(expected = NullPointerException.class) public void test_compareTo_nullYearWeek() { TEST.compareTo(null); } //----------------------------------------------------------------------- // from(TemporalAccessor) //----------------------------------------------------------------------- @Test @UseDataProvider("data_sampleAtDay") public void test_from(int weekBasedYear, int weekOfWeekBasedYear, DayOfWeek dayOfWeek, int year, int month, int dayOfMonth) { YearWeek expected = YearWeek.of(weekBasedYear, weekOfWeekBasedYear); LocalDate ld = LocalDate.of(year, month, dayOfMonth); assertEquals(expected, YearWeek.from(ld)); assertEquals(expected, YearWeek.from(ThaiBuddhistDate.from(ld))); assertEquals(expected, YearWeek.from(expected)); } @Test(expected = DateTimeException.class) public void test_from_TemporalAccessor_noDerive() { YearWeek.from(LocalTime.NOON); } @Test(expected = NullPointerException.class) public void test_from_TemporalAccessor_null() { YearWeek.from((TemporalAccessor) null); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(2015, TEST.get(WEEK_BASED_YEAR)); assertEquals(1, TEST.get(WEEK_OF_WEEK_BASED_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_get_invalidField() { TEST.get(YEAR); } @Test(expected = NullPointerException.class) public void test_get_null() { TEST.get((TemporalField) null); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- @Test public void test_getLong() { assertEquals(2015L, TEST.getLong(WEEK_BASED_YEAR)); assertEquals(1L, TEST.getLong(WEEK_OF_WEEK_BASED_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_invalidField() { TEST.getLong(YEAR); } @Test(expected = NullPointerException.class) public void test_getLong_null() { TEST.getLong((TemporalField) null); } //----------------------------------------------------------------------- // lengthOfYear() //----------------------------------------------------------------------- @Test public void test_lengthOfYear() { assertEquals(364, YearWeek.of(2014, 1).lengthOfYear()); assertEquals(371, YearWeek.of(2015, 1).lengthOfYear()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { assertEquals("2015-W01", TEST.toString()); } //----------------------------------------------------------------------- // parse(CharSequence) //----------------------------------------------------------------------- @Test public void test_parse_CharSequence() { assertEquals(TEST, YearWeek.parse("2015-W01")); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequenceDate_invalidYear() { YearWeek.parse("12345-W7"); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequenceDate_invalidWeek() { YearWeek.parse("2015-W54"); } @Test(expected = NullPointerException.class) public void test_parse_CharSequenceDate_nullCharSequence() { YearWeek.parse((CharSequence) null); } //----------------------------------------------------------------------- // parse(CharSequence,DateTimeFormatter) //----------------------------------------------------------------------- @Test public void test_parse_CharSequenceDateTimeFormatter() { DateTimeFormatter f = DateTimeFormatter.ofPattern("E 'W'w YYYY").withLocale(Locale.ENGLISH); assertEquals(TEST, YearWeek.parse("Mon W1 2015", f)); } @Test(expected = DateTimeParseException.class) public void test_parse_CharSequenceDateDateTimeFormatter_invalidWeek() { DateTimeFormatter f = DateTimeFormatter.ofPattern("E 'W'w YYYY").withLocale(Locale.ENGLISH); YearWeek.parse("Mon W99 2015", f); } @Test(expected = NullPointerException.class) public void test_parse_CharSequenceDateTimeFormatter_nullCharSequence() { DateTimeFormatter f = DateTimeFormatter.ofPattern("E 'W'w YYYY").withLocale(Locale.ENGLISH); YearWeek.parse((CharSequence) null, f); } @Test(expected = NullPointerException.class) public void test_parse_CharSequenceDateTimeFormatter_nullDateTimeFormatter() { YearWeek.parse("", (DateTimeFormatter) null); } //----------------------------------------------------------------------- // format() //----------------------------------------------------------------------- @Test public void test_format() { DateTimeFormatter f = new DateTimeFormatterBuilder() .appendValue(WEEK_BASED_YEAR, 4) .appendLiteral('-') .appendValue(WEEK_OF_WEEK_BASED_YEAR, 2) .toFormatter(); assertEquals("2015-01", TEST.format(f)); } //----------------------------------------------------------------------- // adjustInto() //----------------------------------------------------------------------- @Test public void test_adjustInto() { YearWeek yw = YearWeek.of(2016, 1); LocalDate date = LocalDate.of(2015, 6, 20); assertEquals(LocalDate.of(2016, 1, 9), yw.adjustInto(date)); } @Test(expected = DateTimeException.class) public void test_adjustInto_badChronology() { YearWeek yw = YearWeek.of(2016, 1); ThaiBuddhistDate date = ThaiBuddhistDate.from(LocalDate.of(2015, 6, 20)); yw.adjustInto(date); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- @Test public void test_range() { assertEquals(WEEK_BASED_YEAR.range(), TEST_NON_LEAP.range(WEEK_BASED_YEAR)); assertEquals(WEEK_BASED_YEAR.range(), TEST.range(WEEK_BASED_YEAR)); assertEquals(ValueRange.of(1, 52), TEST_NON_LEAP.range(WEEK_OF_WEEK_BASED_YEAR)); assertEquals(ValueRange.of(1, 53), TEST.range(WEEK_OF_WEEK_BASED_YEAR)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_invalidField() { TEST.range(YEAR); } @Test(expected = NullPointerException.class) public void test_range_null() { TEST.range((TemporalField) null); } //----------------------------------------------------------------------- // withYear(int) //----------------------------------------------------------------------- @Test public void test_withYear() { assertEquals(YearWeek.of(2014, 1), YearWeek.of(2015, 1).withYear(2014)); assertEquals(YearWeek.of(2009, 53), YearWeek.of(2015, 53).withYear(2009)); } @Test public void test_withYear_sameYear() { assertEquals(YearWeek.of(2015, 1), YearWeek.of(2015, 1).withYear(2015)); } @Test public void test_withYear_resolve() { assertEquals(YearWeek.of(2014, 52), YearWeek.of(2015, 53).withYear(2014)); } @Test(expected = DateTimeException.class) public void test_withYear_int_max() { TEST.withYear(Integer.MAX_VALUE); } @Test(expected = DateTimeException.class) public void test_withYear_int_min() { TEST.withYear(Integer.MIN_VALUE); } //----------------------------------------------------------------------- // withWeek(int) //----------------------------------------------------------------------- @Test public void test_withWeek() { assertEquals(YearWeek.of(2015, 52), TEST.withWeek(52)); assertEquals(TEST, YearWeek.of(2014, 1).withWeek(53)); } @Test public void test_withWeek_sameWeek() { assertEquals(YearWeek.of(2014, 2), YearWeek.of(2014, 2).withWeek(2)); } @Test(expected = DateTimeException.class) public void test_withWeek_int_max() { TEST.withWeek(Integer.MAX_VALUE); } @Test(expected = DateTimeException.class) public void test_withWeek_int_min() { TEST.withWeek(Integer.MIN_VALUE); } //----------------------------------------------------------------------- // plusYears(long) //----------------------------------------------------------------------- @Test public void test_plusYears() { assertEquals(YearWeek.of(2013, 1), TEST.plusYears(-2)); assertEquals(YearWeek.of(2014, 1), TEST.plusYears(-1)); assertEquals(TEST, TEST.plusYears(0)); assertEquals(YearWeek.of(2016, 1), TEST.plusYears(1)); assertEquals(YearWeek.of(2017, 1), TEST.plusYears(2)); } @Test public void test_plusYears_changeWeek() { assertEquals(YearWeek.of(2014, 52), YearWeek.of(2015, 53).plusYears(-1)); assertEquals(YearWeek.of(2015, 53), YearWeek.of(2015, 53).plusYears(0)); assertEquals(YearWeek.of(2016, 52), YearWeek.of(2015, 53).plusYears(1)); assertEquals(YearWeek.of(2020, 53), YearWeek.of(2015, 53).plusYears(5)); } @Test(expected = ArithmeticException.class) public void test_plusYears_max_long() { TEST.plusYears(Long.MAX_VALUE); } @Test(expected = ArithmeticException.class) public void test_plusYears_min_long() { TEST.plusYears(Long.MIN_VALUE); } //----------------------------------------------------------------------- // plusWeeks(long) //----------------------------------------------------------------------- @Test public void test_plusWeeks() { assertEquals(TEST, TEST.plusWeeks(0)); assertEquals(YearWeek.of(2015, 2), TEST.plusWeeks(1)); assertEquals(YearWeek.of(2015, 3), TEST.plusWeeks(2)); assertEquals(YearWeek.of(2015, 52), TEST.plusWeeks(51)); assertEquals(YearWeek.of(2015, 53), TEST.plusWeeks(52)); assertEquals(YearWeek.of(2016, 1), TEST.plusWeeks(53)); assertEquals(YearWeek.of(2021, 1), TEST.plusWeeks(314)); } @Test public void test_plusWeeks_negative() { assertEquals(TEST, TEST.plusWeeks(0)); assertEquals(YearWeek.of(2014, 52), TEST.plusWeeks(-1)); assertEquals(YearWeek.of(2014, 51), TEST.plusWeeks(-2)); assertEquals(YearWeek.of(2014, 2), TEST.plusWeeks(-51)); assertEquals(YearWeek.of(2014, 1), TEST.plusWeeks(-52)); assertEquals(YearWeek.of(2013, 52), TEST.plusWeeks(-53)); assertEquals(YearWeek.of(2009, 53), TEST.plusWeeks(-261)); } @Test(expected = ArithmeticException.class) public void test_plusWeeks_max_long() { TEST.plusWeeks(Long.MAX_VALUE); } @Test(expected = ArithmeticException.class) public void test_plusWeeks_min_long() { TEST.plusWeeks(Long.MIN_VALUE); } //----------------------------------------------------------------------- // minusYears(long) //----------------------------------------------------------------------- @Test public void test_minusYears() { assertEquals(YearWeek.of(2017, 1), TEST.minusYears(-2)); assertEquals(YearWeek.of(2016, 1), TEST.minusYears(-1)); assertEquals(TEST, TEST.minusYears(0)); assertEquals(YearWeek.of(2014, 1), TEST.minusYears(1)); assertEquals(YearWeek.of(2013, 1), TEST.minusYears(2)); } @Test public void test_minusYears_changeWeek() { assertEquals(YearWeek.of(2020, 53), YearWeek.of(2015, 53).minusYears(-5)); assertEquals(YearWeek.of(2016, 52), YearWeek.of(2015, 53).minusYears(-1)); assertEquals(YearWeek.of(2015, 53), YearWeek.of(2015, 53).minusYears(0)); assertEquals(YearWeek.of(2014, 52), YearWeek.of(2015, 53).minusYears(1)); } @Test(expected = ArithmeticException.class) public void test_minusYears_max_long() { TEST.minusYears(Long.MAX_VALUE); } @Test(expected = ArithmeticException.class) public void test_minusYears_min_long() { TEST.minusYears(Long.MIN_VALUE); } //----------------------------------------------------------------------- // minusWeeks(long) //----------------------------------------------------------------------- @Test public void test_minusWeeks() { assertEquals(TEST, TEST.minusWeeks(0)); assertEquals(YearWeek.of(2014, 52), TEST.minusWeeks(1)); assertEquals(YearWeek.of(2014, 51), TEST.minusWeeks(2)); assertEquals(YearWeek.of(2014, 2), TEST.minusWeeks(51)); assertEquals(YearWeek.of(2014, 1), TEST.minusWeeks(52)); assertEquals(YearWeek.of(2013, 52), TEST.minusWeeks(53)); assertEquals(YearWeek.of(2009, 53), TEST.minusWeeks(261)); } @Test public void test_minusWeeks_negative() { assertEquals(TEST, TEST.minusWeeks(0)); assertEquals(YearWeek.of(2015, 2), TEST.minusWeeks(-1)); assertEquals(YearWeek.of(2015, 3), TEST.minusWeeks(-2)); assertEquals(YearWeek.of(2015, 52), TEST.minusWeeks(-51)); assertEquals(YearWeek.of(2015, 53), TEST.minusWeeks(-52)); assertEquals(YearWeek.of(2016, 1), TEST.minusWeeks(-53)); assertEquals(YearWeek.of(2021, 1), TEST.minusWeeks(-314)); } @Test(expected = ArithmeticException.class) public void test_minWeeks_max_long() { TEST.plusWeeks(Long.MAX_VALUE); } @Test(expected = ArithmeticException.class) public void test_minWeeks_min_long() { TEST.plusWeeks(Long.MIN_VALUE); } //----------------------------------------------------------------------- // query(TemporalQuery) //----------------------------------------------------------------------- @Test public void test_query() { assertEquals(IsoChronology.INSTANCE, TEST.query(TemporalQueries.chronology())); assertEquals(null, TEST.query(TemporalQueries.localDate())); assertEquals(null, TEST.query(TemporalQueries.localTime())); assertEquals(null, TEST.query(TemporalQueries.offset())); assertEquals(null, TEST.query(TemporalQueries.precision())); assertEquals(null, TEST.query(TemporalQueries.zone())); assertEquals(null, TEST.query(TemporalQueries.zoneId())); } //----------------------------------------------------------------------- // equals() / hashCode() //----------------------------------------------------------------------- @Test @UseDataProvider("data_sampleYearWeeks") public void test_equalsAndHashCodeContract(int year, int week) { YearWeek a = YearWeek.of(year, week); YearWeek b = YearWeek.of(year, week); assertTrue(a.equals(b)); assertTrue(b.equals(a)); assertTrue(a.hashCode() == b.hashCode()); } @Test public void test_equals() { YearWeek a = YearWeek.of(2015, 4); YearWeek b = YearWeek.of(2015, 6); YearWeek c = YearWeek.of(2016, 6); assertFalse(a.equals(b)); assertFalse(a.equals(c)); assertFalse(b.equals(a)); assertFalse(b.equals(c)); assertFalse(c.equals(a)); assertFalse(c.equals(b)); } @Test public void test_equals_incorrectType() { assertTrue(TEST.equals(null) == false); assertEquals(false, TEST.equals("Incorrect type")); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_sampleToString() { return new Object[][]{ {2015, 1, "2015-W01"}, {2015, 10, "2015-W10"}, {999, 1, "0999-W01"}, {-999, 1, "-0999-W01"}, {10000, 1, "+10000-W01"}, {-10000, 1, "-10000-W01"},}; } @Test @UseDataProvider("data_sampleToString") public void test_toString(int year, int week, String expected) { YearWeek yearWeek = YearWeek.of(year, week); String s = yearWeek.toString(); assertEquals(expected, s); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/TestYears.java000066400000000000000000000441121343451174100254660ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.Period; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.IsoFields; import java.time.temporal.Temporal; import java.time.temporal.TemporalAmount; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test class. */ @RunWith(DataProviderRunner.class) public class TestYears { //----------------------------------------------------------------------- @Test public void test_isSerializable() { assertTrue(Serializable.class.isAssignableFrom(Years.class)); } //----------------------------------------------------------------------- @Test public void test_deserializationSingleton() throws Exception { Years test = Years.ZERO; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- @Test public void test_ZERO() { assertSame(Years.of(0), Years.ZERO); assertEquals(Years.of(0), Years.ZERO); assertEquals(0, Years.ZERO.getAmount()); } @Test public void test_ONE() { assertSame(Years.of(1), Years.ONE); assertEquals(Years.of(1), Years.ONE); assertEquals(1, Years.ONE.getAmount()); } //----------------------------------------------------------------------- @Test public void test_of() { assertEquals(1, Years.of(1).getAmount()); assertEquals(2, Years.of(2).getAmount()); assertEquals(Integer.MAX_VALUE, Years.of(Integer.MAX_VALUE).getAmount()); assertEquals(-1, Years.of(-1).getAmount()); assertEquals(-2, Years.of(-2).getAmount()); assertEquals(Integer.MIN_VALUE, Years.of(Integer.MIN_VALUE).getAmount()); } //----------------------------------------------------------------------- @Test public void test_from_P0Y() { assertEquals(Years.of(0), Years.from(Period.ofYears(0))); } @Test public void test_from_P2Y() { assertEquals(Years.of(2), Years.from(Period.ofYears(2))); } @Test public void test_from_P24M() { assertEquals(Years.of(2), Years.from(Period.ofMonths(24))); } @Test public void test_from_yearsAndMonths() { assertEquals(Years.of(5), Years.from(Period.of(3, 24, 0))); } @Test public void test_from_decadesAndMonths() { assertEquals(Years.of(19), Years.from(new MockDecadesMonths(2, -12))); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_remainder() { Years.from(Period.ofMonths(3)); } @Test(expected = DateTimeException.class) public void test_from_wrongUnit_noConversion() { Years.from(Period.ofDays(2)); } @Test(expected = NullPointerException.class) public void test_from_null() { Years.from((TemporalAmount) null); } //----------------------------------------------------------------------- @Test public void test_parse_CharSequence() { assertEquals(Years.of(0), Years.parse("P0Y")); assertEquals(Years.of(1), Years.parse("P1Y")); assertEquals(Years.of(2), Years.parse("P2Y")); assertEquals(Years.of(123456789), Years.parse("P123456789Y")); assertEquals(Years.of(-2), Years.parse("P-2Y")); assertEquals(Years.of(-2), Years.parse("-P2Y")); assertEquals(Years.of(2), Years.parse("-P-2Y")); } @DataProvider public static Object[][] data_invalid() { return new Object[][] { {"P3M"}, {"P3W"}, {"P3D"}, {"3"}, {"-3"}, {"3Y"}, {"-3Y"}, {"P3"}, {"P-3"}, {"PY"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_invalid") public void test_parse_CharSequence_invalid(String str) { Years.parse(str); } @Test(expected = NullPointerException.class) public void test_parse_CharSequence_null() { Years.parse((CharSequence) null); } //----------------------------------------------------------------------- @Test public void test_between() { assertEquals(Years.of(2), Years.between(LocalDate.of(2019, 1, 1), LocalDate.of(2021, 1, 1))); } @Test(expected = NullPointerException.class) public void test_between_date_null() { Years.between(LocalDate.now(), (Temporal) null); } @Test(expected = NullPointerException.class) public void test_between_null_date() { Years.between((Temporal) null, LocalDate.now()); } //----------------------------------------------------------------------- @Test public void test_get() { assertEquals(6, Years.of(6).get(ChronoUnit.YEARS)); } @Test(expected = DateTimeException.class) public void test_get_invalidType() { Years.of(6).get(IsoFields.QUARTER_YEARS); } //----------------------------------------------------------------------- @Test public void test_plus_TemporalAmount_Years() { Years test5 = Years.of(5); assertEquals(Years.of(5), test5.plus(Years.of(0))); assertEquals(Years.of(7), test5.plus(Years.of(2))); assertEquals(Years.of(3), test5.plus(Years.of(-2))); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE - 1).plus(Years.of(1))); assertEquals(Years.of(Integer.MIN_VALUE), Years.of(Integer.MIN_VALUE + 1).plus(Years.of(-1))); } @Test public void test_plus_TemporalAmount_Period() { Years test5 = Years.of(5); assertEquals(Years.of(5), test5.plus(Period.ofYears(0))); assertEquals(Years.of(7), test5.plus(Period.ofYears(2))); assertEquals(Years.of(3), test5.plus(Period.ofYears(-2))); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE - 1).plus(Period.ofYears(1))); assertEquals(Years.of(Integer.MIN_VALUE), Years.of(Integer.MIN_VALUE + 1).plus(Period.ofYears(-1))); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_PeriodMonths() { Years.of(1).plus(Period.ofMonths(2)); } @Test(expected = DateTimeException.class) public void test_plus_TemporalAmount_Duration() { Years.of(1).plus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooBig() { Years.of(Integer.MAX_VALUE - 1).plus(Years.of(2)); } @Test(expected = ArithmeticException.class) public void test_plus_TemporalAmount_overflowTooSmall() { Years.of(Integer.MIN_VALUE + 1).plus(Years.of(-2)); } @Test(expected = NullPointerException.class) public void test_plus_TemporalAmount_null() { Years.of(Integer.MIN_VALUE + 1).plus(null); } //----------------------------------------------------------------------- @Test public void test_plus_int() { Years test5 = Years.of(5); assertEquals(Years.of(5), test5.plus(0)); assertEquals(Years.of(7), test5.plus(2)); assertEquals(Years.of(3), test5.plus(-2)); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE - 1).plus(1)); assertEquals(Years.of(Integer.MIN_VALUE), Years.of(Integer.MIN_VALUE + 1).plus(-1)); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooBig() { Years.of(Integer.MAX_VALUE - 1).plus(2); } @Test(expected = ArithmeticException.class) public void test_plus_int_overflowTooSmall() { Years.of(Integer.MIN_VALUE + 1).plus(-2); } //----------------------------------------------------------------------- @Test public void test_minus_TemporalAmount_Years() { Years test5 = Years.of(5); assertEquals(Years.of(5), test5.minus(Years.of(0))); assertEquals(Years.of(3), test5.minus(Years.of(2))); assertEquals(Years.of(7), test5.minus(Years.of(-2))); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE - 1).minus(Years.of(-1))); assertEquals(Years.of(Integer.MIN_VALUE), Years.of(Integer.MIN_VALUE + 1).minus(Years.of(1))); } @Test public void test_minus_TemporalAmount_Period() { Years test5 = Years.of(5); assertEquals(Years.of(5), test5.minus(Period.ofYears(0))); assertEquals(Years.of(3), test5.minus(Period.ofYears(2))); assertEquals(Years.of(7), test5.minus(Period.ofYears(-2))); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE - 1).minus(Period.ofYears(-1))); assertEquals(Years.of(Integer.MIN_VALUE), Years.of(Integer.MIN_VALUE + 1).minus(Period.ofYears(1))); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_PeriodMonths() { Years.of(1).minus(Period.ofMonths(2)); } @Test(expected = DateTimeException.class) public void test_minus_TemporalAmount_Duration() { Years.of(1).minus(Duration.ofHours(2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooBig() { Years.of(Integer.MAX_VALUE - 1).minus(Years.of(-2)); } @Test(expected = ArithmeticException.class) public void test_minus_TemporalAmount_overflowTooSmall() { Years.of(Integer.MIN_VALUE + 1).minus(Years.of(2)); } @Test(expected = NullPointerException.class) public void test_minus_TemporalAmount_null() { Years.of(Integer.MIN_VALUE + 1).minus(null); } //----------------------------------------------------------------------- @Test public void test_minus_int() { Years test5 = Years.of(5); assertEquals(Years.of(5), test5.minus(0)); assertEquals(Years.of(3), test5.minus(2)); assertEquals(Years.of(7), test5.minus(-2)); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE - 1).minus(-1)); assertEquals(Years.of(Integer.MIN_VALUE), Years.of(Integer.MIN_VALUE + 1).minus(1)); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooBig() { Years.of(Integer.MAX_VALUE - 1).minus(-2); } @Test(expected = ArithmeticException.class) public void test_minus_int_overflowTooSmall() { Years.of(Integer.MIN_VALUE + 1).minus(2); } //----------------------------------------------------------------------- @Test public void test_multipliedBy() { Years test5 = Years.of(5); assertEquals(Years.of(0), test5.multipliedBy(0)); assertEquals(Years.of(5), test5.multipliedBy(1)); assertEquals(Years.of(10), test5.multipliedBy(2)); assertEquals(Years.of(15), test5.multipliedBy(3)); assertEquals(Years.of(-15), test5.multipliedBy(-3)); } @Test public void test_multipliedBy_negate() { Years test5 = Years.of(5); assertEquals(Years.of(-15), test5.multipliedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooBig() { Years.of(Integer.MAX_VALUE / 2 + 1).multipliedBy(2); } @Test(expected = ArithmeticException.class) public void test_multipliedBy_overflowTooSmall() { Years.of(Integer.MIN_VALUE / 2 - 1).multipliedBy(2); } //----------------------------------------------------------------------- @Test public void test_dividedBy() { Years test12 = Years.of(12); assertEquals(Years.of(12), test12.dividedBy(1)); assertEquals(Years.of(6), test12.dividedBy(2)); assertEquals(Years.of(4), test12.dividedBy(3)); assertEquals(Years.of(3), test12.dividedBy(4)); assertEquals(Years.of(2), test12.dividedBy(5)); assertEquals(Years.of(2), test12.dividedBy(6)); assertEquals(Years.of(-4), test12.dividedBy(-3)); } @Test public void test_dividedBy_negate() { Years test12 = Years.of(12); assertEquals(Years.of(-4), test12.dividedBy(-3)); } @Test(expected = ArithmeticException.class) public void test_dividedBy_divideByZero() { Years.of(1).dividedBy(0); } //----------------------------------------------------------------------- @Test public void test_negated() { assertEquals(Years.of(0), Years.of(0).negated()); assertEquals(Years.of(-12), Years.of(12).negated()); assertEquals(Years.of(12), Years.of(-12).negated()); assertEquals(Years.of(-Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE).negated()); } @Test(expected = ArithmeticException.class) public void test_negated_overflow() { Years.of(Integer.MIN_VALUE).negated(); } //----------------------------------------------------------------------- @Test public void test_abs() { assertEquals(Years.of(0), Years.of(0).abs()); assertEquals(Years.of(12), Years.of(12).abs()); assertEquals(Years.of(12), Years.of(-12).abs()); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(Integer.MAX_VALUE).abs()); assertEquals(Years.of(Integer.MAX_VALUE), Years.of(-Integer.MAX_VALUE).abs()); } @Test(expected = ArithmeticException.class) public void test_abs_overflow() { Years.of(Integer.MIN_VALUE).abs(); } //----------------------------------------------------------------------- @Test public void test_addTo() { assertEquals(LocalDate.of(2019, 1, 10), Years.of(0).addTo(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2024, 1, 10), Years.of(5).addTo(LocalDate.of(2019, 1, 10))); } @Test public void test_subtractFrom() { assertEquals(LocalDate.of(2019, 1, 10), Years.of(0).subtractFrom(LocalDate.of(2019, 1, 10))); assertEquals(LocalDate.of(2014, 1, 10), Years.of(5).subtractFrom(LocalDate.of(2019, 1, 10))); } //----------------------------------------------------------------------- @Test public void test_toPeriod() { for (int i = -20; i < 20; i++) { assertEquals(Period.ofYears(i), Years.of(i).toPeriod()); } } //----------------------------------------------------------------------- @Test public void test_compareTo() { Years test5 = Years.of(5); Years test6 = Years.of(6); assertEquals(0, test5.compareTo(test5)); assertEquals(-1, test5.compareTo(test6)); assertEquals(1, test6.compareTo(test5)); } @Test(expected = NullPointerException.class) public void test_compareTo_null() { Years test5 = Years.of(5); test5.compareTo(null); } //----------------------------------------------------------------------- @Test public void test_equals() { Years test5 = Years.of(5); Years test6 = Years.of(6); assertEquals(true, test5.equals(test5)); assertEquals(false, test5.equals(test6)); assertEquals(false, test6.equals(test5)); } @Test public void test_equals_null() { Years test5 = Years.of(5); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { Years test5 = Years.of(5); Object obj = ""; assertEquals(false, test5.equals(obj)); } //----------------------------------------------------------------------- @Test public void test_hashCode() { Years test5 = Years.of(5); Years test6 = Years.of(6); assertEquals(true, test5.hashCode() == test5.hashCode()); assertEquals(false, test5.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- @Test public void test_toString() { Years test5 = Years.of(5); assertEquals("P5Y", test5.toString()); Years testM1 = Years.of(-1); assertEquals("P-1Y", testM1.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/000077500000000000000000000000001343451174100241665ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestAccountingChronology.java000066400000000000000000001007301343451174100320300ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.List; import java.util.function.Predicate; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestAccountingChronology { private static AccountingChronology INSTANCE = new AccountingChronologyBuilder().endsOn(DayOfWeek.SUNDAY).nearestEndOf(Month.AUGUST). withDivision(AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS).leapWeekInMonth(13).toChronology(); //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Assert.assertEquals("Accounting", INSTANCE.getId()); } @Test public void test_chronology_of_name_id() { Assert.assertEquals(null, INSTANCE.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {INSTANCE.date(1, 1, 1), LocalDate.of(0, 9, 4)}, {INSTANCE.date(1, 1, 2), LocalDate.of(0, 9, 5)}, {INSTANCE.date(1, 1, 3), LocalDate.of(0, 9, 6)}, {INSTANCE.date(2011, 13, 28), LocalDate.of(2011, 8, 28)}, {INSTANCE.date(2012, 1, 1), LocalDate.of(2011, 8, 29)}, {INSTANCE.date(2012, 1, 2), LocalDate.of(2011, 8, 30)}, {INSTANCE.date(2012, 1, 3), LocalDate.of(2011, 8, 31)}, {INSTANCE.date(2012, 13, 28), LocalDate.of(2012, 8, 26)}, {INSTANCE.date(2012, 13, 29), LocalDate.of(2012, 8, 27)}, {INSTANCE.date(2012, 13, 30), LocalDate.of(2012, 8, 28)}, {INSTANCE.date(2012, 13, 31), LocalDate.of(2012, 8, 29)}, {INSTANCE.date(2012, 13, 32), LocalDate.of(2012, 8, 30)}, {INSTANCE.date(2012, 13, 33), LocalDate.of(2012, 8, 31)}, {INSTANCE.date(2012, 13, 34), LocalDate.of(2012, 9, 1)}, {INSTANCE.date(2012, 13, 35), LocalDate.of(2012, 9, 2)}, {INSTANCE.date(2013, 1, 1), LocalDate.of(2012, 9, 3)}, {INSTANCE.date(2013, 1, 2), LocalDate.of(2012, 9, 4)}, {INSTANCE.date(2013, 1, 3), LocalDate.of(2012, 9, 5)}, {INSTANCE.date(0, 13, 35), LocalDate.of(0, 9, 3)}, {INSTANCE.date(0, 13, 34), LocalDate.of(0, 9, 2)}, {INSTANCE.date(1583, 2, 18), LocalDate.of(1582, 10, 14)}, {INSTANCE.date(1583, 2, 19), LocalDate.of(1582, 10, 15)}, {INSTANCE.date(1946, 3, 15), LocalDate.of(1945, 11, 12)}, {INSTANCE.date(2012, 12, 4), LocalDate.of(2012, 7, 5)}, {INSTANCE.date(2012, 12, 5), LocalDate.of(2012, 7, 6)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_AccountingDate(AccountingDate accounting, LocalDate iso) { assertEquals(iso, LocalDate.from(accounting)); } @Test @UseDataProvider("data_samples") public void test_AccountingDate_from_LocalDate(AccountingDate accounting, LocalDate iso) { assertEquals(accounting, AccountingDate.from(INSTANCE, iso)); } @Test @UseDataProvider("data_samples") public void test_AccountingDate_chronology_dateEpochDay(AccountingDate accounting, LocalDate iso) { assertEquals(accounting, INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_AccountingDate_toEpochDay(AccountingDate accounting, LocalDate iso) { assertEquals(iso.toEpochDay(), accounting.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_AccountingDate_until_CoptiDate(AccountingDate accounting, LocalDate iso) { assertEquals(INSTANCE.period(0, 0, 0), accounting.until(accounting)); } @Test @UseDataProvider("data_samples") public void test_AccountingDate_until_LocalDate(AccountingDate accounting, LocalDate iso) { assertEquals(INSTANCE.period(0, 0, 0), accounting.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_CoptiDate(AccountingDate accounting, LocalDate iso) { assertEquals(Period.ZERO, iso.until(accounting)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(AccountingDate accounting, LocalDate iso) { assertEquals(accounting, INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(AccountingDate accounting, LocalDate iso) { assertEquals(iso, LocalDate.from(accounting.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(accounting.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(accounting.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(accounting.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(accounting.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(AccountingDate accounting, LocalDate iso) { assertEquals(iso, LocalDate.from(accounting.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(accounting.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(accounting.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(accounting.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(accounting.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(AccountingDate accounting, LocalDate iso) { assertEquals(0, accounting.until(iso.plusDays(0), DAYS)); assertEquals(1, accounting.until(iso.plusDays(1), DAYS)); assertEquals(35, accounting.until(iso.plusDays(35), DAYS)); assertEquals(-40, accounting.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {2012, 0, 0}, {2012, -1, 1}, {2012, 0, 1}, {2012, 14, 1}, {2012, 15, 1}, {2012, 1, -1}, {2012, 1, 0}, {2012, 1, 29}, {2012, 13, -1}, {2012, 13, 0}, {2012, 13, 36}, {2012, 13, 37}, {2012, 13, 38}, {2011, 13, -1}, {2011, 13, 0}, {2011, 13, 29}, {2011, 13, 30}, {2011, 13, 31}, {2011, 13, 32}, {2011, 13, 33}, {2011, 13, 34}, {2011, 13, 35}, {2012, 2, 29}, {2012, 3, 29}, {2012, 4, 29}, {2012, 5, 29}, {2012, 6, 29}, {2012, 7, 29}, {2012, 8, 29}, {2012, 9, 29}, {2012, 10, 29}, {2012, 11, 29}, {2012, 12, 29}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { INSTANCE.date(year, month, dom); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { INSTANCE.dateYearDay(2001, 366); } @Test(expected = NullPointerException.class) public void test_date_create_no_chronology() { AccountingDate.create(null, 2012, 1, 1); } @Test(expected = NullPointerException.class) public void test_date_from_no_chronology() { AccountingDate.from(null, LocalDate.of(2012, 1, 1)); } @Test(expected = NullPointerException.class) public void test_date_now_no_chronology() { AccountingDate.now(null); } @Test(expected = NullPointerException.class) public void test_date_of_no_chronology() { AccountingDate.of(null, 2012, 1, 1); } @Test(expected = NullPointerException.class) public void test_date_ofEpochDay_no_chronology() { AccountingDate.ofEpochDay(null, 0); } @Test(expected = NullPointerException.class) public void test_date_ofYearDay_no_chronology() { AccountingDate.ofYearDay(null, 0, 1); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { Predicate isLeapYear = year -> { LocalDate currentYearEnd = LocalDate.of(year, 9, 3).with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)); LocalDate prevYearEnd = LocalDate.of(year - 1, 9, 3).with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = -200; year < 200; year++) { AccountingDate base = INSTANCE.date(year, 1, 1); assertEquals(isLeapYear.test(year), base.isLeapYear()); assertEquals(isLeapYear.test(year), INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertEquals(false, INSTANCE.isLeapYear(8)); assertEquals(false, INSTANCE.isLeapYear(7)); assertEquals(true, INSTANCE.isLeapYear(6)); assertEquals(false, INSTANCE.isLeapYear(5)); assertEquals(false, INSTANCE.isLeapYear(4)); assertEquals(false, INSTANCE.isLeapYear(3)); assertEquals(false, INSTANCE.isLeapYear(2)); assertEquals(false, INSTANCE.isLeapYear(1)); assertEquals(true, INSTANCE.isLeapYear(0)); assertEquals(false, INSTANCE.isLeapYear(-1)); assertEquals(false, INSTANCE.isLeapYear(-2)); assertEquals(false, INSTANCE.isLeapYear(-3)); assertEquals(false, INSTANCE.isLeapYear(-4)); assertEquals(true, INSTANCE.isLeapYear(-5)); assertEquals(false, INSTANCE.isLeapYear(-6)); } @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {2012, 1, 28}, {2012, 2, 28}, {2012, 3, 28}, {2012, 4, 28}, {2012, 5, 28}, {2012, 6, 28}, {2012, 7, 28}, {2012, 8, 28}, {2012, 9, 28}, {2012, 10, 28}, {2012, 11, 28}, {2012, 12, 28}, {2012, 13, 35}, {2013, 13, 28}, {2014, 13, 28}, {2015, 13, 28}, {2016, 13, 28}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, INSTANCE.date(year, month, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = -200; year < 200; year++) { AccountingDate base = INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); AccountingEra era = (year <= 0 ? AccountingEra.BCE : AccountingEra.CE); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); AccountingDate eraBased = INSTANCE.date(era, yoe, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = -200; year < 200; year++) { AccountingDate base = INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); AccountingEra era = (year <= 0 ? AccountingEra.BCE : AccountingEra.CE); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); AccountingDate eraBased = INSTANCE.dateYearDay(era, yoe, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, INSTANCE.prolepticYear(AccountingEra.CE, 4)); assertEquals(3, INSTANCE.prolepticYear(AccountingEra.CE, 3)); assertEquals(2, INSTANCE.prolepticYear(AccountingEra.CE, 2)); assertEquals(1, INSTANCE.prolepticYear(AccountingEra.CE, 1)); assertEquals(0, INSTANCE.prolepticYear(AccountingEra.BCE, 1)); assertEquals(-1, INSTANCE.prolepticYear(AccountingEra.BCE, 2)); assertEquals(-2, INSTANCE.prolepticYear(AccountingEra.BCE, 3)); assertEquals(-3, INSTANCE.prolepticYear(AccountingEra.BCE, 4)); } @Test(expected = ClassCastException.class) public void test_prolepticYear_badEra() { INSTANCE.prolepticYear(IsoEra.CE, 4); } @Test public void test_Chronology_eraOf() { assertEquals(AccountingEra.CE, INSTANCE.eraOf(1)); assertEquals(AccountingEra.BCE, INSTANCE.eraOf(0)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = INSTANCE.eras(); assertEquals(2, eras.size()); assertEquals(true, eras.contains(AccountingEra.BCE)); assertEquals(true, eras.contains(AccountingEra.CE)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 28, 35), INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 364, 371), INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 13), INSTANCE.range(MONTH_OF_YEAR)); assertEquals(ValueRange.of(1, 52, 53), INSTANCE.range(ALIGNED_WEEK_OF_YEAR)); } //----------------------------------------------------------------------- // AccountingDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { {2012, 1, 23, DAY_OF_MONTH, 1, 28}, {2012, 2, 23, DAY_OF_MONTH, 1, 28}, {2012, 3, 23, DAY_OF_MONTH, 1, 28}, {2012, 4, 23, DAY_OF_MONTH, 1, 28}, {2012, 5, 23, DAY_OF_MONTH, 1, 28}, {2012, 6, 23, DAY_OF_MONTH, 1, 28}, {2012, 7, 23, DAY_OF_MONTH, 1, 28}, {2012, 8, 23, DAY_OF_MONTH, 1, 28}, {2012, 9, 23, DAY_OF_MONTH, 1, 28}, {2012, 10, 23, DAY_OF_MONTH, 1, 28}, {2012, 11, 23, DAY_OF_MONTH, 1, 28}, {2012, 12, 23, DAY_OF_MONTH, 1, 28}, {2012, 13, 23, DAY_OF_MONTH, 1, 35}, {2012, 1, 23, DAY_OF_YEAR, 1, 371}, {2012, 12, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, {2012, 13, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2013, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, {2011, 13, 23, DAY_OF_MONTH, 1, 28}, {2011, 13, 23, DAY_OF_YEAR, 1, 364}, {2011, 13, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), INSTANCE.date(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { INSTANCE.date(2012, 6, 28).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // AccountingDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 5}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 28 + 28 + 28 + 28 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 13 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 6, 8, ERA, 1}, {0, 6, 8, ERA, 0}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 5}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, INSTANCE.date(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { INSTANCE.date(2012, 6, 28).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // AccountingDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 3, 2014, 5, 24}, {2014, 5, 26, DAY_OF_WEEK, 5, 2014, 5, 26}, {2014, 5, 26, DAY_OF_MONTH, 28, 2014, 5, 28}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 364, 2014, 13, 28}, {2014, 5, 26, DAY_OF_YEAR, 138, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 19}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 7, 2014, 7, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 13 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 13 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 0, -2013, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2011, 3, 28, MONTH_OF_YEAR, 13, 2011, 13, 28}, {2012, 3, 28, MONTH_OF_YEAR, 13, 2012, 13, 28}, {2012, 13, 35, MONTH_OF_YEAR, 6, 2012, 6, 28}, {2012, 13, 35, YEAR, 2011, 2011, 13, 28}, {-2013, 6, 8, YEAR_OF_ERA, 2012, -2011, 6, 8}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 3, 2014, 5, 24}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(INSTANCE.date(expectedYear, expectedMonth, expectedDom), INSTANCE.date(year, month, dom).with(field, value)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { INSTANCE.date(2012, 6, 28).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // AccountingDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_adjust1() { AccountingDate base = INSTANCE.date(2012, 6, 23); AccountingDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(INSTANCE.date(2012, 6, 28), test); } @Test public void test_adjust2() { AccountingDate base = INSTANCE.date(2012, 13, 23); AccountingDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(INSTANCE.date(2012, 13, 35), test); } //----------------------------------------------------------------------- // AccountingDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { AccountingDate accounting = INSTANCE.date(2000, 1, 4); AccountingDate test = accounting.with(LocalDate.of(2012, 7, 6)); assertEquals(INSTANCE.date(2012, 12, 5), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { AccountingDate accounting = INSTANCE.date(2000, 1, 4); accounting.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(AccountingDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToAccountingDate() { AccountingDate accounting = INSTANCE.date(2012, 6, 23); LocalDate test = LocalDate.MIN.with(accounting); assertEquals(LocalDate.of(2012, 2, 7), test); } @Test public void test_LocalDateTime_adjustToAccountingDate() { AccountingDate accounting = INSTANCE.date(2012, 6, 23); LocalDateTime test = LocalDateTime.MIN.with(accounting); assertEquals(LocalDateTime.of(2012, 2, 7, 0, 0), test); } //----------------------------------------------------------------------- // AccountingDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 6, 6}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 6, 19}, {2014, 5, 26, -5, WEEKS, 2014, 4, 19}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26}, {2014, 5, 26, -5, MONTHS, 2013, 13, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -5, MILLENNIA, 2014 - 5000, 5, 26}, {2014, 5, 26, -1, ERAS, -2013, 5, 26}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(INSTANCE.date(expectedYear, expectedMonth, expectedDom), INSTANCE.date(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(INSTANCE.date(expectedYear, expectedMonth, expectedDom), INSTANCE.date(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { INSTANCE.date(2012, 6, 28).plus(0, MINUTES); } //----------------------------------------------------------------------- // AccountingDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 4, DAYS, 6}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 4, WEEKS, 0}, {2014, 5, 26, 2014, 6, 5, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {-2013, 5, 26, 0, 5, 26, ERAS, 0}, {-2013, 5, 26, 2014, 5, 26, ERAS, 1}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { AccountingDate start = INSTANCE.date(year1, month1, dom1); AccountingDate end = INSTANCE.date(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { AccountingDate start = INSTANCE.date(2012, 6, 28); AccountingDate end = INSTANCE.date(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(INSTANCE.date(2014, 8, 1), INSTANCE.date(2014, 5, 26).plus(INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(INSTANCE.date(2014, 7, 26), INSTANCE.date(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(INSTANCE.date(2014, 3, 23), INSTANCE.date(2014, 5, 26).minus(INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(INSTANCE.date(2014, 3, 26), INSTANCE.date(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { AccountingDate a1 = INSTANCE.date(2000, 1, 3); AccountingDate a2 = INSTANCE.date(2000, 1, 3); AccountingDate b = INSTANCE.date(2000, 1, 4); AccountingDate c = INSTANCE.date(2000, 2, 3); AccountingDate d = INSTANCE.date(2001, 1, 3); AccountingDate other = new AccountingChronologyBuilder().endsOn(DayOfWeek.WEDNESDAY) .nearestEndOf(Month.AUGUST).leapWeekInMonth(13) .withDivision(AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS) .toChronology().date(2000, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertEquals(false, a1.getChronology().equals(other.getChronology())); assertEquals(false, a1.equals(other)); assertTrue(a1.hashCode() == a1.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {INSTANCE.date(1, 1, 1), "Accounting calendar ends on SUNDAY nearest end of AUGUST, year divided in THIRTEEN_EVEN_MONTHS_OF_4_WEEKS with leap-week in month 13 CE 1-01-01"}, {INSTANCE.date(2012, 6, 23), "Accounting calendar ends on SUNDAY nearest end of AUGUST, year divided in THIRTEEN_EVEN_MONTHS_OF_4_WEEKS with leap-week in month 13 CE 2012-06-23"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(AccountingDate accounting, String expected) { assertEquals(expected, accounting.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestAccountingChronologyBuilder.java000066400000000000000000000653151343451174100333500ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoUnit.DAYS; import static org.junit.Assert.assertEquals; import java.time.DateTimeException; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.Month; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAdjusters; import java.time.temporal.ValueRange; import java.util.function.IntFunction; import java.util.function.Predicate; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestAccountingChronologyBuilder { //----------------------------------------------------------------------- // isLeapYear(), date(int, int, int) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_yearEnding() { return new Object[][] { {DayOfWeek.MONDAY, Month.JANUARY}, {DayOfWeek.TUESDAY, Month.MARCH}, {DayOfWeek.WEDNESDAY, Month.APRIL}, {DayOfWeek.THURSDAY, Month.MAY}, {DayOfWeek.FRIDAY, Month.JUNE}, {DayOfWeek.SATURDAY, Month.JULY}, {DayOfWeek.SUNDAY, Month.AUGUST}, {DayOfWeek.MONDAY, Month.SEPTEMBER}, {DayOfWeek.TUESDAY, Month.OCTOBER}, {DayOfWeek.WEDNESDAY, Month.NOVEMBER}, {DayOfWeek.THURSDAY, Month.DECEMBER}, {DayOfWeek.MONDAY, Month.FEBRUARY}, {DayOfWeek.TUESDAY, Month.FEBRUARY}, {DayOfWeek.WEDNESDAY, Month.FEBRUARY}, {DayOfWeek.THURSDAY, Month.FEBRUARY}, {DayOfWeek.FRIDAY, Month.FEBRUARY}, {DayOfWeek.SATURDAY, Month.FEBRUARY}, {DayOfWeek.SUNDAY, Month.FEBRUARY}, }; } @Test @UseDataProvider("data_yearEnding") public void test_isLeapYear_inLastWeekOf(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).inLastWeekOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 1).with(TemporalAdjusters.lastDayOfMonth()).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; Predicate isLeapYear = year -> { LocalDate currentYearEnd = getYearEnd.apply(year); LocalDate prevYearEnd = getYearEnd.apply(year - 1); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = -200; year < 600; year++) { assertEquals(isLeapYear.test(year), chronology.isLeapYear(year)); } } @Test @UseDataProvider("data_yearEnding") public void test_isLeapYear_nearestEndOf(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).nearestEndOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 3).plusMonths(1).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; Predicate isLeapYear = year -> { LocalDate currentYearEnd = getYearEnd.apply(year); LocalDate prevYearEnd = getYearEnd.apply(year - 1); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = -200; year < 600; year++) { assertEquals(isLeapYear.test(year), chronology.isLeapYear(year)); } } @Test @UseDataProvider("data_yearEnding") public void test_previousLeapYears_inLastWeekOf(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).inLastWeekOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 1).with(TemporalAdjusters.lastDayOfMonth()).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; Predicate isLeapYear = year -> { LocalDate currentYearEnd = getYearEnd.apply(year); LocalDate prevYearEnd = getYearEnd.apply(year - 1); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = 1, leapYears = 0; year < 600; year++) { if (year != 1 && isLeapYear.test(year - 1)) { leapYears++; } assertEquals(leapYears, chronology.previousLeapYears(year)); } for (int year = 1, leapYears = 0; year >= -200; year--) { if (year != 1 && isLeapYear.test(year)) { leapYears--; } assertEquals(leapYears, chronology.previousLeapYears(year)); } } @Test @UseDataProvider("data_yearEnding") public void test_previousLeapYears_nearestEndOf(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).nearestEndOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 3).plusMonths(1).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; Predicate isLeapYear = year -> { LocalDate currentYearEnd = getYearEnd.apply(year); LocalDate prevYearEnd = getYearEnd.apply(year - 1); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = 1, leapYears = 0; year < 600; year++) { if (year != 1 && isLeapYear.test(year - 1)) { leapYears++; } assertEquals(leapYears, chronology.previousLeapYears(year)); } for (int year = 1, leapYears = 0; year >= -200; year--) { if (year != 1 && isLeapYear.test(year)) { leapYears--; } assertEquals(leapYears, chronology.previousLeapYears(year)); } } @Test @UseDataProvider("data_yearEnding") public void test_date_int_int_int_inLastWeekOf(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).inLastWeekOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 1).with(TemporalAdjusters.lastDayOfMonth()).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; for (int year = -200; year < 600; year++) { assertEquals(getYearEnd.apply(year - 1).plusDays(1).toEpochDay(), chronology.date(year, 1, 1).toEpochDay()); } } @Test @UseDataProvider("data_yearEnding") public void test_date_int_int_int_nearestEndOf(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).nearestEndOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 3).plusMonths(1).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; for (int year = -200; year < 600; year++) { assertEquals(getYearEnd.apply(year - 1).plusDays(1).toEpochDay(), chronology.date(year, 1, 1).toEpochDay()); } } //----------------------------------------------------------------------- // range(MONTH_OF_YEAR), range(DAY_OF_MONTH) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_range() { return new Object[][] { {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 1, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 2, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 3, ValueRange.of(1, 4, 6), ValueRange.of(1, 28, 42), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 4, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 5, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 6, ValueRange.of(1, 4, 6), ValueRange.of(1, 28, 42), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 7, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 8, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 9, ValueRange.of(1, 4, 6), ValueRange.of(1, 28, 42), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 10, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 11, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 12, ValueRange.of(1, 4, 6), ValueRange.of(1, 28, 42), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, 1, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, 2, ValueRange.of(1, 4, 6), ValueRange.of(1, 28, 42), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, 3, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, 1, ValueRange.of(1, 4, 6), ValueRange.of(1, 28, 42), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, 2, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, 3, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 12), ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11)}, {AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, 1, ValueRange.of(1, 4, 5), ValueRange.of(1, 28, 35), ValueRange.of(1, 13), ValueRange.of(-999_999 * 13L, 999_999 * 13L + 12)}, }; } @Test @UseDataProvider("data_range") public void test_range(AccountingYearDivision division, int leapWeekInMonth, ValueRange expectedWeekOfMonthRange, ValueRange expectedDayOfMonthRange, ValueRange expectedMonthRange, ValueRange expectedProlepticMonthRange) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(DayOfWeek.SUNDAY).nearestEndOf(Month.AUGUST) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); assertEquals(expectedWeekOfMonthRange, chronology.range(ChronoField.ALIGNED_WEEK_OF_MONTH)); assertEquals(expectedDayOfMonthRange, chronology.range(ChronoField.DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 364, 371), chronology.range(ChronoField.DAY_OF_YEAR)); assertEquals(expectedMonthRange, chronology.range(ChronoField.MONTH_OF_YEAR)); assertEquals(expectedProlepticMonthRange, chronology.range(ChronoField.PROLEPTIC_MONTH)); } @Test @UseDataProvider("data_weeksInMonth") public void test_date_dayOfMonth_range(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(DayOfWeek.SUNDAY).nearestEndOf(Month.AUGUST) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); for (int month = 1; month <= weeksInMonth.length; month++) { assertEquals(ValueRange.of(1, weeksInMonth[month - 1] * 7), AccountingDate.of(chronology, 2011, month, 15).range(ChronoField.DAY_OF_MONTH)); assertEquals(ValueRange.of(1, weeksInMonth[month - 1] * 7 + (month == leapWeekInMonth ? 7 : 0)), AccountingDate.of(chronology, 2012, month, 15).range(ChronoField.DAY_OF_MONTH)); assertEquals(ValueRange.of(1, weeksInMonth[month - 1]), AccountingDate.of(chronology, 2011, month, 15).range(ChronoField.ALIGNED_WEEK_OF_MONTH)); assertEquals(ValueRange.of(1, weeksInMonth[month - 1] + (month == leapWeekInMonth ? 1 : 0)), AccountingDate.of(chronology, 2012, month, 15).range(ChronoField.ALIGNED_WEEK_OF_MONTH)); } } @Test @UseDataProvider("data_yearEnding") public void test_date_dayOfYear_inLastWeekOf_range(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).inLastWeekOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 1).with(TemporalAdjusters.lastDayOfMonth()).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; Predicate isLeapYear = year -> { LocalDate currentYearEnd = getYearEnd.apply(year); LocalDate prevYearEnd = getYearEnd.apply(year - 1); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = 2007; year < 2015; year++) { assertEquals(ValueRange.of(1, isLeapYear.test(year) ? 371 : 364), AccountingDate.of(chronology, year, 3, 5).range(ChronoField.DAY_OF_YEAR)); } } @Test @UseDataProvider("data_yearEnding") public void test_date_dayOfYear_nearestEndOf_range(DayOfWeek dayOfWeek, Month ending) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(dayOfWeek).nearestEndOf(ending) .withDivision(AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS).leapWeekInMonth(12) .toChronology(); IntFunction getYearEnd = year -> { return LocalDate.of(year, ending, 3).plusMonths(1).with(TemporalAdjusters.previousOrSame(dayOfWeek)); }; Predicate isLeapYear = year -> { LocalDate currentYearEnd = getYearEnd.apply(year); LocalDate prevYearEnd = getYearEnd.apply(year - 1); return prevYearEnd.until(currentYearEnd, DAYS) == 371; }; for (int year = 2007; year < 2015; year++) { assertEquals(ValueRange.of(1, isLeapYear.test(year) ? 371 : 364), AccountingDate.of(chronology, year, 3, 5).range(ChronoField.DAY_OF_YEAR)); } } //----------------------------------------------------------------------- // getWeeksInMonth(month), // getWeeksAtStartOfMonth(weeks), getMonthFromElapsedWeeks(weeks) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_weeksInMonth() { return new Object[][] { {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 1}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 2}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 3}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 4}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 5}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 6}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 7}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 8}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 9}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 10}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 11}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}, 12}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, new int[] {4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4}, 1}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, new int[] {4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4}, 2}, {AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, new int[] {4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4}, 3}, {AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, new int[] {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4}, 1}, {AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, new int[] {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4}, 2}, {AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, new int[] {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4}, 3}, {AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, new int[] {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, 13}, }; } @Test @UseDataProvider("data_weeksInMonth") public void test_getWeeksInMonth(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(DayOfWeek.SUNDAY).nearestEndOf(Month.AUGUST) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); for (int month = 1; month <= weeksInMonth.length; month++) { assertEquals(weeksInMonth[month - 1], chronology.getDivision().getWeeksInMonth(month)); assertEquals(weeksInMonth[month - 1] + (month == leapWeekInMonth ? 1 : 0), chronology.getDivision().getWeeksInMonth(month, leapWeekInMonth)); } } @Test @UseDataProvider("data_weeksInMonth") public void test_getWeeksAtStartOf(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(DayOfWeek.SUNDAY).nearestEndOf(Month.AUGUST) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); for (int month = 1, elapsedWeeks = 0; month <= weeksInMonth.length; elapsedWeeks += weeksInMonth[month - 1], month++) { assertEquals(elapsedWeeks, chronology.getDivision().getWeeksAtStartOfMonth(month)); assertEquals(elapsedWeeks + (month > leapWeekInMonth ? 1 : 0), chronology.getDivision().getWeeksAtStartOfMonth(month, leapWeekInMonth)); } } @Test @UseDataProvider("data_weeksInMonth") public void test_getMonthFromElapsedWeeks(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { AccountingChronology chronology = new AccountingChronologyBuilder().endsOn(DayOfWeek.SUNDAY).nearestEndOf(Month.AUGUST) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); for (int month = 1, elapsedWeeks = 0; month <= weeksInMonth.length; elapsedWeeks += weeksInMonth[month - 1], month++) { for (int i = 0; i < weeksInMonth[month - 1]; i++) { assertEquals(month, chronology.getDivision().getMonthFromElapsedWeeks(elapsedWeeks + i)); assertEquals(month, chronology.getDivision().getMonthFromElapsedWeeks(elapsedWeeks + i + (month > leapWeekInMonth ? 1 : 0), leapWeekInMonth)); if (month == leapWeekInMonth && i == weeksInMonth[month - 1] - 1) { assertEquals(month, chronology.getDivision().getMonthFromElapsedWeeks(elapsedWeeks + i + 1, leapWeekInMonth)); } } } } @Test(expected = DateTimeException.class) @UseDataProvider("data_weeksInMonth") public void test_negativeWeeks_getMonthFromElapsedWeekspublic(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { assertEquals(1, division.getMonthFromElapsedWeeks(0)); division.getMonthFromElapsedWeeks(-1); } @Test(expected = DateTimeException.class) @UseDataProvider("data_weeksInMonth") public void test_extraWeeks_getMonthFromElapsedWeekspublic(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { int elapsedWeeks = 0; for (int month = 1; month <= weeksInMonth.length; month++) { elapsedWeeks += weeksInMonth[month - 1]; } assertEquals(weeksInMonth.length, division.getMonthFromElapsedWeeks(elapsedWeeks)); division.getMonthFromElapsedWeeks(elapsedWeeks + 1); } @Test(expected = DateTimeException.class) @UseDataProvider("data_weeksInMonth") public void test_extraWeeksLeap_getMonthFromElapsedWeekspublic(AccountingYearDivision division, int[] weeksInMonth, int leapWeekInMonth) { int elapsedWeeks = 1; for (int month = 1; month <= weeksInMonth.length; month++) { elapsedWeeks += weeksInMonth[month - 1]; } assertEquals(weeksInMonth.length, division.getMonthFromElapsedWeeks(elapsedWeeks, leapWeekInMonth)); division.getMonthFromElapsedWeeks(elapsedWeeks + 1, leapWeekInMonth); } //----------------------------------------------------------------------- // toChronology() failures. //----------------------------------------------------------------------- @DataProvider public static Object[][] data_badChronology() { return new Object[][] { {DayOfWeek.MONDAY, Month.JANUARY, AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, 0}, {DayOfWeek.MONDAY, Month.JANUARY, AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, -1}, {DayOfWeek.MONDAY, Month.JANUARY, AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, 14}, {DayOfWeek.MONDAY, Month.JANUARY, AccountingYearDivision.QUARTERS_OF_PATTERN_4_4_5_WEEKS, 13}, {DayOfWeek.MONDAY, Month.JANUARY, AccountingYearDivision.QUARTERS_OF_PATTERN_4_5_4_WEEKS, 13}, {DayOfWeek.MONDAY, Month.JANUARY, AccountingYearDivision.QUARTERS_OF_PATTERN_5_4_4_WEEKS, 13}, {DayOfWeek.MONDAY, Month.JANUARY, null, 13}, {DayOfWeek.MONDAY, null, AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, 13}, {null, Month.JANUARY, AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS, 13}, }; } @Test(expected = IllegalStateException.class) @UseDataProvider("data_badChronology") public void test_badChronology_nearestEndOf(DayOfWeek dayOfWeek, Month ending, AccountingYearDivision division, int leapWeekInMonth) { new AccountingChronologyBuilder().endsOn(dayOfWeek).nearestEndOf(ending) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); } @Test(expected = IllegalStateException.class) @UseDataProvider("data_badChronology") public void test_badChronology_inLastWeekOf(DayOfWeek dayOfWeek, Month ending, AccountingYearDivision division, int leapWeekInMonth) { new AccountingChronologyBuilder().endsOn(dayOfWeek).inLastWeekOf(ending) .withDivision(division).leapWeekInMonth(leapWeekInMonth) .toChronology(); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestBritishCutoverChronology.java000066400000000000000000001657121343451174100327250ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; import java.time.Period; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.TimeZone; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestBritishCutoverChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Chronology chrono = Chronology.of("BritishCutover"); Assert.assertNotNull(chrono); Assert.assertEquals(BritishCutoverChronology.INSTANCE, chrono); Assert.assertEquals("BritishCutover", chrono.getId()); Assert.assertEquals(null, chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {BritishCutoverDate.of(1, 1, 1), LocalDate.of(0, 12, 30)}, {BritishCutoverDate.of(1, 1, 2), LocalDate.of(0, 12, 31)}, {BritishCutoverDate.of(1, 1, 3), LocalDate.of(1, 1, 1)}, {BritishCutoverDate.of(1, 2, 28), LocalDate.of(1, 2, 26)}, {BritishCutoverDate.of(1, 3, 1), LocalDate.of(1, 2, 27)}, {BritishCutoverDate.of(1, 3, 2), LocalDate.of(1, 2, 28)}, {BritishCutoverDate.of(1, 3, 3), LocalDate.of(1, 3, 1)}, {BritishCutoverDate.of(4, 2, 28), LocalDate.of(4, 2, 26)}, {BritishCutoverDate.of(4, 2, 29), LocalDate.of(4, 2, 27)}, {BritishCutoverDate.of(4, 3, 1), LocalDate.of(4, 2, 28)}, {BritishCutoverDate.of(4, 3, 2), LocalDate.of(4, 2, 29)}, {BritishCutoverDate.of(4, 3, 3), LocalDate.of(4, 3, 1)}, {BritishCutoverDate.of(100, 2, 28), LocalDate.of(100, 2, 26)}, {BritishCutoverDate.of(100, 2, 29), LocalDate.of(100, 2, 27)}, {BritishCutoverDate.of(100, 3, 1), LocalDate.of(100, 2, 28)}, {BritishCutoverDate.of(100, 3, 2), LocalDate.of(100, 3, 1)}, {BritishCutoverDate.of(100, 3, 3), LocalDate.of(100, 3, 2)}, {BritishCutoverDate.of(0, 12, 31), LocalDate.of(0, 12, 29)}, {BritishCutoverDate.of(0, 12, 30), LocalDate.of(0, 12, 28)}, {BritishCutoverDate.of(1582, 10, 4), LocalDate.of(1582, 10, 14)}, {BritishCutoverDate.of(1582, 10, 5), LocalDate.of(1582, 10, 15)}, {BritishCutoverDate.of(1751, 12, 20), LocalDate.of(1751, 12, 31)}, {BritishCutoverDate.of(1751, 12, 31), LocalDate.of(1752, 1, 11)}, {BritishCutoverDate.of(1752, 1, 1), LocalDate.of(1752, 1, 12)}, {BritishCutoverDate.of(1752, 9, 1), LocalDate.of(1752, 9, 12)}, {BritishCutoverDate.of(1752, 9, 2), LocalDate.of(1752, 9, 13)}, {BritishCutoverDate.of(1752, 9, 3), LocalDate.of(1752, 9, 14)}, // leniently accept invalid {BritishCutoverDate.of(1752, 9, 13), LocalDate.of(1752, 9, 24)}, // leniently accept invalid {BritishCutoverDate.of(1752, 9, 14), LocalDate.of(1752, 9, 14)}, {BritishCutoverDate.of(1945, 11, 12), LocalDate.of(1945, 11, 12)}, {BritishCutoverDate.of(2012, 7, 5), LocalDate.of(2012, 7, 5)}, {BritishCutoverDate.of(2012, 7, 6), LocalDate.of(2012, 7, 6)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_BritishCutoverDate(BritishCutoverDate cutover, LocalDate iso) { assertEquals(iso, LocalDate.from(cutover)); } @Test @UseDataProvider("data_samples") public void test_BritishCutoverDate_from_LocalDate(BritishCutoverDate cutover, LocalDate iso) { assertEquals(cutover, BritishCutoverDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_BritishCutoverDate_chronology_dateEpochDay(BritishCutoverDate cutover, LocalDate iso) { assertEquals(cutover, BritishCutoverChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_BritishCutoverDate_toEpochDay(BritishCutoverDate cutover, LocalDate iso) { assertEquals(iso.toEpochDay(), cutover.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_BritishCutoverDate_until_BritishCutoverDate(BritishCutoverDate cutover, LocalDate iso) { assertEquals(BritishCutoverChronology.INSTANCE.period(0, 0, 0), cutover.until(cutover)); } @Test @UseDataProvider("data_samples") public void test_BritishCutoverDate_until_LocalDate(BritishCutoverDate cutover, LocalDate iso) { assertEquals(BritishCutoverChronology.INSTANCE.period(0, 0, 0), cutover.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_BritishCutoverDate(BritishCutoverDate cutover, LocalDate iso) { assertEquals(Period.ZERO, iso.until(cutover)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(BritishCutoverDate cutover, LocalDate iso) { assertEquals(cutover, BritishCutoverChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(BritishCutoverDate cutover, LocalDate iso) { assertEquals(iso, LocalDate.from(cutover.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(cutover.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(cutover.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(cutover.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(cutover.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(BritishCutoverDate cutover, LocalDate iso) { assertEquals(iso, LocalDate.from(cutover.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(cutover.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(cutover.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(cutover.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(cutover.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(BritishCutoverDate cutover, LocalDate iso) { assertEquals(0, cutover.until(iso.plusDays(0), DAYS)); assertEquals(1, cutover.until(iso.plusDays(1), DAYS)); assertEquals(35, cutover.until(iso.plusDays(35), DAYS)); assertEquals(-40, cutover.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {1900, 0, 0}, {1900, -1, 1}, {1900, 0, 1}, {1900, 13, 1}, {1900, 14, 1}, {1900, 1, -1}, {1900, 1, 0}, {1900, 1, 32}, {1900, 2, -1}, {1900, 2, 0}, {1900, 2, 30}, {1900, 2, 31}, {1900, 2, 32}, {1899, 2, -1}, {1899, 2, 0}, {1899, 2, 29}, {1899, 2, 30}, {1899, 2, 31}, {1899, 2, 32}, {1900, 12, -1}, {1900, 12, 0}, {1900, 12, 32}, {1900, 3, 32}, {1900, 4, 31}, {1900, 5, 32}, {1900, 6, 31}, {1900, 7, 32}, {1900, 8, 32}, {1900, 9, 31}, {1900, 10, 32}, {1900, 11, 31}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { BritishCutoverDate.of(year, month, dom); } @Test(expected = DateTimeException.class) public void test_Chronology_dateYearDay_badDate() { BritishCutoverChronology.INSTANCE.dateYearDay(2001, 366); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_Chronology_isLeapYear_loop() { for (int year = -200; year < 200; year++) { BritishCutoverDate base = BritishCutoverDate.of(year, 1, 1); assertEquals((year % 4) == 0, base.isLeapYear()); assertEquals((year % 4) == 0, BritishCutoverChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_Chronology_isLeapYear_specific() { assertEquals(true, BritishCutoverChronology.INSTANCE.isLeapYear(8)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(7)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(6)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(5)); assertEquals(true, BritishCutoverChronology.INSTANCE.isLeapYear(4)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(3)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(2)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(1)); assertEquals(true, BritishCutoverChronology.INSTANCE.isLeapYear(0)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(-1)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(-2)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(-3)); assertEquals(true, BritishCutoverChronology.INSTANCE.isLeapYear(-4)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(-5)); assertEquals(false, BritishCutoverChronology.INSTANCE.isLeapYear(-6)); } //----------------------------------------------------------------------- // getCutover() //----------------------------------------------------------------------- @Test public void test_Chronology_getCutover() { assertEquals(LocalDate.of(1752, 9, 14), BritishCutoverChronology.INSTANCE.getCutover()); } //----------------------------------------------------------------------- // lengthOfMonth() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {1700, 1, 31}, {1700, 2, 29}, {1700, 3, 31}, {1700, 4, 30}, {1700, 5, 31}, {1700, 6, 30}, {1700, 7, 31}, {1700, 8, 31}, {1700, 9, 30}, {1700, 10, 31}, {1700, 11, 30}, {1700, 12, 31}, {1751, 1, 31}, {1751, 2, 28}, {1751, 3, 31}, {1751, 4, 30}, {1751, 5, 31}, {1751, 6, 30}, {1751, 7, 31}, {1751, 8, 31}, {1751, 9, 30}, {1751, 10, 31}, {1751, 11, 30}, {1751, 12, 31}, {1752, 1, 31}, {1752, 2, 29}, {1752, 3, 31}, {1752, 4, 30}, {1752, 5, 31}, {1752, 6, 30}, {1752, 7, 31}, {1752, 8, 31}, {1752, 9, 19}, {1752, 10, 31}, {1752, 11, 30}, {1752, 12, 31}, {1753, 1, 31}, {1753, 3, 31}, {1753, 2, 28}, {1753, 4, 30}, {1753, 5, 31}, {1753, 6, 30}, {1753, 7, 31}, {1753, 8, 31}, {1753, 9, 30}, {1753, 10, 31}, {1753, 11, 30}, {1753, 12, 31}, {1500, 2, 29}, {1600, 2, 29}, {1700, 2, 29}, {1800, 2, 28}, {1900, 2, 28}, {1901, 2, 28}, {1902, 2, 28}, {1903, 2, 28}, {1904, 2, 29}, {2000, 2, 29}, {2100, 2, 28}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, BritishCutoverDate.of(year, month, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // lengthOfYear() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_lengthOfYear() { return new Object[][] { {-101, 365}, {-100, 366}, {-99, 365}, {-1, 365}, {0, 366}, {100, 366}, {1600, 366}, {1700, 366}, {1751, 365}, {1748, 366}, {1749, 365}, {1750, 365}, {1751, 365}, {1752, 355}, {1753, 365}, {1500, 366}, {1600, 366}, {1700, 366}, {1800, 365}, {1900, 365}, {1901, 365}, {1902, 365}, {1903, 365}, {1904, 366}, {2000, 366}, {2100, 365}, }; } @Test @UseDataProvider("data_lengthOfYear") public void test_lengthOfYear_atStart(int year, int length) { assertEquals(length, BritishCutoverDate.of(year, 1, 1).lengthOfYear()); } @Test @UseDataProvider("data_lengthOfYear") public void test_lengthOfYear_atEnd(int year, int length) { assertEquals(length, BritishCutoverDate.of(year, 12, 31).lengthOfYear()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = -200; year < 200; year++) { BritishCutoverDate base = BritishCutoverChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); JulianEra era = (year <= 0 ? JulianEra.BC : JulianEra.AD); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); BritishCutoverDate eraBased = BritishCutoverChronology.INSTANCE.date(era, yoe, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = -200; year < 200; year++) { BritishCutoverDate base = BritishCutoverChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); JulianEra era = (year <= 0 ? JulianEra.BC : JulianEra.AD); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); BritishCutoverDate eraBased = BritishCutoverChronology.INSTANCE.dateYearDay(era, yoe, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay() { assertEquals(BritishCutoverDate.of(1752, 1, 1), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 1)); assertEquals(BritishCutoverDate.of(1752, 8, 31), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 244)); assertEquals(BritishCutoverDate.of(1752, 9, 2), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 246)); assertEquals(BritishCutoverDate.of(1752, 9, 14), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 247)); assertEquals(BritishCutoverDate.of(1752, 9, 24), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 257)); assertEquals(BritishCutoverDate.of(1752, 9, 25), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 258)); assertEquals(BritishCutoverDate.of(1752, 12, 31), BritishCutoverChronology.INSTANCE.dateYearDay(1752, 355)); assertEquals(BritishCutoverDate.of(2014, 1, 1), BritishCutoverChronology.INSTANCE.dateYearDay(2014, 1)); } @Test public void test_prolepticYear_specific() { assertEquals(4, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.AD, 4)); assertEquals(3, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.AD, 3)); assertEquals(2, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.AD, 2)); assertEquals(1, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.AD, 1)); assertEquals(0, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.BC, 1)); assertEquals(-1, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.BC, 2)); assertEquals(-2, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.BC, 3)); assertEquals(-3, BritishCutoverChronology.INSTANCE.prolepticYear(JulianEra.BC, 4)); } @Test(expected = ClassCastException.class) public void test_prolepticYear_badEra() { BritishCutoverChronology.INSTANCE.prolepticYear(IsoEra.CE, 4); } @Test public void test_Chronology_eraOf() { assertEquals(JulianEra.AD, BritishCutoverChronology.INSTANCE.eraOf(1)); assertEquals(JulianEra.BC, BritishCutoverChronology.INSTANCE.eraOf(0)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { BritishCutoverChronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = BritishCutoverChronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertEquals(true, eras.contains(JulianEra.BC)); assertEquals(true, eras.contains(JulianEra.AD)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), BritishCutoverChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 28, 31), BritishCutoverChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 355, 366), BritishCutoverChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 12), BritishCutoverChronology.INSTANCE.range(MONTH_OF_YEAR)); assertEquals(ValueRange.of(1, 3, 5), BritishCutoverChronology.INSTANCE.range(ALIGNED_WEEK_OF_MONTH)); assertEquals(ValueRange.of(1, 51, 53), BritishCutoverChronology.INSTANCE.range(ALIGNED_WEEK_OF_YEAR)); } //----------------------------------------------------------------------- // BritishCutoverDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { {1700, 1, 23, DAY_OF_MONTH, 1, 31}, {1700, 2, 23, DAY_OF_MONTH, 1, 29}, {1700, 3, 23, DAY_OF_MONTH, 1, 31}, {1700, 4, 23, DAY_OF_MONTH, 1, 30}, {1700, 5, 23, DAY_OF_MONTH, 1, 31}, {1700, 6, 23, DAY_OF_MONTH, 1, 30}, {1700, 7, 23, DAY_OF_MONTH, 1, 31}, {1700, 8, 23, DAY_OF_MONTH, 1, 31}, {1700, 9, 23, DAY_OF_MONTH, 1, 30}, {1700, 10, 23, DAY_OF_MONTH, 1, 31}, {1700, 11, 23, DAY_OF_MONTH, 1, 30}, {1700, 12, 23, DAY_OF_MONTH, 1, 31}, {1751, 1, 23, DAY_OF_MONTH, 1, 31}, {1751, 2, 23, DAY_OF_MONTH, 1, 28}, {1751, 3, 23, DAY_OF_MONTH, 1, 31}, {1751, 4, 23, DAY_OF_MONTH, 1, 30}, {1751, 5, 23, DAY_OF_MONTH, 1, 31}, {1751, 6, 23, DAY_OF_MONTH, 1, 30}, {1751, 7, 23, DAY_OF_MONTH, 1, 31}, {1751, 8, 23, DAY_OF_MONTH, 1, 31}, {1751, 9, 23, DAY_OF_MONTH, 1, 30}, {1751, 10, 23, DAY_OF_MONTH, 1, 31}, {1751, 11, 23, DAY_OF_MONTH, 1, 30}, {1751, 12, 23, DAY_OF_MONTH, 1, 31}, {1752, 1, 23, DAY_OF_MONTH, 1, 31}, {1752, 2, 23, DAY_OF_MONTH, 1, 29}, {1752, 3, 23, DAY_OF_MONTH, 1, 31}, {1752, 4, 23, DAY_OF_MONTH, 1, 30}, {1752, 5, 23, DAY_OF_MONTH, 1, 31}, {1752, 6, 23, DAY_OF_MONTH, 1, 30}, {1752, 7, 23, DAY_OF_MONTH, 1, 31}, {1752, 8, 23, DAY_OF_MONTH, 1, 31}, {1752, 9, 23, DAY_OF_MONTH, 1, 30}, {1752, 10, 23, DAY_OF_MONTH, 1, 31}, {1752, 11, 23, DAY_OF_MONTH, 1, 30}, {1752, 12, 23, DAY_OF_MONTH, 1, 31}, {2012, 1, 23, DAY_OF_MONTH, 1, 31}, {2012, 2, 23, DAY_OF_MONTH, 1, 29}, {2012, 3, 23, DAY_OF_MONTH, 1, 31}, {2012, 4, 23, DAY_OF_MONTH, 1, 30}, {2012, 5, 23, DAY_OF_MONTH, 1, 31}, {2012, 6, 23, DAY_OF_MONTH, 1, 30}, {2012, 7, 23, DAY_OF_MONTH, 1, 31}, {2012, 8, 23, DAY_OF_MONTH, 1, 31}, {2012, 9, 23, DAY_OF_MONTH, 1, 30}, {2012, 10, 23, DAY_OF_MONTH, 1, 31}, {2012, 11, 23, DAY_OF_MONTH, 1, 30}, {2012, 12, 23, DAY_OF_MONTH, 1, 31}, {2011, 2, 23, DAY_OF_MONTH, 1, 28}, {1700, 1, 23, DAY_OF_YEAR, 1, 366}, {1751, 1, 23, DAY_OF_YEAR, 1, 365}, {1752, 1, 23, DAY_OF_YEAR, 1, 355}, {1753, 1, 23, DAY_OF_YEAR, 1, 365}, {2012, 1, 23, DAY_OF_YEAR, 1, 366}, {2011, 2, 23, DAY_OF_YEAR, 1, 365}, {1752, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 2, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 3, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 4, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 5, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 6, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 7, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 8, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 9, 23, ALIGNED_WEEK_OF_MONTH, 1, 3}, {1752, 10, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 11, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1752, 12, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2012, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2012, 2, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2012, 3, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2011, 2, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, {1752, 12, 23, ALIGNED_WEEK_OF_YEAR, 1, 51}, {2011, 2, 23, ALIGNED_WEEK_OF_YEAR, 1, 53}, {2012, 2, 23, ALIGNED_WEEK_OF_YEAR, 1, 53}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), BritishCutoverDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { BritishCutoverDate.of(2012, 6, 30).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // BritishCutoverDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {1752, 5, 26, DAY_OF_WEEK, 2}, {1752, 5, 26, DAY_OF_MONTH, 26}, {1752, 5, 26, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 26}, {1752, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {1752, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {1752, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7}, {1752, 5, 26, ALIGNED_WEEK_OF_YEAR, 21}, {1752, 5, 26, MONTH_OF_YEAR, 5}, {1752, 9, 2, DAY_OF_WEEK, 3}, {1752, 9, 2, DAY_OF_MONTH, 2}, {1752, 9, 2, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 2}, {1752, 9, 2, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2}, {1752, 9, 2, ALIGNED_WEEK_OF_MONTH, 1}, {1752, 9, 2, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1}, {1752, 9, 2, ALIGNED_WEEK_OF_YEAR, 36}, {1752, 9, 2, MONTH_OF_YEAR, 9}, {1752, 9, 14, DAY_OF_WEEK, 4}, {1752, 9, 14, DAY_OF_MONTH, 14}, {1752, 9, 14, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 3}, {1752, 9, 14, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3}, {1752, 9, 14, ALIGNED_WEEK_OF_MONTH, 1}, {1752, 9, 14, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2}, {1752, 9, 14, ALIGNED_WEEK_OF_YEAR, 36}, {1752, 9, 14, MONTH_OF_YEAR, 9}, {2014, 5, 26, DAY_OF_WEEK, 1}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 31 + 28 + 31 + 30 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 21}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 6, 8, ERA, 1}, {0, 6, 8, ERA, 0}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 1}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, BritishCutoverDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { BritishCutoverDate.of(2012, 6, 30).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // BritishCutoverDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {1752, 9, 2, DAY_OF_WEEK, 1, 1752, 8, 31}, {1752, 9, 2, DAY_OF_WEEK, 4, 1752, 9, 14}, {1752, 9, 2, DAY_OF_MONTH, 1, 1752, 9, 1}, {1752, 9, 2, DAY_OF_MONTH, 3, 1752, 9, 14}, // lenient {1752, 9, 2, DAY_OF_MONTH, 13, 1752, 9, 24}, // lenient {1752, 9, 2, DAY_OF_MONTH, 14, 1752, 9, 14}, {1752, 9, 2, DAY_OF_MONTH, 30, 1752, 9, 30}, {1752, 9, 2, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 1, 1752, 9, 1}, {1752, 9, 2, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 3, 1752, 9, 14}, {1752, 9, 2, DAY_OF_YEAR, 356, 1753, 1, 1}, // lenient {1752, 9, 2, DAY_OF_YEAR, 366, 1753, 1, 11}, // lenient {1752, 9, 2, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 1752, 9, 1}, {1752, 9, 2, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 1752, 9, 14}, {1752, 9, 2, ALIGNED_WEEK_OF_MONTH, 2, 1752, 9, 20}, {1752, 9, 2, ALIGNED_WEEK_OF_MONTH, 3, 1752, 9, 27}, {1752, 9, 2, ALIGNED_WEEK_OF_MONTH, 4, 1752, 10, 4}, // lenient {1752, 9, 2, ALIGNED_WEEK_OF_MONTH, 5, 1752, 10, 11}, // lenient {1752, 9, 2, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 1752, 9, 14}, {1752, 9, 2, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 1752, 9, 15}, {1752, 9, 2, ALIGNED_WEEK_OF_YEAR, 1, 1752, 1, 1}, {1752, 9, 2, ALIGNED_WEEK_OF_YEAR, 35, 1752, 8, 26}, {1752, 9, 2, ALIGNED_WEEK_OF_YEAR, 37, 1752, 9, 20}, {1752, 9, 2, ALIGNED_WEEK_OF_YEAR, 51, 1752, 12, 27}, {1752, 9, 2, ALIGNED_WEEK_OF_YEAR, 52, 1753, 1, 3}, // lenient {1752, 9, 2, MONTH_OF_YEAR, 8, 1752, 8, 2}, {1752, 9, 2, MONTH_OF_YEAR, 10, 1752, 10, 2}, {1752, 9, 14, DAY_OF_WEEK, 1, 1752, 8, 31}, {1752, 9, 14, DAY_OF_WEEK, 3, 1752, 9, 2}, {1752, 9, 14, DAY_OF_MONTH, 1, 1752, 9, 1}, {1752, 9, 14, DAY_OF_MONTH, 2, 1752, 9, 2}, {1752, 9, 14, DAY_OF_MONTH, 3, 1752, 9, 14}, // lenient {1752, 9, 14, DAY_OF_MONTH, 30, 1752, 9, 30}, {1752, 9, 14, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 1, 1752, 9, 1}, {1752, 9, 14, DAY_OF_YEAR, 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 2, 1752, 9, 2}, {1752, 9, 14, DAY_OF_YEAR, 356, 1753, 1, 1}, // lenient {1752, 9, 14, DAY_OF_YEAR, 366, 1753, 1, 11}, // lenient {1752, 9, 14, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 1752, 9, 1}, {1752, 9, 14, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2, 1752, 9, 2}, {1752, 9, 14, ALIGNED_WEEK_OF_MONTH, 2, 1752, 9, 21}, {1752, 9, 14, ALIGNED_WEEK_OF_MONTH, 3, 1752, 9, 28}, {1752, 9, 14, ALIGNED_WEEK_OF_MONTH, 4, 1752, 10, 5}, // lenient {1752, 9, 14, ALIGNED_WEEK_OF_MONTH, 5, 1752, 10, 12}, // lenient {1752, 9, 14, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 1752, 9, 14}, {1752, 9, 14, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 1752, 9, 15}, {1752, 9, 14, ALIGNED_WEEK_OF_YEAR, 1, 1752, 1, 2}, {1752, 9, 14, ALIGNED_WEEK_OF_YEAR, 35, 1752, 8, 27}, {1752, 9, 14, ALIGNED_WEEK_OF_YEAR, 37, 1752, 9, 21}, {1752, 9, 14, ALIGNED_WEEK_OF_YEAR, 51, 1752, 12, 28}, {1752, 9, 14, ALIGNED_WEEK_OF_YEAR, 52, 1753, 1, 4}, // lenient {1752, 9, 14, MONTH_OF_YEAR, 8, 1752, 8, 14}, {1752, 9, 14, MONTH_OF_YEAR, 10, 1752, 10, 14}, // into cutover zone {1752, 8, 4, MONTH_OF_YEAR, 9, 1752, 9, 15}, // lenient {1752, 10, 8, MONTH_OF_YEAR, 9, 1752, 9, 19}, // lenient {1751, 9, 4, YEAR, 1752, 1752, 9, 15}, // lenient {1753, 9, 8, YEAR, 1752, 1752, 9, 19}, // lenient {1751, 9, 4, YEAR_OF_ERA, 1752, 1752, 9, 15}, // lenient {1753, 9, 8, YEAR_OF_ERA, 1752, 1752, 9, 19}, // lenient {2014, 5, 26, DAY_OF_WEEK, 3, 2014, 5, 28}, {2014, 5, 26, DAY_OF_WEEK, 7, 2014, 6, 1}, {2014, 5, 26, DAY_OF_MONTH, 31, 2014, 5, 31}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 365, 2014, 12, 31}, {2014, 5, 26, DAY_OF_YEAR, 146, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 5, 22}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 9}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 21, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 7, 2014, 7, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 12 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 0, -2013, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2011, 3, 31, MONTH_OF_YEAR, 2, 2011, 2, 28}, {2012, 3, 31, MONTH_OF_YEAR, 2, 2012, 2, 29}, {2012, 3, 31, MONTH_OF_YEAR, 6, 2012, 6, 30}, {2012, 2, 29, YEAR, 2011, 2011, 2, 28}, {-2013, 6, 8, YEAR_OF_ERA, 2012, -2011, 6, 8}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 2, 2014, 5, 27}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(BritishCutoverDate.of(expectedYear, expectedMonth, expectedDom), BritishCutoverDate.of(year, month, dom).with(field, value)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { BritishCutoverDate.of(2012, 6, 30).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // BritishCutoverDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_lastDayOfMonth() { return new Object[][] { {BritishCutoverDate.of(1752, 2, 23), BritishCutoverDate.of(1752, 2, 29)}, {BritishCutoverDate.of(1752, 6, 23), BritishCutoverDate.of(1752, 6, 30)}, {BritishCutoverDate.of(1752, 9, 2), BritishCutoverDate.of(1752, 9, 30)}, {BritishCutoverDate.of(1752, 9, 14), BritishCutoverDate.of(1752, 9, 30)}, {BritishCutoverDate.of(2012, 2, 23), BritishCutoverDate.of(2012, 2, 29)}, {BritishCutoverDate.of(2012, 6, 23), BritishCutoverDate.of(2012, 6, 30)}, }; } @Test @UseDataProvider("data_lastDayOfMonth") public void test_adjust_lastDayOfMonth(BritishCutoverDate input, BritishCutoverDate expected) { BritishCutoverDate test = input.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(expected, test); } //----------------------------------------------------------------------- // BritishCutoverDate.with(Local*) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_withLocalDate() { return new Object[][] { {BritishCutoverDate.of(1752, 9, 2), LocalDate.of(1752, 9, 12), BritishCutoverDate.of(1752, 9, 1)}, {BritishCutoverDate.of(1752, 9, 14), LocalDate.of(1752, 9, 12), BritishCutoverDate.of(1752, 9, 1)}, {BritishCutoverDate.of(1752, 9, 2), LocalDate.of(1752, 9, 14), BritishCutoverDate.of(1752, 9, 14)}, {BritishCutoverDate.of(1752, 9, 15), LocalDate.of(1752, 9, 14), BritishCutoverDate.of(1752, 9, 14)}, {BritishCutoverDate.of(2012, 2, 23), LocalDate.of(2012, 2, 23), BritishCutoverDate.of(2012, 2, 23)}, }; } @Test @UseDataProvider("data_withLocalDate") public void test_adjust_LocalDate(BritishCutoverDate input, LocalDate local, BritishCutoverDate expected) { BritishCutoverDate test = input.with(local); assertEquals(expected, test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { BritishCutoverDate cutover = BritishCutoverDate.of(2000, 1, 4); cutover.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(BritishCutoverDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_withBritishCutoverDate() { BritishCutoverDate cutover = BritishCutoverDate.of(2012, 6, 23); LocalDate test = LocalDate.MIN.with(cutover); assertEquals(LocalDate.of(2012, 6, 23), test); } @Test public void test_LocalDateTime_withBritishCutoverDate() { BritishCutoverDate cutover = BritishCutoverDate.of(2012, 6, 23); LocalDateTime test = LocalDateTime.MIN.with(cutover); assertEquals(LocalDateTime.of(2012, 6, 23, 0, 0), test); } //----------------------------------------------------------------------- // BritishCutoverDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {1752, 9, 2, -1, DAYS, 1752, 9, 1, true}, {1752, 9, 2, 0, DAYS, 1752, 9, 2, true}, {1752, 9, 2, 1, DAYS, 1752, 9, 14, true}, {1752, 9, 2, 2, DAYS, 1752, 9, 15, true}, {1752, 9, 14, -1, DAYS, 1752, 9, 2, true}, {1752, 9, 14, 0, DAYS, 1752, 9, 14, true}, {1752, 9, 14, 1, DAYS, 1752, 9, 15, true}, {2014, 5, 26, 0, DAYS, 2014, 5, 26, true}, {2014, 5, 26, 8, DAYS, 2014, 6, 3, true}, {2014, 5, 26, -3, DAYS, 2014, 5, 23, true}, {1752, 9, 2, -1, WEEKS, 1752, 8, 26, true}, {1752, 9, 2, 0, WEEKS, 1752, 9, 2, true}, {1752, 9, 2, 1, WEEKS, 1752, 9, 20, true}, {1752, 9, 14, -1, WEEKS, 1752, 8, 27, true}, {1752, 9, 14, 0, WEEKS, 1752, 9, 14, true}, {1752, 9, 14, 1, WEEKS, 1752, 9, 21, true}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26, true}, {2014, 5, 26, 3, WEEKS, 2014, 6, 16, true}, {2014, 5, 26, -5, WEEKS, 2014, 4, 21, true}, {1752, 9, 2, -1, MONTHS, 1752, 8, 2, true}, {1752, 9, 2, 0, MONTHS, 1752, 9, 2, true}, {1752, 9, 2, 1, MONTHS, 1752, 10, 2, true}, {1752, 9, 14, -1, MONTHS, 1752, 8, 14, true}, {1752, 9, 14, 0, MONTHS, 1752, 9, 14, true}, {1752, 9, 14, 1, MONTHS, 1752, 10, 14, true}, {1752, 8, 12, 1, MONTHS, 1752, 9, 23, false}, {1752, 10, 12, -1, MONTHS, 1752, 9, 23, false}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26, true}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26, true}, {2014, 5, 26, -5, MONTHS, 2013, 12, 26, true}, {2014, 5, 26, 0, YEARS, 2014, 5, 26, true}, {2014, 5, 26, 3, YEARS, 2017, 5, 26, true}, {2014, 5, 26, -5, YEARS, 2009, 5, 26, true}, {2014, 5, 26, 0, DECADES, 2014, 5, 26, true}, {2014, 5, 26, 3, DECADES, 2044, 5, 26, true}, {2014, 5, 26, -5, DECADES, 1964, 5, 26, true}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26, true}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26, true}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26, true}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26, true}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26, true}, {2014, 5, 26, -5, MILLENNIA, 2014 - 5000, 5, 26, true}, {2014, 5, 26, -1, ERAS, -2013, 5, 26, true}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom, boolean bidi) { assertEquals(BritishCutoverDate.of(expectedYear, expectedMonth, expectedDom), BritishCutoverDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom, boolean bidi) { if (bidi) { assertEquals(BritishCutoverDate.of(expectedYear, expectedMonth, expectedDom), BritishCutoverDate.of(year, month, dom).minus(amount, unit)); } } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { BritishCutoverDate.of(2012, 6, 30).plus(0, MINUTES); } //----------------------------------------------------------------------- // BritishCutoverDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {1752, 9, 1, 1752, 9, 2, DAYS, 1}, {1752, 9, 1, 1752, 9, 14, DAYS, 2}, {1752, 9, 2, 1752, 9, 14, DAYS, 1}, {1752, 9, 2, 1752, 9, 15, DAYS, 2}, {1752, 9, 14, 1752, 9, 1, DAYS, -2}, {1752, 9, 14, 1752, 9, 2, DAYS, -1}, {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 1, DAYS, 6}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {1752, 9, 1, 1752, 9, 14, WEEKS, 0}, {1752, 9, 1, 1752, 9, 18, WEEKS, 0}, {1752, 9, 1, 1752, 9, 19, WEEKS, 1}, {1752, 9, 2, 1752, 9, 14, WEEKS, 0}, {1752, 9, 2, 1752, 9, 19, WEEKS, 0}, {1752, 9, 2, 1752, 9, 20, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 1, WEEKS, 0}, {2014, 5, 26, 2014, 6, 2, WEEKS, 1}, {1752, 9, 1, 1752, 9, 14, MONTHS, 0}, {1752, 9, 1, 1752, 9, 30, MONTHS, 0}, {1752, 9, 1, 1752, 10, 1, MONTHS, 1}, {1752, 9, 2, 1752, 9, 14, MONTHS, 0}, {1752, 9, 2, 1752, 10, 1, MONTHS, 0}, {1752, 9, 2, 1752, 10, 2, MONTHS, 1}, {1752, 9, 14, 1752, 9, 15, MONTHS, 0}, {1752, 9, 14, 1752, 10, 13, MONTHS, 0}, {1752, 9, 14, 1752, 10, 14, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {-2013, 5, 26, 0, 5, 26, ERAS, 0}, {-2013, 5, 26, 2014, 5, 26, ERAS, 1}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { BritishCutoverDate start = BritishCutoverDate.of(year1, month1, dom1); BritishCutoverDate end = BritishCutoverDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { BritishCutoverDate start = BritishCutoverDate.of(2012, 6, 30); BritishCutoverDate end = BritishCutoverDate.of(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals( BritishCutoverDate.of(1752, 10, 5), BritishCutoverDate.of(1752, 9, 2).plus(BritishCutoverChronology.INSTANCE.period(0, 1, 3))); assertEquals( BritishCutoverDate.of(1752, 9, 23), BritishCutoverDate.of(1752, 8, 12).plus(BritishCutoverChronology.INSTANCE.period(0, 1, 0))); assertEquals( BritishCutoverDate.of(2014, 7, 29), BritishCutoverDate.of(2014, 5, 26).plus(BritishCutoverChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals( BritishCutoverDate.of(2014, 7, 26), BritishCutoverDate.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals( BritishCutoverDate.of(1752, 9, 23), BritishCutoverDate.of(1752, 10, 12).minus(BritishCutoverChronology.INSTANCE.period(0, 1, 0))); assertEquals( BritishCutoverDate.of(2014, 3, 23), BritishCutoverDate.of(2014, 5, 26).minus(BritishCutoverChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals( BritishCutoverDate.of(2014, 3, 26), BritishCutoverDate.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- @DataProvider public static Object[][] data_untilCLD() { return new Object[][] { {1752, 7, 2, 1752, 7, 1, 0, 0, -1}, {1752, 7, 2, 1752, 7, 2, 0, 0, 0}, {1752, 7, 2, 1752, 9, 1, 0, 1, 30}, // 30 days after 1752-08-02 {1752, 7, 2, 1752, 9, 2, 0, 2, 0}, // 2 whole months {1752, 7, 2, 1752, 9, 14, 0, 2, 1}, // 1 day after 1752-09-02 {1752, 7, 2, 1752, 9, 30, 0, 2, 17}, // 17 days after 1752-09-02 {1752, 7, 2, 1752, 10, 1, 0, 2, 18}, // 18 days after 1752-09-02 {1752, 7, 2, 1752, 10, 2, 0, 3, 0}, // 3 whole months {1752, 7, 2, 1752, 10, 3, 0, 3, 1}, {1752, 7, 2, 1752, 10, 30, 0, 3, 28}, {1752, 7, 2, 1752, 11, 1, 0, 3, 30}, {1752, 7, 2, 1752, 11, 2, 0, 4, 0}, {1752, 7, 3, 1752, 9, 2, 0, 1, 30}, // 30 days after 1752-08-03 {1752, 7, 3, 1752, 9, 14, 0, 2, 0}, // 2 months {1752, 7, 3, 1752, 9, 15, 0, 2, 1}, // 1 day after 1752-09-03 (1752-09-14) {1752, 7, 3, 1752, 9, 30, 0, 2, 16}, // 16 days after 1752-09-03 (1752-09-14) {1752, 7, 3, 1752, 10, 1, 0, 2, 17}, // 17 days after 1752-09-03 (1752-09-14) {1752, 7, 3, 1752, 10, 3, 0, 3, 0}, {1752, 7, 3, 1752, 10, 4, 0, 3, 1}, {1752, 7, 4, 1752, 9, 2, 0, 1, 29}, // 29 days after 1752-08-04 {1752, 7, 4, 1752, 9, 14, 0, 1, 30}, // 30 days after 1752-08-04 {1752, 7, 4, 1752, 9, 15, 0, 2, 0}, // 2 months {1752, 7, 4, 1752, 9, 30, 0, 2, 15}, // 15 days after 1752-09-04 (1752-09-15) {1752, 7, 4, 1752, 10, 1, 0, 2, 16}, // 16 days after 1752-09-04 (1752-09-15) {1752, 7, 4, 1752, 10, 4, 0, 3, 0}, {1752, 7, 4, 1752, 10, 5, 0, 3, 1}, {1752, 7, 13, 1752, 9, 2, 0, 1, 20}, // 20 days after 1752-08-13 {1752, 7, 13, 1752, 9, 14, 0, 1, 21}, // 21 days after 752-08-13 {1752, 7, 13, 1752, 9, 24, 0, 2, 0}, // 2 months {1752, 7, 13, 1752, 9, 30, 0, 2, 6}, // 6 days after 1752-09-13 (1752-09-24) {1752, 7, 13, 1752, 10, 1, 0, 2, 7}, // 7 days after 1752-09-13 (1752-09-24) {1752, 7, 13, 1752, 10, 12, 0, 2, 18}, // 18 days after 1752-09-13 (1752-09-24) {1752, 7, 13, 1752, 10, 13, 0, 3, 0}, {1752, 7, 13, 1752, 10, 14, 0, 3, 1}, {1752, 7, 14, 1752, 9, 2, 0, 1, 19}, // 19 days after 1752-08-14 {1752, 7, 14, 1752, 9, 14, 0, 2, 0}, // 2 months {1752, 7, 14, 1752, 9, 15, 0, 2, 1}, // 1 day after 1752-09-14 {1752, 7, 14, 1752, 9, 30, 0, 2, 16}, // 16 days after 1752-09-14 {1752, 7, 14, 1752, 10, 1, 0, 2, 17}, // 17 days after 1752-09-14 {1752, 7, 14, 1752, 10, 13, 0, 2, 29}, // 29 days after 1752-09-14 {1752, 7, 14, 1752, 10, 14, 0, 3, 0}, {1752, 7, 14, 1752, 10, 15, 0, 3, 1}, {1752, 8, 2, 1752, 9, 2, 0, 1, 0}, {1752, 8, 2, 1752, 9, 14, 0, 1, 1}, // 1 day after 1752-09-02 {1752, 8, 2, 1752, 9, 30, 0, 1, 17}, // 17 days after 1752-09-02 {1752, 8, 2, 1752, 10, 1, 0, 1, 18}, // 18 days after 1752-09-02 {1752, 8, 2, 1752, 10, 2, 0, 2, 0}, {1752, 8, 2, 1752, 10, 3, 0, 2, 1}, {1752, 8, 2, 1752, 10, 30, 0, 2, 28}, {1752, 8, 16, 1752, 9, 2, 0, 0, 17}, {1752, 8, 16, 1752, 9, 14, 0, 0, 18}, {1752, 8, 16, 1752, 9, 15, 0, 0, 19}, {1752, 8, 16, 1752, 9, 16, 0, 1, 0}, {1752, 8, 16, 1752, 10, 2, 0, 1, 16}, {1752, 8, 16, 1752, 10, 15, 0, 1, 29}, {1752, 8, 16, 1752, 10, 16, 0, 2, 0}, {1752, 8, 16, 1752, 10, 17, 0, 2, 1}, {1752, 9, 1, 1752, 8, 31, 0, 0, -1}, {1752, 9, 1, 1752, 9, 1, 0, 0, 0}, {1752, 9, 1, 1752, 9, 2, 0, 0, 1}, {1752, 9, 1, 1752, 9, 14, 0, 0, 2}, {1752, 9, 1, 1752, 9, 15, 0, 0, 3}, {1752, 9, 1, 1752, 9, 30, 0, 0, 18}, {1752, 9, 1, 1752, 10, 1, 0, 1, 0}, {1752, 9, 1, 1752, 10, 2, 0, 1, 1}, {1752, 9, 2, 1752, 8, 31, 0, 0, -2}, {1752, 9, 2, 1752, 9, 1, 0, 0, -1}, {1752, 9, 2, 1752, 9, 2, 0, 0, 0}, {1752, 9, 2, 1752, 9, 14, 0, 0, 1}, {1752, 9, 2, 1752, 9, 30, 0, 0, 17}, {1752, 9, 2, 1752, 10, 1, 0, 0, 18}, {1752, 9, 2, 1752, 10, 2, 0, 1, 0}, {1752, 9, 2, 1752, 10, 3, 0, 1, 1}, {1752, 9, 2, 1752, 11, 1, 0, 1, 30}, {1752, 9, 2, 1752, 11, 2, 0, 2, 0}, {1752, 9, 2, 1752, 11, 3, 0, 2, 1}, {1752, 9, 14, 1752, 7, 13, 0, -2, -1}, {1752, 9, 14, 1752, 7, 14, 0, -2, 0}, {1752, 9, 14, 1752, 8, 13, 0, -1, -1}, {1752, 9, 14, 1752, 8, 14, 0, -1, 0}, {1752, 9, 14, 1752, 8, 15, 0, 0, -19}, // 19 days before {1752, 9, 14, 1752, 8, 31, 0, 0, -3}, // 3 days before {1752, 9, 14, 1752, 9, 1, 0, 0, -2}, // 2 days before {1752, 9, 14, 1752, 9, 2, 0, 0, -1}, // 1 day before {1752, 9, 14, 1752, 9, 14, 0, 0, 0}, {1752, 9, 14, 1752, 9, 15, 0, 0, 1}, {1752, 9, 14, 1752, 9, 30, 0, 0, 16}, {1752, 9, 14, 1752, 10, 13, 0, 0, 29}, {1752, 9, 14, 1752, 10, 14, 0, 1, 0}, {1752, 9, 14, 1752, 10, 15, 0, 1, 1}, {1752, 9, 24, 1752, 7, 23, 0, -2, -1}, {1752, 9, 24, 1752, 7, 24, 0, -2, 0}, {1752, 9, 24, 1752, 8, 23, 0, -1, -1}, {1752, 9, 24, 1752, 8, 24, 0, -1, 0}, {1752, 9, 24, 1752, 8, 25, 0, 0, -19}, // 19 days before {1752, 9, 24, 1752, 8, 31, 0, 0, -13}, // 13 days before {1752, 9, 24, 1752, 9, 1, 0, 0, -12}, // 12 days before {1752, 9, 24, 1752, 9, 2, 0, 0, -11}, // 11 days before {1752, 9, 24, 1752, 9, 14, 0, 0, -10}, // 10 days before {1752, 9, 24, 1752, 9, 23, 0, 0, -1}, // 1 day before {1752, 9, 24, 1752, 9, 24, 0, 0, 0}, {1752, 9, 24, 1752, 9, 25, 0, 0, 1}, {1752, 9, 24, 1752, 9, 30, 0, 0, 6}, {1752, 9, 24, 1752, 10, 23, 0, 0, 29}, {1752, 9, 24, 1752, 10, 24, 0, 1, 0}, {1752, 9, 24, 1752, 10, 25, 0, 1, 1}, {1752, 10, 3, 1752, 10, 1, 0, 0, -2}, {1752, 10, 3, 1752, 9, 30, 0, 0, -3}, {1752, 10, 3, 1752, 9, 16, 0, 0, -17}, {1752, 10, 3, 1752, 9, 15, 0, 0, -18}, {1752, 10, 3, 1752, 9, 14, 0, 0, -19}, {1752, 10, 3, 1752, 9, 2, 0, -1, -1}, {1752, 10, 3, 1752, 9, 1, 0, -1, -2}, {1752, 10, 3, 1752, 8, 31, 0, -1, -3}, {1752, 10, 3, 1752, 8, 4, 0, -1, -30}, {1752, 10, 3, 1752, 8, 3, 0, -2, 0}, {1752, 10, 3, 1752, 8, 2, 0, -2, -1}, {1752, 10, 4, 1752, 10, 1, 0, 0, -3}, {1752, 10, 4, 1752, 9, 30, 0, 0, -4}, {1752, 10, 4, 1752, 9, 16, 0, 0, -18}, {1752, 10, 4, 1752, 9, 15, 0, 0, -19}, {1752, 10, 4, 1752, 9, 14, 0, 0, -20}, {1752, 10, 4, 1752, 9, 2, 0, -1, -2}, {1752, 10, 4, 1752, 9, 1, 0, -1, -3}, {1752, 10, 4, 1752, 8, 31, 0, -1, -4}, {1752, 10, 4, 1752, 8, 5, 0, -1, -30}, {1752, 10, 4, 1752, 8, 4, 0, -2, 0}, {1752, 10, 4, 1752, 8, 3, 0, -2, -1}, }; } @Test @UseDataProvider("data_untilCLD") public void test_until_CLD( int year1, int month1, int dom1, int year2, int month2, int dom2, int expectedYears, int expectedMonths, int expectedDays) { BritishCutoverDate a = BritishCutoverDate.of(year1, month1, dom1); BritishCutoverDate b = BritishCutoverDate.of(year2, month2, dom2); ChronoPeriod c = a.until(b); assertEquals( BritishCutoverChronology.INSTANCE.period(expectedYears, expectedMonths, expectedDays), c); } @Test @UseDataProvider("data_untilCLD") public void test_until_CLD_plus( int year1, int month1, int dom1, int year2, int month2, int dom2, int expectedYears, int expectedMonths, int expectedDays) { BritishCutoverDate a = BritishCutoverDate.of(year1, month1, dom1); BritishCutoverDate b = BritishCutoverDate.of(year2, month2, dom2); ChronoPeriod c = a.until(b); assertEquals(b, a.plus(c)); } //------------------------------------------------------------------------- // atTime(LocalTime) //------------------------------------------------------------------------- @Test public void test_atTime() { BritishCutoverDate date = BritishCutoverDate.of(2014, 10, 12); ChronoLocalDateTime test = date.atTime(LocalTime.of(12, 30)); assertEquals(date, test.toLocalDate()); assertEquals(LocalTime.of(12, 30), test.toLocalTime()); ChronoLocalDateTime test2 = BritishCutoverChronology.INSTANCE.localDateTime(LocalDateTime.from(test)); assertEquals(test, test2); } @Test(expected = NullPointerException.class) public void test_atTime_null() { BritishCutoverDate.of(2014, 5, 26).atTime(null); } //----------------------------------------------------------------------- // check against GregorianCalendar //----------------------------------------------------------------------- @Test public void test_crossCheck() { BritishCutoverDate test = BritishCutoverDate.of(1700, 1, 1); BritishCutoverDate end = BritishCutoverDate.of(1800, 1, 1); Instant cutover = ZonedDateTime.of(1752, 9, 14, 0, 0, 0, 0, ZoneOffset.UTC).toInstant(); GregorianCalendar gcal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); gcal.setGregorianChange(Date.from(cutover)); gcal.clear(); gcal.set(1700, Calendar.JANUARY, 1); while (test.isBefore(end)) { assertEquals(gcal.get(Calendar.YEAR), test.get(YEAR_OF_ERA)); assertEquals(gcal.get(Calendar.MONTH) + 1, test.get(MONTH_OF_YEAR)); assertEquals(gcal.get(Calendar.DAY_OF_MONTH), test.get(DAY_OF_MONTH)); assertEquals(gcal.toZonedDateTime().toLocalDate(), LocalDate.from(test)); gcal.add(Calendar.DAY_OF_MONTH, 1); test = test.plus(1, DAYS); } } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { BritishCutoverDate a1 = BritishCutoverDate.of(2000, 1, 3); BritishCutoverDate a2 = BritishCutoverDate.of(2000, 1, 3); BritishCutoverDate b = BritishCutoverDate.of(2000, 1, 4); BritishCutoverDate c = BritishCutoverDate.of(2000, 2, 3); BritishCutoverDate d = BritishCutoverDate.of(2001, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertTrue(a1.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {BritishCutoverDate.of(1, 1, 1), "BritishCutover AD 1-01-01"}, {BritishCutoverDate.of(2012, 6, 23), "BritishCutover AD 2012-06-23"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(BritishCutoverDate cutover, String expected) { assertEquals(expected, cutover.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestCopticChronology.java000066400000000000000000000737401343451174100311710ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.List; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestCopticChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Chronology chrono = Chronology.of("Coptic"); Assert.assertNotNull(chrono); Assert.assertEquals(CopticChronology.INSTANCE, chrono); Assert.assertEquals("Coptic", chrono.getId()); Assert.assertEquals("coptic", chrono.getCalendarType()); } @Test public void test_chronology_of_name_id() { Chronology chrono = Chronology.of("coptic"); Assert.assertNotNull(chrono); Assert.assertEquals(CopticChronology.INSTANCE, chrono); Assert.assertEquals("Coptic", chrono.getId()); Assert.assertEquals("coptic", chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {CopticDate.of(-1, 13, 6), LocalDate.of(283, 8, 29)}, {CopticDate.of(0, 1, 1), LocalDate.of(283, 8, 30)}, {CopticDate.of(0, 1, 30), LocalDate.of(283, 9, 28)}, {CopticDate.of(0, 12, 30), LocalDate.of(284, 8, 23)}, {CopticDate.of(0, 13, 1), LocalDate.of(284, 8, 24)}, {CopticDate.of(0, 13, 5), LocalDate.of(284, 8, 28)}, {CopticDate.of(0, 13, 4), LocalDate.of(284, 8, 27)}, {CopticDate.of(1, 1, 1), LocalDate.of(284, 8, 29)}, {CopticDate.of(1, 1, 2), LocalDate.of(284, 8, 30)}, {CopticDate.of(1, 1, 3), LocalDate.of(284, 8, 31)}, {CopticDate.of(2, 1, 1), LocalDate.of(285, 8, 29)}, {CopticDate.of(3, 1, 1), LocalDate.of(286, 8, 29)}, {CopticDate.of(3, 13, 6), LocalDate.of(287, 8, 29)}, {CopticDate.of(4, 1, 1), LocalDate.of(287, 8, 30)}, {CopticDate.of(4, 7, 3), LocalDate.of(288, 2, 28)}, {CopticDate.of(4, 7, 4), LocalDate.of(288, 2, 29)}, {CopticDate.of(5, 1, 1), LocalDate.of(288, 8, 29)}, {CopticDate.of(1662, 3, 3), LocalDate.of(1945, 11, 12)}, {CopticDate.of(1728, 10, 28), LocalDate.of(2012, 7, 5)}, {CopticDate.of(1728, 10, 29), LocalDate.of(2012, 7, 6)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_CopticDate(CopticDate coptic, LocalDate iso) { assertEquals(iso, LocalDate.from(coptic)); } @Test @UseDataProvider("data_samples") public void test_CopticDate_from_LocalDate(CopticDate coptic, LocalDate iso) { assertEquals(coptic, CopticDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_CopticDate_chronology_dateEpochDay(CopticDate coptic, LocalDate iso) { assertEquals(coptic, CopticChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_CopticDate_toEpochDay(CopticDate coptic, LocalDate iso) { assertEquals(iso.toEpochDay(), coptic.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_CopticDate_until_CopticDate(CopticDate coptic, LocalDate iso) { assertEquals(CopticChronology.INSTANCE.period(0, 0, 0), coptic.until(coptic)); } @Test @UseDataProvider("data_samples") public void test_CopticDate_until_LocalDate(CopticDate coptic, LocalDate iso) { assertEquals(CopticChronology.INSTANCE.period(0, 0, 0), coptic.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_CopticDate(CopticDate coptic, LocalDate iso) { assertEquals(Period.ZERO, iso.until(coptic)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(CopticDate coptic, LocalDate iso) { assertEquals(coptic, CopticChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(CopticDate coptic, LocalDate iso) { assertEquals(iso, LocalDate.from(coptic.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(coptic.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(coptic.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(coptic.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(coptic.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(CopticDate coptic, LocalDate iso) { assertEquals(iso, LocalDate.from(coptic.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(coptic.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(coptic.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(coptic.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(coptic.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(CopticDate coptic, LocalDate iso) { assertEquals(0, coptic.until(iso.plusDays(0), DAYS)); assertEquals(1, coptic.until(iso.plusDays(1), DAYS)); assertEquals(35, coptic.until(iso.plusDays(35), DAYS)); assertEquals(-40, coptic.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {1728, 0, 0}, {1728, -1, 1}, {1728, 0, 1}, {1728, 14, 1}, {1728, 15, 1}, {1728, 1, -1}, {1728, 1, 0}, {1728, 1, 31}, {1728, 1, 32}, {1728, 12, -1}, {1728, 12, 0}, {1728, 12, 31}, {1728, 12, 32}, {1728, 13, -1}, {1728, 13, 0}, {1728, 13, 6}, {1728, 13, 7}, {1727, 13, -1}, {1727, 13, 0}, {1727, 13, 7}, {1727, 13, 8}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { CopticDate.of(year, month, dom); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { CopticChronology.INSTANCE.dateYearDay(1728, 366); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { for (int year = -200; year < 200; year++) { CopticDate base = CopticDate.of(year, 1, 1); assertEquals(((year - 3) % 4) == 0, base.isLeapYear()); assertEquals(((year + 400 - 3) % 4) == 0, CopticChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertEquals(false, CopticChronology.INSTANCE.isLeapYear(8)); assertEquals(true, CopticChronology.INSTANCE.isLeapYear(7)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(6)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(5)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(4)); assertEquals(true, CopticChronology.INSTANCE.isLeapYear(3)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(2)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(1)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(0)); assertEquals(true, CopticChronology.INSTANCE.isLeapYear(-1)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(-2)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(-3)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(-4)); assertEquals(true, CopticChronology.INSTANCE.isLeapYear(-5)); assertEquals(false, CopticChronology.INSTANCE.isLeapYear(-6)); } @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {1726, 1, 30}, {1726, 2, 30}, {1726, 3, 30}, {1726, 4, 30}, {1726, 5, 30}, {1726, 6, 30}, {1726, 7, 30}, {1726, 8, 30}, {1726, 9, 30}, {1726, 10, 30}, {1726, 11, 30}, {1726, 12, 30}, {1726, 13, 5}, {1727, 13, 6}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, CopticDate.of(year, month, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = -200; year < 200; year++) { CopticDate base = CopticChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); CopticEra era = (year <= 0 ? CopticEra.BEFORE_AM : CopticEra.AM); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); CopticDate eraBased = CopticChronology.INSTANCE.date(era, yoe, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = -200; year < 200; year++) { CopticDate base = CopticChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); CopticEra era = (year <= 0 ? CopticEra.BEFORE_AM : CopticEra.AM); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); CopticDate eraBased = CopticChronology.INSTANCE.dateYearDay(era, yoe, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, CopticChronology.INSTANCE.prolepticYear(CopticEra.AM, 4)); assertEquals(3, CopticChronology.INSTANCE.prolepticYear(CopticEra.AM, 3)); assertEquals(2, CopticChronology.INSTANCE.prolepticYear(CopticEra.AM, 2)); assertEquals(1, CopticChronology.INSTANCE.prolepticYear(CopticEra.AM, 1)); assertEquals(0, CopticChronology.INSTANCE.prolepticYear(CopticEra.BEFORE_AM, 1)); assertEquals(-1, CopticChronology.INSTANCE.prolepticYear(CopticEra.BEFORE_AM, 2)); assertEquals(-2, CopticChronology.INSTANCE.prolepticYear(CopticEra.BEFORE_AM, 3)); assertEquals(-3, CopticChronology.INSTANCE.prolepticYear(CopticEra.BEFORE_AM, 4)); } @Test public void test_Chronology_eraOf() { assertEquals(CopticEra.AM, CopticChronology.INSTANCE.eraOf(1)); assertEquals(CopticEra.BEFORE_AM, CopticChronology.INSTANCE.eraOf(0)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { CopticChronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = CopticChronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertEquals(true, eras.contains(CopticEra.BEFORE_AM)); assertEquals(true, eras.contains(CopticEra.AM)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), CopticChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 5, 30), CopticChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 365, 366), CopticChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 13), CopticChronology.INSTANCE.range(MONTH_OF_YEAR)); } //----------------------------------------------------------------------- // CopticDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { {1727, 1, 23, DAY_OF_MONTH, 1, 30}, {1727, 2, 23, DAY_OF_MONTH, 1, 30}, {1727, 3, 23, DAY_OF_MONTH, 1, 30}, {1727, 4, 23, DAY_OF_MONTH, 1, 30}, {1727, 5, 23, DAY_OF_MONTH, 1, 30}, {1727, 6, 23, DAY_OF_MONTH, 1, 30}, {1727, 7, 23, DAY_OF_MONTH, 1, 30}, {1727, 8, 23, DAY_OF_MONTH, 1, 30}, {1727, 9, 23, DAY_OF_MONTH, 1, 30}, {1727, 10, 23, DAY_OF_MONTH, 1, 30}, {1727, 11, 23, DAY_OF_MONTH, 1, 30}, {1727, 12, 23, DAY_OF_MONTH, 1, 30}, {1727, 13, 2, DAY_OF_MONTH, 1, 6}, {1727, 1, 23, DAY_OF_YEAR, 1, 366}, {1727, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1727, 12, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {1727, 13, 2, ALIGNED_WEEK_OF_MONTH, 1, 1}, {1726, 13, 2, DAY_OF_MONTH, 1, 5}, {1726, 13, 2, DAY_OF_YEAR, 1, 365}, {1726, 13, 2, ALIGNED_WEEK_OF_MONTH, 1, 1}, {1726, 2, 23, WeekFields.ISO.dayOfWeek(), 1, 7}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), CopticDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { CopticDate.of(1727, 6, 30).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // CopticDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {1727, 6, 8, DAY_OF_WEEK, 2}, {1727, 6, 8, DAY_OF_MONTH, 8}, {1727, 6, 8, DAY_OF_YEAR, 30 * 5 + 8}, {1727, 6, 8, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1}, {1727, 6, 8, ALIGNED_WEEK_OF_MONTH, 2}, {1727, 6, 8, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4}, {1727, 6, 8, ALIGNED_WEEK_OF_YEAR, 23}, {1727, 6, 8, MONTH_OF_YEAR, 6}, {1727, 6, 8, PROLEPTIC_MONTH, 1727 * 13 + 6 - 1}, {1727, 6, 8, YEAR, 1727}, {1727, 6, 8, ERA, 1}, {1, 6, 8, ERA, 1}, {0, 6, 8, ERA, 0}, {1727, 6, 8, WeekFields.ISO.dayOfWeek(), 2}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, CopticDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { CopticDate.of(1727, 6, 30).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // CopticDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {1727, 6, 8, DAY_OF_WEEK, 4, 1727, 6, 10}, {1727, 6, 8, DAY_OF_WEEK, 2, 1727, 6, 8}, {1727, 6, 8, DAY_OF_MONTH, 30, 1727, 6, 30}, {1727, 6, 8, DAY_OF_MONTH, 8, 1727, 6, 8}, {1727, 6, 8, DAY_OF_YEAR, 365, 1727, 13, 5}, {1727, 6, 8, DAY_OF_YEAR, 158, 1727, 6, 8}, {1727, 6, 8, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 1727, 6, 10}, {1727, 6, 8, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 1727, 6, 8}, {1727, 6, 8, ALIGNED_WEEK_OF_MONTH, 1, 1727, 6, 1}, {1727, 6, 8, ALIGNED_WEEK_OF_MONTH, 2, 1727, 6, 8}, {1727, 6, 8, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 1727, 6, 6}, {1727, 6, 8, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 1727, 6, 8}, {1727, 6, 8, ALIGNED_WEEK_OF_YEAR, 22, 1727, 6, 1}, {1727, 6, 8, ALIGNED_WEEK_OF_YEAR, 23, 1727, 6, 8}, {1727, 6, 8, MONTH_OF_YEAR, 7, 1727, 7, 8}, {1727, 6, 8, MONTH_OF_YEAR, 6, 1727, 6, 8}, {1727, 6, 8, PROLEPTIC_MONTH, 2009 * 13 + 3 - 1, 2009, 3, 8}, {1727, 6, 8, PROLEPTIC_MONTH, 1727 * 13 + 6 - 1, 1727, 6, 8}, {1727, 6, 8, YEAR, 1728, 1728, 6, 8}, {1727, 6, 8, YEAR, 1727, 1727, 6, 8}, {1727, 6, 8, YEAR_OF_ERA, 2012, 2012, 6, 8}, {1727, 6, 8, YEAR_OF_ERA, 1727, 1727, 6, 8}, {1727, 6, 8, ERA, 0, -1726, 6, 8}, {1727, 6, 8, ERA, 1, 1727, 6, 8}, {1726, 3, 30, MONTH_OF_YEAR, 13, 1726, 13, 5}, {1727, 3, 30, MONTH_OF_YEAR, 13, 1727, 13, 6}, {1727, 13, 6, YEAR, 2006, 2006, 13, 5}, {-1727, 6, 8, YEAR_OF_ERA, 1722, -1721, 6, 8}, {1727, 6, 8, WeekFields.ISO.dayOfWeek(), 5, 1727, 6, 11}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(CopticDate.of(expectedYear, expectedMonth, expectedDom), CopticDate.of(year, month, dom).with(field, value)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { CopticDate.of(1727, 6, 30).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // CopticDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_adjust1() { CopticDate base = CopticDate.of(1728, 10, 29); CopticDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(CopticDate.of(1728, 10, 30), test); } @Test public void test_adjust2() { CopticDate base = CopticDate.of(1728, 13, 2); CopticDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(CopticDate.of(1728, 13, 5), test); } //----------------------------------------------------------------------- // CopticDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { CopticDate coptic = CopticDate.of(1726, 1, 4); CopticDate test = coptic.with(LocalDate.of(2012, 7, 6)); assertEquals(CopticDate.of(1728, 10, 29), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { CopticDate coptic = CopticDate.of(1726, 1, 4); coptic.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(CopticDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToCopticDate() { CopticDate coptic = CopticDate.of(1728, 10, 29); LocalDate test = LocalDate.MIN.with(coptic); assertEquals(LocalDate.of(2012, 7, 6), test); } @Test public void test_LocalDateTime_adjustToCopticDate() { CopticDate coptic = CopticDate.of(1728, 10, 29); LocalDateTime test = LocalDateTime.MIN.with(coptic); assertEquals(LocalDateTime.of(2012, 7, 6, 0, 0), test); } //----------------------------------------------------------------------- // CopticDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {1726, 5, 26, 0, DAYS, 1726, 5, 26}, {1726, 5, 26, 8, DAYS, 1726, 6, 4}, {1726, 5, 26, -3, DAYS, 1726, 5, 23}, {1726, 5, 26, 0, WEEKS, 1726, 5, 26}, {1726, 5, 26, 3, WEEKS, 1726, 6, 17}, {1726, 5, 26, -5, WEEKS, 1726, 4, 21}, {1726, 5, 26, 0, MONTHS, 1726, 5, 26}, {1726, 5, 26, 3, MONTHS, 1726, 8, 26}, {1726, 5, 26, -6, MONTHS, 1725, 12, 26}, {1726, 5, 26, 0, YEARS, 1726, 5, 26}, {1726, 5, 26, 3, YEARS, 1729, 5, 26}, {1726, 5, 26, -5, YEARS, 1721, 5, 26}, {1726, 5, 26, 0, DECADES, 1726, 5, 26}, {1726, 5, 26, 3, DECADES, 1756, 5, 26}, {1726, 5, 26, -5, DECADES, 1676, 5, 26}, {1726, 5, 26, 0, CENTURIES, 1726, 5, 26}, {1726, 5, 26, 3, CENTURIES, 2026, 5, 26}, {1726, 5, 26, -5, CENTURIES, 1226, 5, 26}, {1726, 5, 26, 0, MILLENNIA, 1726, 5, 26}, {1726, 5, 26, 3, MILLENNIA, 4726, 5, 26}, {1726, 5, 26, -5, MILLENNIA, 1726 - 5000, 5, 26}, {1726, 5, 26, -1, ERAS, -1725, 5, 26}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(CopticDate.of(expectedYear, expectedMonth, expectedDom), CopticDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(CopticDate.of(expectedYear, expectedMonth, expectedDom), CopticDate.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { CopticDate.of(1727, 6, 30).plus(0, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(CopticDate.of(1727, 7, 29), CopticDate.of(1727, 5, 26).plus(CopticChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(CopticDate.of(1727, 7, 26), CopticDate.of(1727, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(CopticDate.of(1727, 3, 23), CopticDate.of(1727, 5, 26).minus(CopticChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(CopticDate.of(1727, 3, 26), CopticDate.of(1727, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // CopticDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {1726, 5, 26, 1726, 5, 26, DAYS, 0}, {1726, 5, 26, 1726, 6, 1, DAYS, 5}, {1726, 5, 26, 1726, 5, 20, DAYS, -6}, {1726, 5, 26, 1726, 5, 26, WEEKS, 0}, {1726, 5, 26, 1726, 6, 2, WEEKS, 0}, {1726, 5, 26, 1726, 6, 3, WEEKS, 1}, {1726, 5, 26, 1726, 5, 26, MONTHS, 0}, {1726, 5, 26, 1726, 6, 25, MONTHS, 0}, {1726, 5, 26, 1726, 6, 26, MONTHS, 1}, {1726, 5, 26, 1726, 5, 26, YEARS, 0}, {1726, 5, 26, 1727, 5, 25, YEARS, 0}, {1726, 5, 26, 1727, 5, 26, YEARS, 1}, {1726, 5, 26, 1726, 5, 26, DECADES, 0}, {1726, 5, 26, 1736, 5, 25, DECADES, 0}, {1726, 5, 26, 1736, 5, 26, DECADES, 1}, {1726, 5, 26, 1726, 5, 26, CENTURIES, 0}, {1726, 5, 26, 1826, 5, 25, CENTURIES, 0}, {1726, 5, 26, 1826, 5, 26, CENTURIES, 1}, {1726, 5, 26, 1726, 5, 26, MILLENNIA, 0}, {1726, 5, 26, 2726, 5, 25, MILLENNIA, 0}, {1726, 5, 26, 2726, 5, 26, MILLENNIA, 1}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { CopticDate start = CopticDate.of(year1, month1, dom1); CopticDate end = CopticDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { CopticDate start = CopticDate.of(1726, 6, 30); CopticDate end = CopticDate.of(1726, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { CopticDate a1 = CopticDate.of(1728, 1, 3); CopticDate a2 = CopticDate.of(1728, 1, 3); CopticDate b = CopticDate.of(1728, 1, 4); CopticDate c = CopticDate.of(1728, 2, 3); CopticDate d = CopticDate.of(1729, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertTrue(a1.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {CopticDate.of(1, 1, 1), "Coptic AM 1-01-01"}, {CopticDate.of(1728, 10, 28), "Coptic AM 1728-10-28"}, {CopticDate.of(1728, 10, 29), "Coptic AM 1728-10-29"}, {CopticDate.of(1727, 13, 5), "Coptic AM 1727-13-05"}, {CopticDate.of(1727, 13, 6), "Coptic AM 1727-13-06"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(CopticDate coptic, String expected) { assertEquals(expected, coptic.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestDiscordianChronology.java000066400000000000000000001274621343451174100320300ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.List; import java.util.function.Predicate; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestDiscordianChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Chronology chrono = Chronology.of("Discordian"); Assert.assertNotNull(chrono); Assert.assertEquals(DiscordianChronology.INSTANCE, chrono); Assert.assertEquals("Discordian", chrono.getId()); Assert.assertEquals("discordian", chrono.getCalendarType()); } @Test public void test_chronology_of_name_id() { Chronology chrono = Chronology.of("discordian"); Assert.assertNotNull(chrono); Assert.assertEquals(DiscordianChronology.INSTANCE, chrono); Assert.assertEquals("Discordian", chrono.getId()); Assert.assertEquals("discordian", chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {DiscordianDate.of(2, 1, 1), LocalDate.of(-1164, 1, 1)}, {DiscordianDate.of(166, 1, 1), LocalDate.of(-1000, 1, 1)}, {DiscordianDate.of(1156, 1, 1), LocalDate.of(-10, 1, 1)}, {DiscordianDate.of(1166, 1, 1), LocalDate.of(0, 1, 1)}, {DiscordianDate.of(1167, 1, 1), LocalDate.of(1, 1, 1)}, {DiscordianDate.of(1167, 1, 2), LocalDate.of(1, 1, 2)}, {DiscordianDate.of(1167, 1, 3), LocalDate.of(1, 1, 3)}, {DiscordianDate.of(1167, 1, 57), LocalDate.of(1, 2, 26)}, {DiscordianDate.of(1167, 1, 58), LocalDate.of(1, 2, 27)}, {DiscordianDate.of(1167, 1, 59), LocalDate.of(1, 2, 28)}, {DiscordianDate.of(1167, 1, 60), LocalDate.of(1, 3, 1)}, {DiscordianDate.of(1170, 1, 57), LocalDate.of(4, 2, 26)}, {DiscordianDate.of(1170, 1, 58), LocalDate.of(4, 2, 27)}, {DiscordianDate.of(1170, 1, 59), LocalDate.of(4, 2, 28)}, {DiscordianDate.of(1170, 0, 0), LocalDate.of(4, 2, 29)}, {DiscordianDate.of(1170, 1, 60), LocalDate.of(4, 3, 1)}, {DiscordianDate.of(1266, 1, 57), LocalDate.of(100, 2, 26)}, {DiscordianDate.of(1266, 1, 58), LocalDate.of(100, 2, 27)}, {DiscordianDate.of(1266, 1, 59), LocalDate.of(100, 2, 28)}, {DiscordianDate.of(1266, 1, 60), LocalDate.of(100, 3, 1)}, {DiscordianDate.of(1266, 1, 61), LocalDate.of(100, 3, 2)}, {DiscordianDate.of(1166, 5, 73), LocalDate.of(0, 12, 31)}, {DiscordianDate.of(1166, 5, 72), LocalDate.of(0, 12, 30)}, {DiscordianDate.of(2748, 4, 68), LocalDate.of(1582, 10, 14)}, {DiscordianDate.of(2748, 4, 69), LocalDate.of(1582, 10, 15)}, {DiscordianDate.of(3111, 5, 24), LocalDate.of(1945, 11, 12)}, {DiscordianDate.of(3178, 3, 40), LocalDate.of(2012, 7, 5)}, {DiscordianDate.of(3178, 3, 41), LocalDate.of(2012, 7, 6)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_DiscordianDate(DiscordianDate discordian, LocalDate iso) { assertEquals(iso, LocalDate.from(discordian)); } @Test @UseDataProvider("data_samples") public void test_DiscordianDate_from_LocalDate(DiscordianDate discordian, LocalDate iso) { assertEquals(discordian, DiscordianDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_DiscordianDate_chronology_dateEpochDay(DiscordianDate discordian, LocalDate iso) { assertEquals(discordian, DiscordianChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_DiscordianDate_toEpochDay(DiscordianDate discordian, LocalDate iso) { assertEquals(iso.toEpochDay(), discordian.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_DiscordianDate_until_DiscordianDate(DiscordianDate discordian, LocalDate iso) { assertEquals(DiscordianChronology.INSTANCE.period(0, 0, 0), discordian.until(discordian)); } @Test @UseDataProvider("data_samples") public void test_DiscordianDate_until_LocalDate(DiscordianDate discordian, LocalDate iso) { assertEquals(DiscordianChronology.INSTANCE.period(0, 0, 0), discordian.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_DiscordianDate(DiscordianDate discordian, LocalDate iso) { assertEquals(Period.ZERO, iso.until(discordian)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(DiscordianDate discordian, LocalDate iso) { assertEquals(discordian, DiscordianChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(DiscordianDate discordian, LocalDate iso) { assertEquals(iso, LocalDate.from(discordian.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(discordian.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(discordian.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(discordian.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(discordian.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(DiscordianDate discordian, LocalDate iso) { assertEquals(iso, LocalDate.from(discordian.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(discordian.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(discordian.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(discordian.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(discordian.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(DiscordianDate discordian, LocalDate iso) { assertEquals(0, discordian.until(iso.plusDays(0), DAYS)); assertEquals(1, discordian.until(iso.plusDays(1), DAYS)); assertEquals(35, discordian.until(iso.plusDays(35), DAYS)); assertEquals(-40, discordian.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {1900, 0, 0}, {1900, -1, 1}, {1900, 0, 1}, {1900, 6, 1}, {1900, 7, 1}, {1900, 1, -1}, {1900, 1, 0}, {1900, 1, 74}, {1900, 0, 0}, {1900, 5, -1}, {1900, 5, 0}, {1900, 5, 74}, {1900, 2, 74}, {1900, 3, 74}, {1900, 4, 74}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { DiscordianDate.of(year, month, dom); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { DiscordianChronology.INSTANCE.dateYearDay(2001, 366); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { Predicate isLeapYear = year -> { int offsetYear = year - 1166; return offsetYear % 4 == 0 && (offsetYear % 400 == 0 || offsetYear % 100 != 0); }; for (int year = 1066; year < 1567; year++) { DiscordianDate base = DiscordianDate.of(year, 1, 1); assertEquals(isLeapYear.test(year), base.isLeapYear()); assertEquals(isLeapYear.test(year), DiscordianChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertEquals(true, DiscordianChronology.INSTANCE.isLeapYear(1174)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1173)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1172)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1171)); assertEquals(true, DiscordianChronology.INSTANCE.isLeapYear(1170)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1169)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1168)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1167)); assertEquals(true, DiscordianChronology.INSTANCE.isLeapYear(1166)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1165)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1164)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1163)); assertEquals(true, DiscordianChronology.INSTANCE.isLeapYear(1162)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1161)); assertEquals(false, DiscordianChronology.INSTANCE.isLeapYear(1160)); } @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {1900, 1, 73}, {1900, 2, 73}, {1900, 3, 73}, {1900, 4, 73}, {1900, 5, 73}, {1901, 1, 73}, {1902, 1, 73}, {1903, 1, 73}, {1904, 1, 73}, {1966, 1, 73}, {2066, 1, 73}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, DiscordianDate.of(year, month, 1).lengthOfMonth()); } @Test public void test_lengthOfMonth_specific() { assertEquals(1, DiscordianDate.of(3178, 0, 0).lengthOfMonth()); assertEquals(73, DiscordianDate.of(3178, 1, 1).lengthOfMonth()); assertEquals(73, DiscordianDate.of(3178, 1, 73).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = 1; year < 200; year++) { DiscordianDate base = DiscordianChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); assertEquals(DiscordianEra.YOLD, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); DiscordianDate eraBased = DiscordianChronology.INSTANCE.date(DiscordianEra.YOLD, year, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = 1; year < 200; year++) { DiscordianDate base = DiscordianChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); assertEquals(DiscordianEra.YOLD, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); DiscordianDate eraBased = DiscordianChronology.INSTANCE.dateYearDay(DiscordianEra.YOLD, year, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, DiscordianChronology.INSTANCE.prolepticYear(DiscordianEra.YOLD, 4)); assertEquals(3, DiscordianChronology.INSTANCE.prolepticYear(DiscordianEra.YOLD, 3)); assertEquals(2, DiscordianChronology.INSTANCE.prolepticYear(DiscordianEra.YOLD, 2)); assertEquals(1, DiscordianChronology.INSTANCE.prolepticYear(DiscordianEra.YOLD, 1)); } @Test(expected = ClassCastException.class) public void test_prolepticYear_badEra() { DiscordianChronology.INSTANCE.prolepticYear(IsoEra.CE, 4); } @Test public void test_Chronology_eraOf() { assertEquals(DiscordianEra.YOLD, DiscordianChronology.INSTANCE.eraOf(1)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { DiscordianChronology.INSTANCE.eraOf(2); DiscordianChronology.INSTANCE.eraOf(0); } @Test public void test_Chronology_eras() { List eras = DiscordianChronology.INSTANCE.eras(); assertEquals(1, eras.size()); assertEquals(true, eras.contains(DiscordianEra.YOLD)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(0, 1, 0, 5), DiscordianChronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(ValueRange.of(0, 1, 5, 5), DiscordianChronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(ValueRange.of(0, 1, 0, 15), DiscordianChronology.INSTANCE.range(ALIGNED_WEEK_OF_MONTH)); assertEquals(ValueRange.of(0, 1, 73, 73), DiscordianChronology.INSTANCE.range(ALIGNED_WEEK_OF_YEAR)); assertEquals(ValueRange.of(0, 1, 0, 5), DiscordianChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(0, 1, 0, 73), DiscordianChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 365, 366), DiscordianChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(-1_145_400, 999_999 * 365L + 242_499), DiscordianChronology.INSTANCE.range(EPOCH_DAY)); assertEquals(ValueRange.of(1, 1), DiscordianChronology.INSTANCE.range(ERA)); assertEquals(ValueRange.of(0, 1, 5, 5), DiscordianChronology.INSTANCE.range(MONTH_OF_YEAR)); assertEquals(ValueRange.of(0, 999_999 * 5L + 5 - 1), DiscordianChronology.INSTANCE.range(PROLEPTIC_MONTH)); assertEquals(ValueRange.of(1, 999_999), DiscordianChronology.INSTANCE.range(YEAR)); assertEquals(ValueRange.of(1, 999_999), DiscordianChronology.INSTANCE.range(YEAR_OF_ERA)); } //----------------------------------------------------------------------- // DiscordianDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { // St Tibs is in its own month, so (0 to 0) or (1 to 73) {2010, 0, 0, DAY_OF_MONTH, 0, 0}, {2010, 1, 23, DAY_OF_MONTH, 1, 73}, {2010, 2, 23, DAY_OF_MONTH, 1, 73}, {2010, 3, 23, DAY_OF_MONTH, 1, 73}, {2010, 4, 23, DAY_OF_MONTH, 1, 73}, {2010, 5, 23, DAY_OF_MONTH, 1, 73}, // Day of year is an ordinal count including St Tibs {2010, 0, 0, DAY_OF_YEAR, 1, 366}, {2010, 1, 23, DAY_OF_YEAR, 1, 366}, {2011, 2, 23, DAY_OF_YEAR, 1, 365}, // St Tibs is still in same year, so (0 to 5) in leap year and (1 to 5) in non-leap year {2010, 0, 0, MONTH_OF_YEAR, 0, 5}, {2010, 1, 1, MONTH_OF_YEAR, 0, 5}, {2011, 1, 23, MONTH_OF_YEAR, 1, 5}, // St Tibs is in its own month, so (0 to 0) or (1 to 5) {2010, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0, 0}, {2010, 1, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 5}, {2010, 1, 59, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 5}, {2010, 1, 60, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 5}, // St Tibs is still in same year, so (0 to 5) in leap year and (1 to 5) in non-leap year {2010, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 5}, {2010, 1, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 5}, {2010, 1, 59, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 5}, {2010, 1, 60, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 5}, {2011, 1, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1, 5}, // St Tibs is in its own month, so (0 to 0) or (1 to 15) {2010, 0, 0, ALIGNED_WEEK_OF_MONTH, 0, 0}, {2010, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 15}, // St Tibs is still in same year, so (0 to 73) in leap year and (1 to 73) in non-leap year {2010, 0, 0, ALIGNED_WEEK_OF_YEAR, 0, 73}, {2010, 1, 23, ALIGNED_WEEK_OF_YEAR, 0, 73}, {2011, 1, 23, ALIGNED_WEEK_OF_YEAR, 1, 73}, // St Tibs is in its own week, so (0 to 0) or (1 to 5) {2010, 0, 0, DAY_OF_WEEK, 0, 0}, {2010, 1, 1, DAY_OF_WEEK, 1, 5}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), DiscordianDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { DiscordianDate.of(2012, 5, 30).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // DiscordianDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 1, 26, DAY_OF_WEEK, 1}, {2014, 1, 26, DAY_OF_MONTH, 26}, {2014, 1, 26, DAY_OF_YEAR, 26}, {2014, 1, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1}, {2014, 1, 26, ALIGNED_WEEK_OF_MONTH, 6}, {2014, 1, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1}, {2014, 1, 26, ALIGNED_WEEK_OF_YEAR, 6}, {2014, 1, 26, MONTH_OF_YEAR, 1}, {2014, 1, 26, PROLEPTIC_MONTH, 2014 * 5 + 1 - 1}, {2014, 1, 26, YEAR, 2014}, {2014, 1, 26, ERA, 1}, {2014, 5, 26, DAY_OF_WEEK, 3}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 1 + 73 + 73 + 73 + 73 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 6}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 64}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 5 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 5, 8, ERA, 1}, {2014, 0, 0, DAY_OF_WEEK, 0}, {2014, 0, 0, DAY_OF_MONTH, 0}, {2014, 0, 0, DAY_OF_YEAR, 60}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0}, {2014, 0, 0, ALIGNED_WEEK_OF_MONTH, 0}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0}, {2014, 0, 0, ALIGNED_WEEK_OF_YEAR, 0}, {2014, 0, 0, MONTH_OF_YEAR, 0}, {2014, 0, 0, PROLEPTIC_MONTH, 2014 * 5 + 1 - 1}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, DiscordianDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { DiscordianDate.of(2012, 1, 30).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // DiscordianDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 1, 2014, 5, 24}, {2014, 5, 26, DAY_OF_WEEK, 3, 2014, 5, 26}, {2014, 5, 26, DAY_OF_MONTH, 31, 2014, 5, 31}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 365, 2014, 5, 72}, {2014, 5, 26, DAY_OF_YEAR, 319, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 28}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 1}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 6, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 5, 25}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 2, 40}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 64, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 4, 2014, 4, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 5 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 5 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0, 2014, 0, 0}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2014, 1, 56}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2, 2014, 1, 57}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 1, 58}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 4, 2014, 1, 59}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 1, 60}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 2014, 0, 0}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1, 2014, 1, 56}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 1, 57}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2014, 1, 58}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 2014, 1, 59}, {2014, 0, 0, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 1, 60}, {2014, 0, 0, ALIGNED_WEEK_OF_MONTH, 0, 2014, 0, 0}, {2014, 0, 0, ALIGNED_WEEK_OF_MONTH, 3, 2014, 1, 15}, {2014, 0, 0, ALIGNED_WEEK_OF_YEAR, 0, 2014, 0, 0}, {2014, 0, 0, ALIGNED_WEEK_OF_YEAR, 3, 2014, 1, 15}, {2014, 0, 0, DAY_OF_WEEK, 0, 2014, 0, 0}, {2014, 0, 0, DAY_OF_WEEK, 1, 2014, 1, 56}, {2014, 0, 0, DAY_OF_WEEK, 2, 2014, 1, 57}, {2014, 0, 0, DAY_OF_WEEK, 3, 2014, 1, 58}, {2014, 0, 0, DAY_OF_WEEK, 4, 2014, 1, 59}, {2014, 0, 0, DAY_OF_WEEK, 5, 2014, 1, 60}, {2014, 0, 0, DAY_OF_MONTH, 0, 2014, 0, 0}, {2014, 0, 0, DAY_OF_MONTH, 3, 2014, 1, 3}, {2014, 0, 0, MONTH_OF_YEAR, 0, 2014, 0, 0}, {2014, 0, 0, MONTH_OF_YEAR, 1, 2014, 1, 60}, {2014, 0, 0, MONTH_OF_YEAR, 2, 2014, 2, 60}, {2014, 0, 0, YEAR, 2014, 2014, 0, 0}, {2014, 0, 0, YEAR, 2013, 2013, 1, 60}, {2014, 0, 0, YEAR, 2015, 2015, 1, 60}, {2014, 0, 0, YEAR, 2018, 2018, 0, 0}, {2014, 3, 31, DAY_OF_MONTH, 0, 2014, 0, 0}, {2014, 1, 31, DAY_OF_MONTH, 0, 2014, 0, 0}, {2014, 3, 31, MONTH_OF_YEAR, 0, 2014, 0, 0}, {2014, 3, 31, DAY_OF_YEAR, 60, 2014, 0, 0}, {2013, 3, 31, DAY_OF_YEAR, 60, 2013, 1, 60}, {2013, 1, 60, YEAR, 2014, 2014, 1, 60}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(DiscordianDate.of(expectedYear, expectedMonth, expectedDom), DiscordianDate.of(year, month, dom).with(field, value)); } @DataProvider public static Object[][] data_with_bad() { return new Object[][] { {2013, 1, 1, DAY_OF_WEEK, 0}, {2013, 1, 1, DAY_OF_WEEK, 6}, {2014, 1, 1, DAY_OF_WEEK, -1}, {2014, 1, 1, DAY_OF_WEEK, 6}, {2013, 1, 1, DAY_OF_MONTH, 0}, {2013, 1, 1, DAY_OF_MONTH, 74}, {2014, 1, 1, DAY_OF_MONTH, -1}, {2014, 1, 1, DAY_OF_MONTH, 74}, {2013, 1, 1, DAY_OF_YEAR, 0}, {2014, 1, 1, DAY_OF_YEAR, 0}, {2013, 1, 1, DAY_OF_YEAR, 367}, {2014, 1, 1, DAY_OF_YEAR, 367}, {2013, 1, 1, MONTH_OF_YEAR, 0}, {2013, 1, 1, MONTH_OF_YEAR, 6}, {2014, 1, 1, MONTH_OF_YEAR, -1}, {2014, 1, 1, MONTH_OF_YEAR, 6}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_with_bad") public void test_with_TemporalField_badValue(int year, int month, int dom, TemporalField field, long value) { DiscordianDate.of(year, month, dom).with(field, value); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { DiscordianDate.of(2012, 5, 30).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // DiscordianDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_adjust1() { DiscordianDate base = DiscordianDate.of(2014, 0, 0); DiscordianDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(DiscordianDate.of(2014, 0, 0), test); } @Test public void test_adjust2() { DiscordianDate base = DiscordianDate.of(2012, 2, 23); DiscordianDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(DiscordianDate.of(2012, 2, 73), test); } //----------------------------------------------------------------------- // DiscordianDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { DiscordianDate discordian = DiscordianDate.of(2000, 1, 4); DiscordianDate test = discordian.with(LocalDate.of(2012, 7, 6)); assertEquals(DiscordianDate.of(3178, 3, 41), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { DiscordianDate discordian = DiscordianDate.of(2000, 1, 4); discordian.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(DiscordianDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToDiscordianDate() { DiscordianDate discordian = DiscordianDate.of(3178, 3, 41); LocalDate test = LocalDate.MIN.with(discordian); assertEquals(LocalDate.of(2012, 7, 6), test); } @Test public void test_LocalDateTime_adjustToDiscordianDate() { DiscordianDate discordian = DiscordianDate.of(3178, 3, 41); LocalDateTime test = LocalDateTime.MIN.with(discordian); assertEquals(LocalDateTime.of(2012, 7, 6, 0, 0), test); } //----------------------------------------------------------------------- // DiscordianDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 5, 34}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 5, 41}, {2014, 5, 26, -5, WEEKS, 2014, 5, 1}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2015, 3, 26}, {2014, 5, 26, -5, MONTHS, 2013, 5, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -1, MILLENNIA, 2014 - 1000, 5, 26}, }; } @DataProvider public static Object[][] data_plus_leap() { return new Object[][] { {2014, 0, 0, 0, DAYS, 2014, 0, 0}, {2014, 0, 0, 8, DAYS, 2014, 1, 67}, {2014, 0, 0, -3, DAYS, 2014, 1, 57}, {2014, 0, 0, 0, WEEKS, 2014, 0, 0}, {2014, 0, 0, 3, WEEKS, 2014, 2, 2}, {2014, 0, 0, -5, WEEKS, 2014, 1, 35}, {2014, 0, 0, 73 * 4, WEEKS, 2018, 0, 0}, {2014, 0, 0, 0, MONTHS, 2014, 0, 0}, {2014, 0, 0, 3, MONTHS, 2014, 4, 60}, {2014, 0, 0, -5, MONTHS, 2013, 1, 60}, {2014, 0, 0, 20, MONTHS, 2018, 0, 0}, {2014, 0, 0, 0, YEARS, 2014, 0, 0}, {2014, 0, 0, 3, YEARS, 2017, 1, 60}, {2014, 0, 0, -5, YEARS, 2009, 1, 60}, {2014, 0, 0, 4, YEARS, 2018, 0, 0}, }; } @DataProvider public static Object[][] data_minus_leap() { return new Object[][] { {2014, 0, 0, 0, DAYS, 2014, 0, 0}, {2014, 1, 52, 8, DAYS, 2014, 0, 0}, {2014, 1, 62, -3, DAYS, 2014, 0, 0}, {2014, 0, 0, 0, WEEKS, 2014, 0, 0}, {2014, 1, 45, 3, WEEKS, 2014, 0, 0}, {2014, 2, 12, -5, WEEKS, 2014, 0, 0}, {2010, 0, 0, 73 * 4, WEEKS, 2014, 0, 0}, {2014, 0, 0, 0, MONTHS, 2014, 0, 0}, {2013, 3, 60, 3, MONTHS, 2014, 0, 0}, {2015, 1, 60, -5, MONTHS, 2014, 0, 0}, {2010, 0, 0, 20, MONTHS, 2014, 0, 0}, {2014, 0, 0, 0, YEARS, 2014, 0, 0}, {2011, 1, 60, 3, YEARS, 2014, 0, 0}, {2019, 1, 60, -5, YEARS, 2014, 0, 0}, {2010, 0, 0, 4, YEARS, 2014, 0, 0}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(DiscordianDate.of(expectedYear, expectedMonth, expectedDom), DiscordianDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus_leap") public void test_plus_leap_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(DiscordianDate.of(expectedYear, expectedMonth, expectedDom), DiscordianDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(DiscordianDate.of(expectedYear, expectedMonth, expectedDom), DiscordianDate.of(year, month, dom).minus(amount, unit)); } @Test @UseDataProvider("data_minus_leap") public void test_minus_leap_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(DiscordianDate.of(expectedYear, expectedMonth, expectedDom), DiscordianDate.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { DiscordianDate.of(2012, 5, 30).plus(0, MINUTES); } //----------------------------------------------------------------------- // DiscordianDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 5, 32, DAYS, 6}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 5, 30, WEEKS, 0}, {2014, 5, 26, 2014, 5, 31, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2015, 1, 25, MONTHS, 0}, {2014, 5, 26, 2015, 1, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {2013, 5, 26, 3014, 5, 26, ERAS, 0}, {2014, 1, 59, 2014, 1, 60, DAYS, 2}, {2014, 1, 59, 2014, 0, 0, DAYS, 1}, {2014, 0, 0, 2014, 1, 60, DAYS, 1}, {2014, 1, 60, 2014, 1, 55, DAYS, -6}, {2014, 0, 0, 2014, 0, 0, WEEKS, 0}, {2014, 1, 60, 2014, 1, 60, WEEKS, 0}, {2014, 1, 60, 2014, 1, 59, WEEKS, 0}, {2014, 1, 60, 2014, 1, 56, WEEKS, 0}, {2014, 1, 60, 2014, 1, 55, WEEKS, -1}, {2014, 0, 0, 2014, 1, 54, WEEKS, -1}, {2014, 0, 0, 2014, 1, 55, WEEKS, 0}, {2014, 0, 0, 2014, 1, 64, WEEKS, 0}, {2014, 0, 0, 2014, 1, 65, WEEKS, 1}, {2014, 1, 54, 2014, 0, 0, WEEKS, 1}, {2014, 1, 55, 2014, 0, 0, WEEKS, 0}, {2014, 1, 64, 2014, 0, 0, WEEKS, 0}, {2014, 1, 65, 2014, 0, 0, WEEKS, -1}, {2014, 0, 0, 2014, 0, 0, MONTHS, 0}, {2014, 0, 0, 2014, 2, 59, MONTHS, 0}, {2014, 0, 0, 2014, 2, 60, MONTHS, 1}, {2014, 2, 60, 2014, 0, 0, MONTHS, -1}, {2014, 2, 59, 2014, 0, 0, MONTHS, 0}, {2013, 5, 59, 2014, 0, 0, MONTHS, 1}, {2013, 5, 60, 2014, 0, 0, MONTHS, 0}, {2013, 5, 60, 2014, 1, 60, MONTHS, 1}, {2014, 0, 0, 2014, 0, 0, YEARS, 0}, {2014, 0, 0, 2015, 1, 59, YEARS, 0}, {2014, 0, 0, 2015, 1, 60, YEARS, 1}, {2013, 1, 60, 2014, 0, 0, YEARS, 0}, {2013, 1, 59, 2014, 0, 0, YEARS, 1}, {2013, 1, 60, 2014, 1, 60, YEARS, 1}, {2014, 0, 0, 2013, 1, 59, YEARS, -1}, {2014, 0, 0, 2013, 1, 60, YEARS, 0}, {2015, 1, 60, 2014, 0, 0, YEARS, -1}, {2015, 1, 59, 2014, 0, 0, YEARS, 0}, {2018, 0, 0, 2014, 0, 0, YEARS, -4}, {2014, 0, 0, 2018, 0, 0, YEARS, 4}, }; } @DataProvider public static Object[][] data_until_period() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, 0, 0, 0}, {2014, 5, 26, 2014, 5, 32, 0, 0, 6}, {2014, 5, 26, 2014, 5, 20, 0, 0, -6}, {2014, 5, 26, 2014, 5, 30, 0, 0, 4}, {2014, 5, 26, 2014, 5, 31, 0, 0, 5}, {2014, 5, 26, 2015, 1, 25, 0, 0, 72}, {2014, 5, 26, 2015, 1, 26, 0, 1, 0}, {2014, 5, 26, 2015, 5, 25, 0, 4, 72}, {2014, 5, 26, 2015, 5, 26, 1, 0, 0}, {2014, 5, 26, 2024, 5, 25, 9, 4, 72}, {2014, 5, 26, 2024, 5, 26, 10, 0, 0}, {2014, 1, 59, 2014, 1, 60, 0, 0, 2}, {2014, 1, 59, 2014, 0, 0, 0, 0, 1}, {2014, 0, 0, 2014, 1, 60, 0, 0, 1}, {2014, 1, 60, 2014, 1, 55, 0, 0, -6}, {2014, 1, 60, 2014, 1, 59, 0, 0, -2}, {2014, 1, 60, 2014, 1, 55, 0, 0, -6}, {2014, 0, 0, 2014, 1, 54, 0, 0, -6}, {2014, 0, 0, 2014, 1, 65, 0, 0, 6}, {2014, 1, 55, 2014, 0, 0, 0, 0, 5}, {2014, 1, 64, 2014, 0, 0, 0, 0, -5}, {2014, 0, 0, 2014, 2, 59, 0, 0, 73}, {2014, 0, 0, 2014, 2, 60, 0, 1, 0}, {2014, 2, 60, 2014, 0, 0, 0, -1, -1}, {2014, 2, 59, 2014, 0, 0, 0, 0, -73}, {2013, 5, 59, 2014, 0, 0, 0, 1, 1}, {2013, 5, 60, 2014, 0, 0, 0, 0, 73}, {2013, 5, 60, 2014, 1, 60, 0, 1, 0}, {2014, 0, 0, 2015, 1, 59, 0, 4, 72}, {2014, 0, 0, 2015, 1, 60, 1, 0, 0}, {2013, 1, 60, 2014, 0, 0, 0, 4, 73}, {2013, 1, 59, 2014, 0, 0, 1, 0, 1}, {2013, 1, 60, 2014, 1, 60, 1, 0, 0}, {2014, 0, 0, 2013, 1, 59, -1, 0, -1}, {2014, 0, 0, 2013, 1, 60, 0, -4, -73}, {2015, 1, 60, 2014, 0, 0, -1, 0, -1}, {2015, 1, 59, 2014, 0, 0, 0, -4, -73}, {2018, 0, 0, 2014, 0, 0, -4, 0, 0}, {2014, 0, 0, 2018, 0, 0, 4, 0, 0}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { DiscordianDate start = DiscordianDate.of(year1, month1, dom1); DiscordianDate end = DiscordianDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test @UseDataProvider("data_until_period") public void test_until_end( int year1, int month1, int dom1, int year2, int month2, int dom2, int yearPeriod, int monthPeriod, int domPeriod) { DiscordianDate start = DiscordianDate.of(year1, month1, dom1); DiscordianDate end = DiscordianDate.of(year2, month2, dom2); ChronoPeriod period = DiscordianChronology.INSTANCE.period(yearPeriod, monthPeriod, domPeriod); assertEquals(period, start.until(end)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { DiscordianDate start = DiscordianDate.of(2012, 1, 30); DiscordianDate end = DiscordianDate.of(2012, 2, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(DiscordianDate.of(2015, 2, 29), DiscordianDate.of(2014, 5, 26).plus(DiscordianChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(DiscordianDate.of(2015, 2, 26), DiscordianDate.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(DiscordianDate.of(2014, 3, 23), DiscordianDate.of(2014, 5, 26).minus(DiscordianChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(DiscordianDate.of(2014, 3, 26), DiscordianDate.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { DiscordianDate a1 = DiscordianDate.of(2000, 1, 3); DiscordianDate a2 = DiscordianDate.of(2000, 1, 3); DiscordianDate b = DiscordianDate.of(2000, 1, 4); DiscordianDate c = DiscordianDate.of(2000, 2, 3); DiscordianDate d = DiscordianDate.of(2001, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertTrue(a1.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {DiscordianDate.of(1, 1, 1), "Discordian YOLD 1-1-01"}, {DiscordianDate.of(2012, 5, 23), "Discordian YOLD 2012-5-23"}, {DiscordianDate.of(2014, 0, 0), "Discordian YOLD 2014 St. Tib's Day"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(DiscordianDate discordian, String expected) { assertEquals(expected, discordian.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestEthiopicChronology.java000066400000000000000000000771251343451174100315150ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.List; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestEthiopicChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Chronology chrono = Chronology.of("Ethiopic"); Assert.assertNotNull(chrono); Assert.assertEquals(EthiopicChronology.INSTANCE, chrono); Assert.assertEquals("Ethiopic", chrono.getId()); Assert.assertEquals("ethiopic", chrono.getCalendarType()); } @Test public void test_chronology_of_name_id() { Chronology chrono = Chronology.of("ethiopic"); Assert.assertNotNull(chrono); Assert.assertEquals(EthiopicChronology.INSTANCE, chrono); Assert.assertEquals("Ethiopic", chrono.getId()); Assert.assertEquals("ethiopic", chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {EthiopicDate.of(-1, 13, 6), LocalDate.of(7, 8, 27)}, {EthiopicDate.of(0, 1, 1), LocalDate.of(7, 8, 28)}, {EthiopicDate.of(0, 1, 30), LocalDate.of(7, 9, 26)}, {EthiopicDate.of(0, 12, 30), LocalDate.of(8, 8, 21)}, {EthiopicDate.of(0, 13, 1), LocalDate.of(8, 8, 22)}, {EthiopicDate.of(0, 13, 4), LocalDate.of(8, 8, 25)}, {EthiopicDate.of(0, 13, 5), LocalDate.of(8, 8, 26)}, {EthiopicDate.of(1, 1, 1), LocalDate.of(8, 8, 27)}, {EthiopicDate.of(1, 1, 2), LocalDate.of(8, 8, 28)}, {EthiopicDate.of(1, 1, 3), LocalDate.of(8, 8, 29)}, {EthiopicDate.of(2, 1, 1), LocalDate.of(9, 8, 27)}, {EthiopicDate.of(3, 1, 1), LocalDate.of(10, 8, 27)}, {EthiopicDate.of(3, 13, 6), LocalDate.of(11, 8, 27)}, {EthiopicDate.of(4, 1, 1), LocalDate.of(11, 8, 28)}, {EthiopicDate.of(4, 7, 5), LocalDate.of(12, 2, 28)}, {EthiopicDate.of(4, 7, 6), LocalDate.of(12, 2, 29)}, {EthiopicDate.of(5, 1, 1), LocalDate.of(12, 8, 27)}, {EthiopicDate.of(1938, 3, 3), LocalDate.of(1945, 11, 12)}, {EthiopicDate.of(2004, 2, 5), LocalDate.of(2011, 10, 16)}, {EthiopicDate.of(2004, 10, 28), LocalDate.of(2012, 7, 5)}, {EthiopicDate.of(2004, 10, 29), LocalDate.of(2012, 7, 6)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_EthiopicDate(EthiopicDate ethiopic, LocalDate iso) { assertEquals(iso, LocalDate.from(ethiopic)); } @Test @UseDataProvider("data_samples") public void test_EthiopicDate_from_LocalDate(EthiopicDate ethiopic, LocalDate iso) { assertEquals(ethiopic, EthiopicDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_EthiopicDate_chronology_dateEpochDay(EthiopicDate ethiopic, LocalDate iso) { assertEquals(ethiopic, EthiopicChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_EthiopicDate_toEpochDay(EthiopicDate ethiopic, LocalDate iso) { assertEquals(iso.toEpochDay(), ethiopic.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_EthiopicDate_until_EthiopicDate(EthiopicDate ethiopic, LocalDate iso) { assertEquals(EthiopicChronology.INSTANCE.period(0, 0, 0), ethiopic.until(ethiopic)); } @Test @UseDataProvider("data_samples") public void test_EthiopicDate_until_LocalDate(EthiopicDate ethiopic, LocalDate iso) { assertEquals(EthiopicChronology.INSTANCE.period(0, 0, 0), ethiopic.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_EthiopicDate(EthiopicDate ethiopic, LocalDate iso) { assertEquals(Period.ZERO, iso.until(ethiopic)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(EthiopicDate ethiopic, LocalDate iso) { assertEquals(ethiopic, EthiopicChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(EthiopicDate ethiopic, LocalDate iso) { assertEquals(iso, LocalDate.from(ethiopic.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(ethiopic.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(ethiopic.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(ethiopic.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(ethiopic.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(EthiopicDate ethiopic, LocalDate iso) { assertEquals(iso, LocalDate.from(ethiopic.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(ethiopic.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(ethiopic.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(ethiopic.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(ethiopic.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(EthiopicDate ethiopic, LocalDate iso) { assertEquals(0, ethiopic.until(iso.plusDays(0), DAYS)); assertEquals(1, ethiopic.until(iso.plusDays(1), DAYS)); assertEquals(35, ethiopic.until(iso.plusDays(35), DAYS)); assertEquals(-40, ethiopic.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {2008, 0, 0}, {2008, -1, 1}, {2008, 0, 1}, {2008, 14, 1}, {2008, 15, 1}, {2008, 1, -1}, {2008, 1, 0}, {2008, 1, 31}, {2008, 1, 32}, {2008, 12, -1}, {2008, 12, 0}, {2008, 12, 31}, {2008, 12, 32}, {2008, 13, -1}, {2008, 13, 0}, {2008, 13, 6}, {2008, 13, 7}, {2007, 13, -1}, {2007, 13, 0}, {2007, 13, 7}, {2007, 13, 8}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { EthiopicDate.of(year, month, dom); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { EthiopicChronology.INSTANCE.dateYearDay(2008, 366); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { for (int year = -200; year < 200; year++) { EthiopicDate base = EthiopicDate.of(year, 1, 1); assertEquals(((year - 3) % 4) == 0, base.isLeapYear()); assertEquals(((year + 400 - 3) % 4) == 0, EthiopicChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(8)); assertEquals(true, EthiopicChronology.INSTANCE.isLeapYear(7)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(6)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(5)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(4)); assertEquals(true, EthiopicChronology.INSTANCE.isLeapYear(3)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(2)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(1)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(0)); assertEquals(true, EthiopicChronology.INSTANCE.isLeapYear(-1)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(-2)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(-3)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(-4)); assertEquals(true, EthiopicChronology.INSTANCE.isLeapYear(-5)); assertEquals(false, EthiopicChronology.INSTANCE.isLeapYear(-6)); } @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {2006, 1, 30}, {2006, 2, 30}, {2006, 3, 30}, {2006, 4, 30}, {2006, 5, 30}, {2006, 6, 30}, {2006, 7, 30}, {2006, 8, 30}, {2006, 9, 30}, {2006, 10, 30}, {2006, 11, 30}, {2006, 12, 30}, {2006, 13, 5}, {2007, 13, 6}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, EthiopicDate.of(year, month, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = -200; year < 200; year++) { EthiopicDate base = EthiopicChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); EthiopicEra era = (year <= 0 ? EthiopicEra.BEFORE_INCARNATION : EthiopicEra.INCARNATION); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); EthiopicDate eraBased = EthiopicChronology.INSTANCE.date(era, yoe, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = -200; year < 200; year++) { EthiopicDate base = EthiopicChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); EthiopicEra era = (year <= 0 ? EthiopicEra.BEFORE_INCARNATION : EthiopicEra.INCARNATION); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); EthiopicDate eraBased = EthiopicChronology.INSTANCE.dateYearDay(era, yoe, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.INCARNATION, 4)); assertEquals(3, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.INCARNATION, 3)); assertEquals(2, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.INCARNATION, 2)); assertEquals(1, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.INCARNATION, 1)); assertEquals(0, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.BEFORE_INCARNATION, 1)); assertEquals(-1, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.BEFORE_INCARNATION, 2)); assertEquals(-2, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.BEFORE_INCARNATION, 3)); assertEquals(-3, EthiopicChronology.INSTANCE.prolepticYear(EthiopicEra.BEFORE_INCARNATION, 4)); } @Test public void test_Chronology_eraOf() { assertEquals(EthiopicEra.INCARNATION, EthiopicChronology.INSTANCE.eraOf(1)); assertEquals(EthiopicEra.BEFORE_INCARNATION, EthiopicChronology.INSTANCE.eraOf(0)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { EthiopicChronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = EthiopicChronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertEquals(true, eras.contains(EthiopicEra.BEFORE_INCARNATION)); assertEquals(true, eras.contains(EthiopicEra.INCARNATION)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), EthiopicChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 5, 30), EthiopicChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 365, 366), EthiopicChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 13), EthiopicChronology.INSTANCE.range(MONTH_OF_YEAR)); } //----------------------------------------------------------------------- // EthiopicDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { {2007, 1, 23, DAY_OF_MONTH, 1, 30}, {2007, 2, 23, DAY_OF_MONTH, 1, 30}, {2007, 3, 23, DAY_OF_MONTH, 1, 30}, {2007, 4, 23, DAY_OF_MONTH, 1, 30}, {2007, 5, 23, DAY_OF_MONTH, 1, 30}, {2007, 6, 23, DAY_OF_MONTH, 1, 30}, {2007, 7, 23, DAY_OF_MONTH, 1, 30}, {2007, 8, 23, DAY_OF_MONTH, 1, 30}, {2007, 9, 23, DAY_OF_MONTH, 1, 30}, {2007, 10, 23, DAY_OF_MONTH, 1, 30}, {2007, 11, 23, DAY_OF_MONTH, 1, 30}, {2007, 12, 23, DAY_OF_MONTH, 1, 30}, {2007, 13, 2, DAY_OF_MONTH, 1, 6}, {2007, 1, 23, DAY_OF_YEAR, 1, 366}, {2007, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2007, 12, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2007, 13, 2, ALIGNED_WEEK_OF_MONTH, 1, 1}, {2006, 13, 2, DAY_OF_MONTH, 1, 5}, {2006, 13, 2, DAY_OF_YEAR, 1, 365}, {2006, 13, 2, ALIGNED_WEEK_OF_MONTH, 1, 1}, {2006, 2, 23, WeekFields.ISO.dayOfWeek(), 1, 7}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), EthiopicDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { EthiopicDate.of(2007, 6, 30).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // EthiopicDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2007, 6, 8, DAY_OF_WEEK, 7}, {2007, 6, 8, DAY_OF_MONTH, 8}, {2007, 6, 8, DAY_OF_YEAR, 30 * 5 + 8}, {2007, 6, 8, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1}, {2007, 6, 8, ALIGNED_WEEK_OF_MONTH, 2}, {2007, 6, 8, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4}, {2007, 6, 8, ALIGNED_WEEK_OF_YEAR, 23}, {2007, 6, 8, MONTH_OF_YEAR, 6}, {2007, 6, 8, PROLEPTIC_MONTH, 2007 * 13 + 6 - 1}, {2007, 6, 8, YEAR, 2007}, {2007, 6, 8, ERA, 1}, {1, 6, 8, ERA, 1}, {0, 6, 8, ERA, 0}, {2007, 6, 8, WeekFields.ISO.dayOfWeek(), 7}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, EthiopicDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { EthiopicDate.of(2007, 6, 30).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // EthiopicDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2007, 6, 8, DAY_OF_WEEK, 3, 2007, 6, 4}, {2007, 6, 8, DAY_OF_WEEK, 7, 2007, 6, 8}, {2007, 6, 8, DAY_OF_MONTH, 30, 2007, 6, 30}, {2007, 6, 8, DAY_OF_MONTH, 8, 2007, 6, 8}, {2007, 6, 8, DAY_OF_YEAR, 365, 2007, 13, 5}, {2007, 6, 8, DAY_OF_YEAR, 366, 2007, 13, 6}, {2007, 6, 8, DAY_OF_YEAR, 158, 2007, 6, 8}, {2007, 6, 8, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2007, 6, 10}, {2007, 6, 8, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2007, 6, 8}, {2007, 6, 8, ALIGNED_WEEK_OF_MONTH, 1, 2007, 6, 1}, {2007, 6, 8, ALIGNED_WEEK_OF_MONTH, 2, 2007, 6, 8}, {2007, 6, 8, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2007, 6, 6}, {2007, 6, 8, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 2007, 6, 8}, {2007, 6, 8, ALIGNED_WEEK_OF_YEAR, 22, 2007, 6, 1}, {2007, 6, 8, ALIGNED_WEEK_OF_YEAR, 23, 2007, 6, 8}, {2007, 6, 8, MONTH_OF_YEAR, 7, 2007, 7, 8}, {2007, 6, 8, MONTH_OF_YEAR, 6, 2007, 6, 8}, {2007, 6, 8, PROLEPTIC_MONTH, 2009 * 13 + 3 - 1, 2009, 3, 8}, {2007, 6, 8, PROLEPTIC_MONTH, 2007 * 13 + 6 - 1, 2007, 6, 8}, {2007, 6, 8, YEAR, 2012, 2012, 6, 8}, {2007, 6, 8, YEAR, 2007, 2007, 6, 8}, {2007, 6, 8, YEAR_OF_ERA, 2012, 2012, 6, 8}, {2007, 6, 8, YEAR_OF_ERA, 2007, 2007, 6, 8}, {2007, 6, 8, ERA, 0, -2006, 6, 8}, {2007, 6, 8, ERA, 1, 2007, 6, 8}, {2006, 6, 8, DAY_OF_YEAR, 366, 2006, 13, 5}, {2006, 3, 30, MONTH_OF_YEAR, 13, 2006, 13, 5}, {2007, 3, 30, MONTH_OF_YEAR, 13, 2007, 13, 6}, {2007, 13, 6, DAY_OF_MONTH, 30, 2007, 13, 6}, {2007, 13, 6, YEAR, 2006, 2006, 13, 5}, {-2005, 6, 8, YEAR_OF_ERA, 2004, -2003, 6, 8}, {2007, 6, 8, WeekFields.ISO.dayOfWeek(), 4, 2007, 6, 5}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(EthiopicDate.of(expectedYear, expectedMonth, expectedDom), EthiopicDate.of(year, month, dom).with(field, value)); } @DataProvider public static Object[][] data_with_bad() { return new Object[][] { {2007, 6, 8, DAY_OF_WEEK, 0}, {2007, 6, 8, DAY_OF_WEEK, 8}, {2007, 6, 8, DAY_OF_MONTH, 0}, {2007, 6, 8, DAY_OF_MONTH, 31}, {2007, 13, 1, DAY_OF_MONTH, 31}, {2007, 6, 8, DAY_OF_YEAR, 0}, {2007, 6, 8, DAY_OF_YEAR, 367}, {2007, 6, 8, MONTH_OF_YEAR, 0}, {2007, 6, 8, MONTH_OF_YEAR, 14}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_with_bad") public void test_with_TemporalField_badValue(int year, int month, int dom, TemporalField field, long value) { EthiopicDate.of(year, month, dom).with(field, value); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { EthiopicDate.of(2006, 6, 30).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // EthiopicDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_adjust1() { EthiopicDate base = EthiopicDate.of(2005, 10, 29); EthiopicDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(EthiopicDate.of(2005, 10, 30), test); } @Test public void test_adjust2() { EthiopicDate base = EthiopicDate.of(2005, 13, 2); EthiopicDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(EthiopicDate.of(2005, 13, 5), test); } //----------------------------------------------------------------------- // EthiopicDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { EthiopicDate ethiopic = EthiopicDate.of(2001, 1, 4); EthiopicDate test = ethiopic.with(LocalDate.of(2011, 10, 16)); assertEquals(EthiopicDate.of(2004, 2, 5), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { EthiopicDate ethiopic = EthiopicDate.of(2004, 1, 4); ethiopic.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(EthiopicDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToEthiopicDate() { EthiopicDate ethiopic = EthiopicDate.of(2004, 2, 5); LocalDate test = LocalDate.MIN.with(ethiopic); assertEquals(LocalDate.of(2011, 10, 16), test); } @Test public void test_LocalDateTime_adjustToEthiopicDate() { EthiopicDate ethiopic = EthiopicDate.of(2004, 2, 5); LocalDateTime test = LocalDateTime.MIN.with(ethiopic); assertEquals(LocalDateTime.of(2011, 10, 16, 0, 0), test); } //----------------------------------------------------------------------- // EthiopicDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2006, 5, 26, 0, DAYS, 2006, 5, 26}, {2006, 5, 26, 8, DAYS, 2006, 6, 4}, {2006, 5, 26, -3, DAYS, 2006, 5, 23}, {2006, 5, 26, 0, WEEKS, 2006, 5, 26}, {2006, 5, 26, 3, WEEKS, 2006, 6, 17}, {2006, 5, 26, -5, WEEKS, 2006, 4, 21}, {2006, 5, 26, 0, MONTHS, 2006, 5, 26}, {2006, 5, 26, 3, MONTHS, 2006, 8, 26}, {2006, 5, 26, -6, MONTHS, 2005, 12, 26}, {2006, 5, 26, 0, YEARS, 2006, 5, 26}, {2006, 5, 26, 3, YEARS, 2009, 5, 26}, {2006, 5, 26, -5, YEARS, 2001, 5, 26}, {2006, 5, 26, 0, DECADES, 2006, 5, 26}, {2006, 5, 26, 3, DECADES, 2036, 5, 26}, {2006, 5, 26, -5, DECADES, 1956, 5, 26}, {2006, 5, 26, 0, CENTURIES, 2006, 5, 26}, {2006, 5, 26, 3, CENTURIES, 2306, 5, 26}, {2006, 5, 26, -5, CENTURIES, 1506, 5, 26}, {2006, 5, 26, 0, MILLENNIA, 2006, 5, 26}, {2006, 5, 26, 3, MILLENNIA, 5006, 5, 26}, {2006, 5, 26, -5, MILLENNIA, 2006 - 5000, 5, 26}, {2006, 5, 26, -1, ERAS, -2005, 5, 26}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(EthiopicDate.of(expectedYear, expectedMonth, expectedDom), EthiopicDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(EthiopicDate.of(expectedYear, expectedMonth, expectedDom), EthiopicDate.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { EthiopicDate.of(2006, 6, 30).plus(0, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(EthiopicDate.of(2006, 7, 29), EthiopicDate.of(2006, 5, 26).plus(EthiopicChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(EthiopicDate.of(2006, 7, 26), EthiopicDate.of(2006, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(EthiopicDate.of(2006, 3, 23), EthiopicDate.of(2006, 5, 26).minus(EthiopicChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(EthiopicDate.of(2006, 3, 26), EthiopicDate.of(2006, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // EthiopicDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2006, 5, 26, 2006, 5, 26, DAYS, 0}, {2006, 5, 26, 2006, 6, 1, DAYS, 5}, {2006, 5, 26, 2006, 5, 20, DAYS, -6}, {2006, 5, 26, 2006, 5, 26, WEEKS, 0}, {2006, 5, 26, 2006, 6, 2, WEEKS, 0}, {2006, 5, 26, 2006, 6, 3, WEEKS, 1}, {2006, 5, 26, 2006, 5, 26, MONTHS, 0}, {2006, 5, 26, 2006, 6, 25, MONTHS, 0}, {2006, 5, 26, 2006, 6, 26, MONTHS, 1}, {2006, 5, 26, 2006, 5, 26, YEARS, 0}, {2006, 5, 26, 2007, 5, 25, YEARS, 0}, {2006, 5, 26, 2007, 5, 26, YEARS, 1}, {2006, 5, 26, 2006, 5, 26, DECADES, 0}, {2006, 5, 26, 2016, 5, 25, DECADES, 0}, {2006, 5, 26, 2016, 5, 26, DECADES, 1}, {2006, 5, 26, 2006, 5, 26, CENTURIES, 0}, {2006, 5, 26, 2106, 5, 25, CENTURIES, 0}, {2006, 5, 26, 2106, 5, 26, CENTURIES, 1}, {2006, 5, 26, 2006, 5, 26, MILLENNIA, 0}, {2006, 5, 26, 3006, 5, 25, MILLENNIA, 0}, {2006, 5, 26, 3006, 5, 26, MILLENNIA, 1}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { EthiopicDate start = EthiopicDate.of(year1, month1, dom1); EthiopicDate end = EthiopicDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { EthiopicDate start = EthiopicDate.of(2006, 6, 30); EthiopicDate end = EthiopicDate.of(2006, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { EthiopicDate a1 = EthiopicDate.of(2004, 1, 3); EthiopicDate a2 = EthiopicDate.of(2004, 1, 3); EthiopicDate b = EthiopicDate.of(2004, 1, 4); EthiopicDate c = EthiopicDate.of(2004, 2, 3); EthiopicDate d = EthiopicDate.of(2005, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertTrue(a1.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {EthiopicDate.of(1, 1, 1), "Ethiopic INCARNATION 1-01-01"}, {EthiopicDate.of(2007, 10, 28), "Ethiopic INCARNATION 2007-10-28"}, {EthiopicDate.of(2007, 10, 29), "Ethiopic INCARNATION 2007-10-29"}, {EthiopicDate.of(2007, 13, 5), "Ethiopic INCARNATION 2007-13-05"}, {EthiopicDate.of(2007, 13, 6), "Ethiopic INCARNATION 2007-13-06"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(EthiopicDate ethiopic, String expected) { assertEquals(expected, ethiopic.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestInternationalFixedChronology.java000066400000000000000000001733121343451174100335330ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.List; import java.util.function.Predicate; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @SuppressWarnings({"static-method", "javadoc"}) @RunWith(DataProviderRunner.class) public class TestInternationalFixedChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology() { Chronology chrono = Chronology.of("Ifc"); assertNotNull(chrono); assertEquals(InternationalFixedChronology.INSTANCE, chrono); assertEquals("Ifc", chrono.getId()); assertEquals(null, chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {InternationalFixedDate.of(1, 1, 1), LocalDate.of(1, 1, 1)}, {InternationalFixedDate.of(1, 1, 2), LocalDate.of(1, 1, 2)}, {InternationalFixedDate.of(1, 6, 27), LocalDate.of(1, 6, 16)}, {InternationalFixedDate.of(1, 6, 28), LocalDate.of(1, 6, 17)}, {InternationalFixedDate.of(1, 7, 1), LocalDate.of(1, 6, 18)}, {InternationalFixedDate.of(1, 7, 2), LocalDate.of(1, 6, 19)}, {InternationalFixedDate.of(1, 13, 28), LocalDate.of(1, 12, 30)}, {InternationalFixedDate.of(1, 13, 27), LocalDate.of(1, 12, 29)}, {InternationalFixedDate.of(1, 13, 29), LocalDate.of(1, 12, 31)}, {InternationalFixedDate.of(2, 1, 1), LocalDate.of(2, 1, 1)}, {InternationalFixedDate.of(4, 6, 27), LocalDate.of(4, 6, 15)}, {InternationalFixedDate.of(4, 6, 28), LocalDate.of(4, 6, 16)}, {InternationalFixedDate.of(4, 6, 29), LocalDate.of(4, 6, 17)}, {InternationalFixedDate.of(4, 7, 1), LocalDate.of(4, 6, 18)}, {InternationalFixedDate.of(4, 7, 2), LocalDate.of(4, 6, 19)}, {InternationalFixedDate.of(4, 13, 28), LocalDate.of(4, 12, 30)}, {InternationalFixedDate.of(4, 13, 27), LocalDate.of(4, 12, 29)}, {InternationalFixedDate.of(4, 13, 29), LocalDate.of(4, 12, 31)}, {InternationalFixedDate.of(5, 1, 1), LocalDate.of(5, 1, 1)}, {InternationalFixedDate.of(100, 6, 27), LocalDate.of(100, 6, 16)}, {InternationalFixedDate.of(100, 6, 28), LocalDate.of(100, 6, 17)}, {InternationalFixedDate.of(100, 7, 1), LocalDate.of(100, 6, 18)}, {InternationalFixedDate.of(100, 7, 2), LocalDate.of(100, 6, 19)}, {InternationalFixedDate.of(400, 6, 27), LocalDate.of(400, 6, 15)}, {InternationalFixedDate.of(400, 6, 28), LocalDate.of(400, 6, 16)}, {InternationalFixedDate.of(400, 6, 29), LocalDate.of(400, 6, 17)}, {InternationalFixedDate.of(400, 7, 1), LocalDate.of(400, 6, 18)}, {InternationalFixedDate.of(400, 7, 2), LocalDate.of(400, 6, 19)}, {InternationalFixedDate.of(1582, 9, 28), LocalDate.of(1582, 9, 9)}, {InternationalFixedDate.of(1582, 10, 1), LocalDate.of(1582, 9, 10)}, {InternationalFixedDate.of(1945, 10, 27), LocalDate.of(1945, 10, 6)}, {InternationalFixedDate.of(2012, 6, 15), LocalDate.of(2012, 6, 3)}, {InternationalFixedDate.of(2012, 6, 16), LocalDate.of(2012, 6, 4)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_InternationalFixedDate(InternationalFixedDate fixed, LocalDate iso) { assertEquals(iso, LocalDate.from(fixed)); } @Test @UseDataProvider("data_samples") public void test_InternationalFixedDate_from_LocalDate(InternationalFixedDate fixed, LocalDate iso) { assertEquals(fixed, InternationalFixedDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_InternationalFixedDate_chronology_dateEpochDay(InternationalFixedDate fixed, LocalDate iso) { assertEquals(fixed, InternationalFixedChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_InternationalFixedDate_toEpochDay(InternationalFixedDate fixed, LocalDate iso) { assertEquals(iso.toEpochDay(), fixed.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_InternationalFixedDate_until_InternationalFixedDate(InternationalFixedDate fixed, LocalDate iso) { assertEquals(InternationalFixedChronology.INSTANCE.period(0, 0, 0), fixed.until(fixed)); } @Test @UseDataProvider("data_samples") public void test_InternationalFixedDate_until_LocalDate(InternationalFixedDate fixed, LocalDate iso) { assertEquals(InternationalFixedChronology.INSTANCE.period(0, 0, 0), fixed.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_InternationalFixedDate(InternationalFixedDate fixed, LocalDate iso) { assertEquals(Period.ZERO, iso.until(fixed)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(InternationalFixedDate fixed, LocalDate iso) { assertEquals(fixed, InternationalFixedChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(InternationalFixedDate fixed, LocalDate iso) { assertEquals(iso, LocalDate.from(fixed.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(fixed.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(fixed.plus(35, DAYS))); if (LocalDate.ofYearDay(1, 60).isBefore(iso)) { assertEquals(iso.plusDays(-1), LocalDate.from(fixed.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(fixed.plus(-60, DAYS))); } } @Test @UseDataProvider("data_samples") public void test_minusDays(InternationalFixedDate fixed, LocalDate iso) { assertEquals(iso, LocalDate.from(fixed.minus(0, DAYS))); if (LocalDate.ofYearDay(1, 35).isBefore(iso)) { assertEquals(iso.minusDays(1), LocalDate.from(fixed.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(fixed.minus(35, DAYS))); } assertEquals(iso.minusDays(-1), LocalDate.from(fixed.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(fixed.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(InternationalFixedDate fixed, LocalDate iso) { assertEquals(0, fixed.until(iso.plusDays(0), DAYS)); assertEquals(1, fixed.until(iso.plusDays(1), DAYS)); assertEquals(35, fixed.until(iso.plusDays(35), DAYS)); if (LocalDate.ofYearDay(1, 40).isBefore(iso)) { assertEquals(-40, fixed.until(iso.minusDays(40), DAYS)); } } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {-1, 13, 28}, {-1, 13, 29}, {0, 1, 1}, {1900, -2, 1}, {1900, 14, 1}, {1900, 15, 1}, {1900, 1, -1}, {1900, 1, 0}, {1900, 1, 29}, {1904, -1, -2}, {1904, -1, 0}, {1904, -1, 1}, {1900, -1, 0}, {1900, -1, -2}, {1900, 0, -1}, {1900, 0, 1}, {1900, 0, 2}, {1900, 2, 29}, {1900, 3, 29}, {1900, 4, 29}, {1900, 5, 29}, {1900, 6, 29}, {1900, 7, 29}, {1900, 8, 29}, {1900, 9, 29}, {1900, 10, 29}, {1900, 11, 29}, {1900, 12, 29}, {1900, 13, 30}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { InternationalFixedDate.of(year, month, dom); } @DataProvider public static Object[][] data_badLeapDates() { return new Object[][] { {1}, {100}, {200}, {300}, {1900} }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badLeapDates") public void badLeapDayDates(int year) { InternationalFixedDate.of(year, 6, 29); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { InternationalFixedChronology.INSTANCE.dateYearDay(2001, 366); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { Predicate isLeapYear = year -> { return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0); }; for (int year = 1; year < 500; year++) { InternationalFixedDate base = InternationalFixedDate.of(year, 1, 1); assertEquals(isLeapYear.test(year), base.isLeapYear()); assertEquals(isLeapYear.test(year) ? 366 : 365, base.lengthOfYear()); assertEquals(isLeapYear.test(year), InternationalFixedChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertTrue(InternationalFixedChronology.INSTANCE.isLeapYear(400)); assertFalse(InternationalFixedChronology.INSTANCE.isLeapYear(100)); assertTrue(InternationalFixedChronology.INSTANCE.isLeapYear(4)); assertFalse(InternationalFixedChronology.INSTANCE.isLeapYear(3)); assertFalse(InternationalFixedChronology.INSTANCE.isLeapYear(2)); assertFalse(InternationalFixedChronology.INSTANCE.isLeapYear(1)); } //----------------------------------------------------------------------- // lengthOfMonth() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {1900, 1, 28, 28}, {1900, 2, 28, 28}, {1900, 3, 28, 28}, {1900, 4, 28, 28}, {1900, 5, 28, 28}, {1900, 6, 28, 28}, {1900, 7, 28, 28}, {1900, 8, 28, 28}, {1900, 9, 28, 28}, {1900, 10, 28, 28}, {1900, 11, 28, 28}, {1900, 12, 28, 28}, {1900, 13, 29, 29}, {1904, 6, 29, 29}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int day, int length) { assertEquals(length, InternationalFixedDate.of(year, month, day).lengthOfMonth()); } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonthFirst(int year, int month, int day, int length) { assertEquals(length, InternationalFixedDate.of(year, month, 1).lengthOfMonth()); } @Test public void test_lengthOfMonth_specific() { assertEquals(29, InternationalFixedDate.of(1900, 13, 29).lengthOfMonth()); assertEquals(29, InternationalFixedDate.of(2000, 13, 29).lengthOfMonth()); assertEquals(29, InternationalFixedDate.of(2000, 6, 29).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = 1; year < 200; year++) { InternationalFixedDate base = InternationalFixedChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); InternationalFixedEra era = InternationalFixedEra.CE; assertEquals(era, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); InternationalFixedDate eraBased = InternationalFixedChronology.INSTANCE.date(era, year, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = 1; year < 200; year++) { InternationalFixedDate base = InternationalFixedChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); InternationalFixedEra era = InternationalFixedEra.CE; assertEquals(era, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); InternationalFixedDate eraBased = InternationalFixedChronology.INSTANCE.dateYearDay(era, year, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, 4)); assertEquals(3, InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, 3)); assertEquals(2, InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, 2)); assertEquals(1, InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, 1)); assertEquals(2000, InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, 2000)); assertEquals(1582, InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, 1582)); } @DataProvider public static Object[][] data_prolepticYear_bad() { return new Object[][] { {-10}, {-1}, {0}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_prolepticYear_bad") public void test_prolepticYearBad(int year) { InternationalFixedChronology.INSTANCE.prolepticYear(InternationalFixedEra.CE, year); } @Test(expected = ClassCastException.class) public void test_prolepticYear_badEra() { InternationalFixedChronology.INSTANCE.prolepticYear(IsoEra.CE, 4); } @Test public void test_Chronology_eraOf() { assertEquals(InternationalFixedEra.CE, InternationalFixedChronology.INSTANCE.eraOf(1)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { InternationalFixedChronology.INSTANCE.eraOf(0); } @Test public void test_Chronology_eras() { List eras = InternationalFixedChronology.INSTANCE.eras(); assertEquals(1, eras.size()); assertTrue(eras.contains(InternationalFixedEra.CE)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(0, 1, 0, 7), InternationalFixedChronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(ValueRange.of(0, 1, 0, 7), InternationalFixedChronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(ValueRange.of(0, 1, 0, 4), InternationalFixedChronology.INSTANCE.range(ALIGNED_WEEK_OF_MONTH)); assertEquals(ValueRange.of(0, 1, 0, 52), InternationalFixedChronology.INSTANCE.range(ALIGNED_WEEK_OF_YEAR)); assertEquals(ValueRange.of(0, 1, 0, 7), InternationalFixedChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 29), InternationalFixedChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 365, 366), InternationalFixedChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 1), InternationalFixedChronology.INSTANCE.range(ERA)); assertEquals(ValueRange.of(-719_528, 1_000_000 * 365L + 242_499 - 719_528), InternationalFixedChronology.INSTANCE.range(EPOCH_DAY)); assertEquals(ValueRange.of(1, 13), InternationalFixedChronology.INSTANCE.range(MONTH_OF_YEAR)); assertEquals(ValueRange.of(13, 1_000_000 * 13L - 1), InternationalFixedChronology.INSTANCE.range(PROLEPTIC_MONTH)); assertEquals(ValueRange.of(1, 1_000_000), InternationalFixedChronology.INSTANCE.range(YEAR)); assertEquals(ValueRange.of(1, 1_000_000), InternationalFixedChronology.INSTANCE.range(YEAR_OF_ERA)); } //----------------------------------------------------------------------- // InternationalFixedDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { // Leap Day and Year Day are members of months {2012, 6, 29, DAY_OF_MONTH, ValueRange.of(1, 29)}, {2012, 13, 29, DAY_OF_MONTH, ValueRange.of(1, 29)}, {2012, 1, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 2, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 3, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 4, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 5, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 6, 23, DAY_OF_MONTH, ValueRange.of(1, 29)}, {2012, 7, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 8, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 9, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 10, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 11, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 12, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 13, 23, DAY_OF_MONTH, ValueRange.of(1, 29)}, {2012, 1, 23, DAY_OF_YEAR, ValueRange.of(1, 366)}, // Leap Day is still in same year, so (1 to 13) in leap year {2012, 1, 23, MONTH_OF_YEAR, ValueRange.of(1, 13)}, // Leap Day/Year Day in own months, so (0 to 0) or (1 to 7) {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(0, 0)}, {2012, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(0, 0)}, {2012, 1, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 6, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 12, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, // Leap Day/Year Day in own months, so (0 to 0) or (1 to 4) {2012, 6, 29, ALIGNED_WEEK_OF_MONTH, ValueRange.of(0, 0)}, {2012, 13, 29, ALIGNED_WEEK_OF_MONTH, ValueRange.of(0, 0)}, {2012, 1, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 4)}, {2012, 6, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 4)}, {2012, 12, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 4)}, // Leap Day/Year Day in own months, so (0 to 0) or (1 to 7) {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(0, 0)}, {2012, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(0, 0)}, {2012, 1, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 6, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 12, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, // Leap Day/Year Day in own months, so (0 to 0) or (1 to 4) {2012, 6, 29, ALIGNED_WEEK_OF_YEAR, ValueRange.of(0, 0)}, {2012, 13, 29, ALIGNED_WEEK_OF_YEAR, ValueRange.of(0, 0)}, {2012, 1, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2012, 6, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2012, 12, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, // Leap Day and Year Day in own 'week's, so (0 to 0) or (1 to 7) {2012, 6, 29, DAY_OF_WEEK, ValueRange.of(0, 0)}, {2012, 13, 29, DAY_OF_WEEK, ValueRange.of(0, 0)}, {2012, 1, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 6, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 12, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2011, 6, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2011, 13, 23, DAY_OF_YEAR, ValueRange.of(1, 365)}, {2011, 13, 23, MONTH_OF_YEAR, ValueRange.of(1, 13)}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, ValueRange range) { assertEquals(range, InternationalFixedDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { InternationalFixedDate.of(2012, 6, 28).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // InternationalFixedDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 5}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 28 + 28 + 28 + 28 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 13 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 5, 8, ERA, 1}, {2012, 9, 26, DAY_OF_WEEK, 5}, {2012, 9, 26, DAY_OF_YEAR, 28 + 28 + 28 + 28 + 28 + 28 + 1 + 28 + 28 + 26}, {2012, 9, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2012, 9, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 9, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2012, 9, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2012, 9, 28, ALIGNED_WEEK_OF_YEAR, 36}, {2014, 9, 28, ALIGNED_WEEK_OF_YEAR, 36}, {2014, 13, 29, DAY_OF_WEEK, 0}, {2014, 13, 29, DAY_OF_MONTH, 29}, {2014, 13, 29, DAY_OF_YEAR, 13 * 28 + 1}, {2012, 13, 29, DAY_OF_YEAR, 13 * 28 + 1 + 1}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0}, {2014, 13, 29, ALIGNED_WEEK_OF_MONTH, 0}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0}, {2014, 13, 29, ALIGNED_WEEK_OF_YEAR, 0}, {2014, 13, 29, MONTH_OF_YEAR, 13}, {2014, 13, 29, PROLEPTIC_MONTH, 2014 * 13 + 13 - 1}, {2012, 6, 28, DAY_OF_WEEK, 7}, {2012, 6, 28, DAY_OF_MONTH, 28}, {2012, 6, 28, DAY_OF_YEAR, 6 * 28}, {2012, 6, 28, ALIGNED_DAY_OF_WEEK_IN_MONTH, 7}, {2012, 6, 28, ALIGNED_WEEK_OF_MONTH, 4}, {2012, 6, 28, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7}, {2012, 6, 28, ALIGNED_WEEK_OF_YEAR, 24}, {2012, 6, 28, MONTH_OF_YEAR, 6}, {2012, 6, 28, PROLEPTIC_MONTH, 2012 * 13 + 6 - 1}, {2012, 6, 29, DAY_OF_WEEK, 0}, {2012, 6, 29, DAY_OF_MONTH, 29}, {2012, 6, 29, DAY_OF_YEAR, 6 * 28 + 1}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0}, {2012, 6, 29, ALIGNED_WEEK_OF_MONTH, 0}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0}, {2012, 6, 29, ALIGNED_WEEK_OF_YEAR, 0}, {2012, 6, 29, MONTH_OF_YEAR, 6}, {2012, 6, 29, PROLEPTIC_MONTH, 2012 * 13 + 6 - 1}, {2012, 7, 1, DAY_OF_WEEK, 1}, {2012, 7, 1, DAY_OF_MONTH, 1}, {2012, 7, 1, DAY_OF_YEAR, 6 * 28 + 2}, {2012, 7, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1}, {2012, 7, 1, ALIGNED_WEEK_OF_MONTH, 1}, {2012, 7, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1}, {2012, 7, 1, ALIGNED_WEEK_OF_YEAR, 25}, {2012, 7, 1, MONTH_OF_YEAR, 7}, {2012, 7, 1, PROLEPTIC_MONTH, 2012 * 13 + 7 - 1}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, InternationalFixedDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { InternationalFixedDate.of(2012, 6, 28).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // InternationalFixedDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 1, 2014, 5, 22}, {2014, 5, 26, DAY_OF_WEEK, 5, 2014, 5, 26}, {2014, 5, 26, DAY_OF_MONTH, 28, 2014, 5, 28}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 364, 2014, 13, 28}, {2014, 5, 26, DAY_OF_YEAR, 138, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 5, 23}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 19}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 4, 2014, 4, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 13 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 13 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0, 2014, 13, 29}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2014, 13, 22}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2, 2014, 13, 23}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 13, 24}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 4, 2014, 13, 25}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 13, 26}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 6, 2014, 13, 27}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 7, 2014, 13, 28}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 2014, 13, 29}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1, 2014, 13, 22}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 13, 23}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2014, 13, 24}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 2014, 13, 25}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 13, 26}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6, 2014, 13, 27}, {2014, 13, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7, 2014, 13, 28}, {2014, 13, 29, ALIGNED_WEEK_OF_MONTH, 0, 2014, 13, 29}, {2014, 13, 29, ALIGNED_WEEK_OF_MONTH, 3, 2014, 13, 15}, {2014, 13, 29, ALIGNED_WEEK_OF_YEAR, 0, 2014, 13, 29}, {2014, 13, 29, ALIGNED_WEEK_OF_YEAR, 3, 2014, 1, 15}, {2014, 13, 29, DAY_OF_WEEK, 0, 2014, 13, 29}, {2014, 13, 28, DAY_OF_WEEK, 1, 2014, 13, 22}, {2014, 13, 28, DAY_OF_WEEK, 2, 2014, 13, 23}, {2014, 13, 28, DAY_OF_WEEK, 3, 2014, 13, 24}, {2014, 13, 28, DAY_OF_WEEK, 4, 2014, 13, 25}, {2014, 13, 28, DAY_OF_WEEK, 5, 2014, 13, 26}, {2014, 13, 28, DAY_OF_WEEK, 6, 2014, 13, 27}, {2014, 13, 28, DAY_OF_WEEK, 7, 2014, 13, 28}, {2014, 13, 29, DAY_OF_MONTH, 1, 2014, 13, 1}, {2014, 13, 29, DAY_OF_MONTH, 3, 2014, 13, 3}, {2014, 13, 29, MONTH_OF_YEAR, 1, 2014, 1, 28}, {2014, 13, 29, MONTH_OF_YEAR, 13, 2014, 13, 29}, {2014, 13, 29, MONTH_OF_YEAR, 2, 2014, 2, 28}, {2014, 13, 29, YEAR, 2014, 2014, 13, 29}, {2014, 13, 29, YEAR, 2013, 2013, 13, 29}, {2014, 3, 28, DAY_OF_MONTH, 1, 2014, 3, 1}, {2014, 1, 28, DAY_OF_MONTH, 1, 2014, 1, 1}, {2014, 3, 28, MONTH_OF_YEAR, 1, 2014, 1, 28}, {2014, 3, 28, DAY_OF_YEAR, 365, 2014, 13, 29}, {2012, 3, 28, DAY_OF_YEAR, 366, 2012, 13, 29}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0, 2012, 6, 29}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2012, 6, 22}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2, 2012, 6, 23}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2012, 6, 24}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 4, 2012, 6, 25}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2012, 6, 26}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 6, 2012, 6, 27}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_MONTH, 7, 2012, 6, 28}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0, 2012, 6, 29}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1, 2012, 6, 22}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2012, 6, 23}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2012, 6, 24}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 2012, 6, 25}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2012, 6, 26}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6, 2012, 6, 27}, {2012, 6, 29, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7, 2012, 6, 28}, {2012, 6, 29, ALIGNED_WEEK_OF_MONTH, 0, 2012, 6, 29}, {2012, 6, 29, ALIGNED_WEEK_OF_MONTH, 3, 2012, 6, 15}, {2012, 6, 29, ALIGNED_WEEK_OF_YEAR, 0, 2012, 6, 29}, {2012, 6, 29, ALIGNED_WEEK_OF_YEAR, 3, 2012, 1, 15}, {2012, 1, 1, ALIGNED_WEEK_OF_YEAR, 52, 2012, 13, 22}, {2012, 13, 28, ALIGNED_WEEK_OF_YEAR, 1, 2012, 1, 7}, {2012, 6, 29, DAY_OF_WEEK, 0, 2012, 6, 29}, {2012, 6, 29, DAY_OF_WEEK, 1, 2012, 6, 22}, {2012, 6, 29, DAY_OF_WEEK, 2, 2012, 6, 23}, {2012, 6, 29, DAY_OF_WEEK, 3, 2012, 6, 24}, {2012, 6, 29, DAY_OF_WEEK, 4, 2012, 6, 25}, {2012, 6, 29, DAY_OF_WEEK, 5, 2012, 6, 26}, {2012, 6, 29, DAY_OF_WEEK, 6, 2012, 6, 27}, {2012, 6, 29, DAY_OF_WEEK, 7, 2012, 6, 28}, {2012, 6, 29, DAY_OF_MONTH, 1, 2012, 6, 1}, {2012, 6, 29, DAY_OF_MONTH, 3, 2012, 6, 3}, {2012, 6, 29, MONTH_OF_YEAR, 6, 2012, 6, 29}, {2012, 6, 29, MONTH_OF_YEAR, 7, 2012, 7, 28}, {2012, 6, 29, MONTH_OF_YEAR, 2, 2012, 2, 28}, {2012, 6, 29, YEAR, 2012, 2012, 6, 29}, {2012, 6, 29, YEAR, 2013, 2013, 6, 28}, {2012, 6, 29, YEAR, 2011, 2011, 6, 28}, {2012, 6, 29, YEAR, 2016, 2016, 6, 29}, {2012, 6, 22, DAY_OF_MONTH, 29, 2012, 6, 29}, {2012, 3, 28, DAY_OF_MONTH, 1, 2012, 3, 1}, {2012, 1, 28, DAY_OF_MONTH, 1, 2012, 1, 1}, {2012, 3, 28, MONTH_OF_YEAR, 1, 2012, 1, 28}, {2012, 3, 28, DAY_OF_YEAR, 169, 2012, 6, 29}, {2013, 3, 28, DAY_OF_YEAR, 169, 2013, 7, 1}, {2013, 7, 1, YEAR, 2012, 2012, 7, 1}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(InternationalFixedDate.of(expectedYear, expectedMonth, expectedDom), InternationalFixedDate.of(year, month, dom).with(field, value)); } @DataProvider public static Object[][] data_with_bad() { return new Object[][] { {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 8}, {2012, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0}, {2012, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 8}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 8}, {2012, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0}, {2012, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 8}, {2013, 1, 1, ALIGNED_WEEK_OF_MONTH, 0}, {2013, 1, 1, ALIGNED_WEEK_OF_MONTH, 5}, {2012, 1, 1, ALIGNED_WEEK_OF_MONTH, 0}, {2012, 1, 1, ALIGNED_WEEK_OF_MONTH, 5}, {2013, 1, 1, ALIGNED_WEEK_OF_YEAR, 0}, {2013, 1, 1, ALIGNED_WEEK_OF_YEAR, 53}, {2012, 1, 1, ALIGNED_WEEK_OF_YEAR, 0}, {2012, 1, 1, ALIGNED_WEEK_OF_YEAR, 53}, {2013, 1, 1, DAY_OF_WEEK, 0}, {2013, 1, 1, DAY_OF_WEEK, 8}, {2012, 1, 1, DAY_OF_WEEK, 0}, {2012, 1, 1, DAY_OF_WEEK, 8}, {2013, 1, 1, DAY_OF_MONTH, -1}, {2013, 1, 1, DAY_OF_MONTH, 29}, {2013, 6, 1, DAY_OF_MONTH, 29}, {2012, 6, 1, DAY_OF_MONTH, 30}, {2012, 1, 1, DAY_OF_MONTH, -2}, {2012, 1, 1, DAY_OF_MONTH, 29}, {2013, 13, 1, DAY_OF_MONTH, 30}, {2012, 13, 1, DAY_OF_MONTH, 30}, {2013, 1, 1, DAY_OF_YEAR, 0}, {2012, 1, 1, DAY_OF_YEAR, 0}, {2013, 1, 1, DAY_OF_YEAR, 366}, {2012, 1, 1, DAY_OF_YEAR, 367}, {2013, 1, 1, EPOCH_DAY, -719_529}, {2013, 1, 1, EPOCH_DAY, 1_000_000 * 365L + 242_499 - 719_528 + 1}, {2013, 1, 1, MONTH_OF_YEAR, -1}, {2013, 1, 1, MONTH_OF_YEAR, 14}, {2012, 1, 1, MONTH_OF_YEAR, -2}, {2012, 1, 1, MONTH_OF_YEAR, 14}, {2013, 1, 1, YEAR, 0}, {2012, 6, 21, DAY_OF_WEEK, 0}, {2012, 6, 21, ALIGNED_DAY_OF_WEEK_IN_MONTH, 0}, {2012, 6, 21, ALIGNED_WEEK_OF_MONTH, 0}, {2012, 6, 21, ALIGNED_DAY_OF_WEEK_IN_YEAR, 0}, {2012, 6, 21, ALIGNED_WEEK_OF_YEAR, 0}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_with_bad") public void test_with_TemporalField_badValue(int year, int month, int dom, TemporalField field, long value) { InternationalFixedDate.of(year, month, dom).with(field, value); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { InternationalFixedDate.of(2012, 6, 28).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // InternationalFixedDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_temporalAdjusters_lastDayOfMonth() { return new Object[][] { {2012, 6, 23, 2012, 6, 29}, {2012, 6, 29, 2012, 6, 29}, {2009, 6, 23, 2009, 6, 28}, {2007, 13, 23, 2007, 13, 29}, {2005, 13, 29, 2005, 13, 29}, }; } @Test @UseDataProvider("data_temporalAdjusters_lastDayOfMonth") public void test_temporalAdjusters_LastDayOfMonth(int year, int month, int day, int expectedYear, int expectedMonth, int expectedDay) { InternationalFixedDate base = InternationalFixedDate.of(year, month, day); InternationalFixedDate expected = InternationalFixedDate.of(expectedYear, expectedMonth, expectedDay); InternationalFixedDate actual = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(expected, actual); } //----------------------------------------------------------------------- // InternationalFixedDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { InternationalFixedDate fixed = InternationalFixedDate.of(2000, 1, 4); InternationalFixedDate test = fixed.with(LocalDate.of(2012, 7, 6)); assertEquals(InternationalFixedDate.of(2012, 7, 19), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { InternationalFixedDate fixed = InternationalFixedDate.of(2000, 1, 4); fixed.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(InternationalFixedDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToInternationalFixedDate() { InternationalFixedDate fixed = InternationalFixedDate.of(2012, 7, 19); LocalDate test = LocalDate.MIN.with(fixed); assertEquals(LocalDate.of(2012, 7, 6), test); } @Test public void test_LocalDateTime_adjustToInternationalFixedDate() { InternationalFixedDate fixed = InternationalFixedDate.of(2012, 7, 19); LocalDateTime test = LocalDateTime.MIN.with(fixed); assertEquals(LocalDateTime.of(2012, 7, 6, 0, 0), test); } //----------------------------------------------------------------------- // InternationalFixedDate.plus // InternationalFixedDate.minus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 6, 6}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 6, 19}, {2014, 5, 26, -5, WEEKS, 2014, 4, 19}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26}, {2014, 5, 26, -5, MONTHS, 2013, 13, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -1, MILLENNIA, 2014 - 1000, 5, 26}, {2014, 13, 26, 3, WEEKS, 2015, 1, 19}, {2014, 1, 26, -5, WEEKS, 2013, 13, 19}, {2012, 6, 26, 3, WEEKS, 2012, 7, 19}, {2012, 7, 26, -5, WEEKS, 2012, 6, 19}, {2012, 6, 21, 52 + 1, WEEKS, 2013, 6, 28}, {2013, 6, 21, 6 * 52 + 1, WEEKS, 2019, 6, 28}, }; } @DataProvider public static Object[][] data_plus_leap_and_year_day() { return new Object[][] { {2014, 13, 29, 0, DAYS, 2014, 13, 29}, {2014, 13, 29, 8, DAYS, 2015, 1, 8}, {2014, 13, 29, -3, DAYS, 2014, 13, 26}, {2014, 13, 29, 0, WEEKS, 2014, 13, 29}, {2014, 13, 29, 3, WEEKS, 2015, 1, 21}, {2014, 13, 29, -5, WEEKS, 2014, 12, 21}, {2014, 13, 29, 52, WEEKS, 2015, 13, 29}, {2014, 13, 29, 0, MONTHS, 2014, 13, 29}, {2014, 13, 29, 3, MONTHS, 2015, 3, 28}, {2014, 13, 29, -5, MONTHS, 2014, 8, 28}, {2014, 13, 29, 13, MONTHS, 2015, 13, 29}, {2014, 13, 29, 0, YEARS, 2014, 13, 29}, {2014, 13, 29, 3, YEARS, 2017, 13, 29}, {2014, 13, 29, -5, YEARS, 2009, 13, 29}, {2011, 13, 29, 4 * 6, WEEKS, 2012, 6, 29}, {2012, 13, 29, 4 * -7, WEEKS, 2012, 6, 29}, {2012, 6, 29, 0, DAYS, 2012, 6, 29}, {2012, 6, 29, 8, DAYS, 2012, 7, 8}, {2012, 6, 29, -3, DAYS, 2012, 6, 26}, {2012, 6, 29, 0, WEEKS, 2012, 6, 29}, {2012, 6, 29, 3, WEEKS, 2012, 7, 22}, {2012, 6, 29, -5, WEEKS, 2012, 5, 22}, {2012, 6, 29, 52 * 4, WEEKS, 2016, 6, 29}, {2012, 6, 29, 0, MONTHS, 2012, 6, 29}, {2012, 6, 29, 3, MONTHS, 2012, 9, 28}, {2012, 6, 29, -5, MONTHS, 2012, 1, 28}, {2012, 6, 29, 13 * 4, MONTHS, 2016, 6, 29}, {2012, 6, 29, 0, YEARS, 2012, 6, 29}, {2012, 6, 29, 3, YEARS, 2015, 6, 28}, {2012, 6, 29, -5, YEARS, 2007, 6, 28}, {2012, 6, 29, 4, YEARS, 2016, 6, 29}, {2012, 6, 29, 4 * 7, WEEKS, 2012, 13, 29}, {2012, 6, 29, 4 * -6, WEEKS, 2011, 13, 29}, }; } @DataProvider public static Object[][] data_minus_leap_and_year_day() { return new Object[][] { {2014, 13, 29, 0, DAYS, 2014, 13, 29}, {2014, 13, 21, 8, DAYS, 2014, 13, 29}, {2015, 1, 3, -3, DAYS, 2014, 13, 29}, {2014, 13, 29, 0, WEEKS, 2014, 13, 29}, {2014, 13, 7, 3, WEEKS, 2014, 13, 29}, {2015, 2, 7, -5, WEEKS, 2014, 13, 29}, {2013, 13, 29, 52, WEEKS, 2014, 13, 29}, {2014, 13, 29, 0, MONTHS, 2014, 13, 29}, {2014, 10, 28, 3, MONTHS, 2014, 13, 29}, {2015, 5, 28, -5, MONTHS, 2014, 13, 29}, {2013, 13, 29, 13, MONTHS, 2014, 13, 29}, {2014, 13, 29, 0, YEARS, 2014, 13, 29}, {2011, 13, 29, 3, YEARS, 2014, 13, 29}, {2019, 13, 29, -5, YEARS, 2014, 13, 29}, {2012, 6, 29, 4 * -6, WEEKS, 2011, 13, 29}, {2012, 6, 29, 4 * 7, WEEKS, 2012, 13, 29}, {2012, 6, 29, 0, DAYS, 2012, 6, 29}, {2012, 6, 21, 8, DAYS, 2012, 6, 29}, {2012, 7, 3, -3, DAYS, 2012, 6, 29}, {2012, 6, 29, 0, WEEKS, 2012, 6, 29}, {2012, 6, 8, 3, WEEKS, 2012, 6, 29}, {2012, 8, 8, -5, WEEKS, 2012, 6, 29}, {2012, 6, 29, 28, WEEKS, 2012, 13, 29}, {2008, 6, 29, 52 * 4, WEEKS, 2012, 6, 29}, {2012, 6, 29, 0, MONTHS, 2012, 6, 29}, {2012, 3, 28, 3, MONTHS, 2012, 6, 29}, {2012, 11, 28, -5, MONTHS, 2012, 6, 29}, {2008, 6, 29, 13 * 4, MONTHS, 2012, 6, 29}, {2012, 6, 29, 0, YEARS, 2012, 6, 29}, {2009, 6, 28, 3, YEARS, 2012, 6, 29}, {2017, 6, 28, -5, YEARS, 2012, 6, 29}, {2008, 6, 29, 4, YEARS, 2012, 6, 29}, {2012, 13, 29, 4 * -7, WEEKS, 2012, 6, 29}, {2011, 13, 29, 4 * 6, WEEKS, 2012, 6, 29}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(InternationalFixedDate.of(expectedYear, expectedMonth, expectedDom), InternationalFixedDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus_leap_and_year_day") public void test_plus_leap_and_year_day_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(InternationalFixedDate.of(expectedYear, expectedMonth, expectedDom), InternationalFixedDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(InternationalFixedDate.of(expectedYear, expectedMonth, expectedDom), InternationalFixedDate.of(year, month, dom).minus(amount, unit)); } @Test @UseDataProvider("data_minus_leap_and_year_day") public void test_minus_leap_and_year_day_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(InternationalFixedDate.of(expectedYear, expectedMonth, expectedDom), InternationalFixedDate.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { InternationalFixedDate.of(2012, 6, 28).plus(0, MINUTES); } //----------------------------------------------------------------------- // InternationalFixedDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 4, DAYS, 6}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 4, WEEKS, 0}, {2014, 5, 26, 2014, 6, 5, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {2014, 5, 26, 3014, 5, 26, ERAS, 0}, {2014, 13, 28, 2015, 1, 1, DAYS, 2}, {2014, 13, 28, 2014, 13, 29, DAYS, 1}, {2014, 13, 29, 2015, 1, 1, DAYS, 1}, {2015, 1, 1, 2014, 13, 24, DAYS, -6}, {2014, 13, 29, 2014, 13, 29, WEEKS, 0}, {2015, 1, 1, 2015, 1, 1, WEEKS, 0}, {2015, 1, 1, 2014, 13, 28, WEEKS, 0}, {2015, 1, 1, 2014, 13, 23, WEEKS, 0}, {2015, 1, 1, 2014, 13, 22, WEEKS, -1}, {2014, 13, 29, 2014, 13, 21, WEEKS, -1}, {2014, 13, 29, 2014, 13, 22, WEEKS, 0}, {2014, 13, 29, 2015, 1, 7, WEEKS, 0}, {2014, 13, 29, 2015, 1, 8, WEEKS, 1}, {2014, 13, 21, 2014, 13, 29, WEEKS, 1}, {2014, 13, 22, 2014, 13, 29, WEEKS, 0}, {2015, 1, 7, 2014, 13, 29, WEEKS, 0}, {2015, 1, 8, 2014, 13, 29, WEEKS, -1}, {2014, 13, 1, 2015, 1, 1, WEEKS, 4}, {2014, 13, 29, 2014, 13, 29, MONTHS, 0}, {2014, 13, 29, 2015, 1, 28, MONTHS, 0}, {2014, 13, 29, 2015, 2, 1, MONTHS, 1}, {2015, 2, 1, 2014, 13, 29, MONTHS, -1}, {2015, 1, 28, 2014, 13, 29, MONTHS, 0}, {2014, 12, 28, 2014, 13, 29, MONTHS, 1}, {2014, 13, 1, 2014, 13, 29, MONTHS, 0}, {2014, 13, 1, 2015, 1, 1, MONTHS, 1}, {2014, 13, 29, 2014, 13, 29, YEARS, 0}, {2014, 13, 29, 2015, 13, 28, YEARS, 0}, {2014, 13, 29, 2015, 13, 29, YEARS, 1}, {2014, 13, 29, 2016, 1, 1, YEARS, 1}, {2014, 1, 1, 2014, 13, 29, YEARS, 0}, {2013, 13, 29, 2014, 13, 29, YEARS, 1}, {2013, 13, 28, 2014, 13, 29, YEARS, 1}, {2012, 6, 28, 2012, 7, 1, DAYS, 2}, {2012, 6, 28, 2012, 6, 29, DAYS, 1}, {2012, 6, 29, 2012, 7, 1, DAYS, 1}, {2012, 7, 1, 2012, 6, 24, DAYS, -6}, {2012, 6, 29, 2012, 6, 29, WEEKS, 0}, {2012, 7, 1, 2012, 7, 1, WEEKS, 0}, {2012, 7, 1, 2012, 6, 28, WEEKS, 0}, {2012, 7, 1, 2012, 6, 23, WEEKS, 0}, {2012, 7, 1, 2012, 6, 22, WEEKS, -1}, {2012, 6, 29, 2012, 6, 21, WEEKS, -1}, {2012, 6, 29, 2012, 6, 22, WEEKS, 0}, {2012, 6, 29, 2012, 7, 7, WEEKS, 0}, {2012, 6, 29, 2012, 7, 8, WEEKS, 1}, {2012, 6, 21, 2012, 6, 29, WEEKS, 1}, {2012, 6, 22, 2012, 6, 29, WEEKS, 0}, {2012, 7, 7, 2012, 6, 29, WEEKS, 0}, {2012, 7, 8, 2012, 6, 29, WEEKS, -1}, {2012, 6, 29, 2012, 6, 29, MONTHS, 0}, {2012, 6, 29, 2012, 7, 28, MONTHS, 0}, {2012, 6, 29, 2012, 8, 1, MONTHS, 1}, {2012, 8, 1, 2012, 6, 29, MONTHS, -1}, {2012, 7, 28, 2012, 6, 29, MONTHS, 0}, {2012, 5, 28, 2012, 6, 29, MONTHS, 1}, {2012, 6, 1, 2012, 6, 29, MONTHS, 0}, {2012, 6, 1, 2012, 7, 1, MONTHS, 1}, {2012, 6, 29, 2012, 6, 29, YEARS, 0}, {2012, 6, 29, 2013, 6, 28, YEARS, 0}, {2012, 6, 29, 2013, 7, 1, YEARS, 1}, {2011, 7, 1, 2012, 6, 29, YEARS, 0}, {2011, 6, 28, 2012, 6, 29, YEARS, 1}, {2011, 7, 1, 2012, 7, 1, YEARS, 1}, {2012, 6, 29, 2011, 6, 28, YEARS, -1}, {2012, 6, 29, 2011, 7, 1, YEARS, 0}, {2013, 7, 1, 2012, 6, 29, YEARS, -1}, {2013, 6, 28, 2012, 6, 29, YEARS, 0}, {2016, 6, 29, 2012, 6, 29, YEARS, -4}, {2012, 6, 29, 2016, 6, 29, YEARS, 4}, // The order is the 28th, Year Day, Leap Day, the 1st. // Year Day is "after the 28th" // Leap Day is "before the 1st" {2012, 6, 29, 2012, 13, 29, DAYS, 197}, {2012, 6, 29, 2012, 13, 28, WEEKS, 27}, {2012, 6, 29, 2012, 13, 29, WEEKS, 28}, {2012, 6, 29, 2013, 1, 1, WEEKS, 28}, {2012, 6, 29, 2011, 13, 28, WEEKS, -24}, {2012, 6, 29, 2011, 13, 29, WEEKS, -24}, {2012, 6, 29, 2012, 1, 1, WEEKS, -23}, {2012, 13, 29, 2012, 6, 28, WEEKS, -28}, {2012, 13, 29, 2012, 6, 29, WEEKS, -28}, {2012, 13, 29, 2012, 7, 1, WEEKS, -27}, {2011, 13, 29, 2012, 6, 28, WEEKS, 23}, {2011, 13, 29, 2012, 6, 29, WEEKS, 24}, {2011, 13, 29, 2012, 7, 1, WEEKS, 24}, {2012, 13, 29, 2013, 13, 29, WEEKS, 52}, {2012, 13, 29, 2016, 13, 29, WEEKS, 52 * 4}, {2012, 6, 29, 2012, 13, 28, MONTHS, 6}, {2012, 6, 29, 2012, 13, 29, MONTHS, 7}, {2012, 6, 29, 2013, 1, 1, MONTHS, 7}, {2012, 6, 29, 2011, 13, 28, MONTHS, -6}, {2012, 6, 29, 2011, 13, 29, MONTHS, -6}, {2012, 6, 29, 2012, 1, 1, MONTHS, -5}, {2012, 6, 29, 2016, 6, 29, WEEKS, 52 * 4}, {2012, 13, 29, 2012, 6, 28, MONTHS, -7}, {2012, 13, 29, 2012, 6, 29, MONTHS, -7}, {2012, 13, 29, 2012, 7, 1, MONTHS, -6}, {2011, 13, 29, 2012, 6, 28, MONTHS, 5}, {2011, 13, 29, 2012, 6, 29, MONTHS, 6}, {2011, 13, 29, 2012, 7, 1, MONTHS, 6}, }; } @DataProvider public static Object[][] data_until_period() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, 0, 0, 0}, {2014, 5, 26, 2014, 6, 4, 0, 0, 6}, {2014, 5, 26, 2014, 5, 20, 0, 0, -6}, {2014, 5, 26, 2014, 6, 5, 0, 0, 7}, {2014, 5, 26, 2014, 6, 25, 0, 0, 27}, {2014, 5, 26, 2014, 6, 26, 0, 1, 0}, {2014, 5, 26, 2015, 5, 25, 0, 12, 27}, {2014, 5, 26, 2015, 5, 26, 1, 0, 0}, {2014, 5, 26, 2024, 5, 25, 9, 12, 27}, {2011, 13, 26, 2013, 13, 26, 2, 0, 0}, {2011, 13, 26, 2012, 13, 26, 1, 0, 0}, {2012, 13, 26, 2011, 13, 26, -1, 0, 0}, {2012, 13, 26, 2013, 13, 26, 1, 0, 0}, {2011, 13, 6, 2012, 13, 6, 1, 0, 0}, {2012, 13, 6, 2011, 13, 6, -1, 0, 0}, {2011, 13, 1, 2012, 13, 7, 1, 0, 6}, {2012, 13, 7, 2011, 13, 1, -1, 0, -6}, {2011, 12, 28, 2012, 13, 1, 1, 0, 1}, {2012, 13, 1, 2011, 12, 28, -1, 0, -1}, {2013, 13, 6, 2012, 13, 6, -1, 0, 0}, {2012, 13, 6, 2013, 13, 6, 1, 0, 0}, // start with Year Day {2012, 13, 29, 2012, 13, 29, 0, 0, 0}, {2012, 13, 29, 2013, 13, 29, 1, 0, 0}, {2011, 13, 29, 2010, 13, 29, -1, 0, 0}, {2000, 13, 29, 2001, 13, 29, 1, 0, 0}, {2007, 13, 29, 2008, 1, 1, 0, 0, 1}, {2005, 13, 29, 2006, 2, 1, 0, 1, 1}, {2003, 13, 29, 2004, 6, 29, 0, 6, 0}, {2003, 13, 29, 2004, 7, 1, 0, 6, 1}, {2004, 13, 29, 2004, 6, 29, 0, -7, 0}, {2004, 13, 29, 2004, 7, 1, 0, -6, -27}, {2003, 13, 29, 2005, 1, 1, 1, 0, 1}, {2003, 13, 29, 2002, 13, 28, -1, 0, -1}, // start with one day before Year Day {2003, 13, 28, 2004, 6, 29, 0, 6, 1}, {2003, 13, 28, 2004, 7, 1, 0, 6, 2}, {2004, 13, 28, 2004, 6, 29, 0, -7, 0}, {2004, 13, 28, 2004, 7, 1, 0, -6, -27}, {2003, 13, 28, 2005, 1, 1, 1, 0, 2}, {2003, 13, 28, 2002, 13, 28, -1, 0, 0}, {2007, 13, 28, 2008, 1, 1, 0, 0, 2}, {2005, 13, 28, 2006, 2, 1, 0, 1, 1}, // start with Leap Day {2008, 6, 29, 2008, 6, 29, 0, 0, 0}, {2012, 6, 29, 2016, 6, 29, 4, 0, 0}, {2024, 6, 29, 2020, 6, 29, -4, 0, 0}, {2000, 6, 29, 2032, 6, 29, 32, 0, 0}, {2024, 6, 29, 2000, 6, 29, -24, 0, 0}, {2004, 6, 29, 2004, 13, 29, 0, 7, 0}, {2004, 6, 29, 2004, 13, 28, 0, 6, 28}, // yes, 6 months and 28 days here, not 7 months flat {2004, 6, 29, 2003, 13, 29, 0, -6, 0}, {2004, 6, 29, 2003, 13, 28, 0, -6, -1}, {2004, 6, 29, 2003, 6, 28, -1, 0, 0}, {2000, 6, 29, 2000, 7, 1, 0, 0, 1}, {2000, 6, 29, 2000, 8, 1, 0, 1, 1}, // start with one day before Leap Day {2004, 6, 28, 2004, 13, 29, 0, 7, 1}, {2004, 6, 28, 2004, 13, 28, 0, 7, 0}, {2004, 6, 28, 2003, 13, 29, 0, -5, -28}, // yes, -5 months and -28 days here, not -6 months flat {2004, 6, 28, 2003, 13, 28, 0, -6, 0}, {2000, 6, 28, 2000, 7, 1, 0, 0, 2}, {2000, 6, 28, 2000, 8, 1, 0, 1, 1}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { InternationalFixedDate start = InternationalFixedDate.of(year1, month1, dom1); InternationalFixedDate end = InternationalFixedDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test @UseDataProvider("data_until_period") public void test_until_end( int year1, int month1, int dom1, int year2, int month2, int dom2, int yearPeriod, int monthPeriod, int dayPeriod) { InternationalFixedDate start = InternationalFixedDate.of(year1, month1, dom1); InternationalFixedDate end = InternationalFixedDate.of(year2, month2, dom2); ChronoPeriod period = InternationalFixedChronology.INSTANCE.period(yearPeriod, monthPeriod, dayPeriod); assertEquals(period, start.until(end)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { InternationalFixedDate start = InternationalFixedDate.of(2012, 6, 28); InternationalFixedDate end = InternationalFixedDate.of(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- // InternationalFixedDate.period //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(InternationalFixedDate.of(2014, 8, 1), InternationalFixedDate.of(2014, 5, 26).plus(InternationalFixedChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(InternationalFixedDate.of(2014, 7, 26), InternationalFixedDate.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(InternationalFixedDate.of(2014, 3, 23), InternationalFixedDate.of(2014, 5, 26).minus(InternationalFixedChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(InternationalFixedDate.of(2014, 3, 26), InternationalFixedDate.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_equals() { return new Object[][] { {InternationalFixedDate.of(2000, 1, 3), InternationalFixedDate.of(2000, 1, 4), InternationalFixedDate.of(2000, 2, 3), InternationalFixedDate.of(2001, 1, 3)}, {InternationalFixedDate.of(2000, 13, 29), InternationalFixedDate.of(2000, 13, 28), InternationalFixedDate.of(2001, 1, 1), InternationalFixedDate.of(2001, 13, 29)}, {InternationalFixedDate.of(2000, 6, 29), InternationalFixedDate.of(2000, 6, 28), InternationalFixedDate.of(2000, 7, 1), InternationalFixedDate.of(2004, 6, 29)}, }; } @Test @UseDataProvider("data_equals") public void test_equals(InternationalFixedDate a1, InternationalFixedDate b, InternationalFixedDate c, InternationalFixedDate d) { assertTrue(a1.equals(a1)); assertFalse(a1.equals(b)); assertFalse(a1.equals(c)); assertFalse(a1.equals(d)); assertFalse(a1.equals(null)); assertFalse("".equals(a1)); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {InternationalFixedDate.of(1, 1, 1), "Ifc CE 1/01/01"}, {InternationalFixedDate.of(2012, 6, 23), "Ifc CE 2012/06/23"}, {InternationalFixedDate.of(1, 13, 29), "Ifc CE 1/13/29"}, {InternationalFixedDate.of(2012, 6, 29), "Ifc CE 2012/06/29"}, {InternationalFixedDate.of(2012, 13, 29), "Ifc CE 2012/13/29"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(InternationalFixedDate date, String expected) { assertEquals(expected, date.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestJulianChronology.java000066400000000000000000000751651343451174100311750ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.List; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @RunWith(DataProviderRunner.class) public class TestJulianChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Chronology chrono = Chronology.of("Julian"); Assert.assertNotNull(chrono); Assert.assertEquals(JulianChronology.INSTANCE, chrono); Assert.assertEquals("Julian", chrono.getId()); Assert.assertEquals("julian", chrono.getCalendarType()); } @Test public void test_chronology_of_name_id() { Chronology chrono = Chronology.of("julian"); Assert.assertNotNull(chrono); Assert.assertEquals(JulianChronology.INSTANCE, chrono); Assert.assertEquals("Julian", chrono.getId()); Assert.assertEquals("julian", chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {JulianDate.of(1, 1, 1), LocalDate.of(0, 12, 30)}, {JulianDate.of(1, 1, 2), LocalDate.of(0, 12, 31)}, {JulianDate.of(1, 1, 3), LocalDate.of(1, 1, 1)}, {JulianDate.of(1, 2, 28), LocalDate.of(1, 2, 26)}, {JulianDate.of(1, 3, 1), LocalDate.of(1, 2, 27)}, {JulianDate.of(1, 3, 2), LocalDate.of(1, 2, 28)}, {JulianDate.of(1, 3, 3), LocalDate.of(1, 3, 1)}, {JulianDate.of(4, 2, 28), LocalDate.of(4, 2, 26)}, {JulianDate.of(4, 2, 29), LocalDate.of(4, 2, 27)}, {JulianDate.of(4, 3, 1), LocalDate.of(4, 2, 28)}, {JulianDate.of(4, 3, 2), LocalDate.of(4, 2, 29)}, {JulianDate.of(4, 3, 3), LocalDate.of(4, 3, 1)}, {JulianDate.of(100, 2, 28), LocalDate.of(100, 2, 26)}, {JulianDate.of(100, 2, 29), LocalDate.of(100, 2, 27)}, {JulianDate.of(100, 3, 1), LocalDate.of(100, 2, 28)}, {JulianDate.of(100, 3, 2), LocalDate.of(100, 3, 1)}, {JulianDate.of(100, 3, 3), LocalDate.of(100, 3, 2)}, {JulianDate.of(0, 12, 31), LocalDate.of(0, 12, 29)}, {JulianDate.of(0, 12, 30), LocalDate.of(0, 12, 28)}, {JulianDate.of(1582, 10, 4), LocalDate.of(1582, 10, 14)}, {JulianDate.of(1582, 10, 5), LocalDate.of(1582, 10, 15)}, {JulianDate.of(1945, 10, 30), LocalDate.of(1945, 11, 12)}, {JulianDate.of(2012, 6, 22), LocalDate.of(2012, 7, 5)}, {JulianDate.of(2012, 6, 23), LocalDate.of(2012, 7, 6)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_JulianDate(JulianDate julian, LocalDate iso) { assertEquals(iso, LocalDate.from(julian)); } @Test @UseDataProvider("data_samples") public void test_JulianDate_from_LocalDate(JulianDate julian, LocalDate iso) { assertEquals(julian, JulianDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_JulianDate_chronology_dateEpochDay(JulianDate julian, LocalDate iso) { assertEquals(julian, JulianChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_JulianDate_toEpochDay(JulianDate julian, LocalDate iso) { assertEquals(iso.toEpochDay(), julian.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_JulianDate_until_JulianDate(JulianDate julian, LocalDate iso) { assertEquals(JulianChronology.INSTANCE.period(0, 0, 0), julian.until(julian)); } @Test @UseDataProvider("data_samples") public void test_JulianDate_until_LocalDate(JulianDate julian, LocalDate iso) { assertEquals(JulianChronology.INSTANCE.period(0, 0, 0), julian.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_JulianDate(JulianDate julian, LocalDate iso) { assertEquals(Period.ZERO, iso.until(julian)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(JulianDate julian, LocalDate iso) { assertEquals(julian, JulianChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(JulianDate julian, LocalDate iso) { assertEquals(iso, LocalDate.from(julian.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(julian.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(julian.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(julian.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(julian.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(JulianDate julian, LocalDate iso) { assertEquals(iso, LocalDate.from(julian.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(julian.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(julian.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(julian.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(julian.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(JulianDate julian, LocalDate iso) { assertEquals(0, julian.until(iso.plusDays(0), DAYS)); assertEquals(1, julian.until(iso.plusDays(1), DAYS)); assertEquals(35, julian.until(iso.plusDays(35), DAYS)); assertEquals(-40, julian.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {1900, 0, 0}, {1900, -1, 1}, {1900, 0, 1}, {1900, 13, 1}, {1900, 14, 1}, {1900, 1, -1}, {1900, 1, 0}, {1900, 1, 32}, {1900, 2, -1}, {1900, 2, 0}, {1900, 2, 30}, {1900, 2, 31}, {1900, 2, 32}, {1899, 2, -1}, {1899, 2, 0}, {1899, 2, 29}, {1899, 2, 30}, {1899, 2, 31}, {1899, 2, 32}, {1900, 12, -1}, {1900, 12, 0}, {1900, 12, 32}, {1900, 3, 32}, {1900, 4, 31}, {1900, 5, 32}, {1900, 6, 31}, {1900, 7, 32}, {1900, 8, 32}, {1900, 9, 31}, {1900, 10, 32}, {1900, 11, 31}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { JulianDate.of(year, month, dom); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { JulianChronology.INSTANCE.dateYearDay(2001, 366); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { for (int year = -200; year < 200; year++) { JulianDate base = JulianDate.of(year, 1, 1); assertEquals((year % 4) == 0, base.isLeapYear()); assertEquals((year % 4) == 0, JulianChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertEquals(true, JulianChronology.INSTANCE.isLeapYear(8)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(7)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(6)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(5)); assertEquals(true, JulianChronology.INSTANCE.isLeapYear(4)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(3)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(2)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(1)); assertEquals(true, JulianChronology.INSTANCE.isLeapYear(0)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(-1)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(-2)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(-3)); assertEquals(true, JulianChronology.INSTANCE.isLeapYear(-4)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(-5)); assertEquals(false, JulianChronology.INSTANCE.isLeapYear(-6)); } @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {1900, 1, 31}, {1900, 2, 29}, {1900, 3, 31}, {1900, 4, 30}, {1900, 5, 31}, {1900, 6, 30}, {1900, 7, 31}, {1900, 8, 31}, {1900, 9, 30}, {1900, 10, 31}, {1900, 11, 30}, {1900, 12, 31}, {1901, 2, 28}, {1902, 2, 28}, {1903, 2, 28}, {1904, 2, 29}, {2000, 2, 29}, {2100, 2, 29}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, JulianDate.of(year, month, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = -200; year < 200; year++) { JulianDate base = JulianChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); JulianEra era = (year <= 0 ? JulianEra.BC : JulianEra.AD); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); JulianDate eraBased = JulianChronology.INSTANCE.date(era, yoe, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = -200; year < 200; year++) { JulianDate base = JulianChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); JulianEra era = (year <= 0 ? JulianEra.BC : JulianEra.AD); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); JulianDate eraBased = JulianChronology.INSTANCE.dateYearDay(era, yoe, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, JulianChronology.INSTANCE.prolepticYear(JulianEra.AD, 4)); assertEquals(3, JulianChronology.INSTANCE.prolepticYear(JulianEra.AD, 3)); assertEquals(2, JulianChronology.INSTANCE.prolepticYear(JulianEra.AD, 2)); assertEquals(1, JulianChronology.INSTANCE.prolepticYear(JulianEra.AD, 1)); assertEquals(0, JulianChronology.INSTANCE.prolepticYear(JulianEra.BC, 1)); assertEquals(-1, JulianChronology.INSTANCE.prolepticYear(JulianEra.BC, 2)); assertEquals(-2, JulianChronology.INSTANCE.prolepticYear(JulianEra.BC, 3)); assertEquals(-3, JulianChronology.INSTANCE.prolepticYear(JulianEra.BC, 4)); } @Test(expected = ClassCastException.class) public void test_prolepticYear_badEra() { JulianChronology.INSTANCE.prolepticYear(IsoEra.CE, 4); } @Test public void test_Chronology_eraOf() { assertEquals(JulianEra.AD, JulianChronology.INSTANCE.eraOf(1)); assertEquals(JulianEra.BC, JulianChronology.INSTANCE.eraOf(0)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { JulianChronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = JulianChronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertEquals(true, eras.contains(JulianEra.BC)); assertEquals(true, eras.contains(JulianEra.AD)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), JulianChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 28, 31), JulianChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 365, 366), JulianChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 12), JulianChronology.INSTANCE.range(MONTH_OF_YEAR)); } //----------------------------------------------------------------------- // JulianDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { {2012, 1, 23, DAY_OF_MONTH, 1, 31}, {2012, 2, 23, DAY_OF_MONTH, 1, 29}, {2012, 3, 23, DAY_OF_MONTH, 1, 31}, {2012, 4, 23, DAY_OF_MONTH, 1, 30}, {2012, 5, 23, DAY_OF_MONTH, 1, 31}, {2012, 6, 23, DAY_OF_MONTH, 1, 30}, {2012, 7, 23, DAY_OF_MONTH, 1, 31}, {2012, 8, 23, DAY_OF_MONTH, 1, 31}, {2012, 9, 23, DAY_OF_MONTH, 1, 30}, {2012, 10, 23, DAY_OF_MONTH, 1, 31}, {2012, 11, 23, DAY_OF_MONTH, 1, 30}, {2012, 12, 23, DAY_OF_MONTH, 1, 31}, {2012, 1, 23, DAY_OF_YEAR, 1, 366}, {2012, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2012, 2, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2012, 3, 23, ALIGNED_WEEK_OF_MONTH, 1, 5}, {2011, 2, 23, DAY_OF_MONTH, 1, 28}, {2011, 2, 23, DAY_OF_YEAR, 1, 365}, {2011, 2, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), JulianDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { JulianDate.of(2012, 6, 30).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // JulianDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 7}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 31 + 28 + 31 + 30 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 21}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 6, 8, ERA, 1}, {0, 6, 8, ERA, 0}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 7}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, JulianDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { JulianDate.of(2012, 6, 30).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // JulianDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 3, 2014, 5, 22}, {2014, 5, 26, DAY_OF_WEEK, 7, 2014, 5, 26}, {2014, 5, 26, DAY_OF_MONTH, 31, 2014, 5, 31}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 365, 2014, 12, 31}, {2014, 5, 26, DAY_OF_YEAR, 146, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 5, 22}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 9}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 21, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 7, 2014, 7, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 12 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 0, -2013, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2011, 3, 31, MONTH_OF_YEAR, 2, 2011, 2, 28}, {2012, 3, 31, MONTH_OF_YEAR, 2, 2012, 2, 29}, {2012, 3, 31, MONTH_OF_YEAR, 6, 2012, 6, 30}, {2012, 2, 29, YEAR, 2011, 2011, 2, 28}, {-2013, 6, 8, YEAR_OF_ERA, 2012, -2011, 6, 8}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 3, 2014, 5, 22}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(JulianDate.of(expectedYear, expectedMonth, expectedDom), JulianDate.of(year, month, dom).with(field, value)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { JulianDate.of(2012, 6, 30).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // JulianDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_adjust1() { JulianDate base = JulianDate.of(2012, 6, 23); JulianDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(JulianDate.of(2012, 6, 30), test); } @Test public void test_adjust2() { JulianDate base = JulianDate.of(2012, 2, 23); JulianDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(JulianDate.of(2012, 2, 29), test); } //----------------------------------------------------------------------- // JulianDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { JulianDate julian = JulianDate.of(2000, 1, 4); JulianDate test = julian.with(LocalDate.of(2012, 7, 6)); assertEquals(JulianDate.of(2012, 6, 23), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { JulianDate julian = JulianDate.of(2000, 1, 4); julian.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(JulianDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToJulianDate() { JulianDate julian = JulianDate.of(2012, 6, 23); LocalDate test = LocalDate.MIN.with(julian); assertEquals(LocalDate.of(2012, 7, 6), test); } @Test public void test_LocalDateTime_adjustToJulianDate() { JulianDate julian = JulianDate.of(2012, 6, 23); LocalDateTime test = LocalDateTime.MIN.with(julian); assertEquals(LocalDateTime.of(2012, 7, 6, 0, 0), test); } //----------------------------------------------------------------------- // JulianDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 6, 3}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 6, 16}, {2014, 5, 26, -5, WEEKS, 2014, 4, 21}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26}, {2014, 5, 26, -5, MONTHS, 2013, 12, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -5, MILLENNIA, 2014 - 5000, 5, 26}, {2014, 5, 26, -1, ERAS, -2013, 5, 26}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(JulianDate.of(expectedYear, expectedMonth, expectedDom), JulianDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(JulianDate.of(expectedYear, expectedMonth, expectedDom), JulianDate.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { JulianDate.of(2012, 6, 30).plus(0, MINUTES); } //----------------------------------------------------------------------- // JulianDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 1, DAYS, 6}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 1, WEEKS, 0}, {2014, 5, 26, 2014, 6, 2, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {-2013, 5, 26, 0, 5, 26, ERAS, 0}, {-2013, 5, 26, 2014, 5, 26, ERAS, 1}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { JulianDate start = JulianDate.of(year1, month1, dom1); JulianDate end = JulianDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { JulianDate start = JulianDate.of(2012, 6, 30); JulianDate end = JulianDate.of(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(JulianDate.of(2014, 7, 29), JulianDate.of(2014, 5, 26).plus(JulianChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(JulianDate.of(2014, 7, 26), JulianDate.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(JulianDate.of(2014, 3, 23), JulianDate.of(2014, 5, 26).minus(JulianChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(JulianDate.of(2014, 3, 26), JulianDate.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { JulianDate a1 = JulianDate.of(2000, 1, 3); JulianDate a2 = JulianDate.of(2000, 1, 3); JulianDate b = JulianDate.of(2000, 1, 4); JulianDate c = JulianDate.of(2000, 2, 3); JulianDate d = JulianDate.of(2001, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertTrue(a1.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {JulianDate.of(1, 1, 1), "Julian AD 1-01-01"}, {JulianDate.of(2012, 6, 23), "Julian AD 2012-06-23"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(JulianDate julian, String expected) { assertEquals(expected, julian.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestPaxChronology.java000066400000000000000000001113041343451174100304650ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.IsoEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; import java.util.List; import java.util.function.Predicate; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @SuppressWarnings({"static-method", "javadoc"}) @RunWith(DataProviderRunner.class) public class TestPaxChronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology_of_name() { Chronology chrono = Chronology.of("Pax"); Assert.assertNotNull(chrono); Assert.assertEquals(PaxChronology.INSTANCE, chrono); Assert.assertEquals("Pax", chrono.getId()); Assert.assertEquals("pax", chrono.getCalendarType()); } @Test public void test_chronology_of_name_id() { Chronology chrono = Chronology.of("pax"); Assert.assertNotNull(chrono); Assert.assertEquals(PaxChronology.INSTANCE, chrono); Assert.assertEquals("Pax", chrono.getId()); Assert.assertEquals("pax", chrono.getCalendarType()); } //----------------------------------------------------------------------- // creation, toLocalDate() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { {PaxDate.of(1, 1, 1), LocalDate.of(0, 12, 31)}, {PaxDate.of(1, 1, 2), LocalDate.of(1, 1, 1)}, {PaxDate.of(1, 1, 3), LocalDate.of(1, 1, 2)}, {PaxDate.of(1, 1, 28), LocalDate.of(1, 1, 27)}, {PaxDate.of(1, 2, 1), LocalDate.of(1, 1, 28)}, {PaxDate.of(1, 2, 2), LocalDate.of(1, 1, 29)}, {PaxDate.of(1, 2, 3), LocalDate.of(1, 1, 30)}, {PaxDate.of(6, 13, 6), LocalDate.of(6, 12, 1)}, {PaxDate.of(6, 13, 7), LocalDate.of(6, 12, 2)}, {PaxDate.of(6, 14, 1), LocalDate.of(6, 12, 3)}, {PaxDate.of(6, 14, 2), LocalDate.of(6, 12, 4)}, {PaxDate.of(6, 14, 3), LocalDate.of(6, 12, 5)}, {PaxDate.of(6, 14, 27), LocalDate.of(6, 12, 29)}, {PaxDate.of(6, 14, 28), LocalDate.of(6, 12, 30)}, {PaxDate.of(7, 1, 1), LocalDate.of(6, 12, 31)}, {PaxDate.of(7, 1, 2), LocalDate.of(7, 1, 1)}, {PaxDate.of(399, 13, 6), LocalDate.of(399, 12, 3)}, {PaxDate.of(399, 13, 7), LocalDate.of(399, 12, 4)}, {PaxDate.of(399, 14, 1), LocalDate.of(399, 12, 5)}, {PaxDate.of(399, 14, 2), LocalDate.of(399, 12, 6)}, {PaxDate.of(399, 14, 3), LocalDate.of(399, 12, 7)}, {PaxDate.of(400, 13, 27), LocalDate.of(400, 12, 29)}, {PaxDate.of(400, 13, 28), LocalDate.of(400, 12, 30)}, {PaxDate.of(401, 1, 1), LocalDate.of(400, 12, 31)}, {PaxDate.of(401, 1, 2), LocalDate.of(401, 1, 1)}, {PaxDate.of(401, 1, 3), LocalDate.of(401, 1, 2)}, {PaxDate.of(0, 13, 28), LocalDate.of(0, 12, 30)}, {PaxDate.of(0, 13, 27), LocalDate.of(0, 12, 29)}, {PaxDate.of(1582, 10, 5), LocalDate.of(1582, 9, 9)}, {PaxDate.of(1582, 10, 6), LocalDate.of(1582, 9, 10)}, {PaxDate.of(1945, 10, 28), LocalDate.of(1945, 10, 6)}, {PaxDate.of(2012, 6, 23), LocalDate.of(2012, 6, 4)}, {PaxDate.of(2012, 6, 24), LocalDate.of(2012, 6, 5)}, {PaxDate.of(-6, 1, 1), LocalDate.of(-6, 1, 2)}, {PaxDate.of(-6, 13, 6), LocalDate.of(-6, 12, 9)}, {PaxDate.of(-6, 13, 7), LocalDate.of(-6, 12, 10)}, {PaxDate.of(-6, 14, 1), LocalDate.of(-6, 12, 11)}, {PaxDate.of(-6, 14, 2), LocalDate.of(-6, 12, 12)}, {PaxDate.of(-6, 14, 27), LocalDate.of(-5, 1, 6)}, {PaxDate.of(-6, 14, 28), LocalDate.of(-5, 1, 7)}, {PaxDate.of(-5, 1, 1), LocalDate.of(-5, 1, 8)}, {PaxDate.of(-5, 1, 2), LocalDate.of(-5, 1, 9)}, {PaxDate.of(-99, 1, 1), LocalDate.of(-99, 1, 6)}, {PaxDate.of(-99, 13, 6), LocalDate.of(-99, 12, 13)}, {PaxDate.of(-99, 13, 7), LocalDate.of(-99, 12, 14)}, {PaxDate.of(-99, 14, 1), LocalDate.of(-99, 12, 15)}, {PaxDate.of(-99, 14, 2), LocalDate.of(-99, 12, 16)}, {PaxDate.of(-100, 1, 1), LocalDate.of(-101, 12, 31)}, {PaxDate.of(-100, 13, 6), LocalDate.of(-100, 12, 7)}, {PaxDate.of(-100, 13, 7), LocalDate.of(-100, 12, 8)}, {PaxDate.of(-100, 14, 1), LocalDate.of(-100, 12, 9)}, {PaxDate.of(-100, 14, 2), LocalDate.of(-100, 12, 10)}, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_PaxDate(PaxDate pax, LocalDate iso) { assertEquals(iso, LocalDate.from(pax)); } @Test @UseDataProvider("data_samples") public void test_PaxDate_from_LocalDate(PaxDate pax, LocalDate iso) { assertEquals(pax, PaxDate.from(iso)); } @Test @UseDataProvider("data_samples") public void test_PaxDate_chronology_dateEpochDay(PaxDate pax, LocalDate iso) { assertEquals(pax, PaxChronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_PaxDate_toEpochDay(PaxDate pax, LocalDate iso) { assertEquals(iso.toEpochDay(), pax.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_PaxDate_until_PaxDate(PaxDate pax, LocalDate iso) { assertEquals(PaxChronology.INSTANCE.period(0, 0, 0), pax.until(pax)); } @Test @UseDataProvider("data_samples") public void test_PaxDate_until_LocalDate(PaxDate pax, LocalDate iso) { assertEquals(PaxChronology.INSTANCE.period(0, 0, 0), pax.until(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_PaxDate(PaxDate pax, LocalDate iso) { assertEquals(Period.ZERO, iso.until(pax)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(PaxDate pax, LocalDate iso) { assertEquals(pax, PaxChronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_plusDays(PaxDate pax, LocalDate iso) { assertEquals(iso, LocalDate.from(pax.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(pax.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(pax.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(pax.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(pax.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(PaxDate pax, LocalDate iso) { assertEquals(iso, LocalDate.from(pax.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(pax.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(pax.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(pax.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(pax.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(PaxDate pax, LocalDate iso) { assertEquals(0, pax.until(iso.plusDays(0), DAYS)); assertEquals(1, pax.until(iso.plusDays(1), DAYS)); assertEquals(35, pax.until(iso.plusDays(35), DAYS)); assertEquals(-40, pax.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {1900, 0, 0}, {1900, -1, 1}, {1900, 0, 1}, {1900, 15, 1}, {1900, 16, 1}, {1900, 1, -1}, {1900, 1, 0}, {1900, 1, 29}, {1900, 13, -1}, {1900, 13, 0}, {1900, 13, 8}, {1900, 14, -1}, {1900, 14, 0}, {1900, 14, 29}, {1900, 14, 30}, {1898, 13, -1}, {1898, 13, 0}, {1898, 14, 29}, {1898, 14, 30}, {1898, 14, 1}, {1898, 14, 2}, {1900, 14, -1}, {1900, 14, 0}, {1900, 14, 29}, {1900, 2, 29}, {1900, 3, 29}, {1900, 4, 29}, {1900, 5, 29}, {1900, 6, 29}, {1900, 7, 29}, {1900, 8, 29}, {1900, 9, 29}, {1900, 10, 29}, {1900, 11, 29}, {1900, 12, 29}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { PaxDate.of(year, month, dom); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { PaxChronology.INSTANCE.dateYearDay(2001, 365); } //----------------------------------------------------------------------- // isLeapYear() //----------------------------------------------------------------------- @Test public void test_isLeapYear_loop() { Predicate isLeapYear = year -> { int lastTwoDigits = Math.abs(year % 100); return (year % 400 != 0 && (lastTwoDigits == 0 || lastTwoDigits % 6 == 0)) || lastTwoDigits == 99; }; for (int year = -500; year < 500; year++) { PaxDate base = PaxDate.of(year, 1, 1); assertEquals(isLeapYear.test(year), base.isLeapYear()); assertEquals(isLeapYear.test(year), PaxChronology.INSTANCE.isLeapYear(year)); } } @Test public void test_isLeapYear_specific() { assertEquals(false, PaxChronology.INSTANCE.isLeapYear(400)); assertEquals(true, PaxChronology.INSTANCE.isLeapYear(100)); assertEquals(true, PaxChronology.INSTANCE.isLeapYear(99)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(7)); assertEquals(true, PaxChronology.INSTANCE.isLeapYear(6)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(5)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(4)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(3)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(2)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(1)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(0)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(-1)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(-2)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(-3)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(-4)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(-5)); assertEquals(true, PaxChronology.INSTANCE.isLeapYear(-6)); assertEquals(true, PaxChronology.INSTANCE.isLeapYear(-99)); assertEquals(true, PaxChronology.INSTANCE.isLeapYear(-100)); assertEquals(false, PaxChronology.INSTANCE.isLeapYear(-400)); } @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {1900, 1, 28}, {1900, 2, 28}, {1900, 3, 28}, {1900, 4, 28}, {1900, 5, 28}, {1900, 6, 28}, {1900, 7, 28}, {1900, 8, 28}, {1900, 9, 28}, {1900, 10, 28}, {1900, 11, 28}, {1900, 12, 28}, {1900, 13, 7}, {1900, 14, 28}, {1901, 13, 28}, {1902, 13, 28}, {1903, 13, 28}, {1904, 13, 28}, {1905, 13, 28}, {1906, 13, 7}, {2000, 13, 28}, {2100, 13, 7}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int length) { assertEquals(length, PaxDate.of(year, month, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // era, prolepticYear and dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = -200; year < 200; year++) { PaxDate base = PaxChronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); PaxEra era = (year <= 0 ? PaxEra.BCE : PaxEra.CE); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); PaxDate eraBased = PaxChronology.INSTANCE.date(era, yoe, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = -200; year < 200; year++) { PaxDate base = PaxChronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); PaxEra era = (year <= 0 ? PaxEra.BCE : PaxEra.CE); assertEquals(era, base.getEra()); int yoe = (year <= 0 ? 1 - year : year); assertEquals(yoe, base.get(YEAR_OF_ERA)); PaxDate eraBased = PaxChronology.INSTANCE.dateYearDay(era, yoe, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, PaxChronology.INSTANCE.prolepticYear(PaxEra.CE, 4)); assertEquals(3, PaxChronology.INSTANCE.prolepticYear(PaxEra.CE, 3)); assertEquals(2, PaxChronology.INSTANCE.prolepticYear(PaxEra.CE, 2)); assertEquals(1, PaxChronology.INSTANCE.prolepticYear(PaxEra.CE, 1)); assertEquals(0, PaxChronology.INSTANCE.prolepticYear(PaxEra.BCE, 1)); assertEquals(-1, PaxChronology.INSTANCE.prolepticYear(PaxEra.BCE, 2)); assertEquals(-2, PaxChronology.INSTANCE.prolepticYear(PaxEra.BCE, 3)); assertEquals(-3, PaxChronology.INSTANCE.prolepticYear(PaxEra.BCE, 4)); } @Test(expected = ClassCastException.class) public void test_prolepticYear_badEra() { PaxChronology.INSTANCE.prolepticYear(IsoEra.CE, 4); } @Test public void test_Chronology_eraOf() { assertEquals(PaxEra.CE, PaxChronology.INSTANCE.eraOf(1)); assertEquals(PaxEra.BCE, PaxChronology.INSTANCE.eraOf(0)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { PaxChronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = PaxChronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertEquals(true, eras.contains(PaxEra.BCE)); assertEquals(true, eras.contains(PaxEra.CE)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), PaxChronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 7, 28), PaxChronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 364, 371), PaxChronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(1, 13, 14), PaxChronology.INSTANCE.range(MONTH_OF_YEAR)); } //----------------------------------------------------------------------- // PaxDate.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { {2012, 1, 23, DAY_OF_MONTH, 1, 28}, {2012, 2, 23, DAY_OF_MONTH, 1, 28}, {2012, 3, 23, DAY_OF_MONTH, 1, 28}, {2012, 4, 23, DAY_OF_MONTH, 1, 28}, {2012, 5, 23, DAY_OF_MONTH, 1, 28}, {2012, 6, 23, DAY_OF_MONTH, 1, 28}, {2012, 7, 23, DAY_OF_MONTH, 1, 28}, {2012, 8, 23, DAY_OF_MONTH, 1, 28}, {2012, 9, 23, DAY_OF_MONTH, 1, 28}, {2012, 10, 23, DAY_OF_MONTH, 1, 28}, {2012, 11, 23, DAY_OF_MONTH, 1, 28}, {2012, 12, 23, DAY_OF_MONTH, 1, 28}, {2012, 13, 3, DAY_OF_MONTH, 1, 7}, {2012, 14, 23, DAY_OF_MONTH, 1, 28}, {2012, 1, 23, MONTH_OF_YEAR, 1, 14}, {2012, 1, 23, DAY_OF_YEAR, 1, 371}, {2012, 1, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, {2012, 13, 3, ALIGNED_WEEK_OF_MONTH, 1, 1}, {2012, 14, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, {2011, 13, 23, DAY_OF_MONTH, 1, 28}, {2011, 1, 23, MONTH_OF_YEAR, 1, 13}, {2011, 13, 23, DAY_OF_YEAR, 1, 364}, {2011, 13, 23, ALIGNED_WEEK_OF_MONTH, 1, 4}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, int expectedMin, int expectedMax) { assertEquals(ValueRange.of(expectedMin, expectedMax), PaxDate.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { PaxDate.of(2012, 6, 28).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // PaxDate.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 4}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 28 + 28 + 28 + 28 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 13 + 20 * 18 - 5 + 2 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 6, 8, ERA, 1}, {0, 6, 8, ERA, 0}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 4}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, PaxDate.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { PaxDate.of(2012, 6, 28).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // PaxDate.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 3, 2014, 5, 25}, {2014, 5, 26, DAY_OF_WEEK, 4, 2014, 5, 26}, {2014, 5, 26, DAY_OF_MONTH, 28, 2014, 5, 28}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 364, 2014, 13, 28}, {2014, 5, 26, DAY_OF_YEAR, 138, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 19}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 7, 2014, 7, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 13 + 20 * 18 - 5 + 2 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 13 + 20 * 18 - 5 + 2 + 5 - 1, 2013, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 0, -2013, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2011, 3, 28, MONTH_OF_YEAR, 13, 2011, 13, 28}, {2012, 3, 28, MONTH_OF_YEAR, 13, 2012, 13, 7}, {2012, 3, 28, MONTH_OF_YEAR, 6, 2012, 6, 28}, {2012, 13, 7, YEAR, 2011, 2011, 13, 7}, {-2013, 6, 8, YEAR_OF_ERA, 2012, -2011, 6, 8}, {2014, 5, 26, WeekFields.ISO.dayOfWeek(), 3, 2014, 5, 25}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(PaxDate.of(expectedYear, expectedMonth, expectedDom), PaxDate.of(year, month, dom).with(field, value)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { PaxDate.of(2012, 6, 28).with(MINUTE_OF_DAY, 0); } //----------------------------------------------------------------------- // PaxDate.with(TemporalAdjuster) //----------------------------------------------------------------------- @Test public void test_adjust1() { PaxDate base = PaxDate.of(2012, 6, 23); PaxDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(PaxDate.of(2012, 6, 28), test); } @Test public void test_adjust2() { PaxDate base = PaxDate.of(2012, 13, 2); PaxDate test = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(PaxDate.of(2012, 13, 7), test); } //----------------------------------------------------------------------- // PaxDate.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { PaxDate pax = PaxDate.of(2000, 1, 4); PaxDate test = pax.with(LocalDate.of(2012, 7, 6)); assertEquals(PaxDate.of(2012, 7, 27), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { PaxDate pax = PaxDate.of(2000, 1, 4); pax.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(PaxDate) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToPaxDate() { PaxDate pax = PaxDate.of(2012, 6, 23); LocalDate test = LocalDate.MIN.with(pax); assertEquals(LocalDate.of(2012, 6, 4), test); } @Test public void test_LocalDateTime_adjustToPaxDate() { PaxDate pax = PaxDate.of(2012, 6, 23); LocalDateTime test = LocalDateTime.MIN.with(pax); assertEquals(LocalDateTime.of(2012, 6, 4, 0, 0), test); } //----------------------------------------------------------------------- // PaxDate.plus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 6, 6}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 6, 19}, {2014, 5, 26, -5, WEEKS, 2014, 4, 19}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26}, {2014, 5, 26, -5, MONTHS, 2013, 13, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -5, MILLENNIA, 2014 - 5000, 5, 26}, {2014, 5, 26, -1, ERAS, -2013, 5, 26}, {2012, 13, 6, 3, MONTHS, 2013, 2, 6}, {2011, 13, 26, 1, YEARS, 2012, 14, 26}, {2014, 13, 26, -2, YEARS, 2012, 14, 26}, {2012, 14, 26, -6, YEARS, 2006, 14, 26}, {2012, 13, 6, -6, YEARS, 2006, 13, 6}, {-2014, 5, 26, 0, MONTHS, -2014, 5, 26}, {-2014, 5, 26, 3, MONTHS, -2014, 8, 26}, {-2014, 5, 26, -5, MONTHS, -2015, 13, 26}, }; } @DataProvider public static Object[][] data_plus_leap() { return new Object[][] { {2012, 12, 26, 1, MONTHS, 2012, 13, 7}, {2012, 14, 26, -1, MONTHS, 2012, 13, 7}, {2012, 13, 6, 3, YEARS, 2015, 13, 6}, }; } @DataProvider public static Object[][] data_minus_leap() { return new Object[][] { {2012, 13, 7, -1, MONTHS, 2012, 12, 26}, {2012, 13, 7, 1, MONTHS, 2012, 14, 26}, {2012, 14, 6, 3, YEARS, 2015, 13, 6}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(PaxDate.of(expectedYear, expectedMonth, expectedDom), PaxDate.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus_leap") public void test_plus_leap_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { test_plus_TemporalUnit(year, month, dom, amount, unit, expectedYear, expectedMonth, expectedDom); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(PaxDate.of(expectedYear, expectedMonth, expectedDom), PaxDate.of(year, month, dom).minus(amount, unit)); } @Test @UseDataProvider("data_minus_leap") public void test_minus_leap_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { test_minus_TemporalUnit(year, month, dom, amount, unit, expectedYear, expectedMonth, expectedDom); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { PaxDate.of(2012, 6, 10).plus(0, MINUTES); } //----------------------------------------------------------------------- // PaxDate.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 4, DAYS, 6}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 4, WEEKS, 0}, {2014, 5, 26, 2014, 6, 5, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {-2013, 5, 26, 0, 5, 26, ERAS, 0}, {-2013, 5, 26, 2014, 5, 26, ERAS, 1}, {2011, 13, 26, 2013, 13, 26, YEARS, 2}, {2011, 13, 26, 2012, 14, 26, YEARS, 1}, {2012, 14, 26, 2011, 13, 26, YEARS, -1}, {2012, 14, 26, 2013, 13, 26, YEARS, 1}, {2011, 13, 6, 2012, 13, 6, YEARS, 0}, {2012, 13, 6, 2011, 13, 6, YEARS, 0}, {2011, 13, 1, 2012, 13, 7, YEARS, 0}, {2012, 13, 7, 2011, 13, 1, YEARS, 0}, {2011, 12, 28, 2012, 13, 1, YEARS, 1}, {2012, 13, 1, 2011, 12, 28, YEARS, -1}, {2013, 13, 6, 2012, 13, 6, YEARS, -1}, {2012, 13, 6, 2013, 13, 6, YEARS, 1}, }; } @DataProvider public static Object[][] data_until_period() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, 0, 0, 0}, {2014, 5, 26, 2014, 6, 4, 0, 0, 6}, {2014, 5, 26, 2014, 5, 20, 0, 0, -6}, {2014, 5, 26, 2014, 6, 5, 0, 0, 7}, {2014, 5, 26, 2014, 6, 25, 0, 0, 27}, {2014, 5, 26, 2014, 6, 26, 0, 1, 0}, {2014, 5, 26, 2015, 5, 25, 0, 12, 27}, {2014, 5, 26, 2015, 5, 26, 1, 0, 0}, {2014, 5, 26, 2024, 5, 25, 9, 12, 27}, {2011, 13, 26, 2013, 13, 26, 2, 0, 0}, {2011, 13, 26, 2012, 14, 26, 1, 0, 0}, {2012, 14, 26, 2011, 13, 26, -1, 0, 0}, {2012, 14, 26, 2013, 13, 26, 1, 0, 0}, {2011, 13, 6, 2012, 13, 6, 0, 13, 0}, {2012, 13, 6, 2011, 13, 6, 0, -13, 0}, {2011, 13, 1, 2012, 13, 7, 0, 13, 6}, {2012, 13, 7, 2011, 13, 1, 0, -13, -6}, {2011, 12, 28, 2012, 13, 1, 1, 0, 1}, {2012, 13, 1, 2011, 12, 28, -1, 0, -1}, {2013, 13, 6, 2012, 13, 6, -1, -1, 0}, {2012, 13, 6, 2013, 13, 6, 1, 0, 0}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { PaxDate start = PaxDate.of(year1, month1, dom1); PaxDate end = PaxDate.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test @UseDataProvider("data_until_period") public void test_until_end( int year1, int month1, int dom1, int year2, int month2, int dom2, int yearPeriod, int monthPeriod, int dayPeriod) { PaxDate start = PaxDate.of(year1, month1, dom1); PaxDate end = PaxDate.of(year2, month2, dom2); ChronoPeriod period = PaxChronology.INSTANCE.period(yearPeriod, monthPeriod, dayPeriod); assertEquals(period, start.until(end)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { PaxDate start = PaxDate.of(2012, 6, 28); PaxDate end = PaxDate.of(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(PaxDate.of(2014, 7, 28), PaxDate.of(2014, 5, 26).plus(PaxChronology.INSTANCE.period(0, 2, 2))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(PaxDate.of(2014, 7, 26), PaxDate.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(PaxDate.of(2014, 3, 23), PaxDate.of(2014, 5, 26).minus(PaxChronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(PaxDate.of(2014, 3, 26), PaxDate.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { PaxDate a1 = PaxDate.of(2000, 1, 3); PaxDate a2 = PaxDate.of(2000, 1, 3); PaxDate b = PaxDate.of(2000, 1, 4); PaxDate c = PaxDate.of(2000, 2, 3); PaxDate d = PaxDate.of(2001, 1, 3); assertEquals(true, a1.equals(a1)); assertEquals(true, a1.equals(a2)); assertEquals(false, a1.equals(b)); assertEquals(false, a1.equals(c)); assertEquals(false, a1.equals(d)); assertEquals(false, a1.equals(null)); assertEquals(false, a1.equals("")); assertTrue(a1.hashCode() == a2.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {PaxDate.of(1, 1, 1), "Pax CE 1-01-01"}, {PaxDate.of(2012, 6, 23), "Pax CE 2012-06-23"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(PaxDate pax, String expected) { assertEquals(expected, pax.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestSymmetry010Chronology.java000066400000000000000000001321031343451174100320070ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.HijrahEra; import java.time.chrono.IsoEra; import java.time.chrono.JapaneseEra; import java.time.chrono.MinguoEra; import java.time.chrono.ThaiBuddhistEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @SuppressWarnings({"static-method", "javadoc"}) @RunWith(DataProviderRunner.class) public class TestSymmetry010Chronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology() { Chronology chrono = Chronology.of("Sym010"); assertNotNull(chrono); assertEquals(Symmetry010Chronology.INSTANCE, chrono); assertEquals("Sym010", chrono.getId()); assertEquals(null, chrono.getCalendarType()); } //----------------------------------------------------------------------- // Symmetry010Date.of //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { { Symmetry010Date.of( 1, 1, 1), LocalDate.of( 1, 1, 1) }, { Symmetry010Date.of( 272, 2, 28), LocalDate.of( 272, 2, 27) }, // Constantine the Great, Roman emperor (d. 337) { Symmetry010Date.of( 272, 2, 27), LocalDate.of( 272, 2, 26) }, { Symmetry010Date.of( 742, 3, 27), LocalDate.of( 742, 4, 2) }, // Charlemagne, Frankish king (d. 814) { Symmetry010Date.of( 742, 4, 2), LocalDate.of( 742, 4, 7) }, { Symmetry010Date.of(1066, 10, 14), LocalDate.of(1066, 10, 14) }, // Norman Conquest: Battle of Hastings { Symmetry010Date.of(1304, 7, 21), LocalDate.of(1304, 7, 20) }, // Francesco Petrarca - Petrarch, Italian scholar and poet in Renaissance Italy, "Father of Humanism" (d. 1374) { Symmetry010Date.of(1304, 7, 20), LocalDate.of(1304, 7, 19) }, { Symmetry010Date.of(1433, 11, 12), LocalDate.of(1433, 11, 10) }, // Charles the Bold, French son of Isabella of Portugal, Duchess of Burgundy (d. 1477) { Symmetry010Date.of(1433, 11, 10), LocalDate.of(1433, 11, 8) }, { Symmetry010Date.of(1452, 4, 11), LocalDate.of(1452, 4, 15) }, // Leonardo da Vinci, Italian painter, sculptor, and architect (d. 1519) { Symmetry010Date.of(1452, 4, 15), LocalDate.of(1452, 4, 19) }, { Symmetry010Date.of(1492, 10, 10), LocalDate.of(1492, 10, 12) }, // Christopher Columbus's expedition makes landfall in the Caribbean. { Symmetry010Date.of(1492, 10, 12), LocalDate.of(1492, 10, 14) }, { Symmetry010Date.of(1564, 2, 18), LocalDate.of(1564, 2, 15) }, // Galileo Galilei, Italian astronomer and physicist (d. 1642) { Symmetry010Date.of(1564, 2, 15), LocalDate.of(1564, 2, 12) }, { Symmetry010Date.of(1564, 4, 28), LocalDate.of(1564, 4, 26) }, // William Shakespeare is baptized in Stratford-upon-Avon, Warwickshire, England (date of actual birth is unknown, d. 1616). { Symmetry010Date.of(1564, 4, 26), LocalDate.of(1564, 4, 24) }, { Symmetry010Date.of(1643, 1 , 7), LocalDate.of(1643, 1, 4) }, // Sir Isaac Newton, English physicist and mathematician (d. 1727) { Symmetry010Date.of(1643, 1, 4), LocalDate.of(1643, 1, 1) }, { Symmetry010Date.of(1707, 4, 12), LocalDate.of(1707, 4, 15) }, // Leonhard Euler, Swiss mathematician and physicist (d. 1783) { Symmetry010Date.of(1707, 4, 15), LocalDate.of(1707, 4, 18) }, { Symmetry010Date.of(1789, 7, 16), LocalDate.of(1789, 7, 14) }, // French Revolution: Citizens of Paris storm the Bastille. { Symmetry010Date.of(1789, 7, 14), LocalDate.of(1789, 7, 12) }, { Symmetry010Date.of(1879, 3, 14), LocalDate.of(1879, 3, 14) }, // Albert Einstein, German theoretical physicist (d. 1955) { Symmetry010Date.of(1941, 9, 11), LocalDate.of(1941, 9, 9) }, // Dennis MacAlistair Ritchie, American computer scientist (d. 2011) { Symmetry010Date.of(1941, 9, 9), LocalDate.of(1941, 9, 7) }, { Symmetry010Date.of(1970, 1, 4), LocalDate.of(1970, 1, 1) }, // Unix time begins at 00:00:00 UTC/GMT. { Symmetry010Date.of(1970, 1, 1), LocalDate.of(1969, 12, 29) }, { Symmetry010Date.of(1999, 12, 29), LocalDate.of(2000, 1, 1) }, // Start of the 21st century / 3rd millennium { Symmetry010Date.of(2000, 1, 1), LocalDate.of(2000, 1, 3) }, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_Symmetry010Date(Symmetry010Date sym010, LocalDate iso) { assertEquals(iso, LocalDate.from(sym010)); } @Test @UseDataProvider("data_samples") public void test_Symmetry010Date_from_LocalDate(Symmetry010Date sym010, LocalDate iso) { assertEquals(sym010, Symmetry010Date.from(iso)); } @Test @UseDataProvider("data_samples") public void test_Symmetry010Date_chronology_dateEpochDay(Symmetry010Date sym010, LocalDate iso) { assertEquals(sym010, Symmetry010Chronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_Symmetry010Date_toEpochDay(Symmetry010Date sym010, LocalDate iso) { assertEquals(iso.toEpochDay(), sym010.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_Symmetry010Date_until_Symmetry010Date(Symmetry010Date sym010, LocalDate iso) { assertEquals(Symmetry010Chronology.INSTANCE.period(0, 0, 0), sym010.until(sym010)); } @Test @UseDataProvider("data_samples") public void test_Symmetry010Date_until_LocalDate(Symmetry010Date sym010, LocalDate iso) { assertEquals(Symmetry010Chronology.INSTANCE.period(0, 0, 0), sym010.until(iso)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(Symmetry010Date sym010, LocalDate iso) { assertEquals(sym010, Symmetry010Chronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_Symmetry010Date(Symmetry010Date sym010, LocalDate iso) { assertEquals(Period.ZERO, iso.until(sym010)); } @Test @UseDataProvider("data_samples") public void test_plusDays(Symmetry010Date sym010, LocalDate iso) { assertEquals(iso, LocalDate.from(sym010.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(sym010.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(sym010.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(sym010.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(sym010.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(Symmetry010Date sym010, LocalDate iso) { assertEquals(iso, LocalDate.from(sym010.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(sym010.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(sym010.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(sym010.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(sym010.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(Symmetry010Date sym010, LocalDate iso) { assertEquals(0, sym010.until(iso.plusDays(0), DAYS)); assertEquals(1, sym010.until(iso.plusDays(1), DAYS)); assertEquals(35, sym010.until(iso.plusDays(35), DAYS)); assertEquals(-40, sym010.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {-1, 13, 28}, {-1, 13, 29}, {2000, -2, 1}, {2000, 13, 1}, {2000, 15, 1}, {2000, 1, -1}, {2000, 1, 0}, {2000, 0, 1}, {2000, -1, 0}, {2000, -1, 1}, {2000, 1, 31}, {2000, 2, 32}, {2000, 3, 31}, {2000, 4, 31}, {2000, 5, 32}, {2000, 6, 31}, {2000, 7, 31}, {2000, 8, 32}, {2000, 9, 31}, {2000, 10, 31}, {2000, 11, 32}, {2000, 12, 31}, {2004, 12, 38}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { Symmetry010Date.of(year, month, dom); } @DataProvider public static Object[][] data_badLeapDates() { return new Object[][] { {1}, {100}, {200}, {2000} }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badLeapDates") public void badLeapDayDates(int year) { Symmetry010Date.of(year, 12, 37); } //----------------------------------------------------------------------- // Symmetry010Date.isLeapYear //----------------------------------------------------------------------- @Test public void test_isLeapYear_specific() { assertTrue(Symmetry010Chronology.INSTANCE.isLeapYear(3)); assertFalse(Symmetry010Chronology.INSTANCE.isLeapYear(6)); assertTrue(Symmetry010Chronology.INSTANCE.isLeapYear(9)); assertFalse(Symmetry010Chronology.INSTANCE.isLeapYear(2000)); assertTrue(Symmetry010Chronology.INSTANCE.isLeapYear(2004)); } //----------------------------------------------------------------------- // Symmetry010Date.isLeapWeek //----------------------------------------------------------------------- @Test public void test_leapWeek() { assertTrue(Symmetry010Date.of (2015, 12, 31).isLeapWeek()); assertTrue(Symmetry010Date.of (2015, 12, 32).isLeapWeek()); assertTrue(Symmetry010Date.of (2015, 12, 33).isLeapWeek()); assertTrue(Symmetry010Date.of (2015, 12, 34).isLeapWeek()); assertTrue(Symmetry010Date.of (2015, 12, 35).isLeapWeek()); assertTrue(Symmetry010Date.of (2015, 12, 36).isLeapWeek()); assertTrue(Symmetry010Date.of (2015, 12, 37).isLeapWeek()); } //----------------------------------------------------------------------- // Symmetry010Date.lengthOfMonth //----------------------------------------------------------------------- @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {2000, 1, 28, 30}, {2000, 2, 28, 31}, {2000, 3, 28, 30}, {2000, 4, 28, 30}, {2000, 5, 28, 31}, {2000, 6, 28, 30}, {2000, 7, 28, 30}, {2000, 8, 28, 31}, {2000, 9, 28, 30}, {2000, 10, 28, 30}, {2000, 11, 28, 31}, {2000, 12, 28, 30}, {2004, 12, 20, 37}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int day, int length) { assertEquals(length, Symmetry010Date.of(year, month, day).lengthOfMonth()); } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonthFirst(int year, int month, int day, int length) { assertEquals(length, Symmetry010Date.of(year, month, 1).lengthOfMonth()); } @Test public void test_lengthOfMonth_specific() { assertEquals(30, Symmetry010Date.of(2000, 12, 1).lengthOfMonth()); assertEquals(37, Symmetry010Date.of(2004, 12, 1).lengthOfMonth()); } //----------------------------------------------------------------------- // Symmetry010Date.era // Symmetry010Date.prolepticYear // Symmetry010Date.dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = 1; year < 200; year++) { Symmetry010Date base = Symmetry010Chronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); IsoEra era = IsoEra.CE; assertEquals(era, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); Symmetry010Date eraBased = Symmetry010Chronology.INSTANCE.date(era, year, 1, 1); assertEquals(base, eraBased); } for (int year = -200; year < 0; year++) { Symmetry010Date base = Symmetry010Chronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); IsoEra era = IsoEra.BCE; assertEquals(era, base.getEra()); assertEquals(1 - year, base.get(YEAR_OF_ERA)); Symmetry010Date eraBased = Symmetry010Chronology.INSTANCE.date(era, year, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = 1; year < 200; year++) { Symmetry010Date base = Symmetry010Chronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); IsoEra era = IsoEra.CE; assertEquals(era, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); Symmetry010Date eraBased = Symmetry010Chronology.INSTANCE.dateYearDay(era, year, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, Symmetry010Chronology.INSTANCE.prolepticYear(IsoEra.CE, 4)); assertEquals(3, Symmetry010Chronology.INSTANCE.prolepticYear(IsoEra.CE, 3)); assertEquals(2, Symmetry010Chronology.INSTANCE.prolepticYear(IsoEra.CE, 2)); assertEquals(1, Symmetry010Chronology.INSTANCE.prolepticYear(IsoEra.CE, 1)); assertEquals(2000, Symmetry010Chronology.INSTANCE.prolepticYear(IsoEra.CE, 2000)); assertEquals(1582, Symmetry010Chronology.INSTANCE.prolepticYear(IsoEra.CE, 1582)); } @DataProvider public static Object[][] data_prolepticYear_badEra() { return new Era[][] { { AccountingEra.BCE }, { AccountingEra.CE }, { CopticEra.BEFORE_AM }, { CopticEra.AM }, { DiscordianEra.YOLD }, { EthiopicEra.BEFORE_INCARNATION }, { EthiopicEra.INCARNATION }, { HijrahEra.AH }, { InternationalFixedEra.CE }, { JapaneseEra.MEIJI }, { JapaneseEra.TAISHO }, { JapaneseEra.SHOWA }, { JapaneseEra.HEISEI }, { JulianEra.BC }, { JulianEra.AD }, { MinguoEra.BEFORE_ROC }, { MinguoEra.ROC }, { PaxEra.BCE }, { PaxEra.CE }, { ThaiBuddhistEra.BEFORE_BE }, { ThaiBuddhistEra.BE }, }; } @Test(expected = ClassCastException.class) @UseDataProvider("data_prolepticYear_badEra") public void test_prolepticYear_badEra(Era era) { Symmetry010Chronology.INSTANCE.prolepticYear(era, 4); } @Test public void test_Chronology_eraOf() { assertEquals(IsoEra.BCE, Symmetry010Chronology.INSTANCE.eraOf(0)); assertEquals(IsoEra.CE, Symmetry010Chronology.INSTANCE.eraOf(1)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { Symmetry010Chronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = Symmetry010Chronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertTrue(eras.contains(IsoEra.BCE)); assertTrue(eras.contains(IsoEra.CE)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), Symmetry010Chronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(ValueRange.of(1, 7), Symmetry010Chronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(ValueRange.of(1, 4, 5), Symmetry010Chronology.INSTANCE.range(ALIGNED_WEEK_OF_MONTH)); assertEquals(ValueRange.of(1, 52, 53), Symmetry010Chronology.INSTANCE.range(ALIGNED_WEEK_OF_YEAR)); assertEquals(ValueRange.of(1, 7), Symmetry010Chronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 30, 37), Symmetry010Chronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 364, 371), Symmetry010Chronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(0, 1), Symmetry010Chronology.INSTANCE.range(ERA)); assertEquals(ValueRange.of(-1_000_000 * 364L - 177_474 * 7 - 719_162, 1_000_000 * 364L + 177_474 * 7 - 719_162), Symmetry010Chronology.INSTANCE.range(EPOCH_DAY)); assertEquals(ValueRange.of(1, 12), Symmetry010Chronology.INSTANCE.range(MONTH_OF_YEAR)); assertEquals(ValueRange.of(-12_000_000L, 11_999_999L), Symmetry010Chronology.INSTANCE.range(PROLEPTIC_MONTH)); assertEquals(ValueRange.of(-1_000_000L, 1_000_000), Symmetry010Chronology.INSTANCE.range(YEAR)); assertEquals(ValueRange.of(-1_000_000, 1_000_000), Symmetry010Chronology.INSTANCE.range(YEAR_OF_ERA)); } //----------------------------------------------------------------------- // Symmetry010Date.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { // Leap Day and Year Day are members of months {2012, 1, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 2, 23, DAY_OF_MONTH, ValueRange.of(1, 31)}, {2012, 3, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 4, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 5, 23, DAY_OF_MONTH, ValueRange.of(1, 31)}, {2012, 6, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 7, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 8, 23, DAY_OF_MONTH, ValueRange.of(1, 31)}, {2012, 9, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 10, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2012, 11, 23, DAY_OF_MONTH, ValueRange.of(1, 31)}, {2012, 12, 23, DAY_OF_MONTH, ValueRange.of(1, 30)}, {2015, 12, 23, DAY_OF_MONTH, ValueRange.of(1, 37)}, {2012, 1, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 6, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 12, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 1, 23, DAY_OF_YEAR, ValueRange.of(1, 364)}, {2015, 1, 23, DAY_OF_YEAR, ValueRange.of(1, 371)}, {2012, 1, 23, MONTH_OF_YEAR, ValueRange.of(1, 12)}, {2012, 1, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 6, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 12, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 1, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 4)}, {2012, 2, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 4)}, {2015, 12, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 5)}, {2012, 1, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 6, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 12, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 1, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2012, 6, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2012, 12, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2015, 12, 30, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 53)}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, ValueRange range) { assertEquals(range, Symmetry010Date.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { Symmetry010Date.of(2012, 6, 28).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // Symmetry010Date.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 2}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 30 + 31 + 30 + 30 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 4 + 5 + 4 + 4 + 4}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 5, 8, ERA, 1}, {2012, 9, 26, DAY_OF_WEEK, 1}, {2012, 9, 26, DAY_OF_YEAR, 3 * (4 + 5 + 4) * 7 - 4}, {2012, 9, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2012, 9, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2012, 9, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3}, {2012, 9, 26, ALIGNED_WEEK_OF_YEAR, 3 * (4 + 5 + 4)}, {2015, 12, 37, DAY_OF_WEEK, 5}, {2015, 12, 37, DAY_OF_MONTH, 37}, {2015, 12, 37, DAY_OF_YEAR, 4 * (4 + 5 + 4) * 7 + 7}, {2015, 12, 37, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2}, {2015, 12, 37, ALIGNED_WEEK_OF_MONTH, 6}, {2015, 12, 37, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7}, {2015, 12, 37, ALIGNED_WEEK_OF_YEAR, 53}, {2015, 12, 37, MONTH_OF_YEAR, 12}, {2015, 12, 37, PROLEPTIC_MONTH, 2016 * 12 - 1}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, Symmetry010Date.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { Symmetry010Date.of(2012, 6, 28).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // Symmetry010Date.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 1, 2014, 5, 20}, {2014, 5, 26, DAY_OF_WEEK, 5, 2014, 5, 24}, {2014, 5, 26, DAY_OF_MONTH, 28, 2014, 5, 28}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 364, 2014, 12, 30}, {2014, 5, 26, DAY_OF_YEAR, 138, 2014, 5, 17}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 5, 21}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 5, 24}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 9}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20, 2014, 5, 19}, {2014, 5, 26, MONTH_OF_YEAR, 4, 2014, 4, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 12 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2015, 12, 22}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2, 2015, 12, 23}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2015, 12, 24}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 4, 2015, 12, 25}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2015, 12, 26}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 6, 2015, 12, 27}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 7, 2015, 12, 28}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1, 2015, 12, 17}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2015, 12, 18}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2015, 12, 19}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 2015, 12, 20}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2015, 12, 21}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6, 2015, 12, 22}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7, 2015, 12, 23}, {2015, 12, 29, ALIGNED_WEEK_OF_MONTH, 0, 2015, 12, 29}, {2015, 12, 29, ALIGNED_WEEK_OF_MONTH, 3, 2015, 12, 15}, {2015, 12, 29, ALIGNED_WEEK_OF_YEAR, 0, 2015, 12, 29}, {2015, 12, 29, ALIGNED_WEEK_OF_YEAR, 3, 2015, 1, 20}, {2015, 12, 29, DAY_OF_WEEK, 0, 2015, 12, 29}, {2015, 12, 28, DAY_OF_WEEK, 1, 2015, 12, 24}, {2015, 12, 28, DAY_OF_WEEK, 2, 2015, 12, 25}, {2015, 12, 28, DAY_OF_WEEK, 3, 2015, 12, 26}, {2015, 12, 28, DAY_OF_WEEK, 4, 2015, 12, 27}, {2015, 12, 28, DAY_OF_WEEK, 5, 2015, 12, 28}, {2015, 12, 28, DAY_OF_WEEK, 6, 2015, 12, 29}, {2015, 12, 28, DAY_OF_WEEK, 7, 2015, 12, 30}, {2015, 12, 29, DAY_OF_MONTH, 1, 2015, 12, 1}, {2015, 12, 29, DAY_OF_MONTH, 3, 2015, 12, 3}, {2015, 12, 29, MONTH_OF_YEAR, 1, 2015, 1, 29}, {2015, 12, 29, MONTH_OF_YEAR, 12, 2015, 12, 29}, {2015, 12, 29, MONTH_OF_YEAR, 2, 2015, 2, 29}, {2015, 12, 37, YEAR, 2004, 2004, 12, 37}, {2015, 12, 37, YEAR, 2013, 2013, 12, 30}, {2014, 3, 28, DAY_OF_MONTH, 1, 2014, 3, 1}, {2014, 1, 28, DAY_OF_MONTH, 1, 2014, 1, 1}, {2014, 3, 28, MONTH_OF_YEAR, 1, 2014, 1, 28}, {2015, 3, 28, DAY_OF_YEAR, 371, 2015, 12, 37}, {2012, 3, 28, DAY_OF_YEAR, 364, 2012, 12, 30}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(Symmetry010Date.of(expectedYear, expectedMonth, expectedDom), Symmetry010Date.of(year, month, dom).with(field, value)); } @DataProvider public static Object[][] data_with_bad() { return new Object[][] { {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, -1}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 8}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, -1}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 8}, {2013, 1, 1, ALIGNED_WEEK_OF_MONTH, -1}, {2013, 2, 1, ALIGNED_WEEK_OF_MONTH, 6}, {2013, 1, 1, ALIGNED_WEEK_OF_YEAR, -1}, {2015, 1, 1, ALIGNED_WEEK_OF_YEAR, 54}, {2013, 1, 1, DAY_OF_WEEK, -1}, {2013, 1, 1, DAY_OF_WEEK, 8}, {2013, 1, 1, DAY_OF_MONTH, -1}, {2013, 1, 1, DAY_OF_MONTH, 31}, {2013, 6, 1, DAY_OF_MONTH, 31}, {2013, 12, 1, DAY_OF_MONTH, 31}, {2015, 12, 1, DAY_OF_MONTH, 38}, {2013, 1, 1, DAY_OF_YEAR, -1}, {2013, 1, 1, DAY_OF_YEAR, 365}, {2015, 1, 1, DAY_OF_YEAR, 372}, {2013, 1, 1, MONTH_OF_YEAR, -1}, {2013, 1, 1, MONTH_OF_YEAR, 14}, {2013, 1, 1, MONTH_OF_YEAR, -2}, {2015, 1, 1, MONTH_OF_YEAR, 14}, {2013, 1, 1, EPOCH_DAY, -365_961_481}, {2013, 1, 1, EPOCH_DAY, 364_523_156}, {2013, 1, 1, YEAR, -1_000_001}, {2013, 1, 1, YEAR, 1_000_001}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_with_bad") public void test_with_TemporalField_badValue(int year, int month, int dom, TemporalField field, long value) { Symmetry010Date.of(year, month, dom).with(field, value); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { Symmetry010Date.of(2012, 6, 28).with(MINUTE_OF_DAY, 10); } //----------------------------------------------------------------------- // Symmetry010Date.with(TemporalAdjuster) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_temporalAdjusters_lastDayOfMonth() { return new Object[][] { {2012, 1, 23, 2012, 1, 30}, {2012, 2, 23, 2012, 2, 31}, {2012, 3, 23, 2012, 3, 30}, {2012, 4, 23, 2012, 4, 30}, {2012, 5, 23, 2012, 5, 31}, {2012, 6, 23, 2012, 6, 30}, {2012, 7, 23, 2012, 7, 30}, {2012, 8, 23, 2012, 8, 31}, {2012, 9, 23, 2012, 9, 30}, {2012, 10, 23, 2012,10, 30}, {2012, 11, 23, 2012, 11, 31}, {2012, 12, 23, 2012, 12, 30}, {2009, 12, 23, 2009, 12, 37}, }; } @Test @UseDataProvider("data_temporalAdjusters_lastDayOfMonth") public void test_temporalAdjusters_LastDayOfMonth(int year, int month, int day, int expectedYear, int expectedMonth, int expectedDay) { Symmetry010Date base = Symmetry010Date.of(year, month, day); Symmetry010Date expected = Symmetry010Date.of(expectedYear, expectedMonth, expectedDay); Symmetry010Date actual = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(expected, actual); } //----------------------------------------------------------------------- // Symmetry010Date.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { Symmetry010Date sym010 = Symmetry010Date.of(2000, 1, 4); Symmetry010Date test = sym010.with(LocalDate.of(2012, 7, 6)); assertEquals(Symmetry010Date.of(2012, 7, 5), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { Symmetry010Date sym010 = Symmetry010Date.of(2000, 1, 4); sym010.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(Symmetry010Date) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToSymmetry010Date() { Symmetry010Date sym010 = Symmetry010Date.of(2012, 7, 19); LocalDate test = LocalDate.MIN.with(sym010); assertEquals(LocalDate.of(2012, 7, 20), test); } @Test public void test_LocalDateTime_adjustToSymmetry010Date() { Symmetry010Date sym010 = Symmetry010Date.of(2012, 7, 19); LocalDateTime test = LocalDateTime.MIN.with(sym010); assertEquals(LocalDateTime.of(2012, 7, 20, 0, 0), test); } //----------------------------------------------------------------------- // Symmetry010Date.plus // Symmetry010Date.minus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 6, 3}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 6, 16}, {2014, 5, 26, -5, WEEKS, 2014, 4, 21}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26}, {2014, 5, 26, -5, MONTHS, 2013, 12, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -1, MILLENNIA, 2014 - 1000, 5, 26}, {2014, 12, 26, 3, WEEKS, 2015, 1, 17}, {2014, 1, 26, -5, WEEKS, 2013, 12, 21}, {2012, 6, 26, 3, WEEKS, 2012, 7, 17}, {2012, 7, 26, -5, WEEKS, 2012, 6, 21}, {2012, 6, 21, 52 + 1, WEEKS, 2013, 6, 28}, {2013, 6, 21, 6 * 52 + 1, WEEKS, 2019, 6, 21}, }; } @DataProvider public static Object[][] data_plus_leapWeek() { return new Object[][] { {2015, 12, 28, 0, DAYS, 2015, 12, 28}, {2015, 12, 28, 8, DAYS, 2015, 12, 36}, {2015, 12, 28, -3, DAYS, 2015, 12, 25}, {2015, 12, 28, 0, WEEKS, 2015, 12, 28}, {2015, 12, 28, 3, WEEKS, 2016, 1, 12}, {2015, 12, 28, -5, WEEKS, 2015, 11, 24}, {2015, 12, 28, 52, WEEKS, 2016, 12, 21}, {2015, 12, 28, 0, MONTHS, 2015, 12, 28}, {2015, 12, 28, 3, MONTHS, 2016, 3, 28}, {2015, 12, 28, -5, MONTHS, 2015, 7, 28}, {2015, 12, 28, 12, MONTHS, 2016, 12, 28}, {2015, 12, 28, 0, YEARS, 2015, 12, 28}, {2015, 12, 28, 3, YEARS, 2018, 12, 28}, {2015, 12, 28, -5, YEARS, 2010, 12, 28}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(Symmetry010Date.of(expectedYear, expectedMonth, expectedDom), Symmetry010Date.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus_leapWeek") public void test_plus_leapWeek_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(Symmetry010Date.of(expectedYear, expectedMonth, expectedDom), Symmetry010Date.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(Symmetry010Date.of(expectedYear, expectedMonth, expectedDom), Symmetry010Date.of(year, month, dom).minus(amount, unit)); } @Test @UseDataProvider("data_plus_leapWeek") public void test_minus_leapWeek_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(Symmetry010Date.of(expectedYear, expectedMonth, expectedDom), Symmetry010Date.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { Symmetry010Date.of(2012, 6, 28).plus(0, MINUTES); } //----------------------------------------------------------------------- // Symmetry010Date.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 4, DAYS, 9}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 1, WEEKS, 1}, {2014, 5, 26, 2014, 6, 5, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {2014, 5, 26, 3014, 5, 26, ERAS, 0}, }; } @DataProvider public static Object[][] data_until_period() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, 0, 0, 0}, {2014, 5, 26, 2014, 6, 4, 0, 0, 9}, {2014, 5, 26, 2014, 5, 20, 0, 0, -6}, {2014, 5, 26, 2014, 6, 5, 0, 0, 10}, {2014, 5, 26, 2014, 6, 25, 0, 0, 30}, {2014, 5, 26, 2014, 6, 26, 0, 1, 0}, {2014, 5, 26, 2015, 5, 25, 0, 11, 29}, {2014, 5, 26, 2015, 5, 26, 1, 0, 0}, {2014, 5, 26, 2024, 5, 25, 9, 11, 29}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { Symmetry010Date start = Symmetry010Date.of(year1, month1, dom1); Symmetry010Date end = Symmetry010Date.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test @UseDataProvider("data_until_period") public void test_until_end( int year1, int month1, int dom1, int year2, int month2, int dom2, int yearPeriod, int monthPeriod, int dayPeriod) { Symmetry010Date start = Symmetry010Date.of(year1, month1, dom1); Symmetry010Date end = Symmetry010Date.of(year2, month2, dom2); ChronoPeriod period = Symmetry010Chronology.INSTANCE.period(yearPeriod, monthPeriod, dayPeriod); assertEquals(period, start.until(end)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { Symmetry010Date start = Symmetry010Date.of(2012, 6, 28); Symmetry010Date end = Symmetry010Date.of(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- // Symmetry010Date.period //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(Symmetry010Date.of(2014, 7, 29), Symmetry010Date.of(2014, 5, 21).plus(Symmetry010Chronology.INSTANCE.period(0, 2, 8))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(Symmetry010Date.of(2014, 7, 26), Symmetry010Date.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(Symmetry010Date.of(2014, 3, 23), Symmetry010Date.of(2014, 5, 26).minus(Symmetry010Chronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(Symmetry010Date.of(2014, 3, 26), Symmetry010Date.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // Symmetry010Date.equals //----------------------------------------------------------------------- @DataProvider public static Object[][] data_equals() { return new Object[][] { {Symmetry010Date.of(2000, 1, 3), Symmetry010Date.of(2000, 1, 4), Symmetry010Date.of(2000, 2, 3), Symmetry010Date.of(2001, 1, 3)}, {Symmetry010Date.of(2000, 12, 28), Symmetry010Date.of(2000, 12, 25), Symmetry010Date.of(2001, 1, 1), Symmetry010Date.of(2001, 12, 28)}, {Symmetry010Date.of(2000, 6, 28), Symmetry010Date.of(2000, 6, 23), Symmetry010Date.of(2000, 7, 1), Symmetry010Date.of(2004, 6, 28)}, }; } @Test @UseDataProvider("data_equals") public void test_equals(Symmetry010Date a1, Symmetry010Date b, Symmetry010Date c, Symmetry010Date d) { assertTrue(a1.equals(a1)); assertFalse(a1.equals(b)); assertFalse(a1.equals(c)); assertFalse(a1.equals(d)); assertFalse(a1.equals(null)); assertFalse("".equals(a1)); } //----------------------------------------------------------------------- // Symmetry010Date.toString //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {Symmetry010Date.of( 1, 1, 1), "Sym010 CE 1/01/01"}, {Symmetry010Date.of(1970, 2, 31), "Sym010 CE 1970/02/31"}, {Symmetry010Date.of(2000, 8, 31), "Sym010 CE 2000/08/31"}, {Symmetry010Date.of(2009, 12, 37), "Sym010 CE 2009/12/37"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(Symmetry010Date date, String expected) { assertEquals(expected, date.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/chrono/TestSymmetry454Chronology.java000066400000000000000000001324171343451174100320330ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.chrono; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.EPOCH_DAY; import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.MINUTE_OF_DAY; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static java.time.temporal.ChronoUnit.CENTURIES; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DECADES; import static java.time.temporal.ChronoUnit.ERAS; import static java.time.temporal.ChronoUnit.MILLENNIA; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.chrono.ChronoPeriod; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.HijrahEra; import java.time.chrono.IsoEra; import java.time.chrono.JapaneseEra; import java.time.chrono.MinguoEra; import java.time.chrono.ThaiBuddhistEra; import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.time.temporal.ValueRange; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test. */ @SuppressWarnings({"static-method", "javadoc"}) @RunWith(DataProviderRunner.class) public class TestSymmetry454Chronology { //----------------------------------------------------------------------- // Chronology.of(String) //----------------------------------------------------------------------- @Test public void test_chronology() { Chronology chrono = Chronology.of("Sym454"); assertNotNull(chrono); assertEquals(Symmetry454Chronology.INSTANCE, chrono); assertEquals("Sym454", chrono.getId()); assertEquals(null, chrono.getCalendarType()); } //----------------------------------------------------------------------- // Symmetry454Date.of //----------------------------------------------------------------------- @DataProvider public static Object[][] data_samples() { return new Object[][] { { Symmetry454Date.of( 1, 1, 1), LocalDate.of( 1, 1, 1) }, { Symmetry454Date.of( 272, 2, 30), LocalDate.of( 272, 2, 27) }, // Constantine the Great, Roman emperor (d. 337) { Symmetry454Date.of( 272, 2, 27), LocalDate.of( 272, 2, 24) }, { Symmetry454Date.of( 742, 3, 25), LocalDate.of( 742, 4, 2) }, // Charlemagne, Frankish king (d. 814) { Symmetry454Date.of( 742, 4, 2), LocalDate.of( 742, 4, 7) }, { Symmetry454Date.of(1066, 10, 14), LocalDate.of(1066, 10, 14) }, // Norman Conquest: Battle of Hastings { Symmetry454Date.of(1304, 7, 21), LocalDate.of(1304, 7, 20) }, // Francesco Petrarca - Petrarch, Italian scholar and poet in Renaissance Italy, "Father of Humanism" (d. 1374). { Symmetry454Date.of(1304, 7, 20), LocalDate.of(1304, 7, 19) }, { Symmetry454Date.of(1433, 11, 14), LocalDate.of(1433, 11, 10) }, // Charles the Bold, French son of Isabella of Portugal, Duchess of Burgundy (d. 1477) { Symmetry454Date.of(1433, 11, 10), LocalDate.of(1433, 11, 6) }, { Symmetry454Date.of(1452, 4, 11), LocalDate.of(1452, 4, 15) }, // Leonardo da Vinci, Italian painter, sculptor, and architect (d. 1519) { Symmetry454Date.of(1452, 4, 15), LocalDate.of(1452, 4, 19) }, { Symmetry454Date.of(1492, 10, 10), LocalDate.of(1492, 10, 12) }, // Christopher Columbus's expedition makes landfall in the Caribbean { Symmetry454Date.of(1492, 10, 12), LocalDate.of(1492, 10, 14) }, { Symmetry454Date.of(1564, 2, 20), LocalDate.of(1564, 2, 15) }, // Galileo Galilei, Italian astronomer and physicist (d. 1642) { Symmetry454Date.of(1564, 2, 15), LocalDate.of(1564, 2, 10) }, { Symmetry454Date.of(1564, 4, 28), LocalDate.of(1564, 4, 26) }, // William Shakespeare is baptized in Stratford-upon-Avon, Warwickshire, England (date of actual birth is unknown, d. 1616). { Symmetry454Date.of(1564, 4, 26), LocalDate.of(1564, 4, 24) }, { Symmetry454Date.of(1643, 1, 7), LocalDate.of(1643, 1, 4) }, // Sir Isaac Newton, English physicist and mathematician (d. 1727) { Symmetry454Date.of(1643, 1, 4), LocalDate.of(1643, 1, 1) }, { Symmetry454Date.of(1707, 4, 12), LocalDate.of(1707, 4, 15) }, // Leonhard Euler, Swiss mathematician and physicist (d. 1783) { Symmetry454Date.of(1707, 4, 15), LocalDate.of(1707, 4, 18) }, { Symmetry454Date.of(1789, 7, 16), LocalDate.of(1789, 7, 14) }, // French Revolution: Citizens of Paris storm the Bastille. { Symmetry454Date.of(1789, 7, 14), LocalDate.of(1789, 7, 12) }, { Symmetry454Date.of(1879, 3, 12), LocalDate.of(1879, 3, 14) }, // Albert Einstein, German theoretical physicist (d. 1955). { Symmetry454Date.of(1879, 3, 14), LocalDate.of(1879, 3, 16) }, { Symmetry454Date.of(1941, 9, 9), LocalDate.of(1941, 9, 9) }, // Dennis MacAlistair Ritchie, American computer scientist (d. 2011) { Symmetry454Date.of(1970, 1, 4), LocalDate.of(1970, 1, 1) }, // Unix time begins at 00:00:00 UTC/GMT. { Symmetry454Date.of(1970, 1, 1), LocalDate.of(1969, 12, 29) }, { Symmetry454Date.of(1999, 12, 27), LocalDate.of(2000, 1, 1) }, // Start of the 21st century or 3rd millennium { Symmetry454Date.of(2000, 1, 1), LocalDate.of(2000, 1, 3) }, }; } @Test @UseDataProvider("data_samples") public void test_LocalDate_from_Symmetry454Date(Symmetry454Date sym454, LocalDate iso) { assertEquals(iso, LocalDate.from(sym454)); } @Test @UseDataProvider("data_samples") public void test_Symmetry454Date_from_LocalDate(Symmetry454Date sym454, LocalDate iso) { assertEquals(sym454, Symmetry454Date.from(iso)); } @Test @UseDataProvider("data_samples") public void test_Symmetry454Date_chronology_dateEpochDay(Symmetry454Date sym454, LocalDate iso) { assertEquals(sym454, Symmetry454Chronology.INSTANCE.dateEpochDay(iso.toEpochDay())); } @Test @UseDataProvider("data_samples") public void test_Symmetry454Date_toEpochDay(Symmetry454Date sym454, LocalDate iso) { assertEquals(iso.toEpochDay(), sym454.toEpochDay()); } @Test @UseDataProvider("data_samples") public void test_Symmetry454Date_until_Symmetry454Date(Symmetry454Date sym454, LocalDate iso) { assertEquals(Symmetry454Chronology.INSTANCE.period(0, 0, 0), sym454.until(sym454)); } @Test @UseDataProvider("data_samples") public void test_Symmetry454Date_until_LocalDate(Symmetry454Date sym454, LocalDate iso) { assertEquals(Symmetry454Chronology.INSTANCE.period(0, 0, 0), sym454.until(iso)); } @Test @UseDataProvider("data_samples") public void test_Chronology_date_Temporal(Symmetry454Date sym454, LocalDate iso) { assertEquals(sym454, Symmetry454Chronology.INSTANCE.date(iso)); } @Test @UseDataProvider("data_samples") public void test_LocalDate_until_Symmetry454Date(Symmetry454Date sym454, LocalDate iso) { assertEquals(Period.ZERO, iso.until(sym454)); } @Test @UseDataProvider("data_samples") public void test_plusDays(Symmetry454Date sym454, LocalDate iso) { assertEquals(iso, LocalDate.from(sym454.plus(0, DAYS))); assertEquals(iso.plusDays(1), LocalDate.from(sym454.plus(1, DAYS))); assertEquals(iso.plusDays(35), LocalDate.from(sym454.plus(35, DAYS))); assertEquals(iso.plusDays(-1), LocalDate.from(sym454.plus(-1, DAYS))); assertEquals(iso.plusDays(-60), LocalDate.from(sym454.plus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_minusDays(Symmetry454Date sym454, LocalDate iso) { assertEquals(iso, LocalDate.from(sym454.minus(0, DAYS))); assertEquals(iso.minusDays(1), LocalDate.from(sym454.minus(1, DAYS))); assertEquals(iso.minusDays(35), LocalDate.from(sym454.minus(35, DAYS))); assertEquals(iso.minusDays(-1), LocalDate.from(sym454.minus(-1, DAYS))); assertEquals(iso.minusDays(-60), LocalDate.from(sym454.minus(-60, DAYS))); } @Test @UseDataProvider("data_samples") public void test_until_DAYS(Symmetry454Date sym454, LocalDate iso) { assertEquals(0, sym454.until(iso.plusDays(0), DAYS)); assertEquals(1, sym454.until(iso.plusDays(1), DAYS)); assertEquals(35, sym454.until(iso.plusDays(35), DAYS)); assertEquals(-40, sym454.until(iso.minusDays(40), DAYS)); } @DataProvider public static Object[][] data_badDates() { return new Object[][] { {-1, 13, 28}, {-1, 13, 29}, {2000, -2, 1}, {2000, 13, 1}, {2000, 15, 1}, {2000, 1, -1}, {2000, 1, 0}, {2000, 0, 1}, {2000, -1, 0}, {2000, -1, 1}, {2000, 1, 29}, {2000, 2, 36}, {2000, 3, 29}, {2000, 4, 29}, {2000, 5, 36}, {2000, 6, 29}, {2000, 7, 29}, {2000, 8, 36}, {2000, 9, 29}, {2000, 10, 29}, {2000, 11, 36}, {2000, 12, 29}, {2004, 12, 36}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badDates") public void test_badDates(int year, int month, int dom) { Symmetry454Date.of(year, month, dom); } @DataProvider public static Object[][] data_badLeapDates() { return new Object[][] { {1}, {100}, {200}, {2000} }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_badLeapDates") public void test_badLeapDayDates(int year) { Symmetry454Date.of(year, 12, 29); } @Test(expected = DateTimeException.class) public void test_chronology_dateYearDay_badDate() { Symmetry454Chronology.INSTANCE.dateYearDay(2000, 365); } //----------------------------------------------------------------------- // Symmetry454Date.isLeapYear //----------------------------------------------------------------------- @Test public void test_isLeapYear_specific() { assertTrue(Symmetry454Chronology.INSTANCE.isLeapYear(3)); assertFalse(Symmetry454Chronology.INSTANCE.isLeapYear(6)); assertTrue(Symmetry454Chronology.INSTANCE.isLeapYear(9)); assertFalse(Symmetry454Chronology.INSTANCE.isLeapYear(2000)); assertTrue(Symmetry454Chronology.INSTANCE.isLeapYear(2004)); } //----------------------------------------------------------------------- // Symmetry454Date.isLeapWeek //----------------------------------------------------------------------- @Test public void test_leapWeek() { assertTrue(Symmetry454Date.of (2015, 12, 29).isLeapWeek()); assertTrue(Symmetry454Date.of (2015, 12, 30).isLeapWeek()); assertTrue(Symmetry454Date.of (2015, 12, 31).isLeapWeek()); assertTrue(Symmetry454Date.of (2015, 12, 32).isLeapWeek()); assertTrue(Symmetry454Date.of (2015, 12, 33).isLeapWeek()); assertTrue(Symmetry454Date.of (2015, 12, 34).isLeapWeek()); assertTrue(Symmetry454Date.of (2015, 12, 35).isLeapWeek()); } //----------------------------------------------------------------------- // Symmetry454Date.lengthOfMonth //----------------------------------------------------------------------- @DataProvider public static Object[][] data_lengthOfMonth() { return new Object[][] { {2000, 1, 28, 28}, {2000, 2, 28, 35}, {2000, 3, 28, 28}, {2000, 4, 28, 28}, {2000, 5, 28, 35}, {2000, 6, 28, 28}, {2000, 7, 28, 28}, {2000, 8, 28, 35}, {2000, 9, 28, 28}, {2000, 10, 28, 28}, {2000, 11, 28, 35}, {2000, 12, 28, 28}, {2004, 12, 20, 35}, }; } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonth(int year, int month, int day, int length) { assertEquals(length, Symmetry454Date.of(year, month, day).lengthOfMonth()); } @Test @UseDataProvider("data_lengthOfMonth") public void test_lengthOfMonthFirst(int year, int month, int day, int length) { assertEquals(length, Symmetry454Date.of(year, month, 1).lengthOfMonth()); } @Test public void test_lengthOfMonth_specific() { assertEquals(28, Symmetry454Date.of(2000, 12, 28).lengthOfMonth()); assertEquals(35, Symmetry454Date.of(2004, 12, 28).lengthOfMonth()); } //----------------------------------------------------------------------- // Symmetry454Date.era // Symmetry454Date.prolepticYear // Symmetry454Date.dateYearDay //----------------------------------------------------------------------- @Test public void test_era_loop() { for (int year = 1; year < 200; year++) { Symmetry454Date base = Symmetry454Chronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); IsoEra era = IsoEra.CE; assertEquals(era, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); Symmetry454Date eraBased = Symmetry454Chronology.INSTANCE.date(era, year, 1, 1); assertEquals(base, eraBased); } for (int year = -200; year < 0; year++) { Symmetry454Date base = Symmetry454Chronology.INSTANCE.date(year, 1, 1); assertEquals(year, base.get(YEAR)); IsoEra era = IsoEra.BCE; assertEquals(era, base.getEra()); assertEquals(1 - year, base.get(YEAR_OF_ERA)); Symmetry454Date eraBased = Symmetry454Chronology.INSTANCE.date(era, year, 1, 1); assertEquals(base, eraBased); } } @Test public void test_era_yearDay_loop() { for (int year = 1; year < 200; year++) { Symmetry454Date base = Symmetry454Chronology.INSTANCE.dateYearDay(year, 1); assertEquals(year, base.get(YEAR)); IsoEra era = IsoEra.CE; assertEquals(era, base.getEra()); assertEquals(year, base.get(YEAR_OF_ERA)); Symmetry454Date eraBased = Symmetry454Chronology.INSTANCE.dateYearDay(era, year, 1); assertEquals(base, eraBased); } } @Test public void test_prolepticYear_specific() { assertEquals(4, Symmetry454Chronology.INSTANCE.prolepticYear(IsoEra.CE, 4)); assertEquals(3, Symmetry454Chronology.INSTANCE.prolepticYear(IsoEra.CE, 3)); assertEquals(2, Symmetry454Chronology.INSTANCE.prolepticYear(IsoEra.CE, 2)); assertEquals(1, Symmetry454Chronology.INSTANCE.prolepticYear(IsoEra.CE, 1)); assertEquals(2000, Symmetry454Chronology.INSTANCE.prolepticYear(IsoEra.CE, 2000)); assertEquals(1582, Symmetry454Chronology.INSTANCE.prolepticYear(IsoEra.CE, 1582)); } @DataProvider public static Object[][] data_prolepticYear_badEra() { return new Era[][] { { AccountingEra.BCE }, { AccountingEra.CE }, { CopticEra.BEFORE_AM }, { CopticEra.AM }, { DiscordianEra.YOLD }, { EthiopicEra.BEFORE_INCARNATION }, { EthiopicEra.INCARNATION }, { HijrahEra.AH }, { InternationalFixedEra.CE }, { JapaneseEra.MEIJI }, { JapaneseEra.TAISHO }, { JapaneseEra.SHOWA }, { JapaneseEra.HEISEI }, { JulianEra.BC }, { JulianEra.AD }, { MinguoEra.BEFORE_ROC }, { MinguoEra.ROC }, { PaxEra.BCE }, { PaxEra.CE }, { ThaiBuddhistEra.BEFORE_BE }, { ThaiBuddhistEra.BE }, }; } @Test(expected = ClassCastException.class) @UseDataProvider("data_prolepticYear_badEra") public void test_prolepticYear_badEra(Era era) { Symmetry454Chronology.INSTANCE.prolepticYear(era, 4); } @Test public void test_Chronology_eraOf() { assertEquals(IsoEra.BCE, Symmetry454Chronology.INSTANCE.eraOf(0)); assertEquals(IsoEra.CE, Symmetry454Chronology.INSTANCE.eraOf(1)); } @Test(expected = DateTimeException.class) public void test_Chronology_eraOf_invalid() { Symmetry454Chronology.INSTANCE.eraOf(2); } @Test public void test_Chronology_eras() { List eras = Symmetry454Chronology.INSTANCE.eras(); assertEquals(2, eras.size()); assertTrue(eras.contains(IsoEra.BCE)); assertTrue(eras.contains(IsoEra.CE)); } //----------------------------------------------------------------------- // Chronology.range //----------------------------------------------------------------------- @Test public void test_Chronology_range() { assertEquals(ValueRange.of(1, 7), Symmetry454Chronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_MONTH)); assertEquals(ValueRange.of(1, 7), Symmetry454Chronology.INSTANCE.range(ALIGNED_DAY_OF_WEEK_IN_YEAR)); assertEquals(ValueRange.of(1, 4, 5), Symmetry454Chronology.INSTANCE.range(ALIGNED_WEEK_OF_MONTH)); assertEquals(ValueRange.of(1, 52, 53), Symmetry454Chronology.INSTANCE.range(ALIGNED_WEEK_OF_YEAR)); assertEquals(ValueRange.of(1, 7), Symmetry454Chronology.INSTANCE.range(DAY_OF_WEEK)); assertEquals(ValueRange.of(1, 28, 35), Symmetry454Chronology.INSTANCE.range(DAY_OF_MONTH)); assertEquals(ValueRange.of(1, 364, 371), Symmetry454Chronology.INSTANCE.range(DAY_OF_YEAR)); assertEquals(ValueRange.of(0, 1), Symmetry454Chronology.INSTANCE.range(ERA)); assertEquals(ValueRange.of(-1_000_000 * 364L - 177_474 * 7 - 719_162, 1_000_000 * 364L + 177_474 * 7 - 719_162), Symmetry454Chronology.INSTANCE.range(EPOCH_DAY)); assertEquals(ValueRange.of(1, 12), Symmetry454Chronology.INSTANCE.range(MONTH_OF_YEAR)); assertEquals(ValueRange.of(-12_000_000L, 11_999_999L), Symmetry454Chronology.INSTANCE.range(PROLEPTIC_MONTH)); assertEquals(ValueRange.of(-1_000_000L, 1_000_000), Symmetry454Chronology.INSTANCE.range(YEAR)); assertEquals(ValueRange.of(-1_000_000, 1_000_000), Symmetry454Chronology.INSTANCE.range(YEAR_OF_ERA)); } //----------------------------------------------------------------------- // Symmetry454Date.range //----------------------------------------------------------------------- @DataProvider public static Object[][] data_ranges() { return new Object[][] { // Leap Day and Year Day are members of months {2012, 1, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 2, 23, DAY_OF_MONTH, ValueRange.of(1, 35)}, {2012, 3, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 4, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 5, 23, DAY_OF_MONTH, ValueRange.of(1, 35)}, {2012, 6, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 7, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 8, 23, DAY_OF_MONTH, ValueRange.of(1, 35)}, {2012, 9, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 10, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2012, 11, 23, DAY_OF_MONTH, ValueRange.of(1, 35)}, {2012, 12, 23, DAY_OF_MONTH, ValueRange.of(1, 28)}, {2015, 12, 23, DAY_OF_MONTH, ValueRange.of(1, 35)}, {2012, 1, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 6, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 12, 23, DAY_OF_WEEK, ValueRange.of(1, 7)}, {2012, 1, 23, DAY_OF_YEAR, ValueRange.of(1, 364)}, {2015, 1, 23, DAY_OF_YEAR, ValueRange.of(1, 371)}, {2012, 1, 23, MONTH_OF_YEAR, ValueRange.of(1, 12)}, {2012, 1, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 6, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 12, 23, ALIGNED_DAY_OF_WEEK_IN_MONTH, ValueRange.of(1, 7)}, {2012, 1, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 4)}, {2012, 2, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 5)}, {2015, 12, 23, ALIGNED_WEEK_OF_MONTH, ValueRange.of(1, 5)}, {2012, 1, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 6, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 12, 23, ALIGNED_DAY_OF_WEEK_IN_YEAR, ValueRange.of(1, 7)}, {2012, 1, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2012, 6, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2012, 12, 23, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 52)}, {2015, 12, 30, ALIGNED_WEEK_OF_YEAR, ValueRange.of(1, 53)}, }; } @Test @UseDataProvider("data_ranges") public void test_range(int year, int month, int dom, TemporalField field, ValueRange range) { assertEquals(range, Symmetry454Date.of(year, month, dom).range(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_range_unsupported() { Symmetry454Date.of(2012, 6, 28).range(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // Symmetry454Date.getLong //----------------------------------------------------------------------- @DataProvider public static Object[][] data_getLong() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 5}, {2014, 5, 26, DAY_OF_MONTH, 26}, {2014, 5, 26, DAY_OF_YEAR, 28 + 35 + 28 + 28 + 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 4 + 5 + 4 + 4 + 4}, {2014, 5, 26, MONTH_OF_YEAR, 5}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1}, {2014, 5, 26, YEAR, 2014}, {2014, 5, 26, ERA, 1}, {1, 5, 8, ERA, 1}, {2012, 9, 26, DAY_OF_WEEK, 5}, {2012, 9, 26, DAY_OF_YEAR, 3 * (4 + 5 + 4) * 7 - 2}, {2012, 9, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5}, {2012, 9, 26, ALIGNED_WEEK_OF_MONTH, 4}, {2012, 9, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5}, {2012, 9, 26, ALIGNED_WEEK_OF_YEAR, 3 * (4 + 5 + 4)}, {2015, 12, 35, DAY_OF_WEEK, 7}, {2015, 12, 35, DAY_OF_MONTH, 35}, {2015, 12, 35, DAY_OF_YEAR, 4 * (4 + 5 + 4) * 7 + 7}, {2015, 12, 35, ALIGNED_DAY_OF_WEEK_IN_MONTH, 7}, {2015, 12, 35, ALIGNED_WEEK_OF_MONTH, 5}, {2015, 12, 35, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7}, {2015, 12, 35, ALIGNED_WEEK_OF_YEAR, 53}, {2015, 12, 35, MONTH_OF_YEAR, 12}, {2015, 12, 35, PROLEPTIC_MONTH, 2016 * 12 - 1}, }; } @Test @UseDataProvider("data_getLong") public void test_getLong(int year, int month, int dom, TemporalField field, long expected) { assertEquals(expected, Symmetry454Date.of(year, month, dom).getLong(field)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_getLong_unsupported() { Symmetry454Date.of(2012, 6, 28).getLong(MINUTE_OF_DAY); } //----------------------------------------------------------------------- // Symmetry454Date.with //----------------------------------------------------------------------- @DataProvider public static Object[][] data_with() { return new Object[][] { {2014, 5, 26, DAY_OF_WEEK, 1, 2014, 5, 22}, {2014, 5, 26, DAY_OF_WEEK, 5, 2014, 5, 26}, {2014, 5, 26, DAY_OF_MONTH, 28, 2014, 5, 28}, {2014, 5, 26, DAY_OF_MONTH, 26, 2014, 5, 26}, {2014, 5, 26, DAY_OF_YEAR, 364, 2014, 12, 28}, {2014, 5, 26, DAY_OF_YEAR, 138, 2014, 5, 19}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2014, 5, 24}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 1, 2014, 5, 5}, {2014, 5, 26, ALIGNED_WEEK_OF_MONTH, 4, 2014, 5, 26}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2014, 5, 23}, {2014, 5, 26, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 23, 2014, 6, 19}, {2014, 5, 26, ALIGNED_WEEK_OF_YEAR, 20, 2014, 5, 26}, {2014, 5, 26, MONTH_OF_YEAR, 4, 2014, 4, 26}, {2014, 5, 26, MONTH_OF_YEAR, 5, 2014, 5, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2013 * 12 + 3 - 1, 2013, 3, 26}, {2014, 5, 26, PROLEPTIC_MONTH, 2014 * 12 + 5 - 1, 2014, 5, 26}, {2014, 5, 26, YEAR, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR, 2014, 2014, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2012, 2012, 5, 26}, {2014, 5, 26, YEAR_OF_ERA, 2014, 2014, 5, 26}, {2014, 5, 26, ERA, 1, 2014, 5, 26}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 1, 2015, 12, 22}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 2, 2015, 12, 23}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, 2015, 12, 24}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 4, 2015, 12, 25}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 5, 2015, 12, 26}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 6, 2015, 12, 27}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_MONTH, 7, 2015, 12, 28}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 1, 2015, 12, 22}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 2, 2015, 12, 23}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 3, 2015, 12, 24}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, 2015, 12, 25}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 5, 2015, 12, 26}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 6, 2015, 12, 27}, {2015, 12, 22, ALIGNED_DAY_OF_WEEK_IN_YEAR, 7, 2015, 12, 28}, {2015, 12, 29, ALIGNED_WEEK_OF_MONTH, 0, 2015, 12, 29}, {2015, 12, 29, ALIGNED_WEEK_OF_MONTH, 3, 2015, 12, 15}, {2015, 12, 29, ALIGNED_WEEK_OF_YEAR, 0, 2015, 12, 29}, {2015, 12, 29, ALIGNED_WEEK_OF_YEAR, 3, 2015, 1, 15}, {2015, 12, 29, DAY_OF_WEEK, 0, 2015, 12, 29}, {2015, 12, 28, DAY_OF_WEEK, 1, 2015, 12, 22}, {2015, 12, 28, DAY_OF_WEEK, 2, 2015, 12, 23}, {2015, 12, 28, DAY_OF_WEEK, 3, 2015, 12, 24}, {2015, 12, 28, DAY_OF_WEEK, 4, 2015, 12, 25}, {2015, 12, 28, DAY_OF_WEEK, 5, 2015, 12, 26}, {2015, 12, 28, DAY_OF_WEEK, 6, 2015, 12, 27}, {2015, 12, 28, DAY_OF_WEEK, 7, 2015, 12, 28}, {2015, 12, 29, DAY_OF_MONTH, 1, 2015, 12, 1}, {2015, 12, 29, DAY_OF_MONTH, 3, 2015, 12, 3}, {2015, 12, 29, MONTH_OF_YEAR, 1, 2015, 1, 28}, {2015, 12, 29, MONTH_OF_YEAR, 12, 2015, 12, 29}, {2015, 12, 29, MONTH_OF_YEAR, 2, 2015, 2, 29}, {2015, 12, 29, YEAR, 2014, 2014, 12, 28}, {2015, 12, 29, YEAR, 2013, 2013, 12, 28}, {2014, 3, 28, DAY_OF_MONTH, 1, 2014, 3, 1}, {2014, 1, 28, DAY_OF_MONTH, 1, 2014, 1, 1}, {2014, 3, 28, MONTH_OF_YEAR, 1, 2014, 1, 28}, {2015, 3, 28, DAY_OF_YEAR, 371, 2015, 12, 35}, {2012, 3, 28, DAY_OF_YEAR, 364, 2012, 12, 28}, }; } @Test @UseDataProvider("data_with") public void test_with_TemporalField(int year, int month, int dom, TemporalField field, long value, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(Symmetry454Date.of(expectedYear, expectedMonth, expectedDom), Symmetry454Date.of(year, month, dom).with(field, value)); } @DataProvider public static Object[][] data_with_bad() { return new Object[][] { {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, -1}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_MONTH, 8}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, -1}, {2013, 1, 1, ALIGNED_DAY_OF_WEEK_IN_YEAR, 8}, {2013, 1, 1, ALIGNED_WEEK_OF_MONTH, -1}, {2013, 1, 1, ALIGNED_WEEK_OF_MONTH, 5}, {2013, 2, 1, ALIGNED_WEEK_OF_MONTH, 6}, {2013, 1, 1, ALIGNED_WEEK_OF_YEAR, -1}, {2013, 1, 1, ALIGNED_WEEK_OF_YEAR, 53}, {2015, 1, 1, ALIGNED_WEEK_OF_YEAR, 54}, {2013, 1, 1, DAY_OF_WEEK, -1}, {2013, 1, 1, DAY_OF_WEEK, 8}, {2013, 1, 1, DAY_OF_MONTH, -1}, {2013, 1, 1, DAY_OF_MONTH, 29}, {2013, 6, 1, DAY_OF_MONTH, 29}, {2013, 12, 1, DAY_OF_MONTH, 30}, {2015, 12, 1, DAY_OF_MONTH, 36}, {2013, 1, 1, DAY_OF_YEAR, -1}, {2013, 1, 1, DAY_OF_YEAR, 365}, {2015, 1, 1, DAY_OF_YEAR, 372}, {2013, 1, 1, MONTH_OF_YEAR, -1}, {2013, 1, 1, MONTH_OF_YEAR, 14}, {2013, 1, 1, MONTH_OF_YEAR, -2}, {2013, 1, 1, MONTH_OF_YEAR, 14}, {2013, 1, 1, EPOCH_DAY, -365_961_481}, {2013, 1, 1, EPOCH_DAY, 364_523_156}, {2013, 1, 1, YEAR, -1_000_001}, {2013, 1, 1, YEAR, 1_000_001}, }; } @Test(expected = DateTimeException.class) @UseDataProvider("data_with_bad") public void test_with_TemporalField_badValue(int year, int month, int dom, TemporalField field, long value) { Symmetry454Date.of(year, month, dom).with(field, value); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_with_TemporalField_unsupported() { Symmetry454Date.of(2012, 6, 28).with(MINUTE_OF_DAY, 10); } //----------------------------------------------------------------------- // Symmetry454Date.with(TemporalAdjuster) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_temporalAdjusters_lastDayOfMonth() { return new Object[][] { {2012, 1, 23, 2012, 1, 28}, {2012, 2, 23, 2012, 2, 35}, {2012, 3, 23, 2012, 3, 28}, {2012, 4, 23, 2012, 4, 28}, {2012, 5, 23, 2012, 5, 35}, {2012, 6, 23, 2012, 6, 28}, {2012, 7, 23, 2012, 7, 28}, {2012, 8, 23, 2012, 8, 35}, {2012, 9, 23, 2012, 9, 28}, {2012, 10, 23, 2012,10, 28}, {2012, 11, 23, 2012, 11, 35}, {2012, 12, 23, 2012, 12, 28}, {2009, 12, 23, 2009, 12, 35}, }; } @Test @UseDataProvider("data_temporalAdjusters_lastDayOfMonth") public void test_temporalAdjusters_LastDayOfMonth(int year, int month, int day, int expectedYear, int expectedMonth, int expectedDay) { Symmetry454Date base = Symmetry454Date.of(year, month, day); Symmetry454Date expected = Symmetry454Date.of(expectedYear, expectedMonth, expectedDay); Symmetry454Date actual = base.with(TemporalAdjusters.lastDayOfMonth()); assertEquals(expected, actual); } //----------------------------------------------------------------------- // Symmetry454Date.with(Local*) //----------------------------------------------------------------------- @Test public void test_adjust_toLocalDate() { Symmetry454Date sym454 = Symmetry454Date.of(2000, 1, 4); Symmetry454Date test = sym454.with(LocalDate.of(2012, 7, 6)); assertEquals(Symmetry454Date.of(2012, 7, 5), test); } @Test(expected = DateTimeException.class) public void test_adjust_toMonth() { Symmetry454Date sym454 = Symmetry454Date.of(2000, 1, 4); sym454.with(Month.APRIL); } //----------------------------------------------------------------------- // LocalDate.with(Symmetry454Date) //----------------------------------------------------------------------- @Test public void test_LocalDate_adjustToSymmetry454Date() { Symmetry454Date sym454 = Symmetry454Date.of(2012, 7, 19); LocalDate test = LocalDate.MIN.with(sym454); assertEquals(LocalDate.of(2012, 7, 20), test); } @Test public void test_LocalDateTime_adjustToSymmetry454Date() { Symmetry454Date sym454 = Symmetry454Date.of(2012, 7, 19); LocalDateTime test = LocalDateTime.MIN.with(sym454); assertEquals(LocalDateTime.of(2012, 7, 20, 0, 0), test); } //----------------------------------------------------------------------- // Symmetry454Date.plus // Symmetry454Date.minus //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {2014, 5, 26, 0, DAYS, 2014, 5, 26}, {2014, 5, 26, 8, DAYS, 2014, 5, 34}, {2014, 5, 26, -3, DAYS, 2014, 5, 23}, {2014, 5, 26, 0, WEEKS, 2014, 5, 26}, {2014, 5, 26, 3, WEEKS, 2014, 6, 12}, {2014, 5, 26, -5, WEEKS, 2014, 4, 19}, {2014, 5, 26, 0, MONTHS, 2014, 5, 26}, {2014, 5, 26, 3, MONTHS, 2014, 8, 26}, {2014, 5, 26, -5, MONTHS, 2013, 12, 26}, {2014, 5, 26, 0, YEARS, 2014, 5, 26}, {2014, 5, 26, 3, YEARS, 2017, 5, 26}, {2014, 5, 26, -5, YEARS, 2009, 5, 26}, {2014, 5, 26, 0, DECADES, 2014, 5, 26}, {2014, 5, 26, 3, DECADES, 2044, 5, 26}, {2014, 5, 26, -5, DECADES, 1964, 5, 26}, {2014, 5, 26, 0, CENTURIES, 2014, 5, 26}, {2014, 5, 26, 3, CENTURIES, 2314, 5, 26}, {2014, 5, 26, -5, CENTURIES, 1514, 5, 26}, {2014, 5, 26, 0, MILLENNIA, 2014, 5, 26}, {2014, 5, 26, 3, MILLENNIA, 5014, 5, 26}, {2014, 5, 26, -1, MILLENNIA, 2014 - 1000, 5, 26}, {2014, 12, 26, 3, WEEKS, 2015, 1, 19}, {2014, 1, 26, -5, WEEKS, 2013, 12, 19}, {2012, 6, 26, 3, WEEKS, 2012, 7, 19}, {2012, 7, 26, -5, WEEKS, 2012, 6, 19}, {2012, 6, 21, 52 + 1, WEEKS, 2013, 6, 28}, {2013, 6, 21, 6 * 52 + 1, WEEKS, 2019, 6, 21}, }; } @DataProvider public static Object[][] data_plus_leapWeek() { return new Object[][] { {2015, 12, 28, 0, DAYS, 2015, 12, 28}, {2015, 12, 28, 8, DAYS, 2016, 1, 1}, {2015, 12, 28, -3, DAYS, 2015, 12, 25}, {2015, 12, 28, 0, WEEKS, 2015, 12, 28}, {2015, 12, 28, 3, WEEKS, 2016, 1, 14}, {2015, 12, 28, -5, WEEKS, 2015, 11, 28}, {2015, 12, 28, 52, WEEKS, 2016, 12, 21}, {2015, 12, 28, 0, MONTHS, 2015, 12, 28}, {2015, 12, 28, 3, MONTHS, 2016, 3, 28}, {2015, 12, 28, -5, MONTHS, 2015, 7, 28}, {2015, 12, 28, 12, MONTHS, 2016, 12, 28}, {2015, 12, 28, 0, YEARS, 2015, 12, 28}, {2015, 12, 28, 3, YEARS, 2018, 12, 28}, {2015, 12, 28, -5, YEARS, 2010, 12, 28}, }; } @Test @UseDataProvider("data_plus") public void test_plus_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(Symmetry454Date.of(expectedYear, expectedMonth, expectedDom), Symmetry454Date.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus_leapWeek") public void test_plus_leapWeek_TemporalUnit(int year, int month, int dom, long amount, TemporalUnit unit, int expectedYear, int expectedMonth, int expectedDom) { assertEquals(Symmetry454Date.of(expectedYear, expectedMonth, expectedDom), Symmetry454Date.of(year, month, dom).plus(amount, unit)); } @Test @UseDataProvider("data_plus") public void test_minus_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(Symmetry454Date.of(expectedYear, expectedMonth, expectedDom), Symmetry454Date.of(year, month, dom).minus(amount, unit)); } @Test @UseDataProvider("data_plus_leapWeek") public void test_minus_leapWeek_TemporalUnit( int expectedYear, int expectedMonth, int expectedDom, long amount, TemporalUnit unit, int year, int month, int dom) { assertEquals(Symmetry454Date.of(expectedYear, expectedMonth, expectedDom), Symmetry454Date.of(year, month, dom).minus(amount, unit)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_plus_TemporalUnit_unsupported() { Symmetry454Date.of(2012, 6, 28).plus(0, MINUTES); } //----------------------------------------------------------------------- // Symmetry454Date.until //----------------------------------------------------------------------- @DataProvider public static Object[][] data_until() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, DAYS, 0}, {2014, 5, 26, 2014, 6, 4, DAYS, 13}, {2014, 5, 26, 2014, 5, 20, DAYS, -6}, {2014, 5, 26, 2014, 5, 26, WEEKS, 0}, {2014, 5, 26, 2014, 6, 4, WEEKS, 0}, {2014, 5, 26, 2014, 6, 5, WEEKS, 1}, {2014, 5, 26, 2014, 5, 26, MONTHS, 0}, {2014, 5, 26, 2014, 6, 25, MONTHS, 0}, {2014, 5, 26, 2014, 6, 26, MONTHS, 1}, {2014, 5, 26, 2014, 5, 26, YEARS, 0}, {2014, 5, 26, 2015, 5, 25, YEARS, 0}, {2014, 5, 26, 2015, 5, 26, YEARS, 1}, {2014, 5, 26, 2014, 5, 26, DECADES, 0}, {2014, 5, 26, 2024, 5, 25, DECADES, 0}, {2014, 5, 26, 2024, 5, 26, DECADES, 1}, {2014, 5, 26, 2014, 5, 26, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 25, CENTURIES, 0}, {2014, 5, 26, 2114, 5, 26, CENTURIES, 1}, {2014, 5, 26, 2014, 5, 26, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 25, MILLENNIA, 0}, {2014, 5, 26, 3014, 5, 26, MILLENNIA, 1}, {2014, 5, 26, 3014, 5, 26, ERAS, 0}, }; } @DataProvider public static Object[][] data_until_period() { return new Object[][] { {2014, 5, 26, 2014, 5, 26, 0, 0, 0}, {2014, 5, 26, 2014, 6, 4, 0, 0, 13}, {2014, 5, 26, 2014, 5, 20, 0, 0, -6}, {2014, 5, 26, 2014, 6, 5, 0, 0, 14}, {2014, 5, 26, 2014, 6, 25, 0, 0, 34}, {2014, 5, 26, 2014, 6, 26, 0, 1, 0}, {2014, 5, 26, 2015, 5, 25, 0, 11, 27}, {2014, 5, 26, 2015, 5, 26, 1, 0, 0}, {2014, 5, 26, 2024, 5, 25, 9, 11, 27}, }; } @Test @UseDataProvider("data_until") public void test_until_TemporalUnit( int year1, int month1, int dom1, int year2, int month2, int dom2, TemporalUnit unit, long expected) { Symmetry454Date start = Symmetry454Date.of(year1, month1, dom1); Symmetry454Date end = Symmetry454Date.of(year2, month2, dom2); assertEquals(expected, start.until(end, unit)); } @Test @UseDataProvider("data_until_period") public void test_until_end( int year1, int month1, int dom1, int year2, int month2, int dom2, int yearPeriod, int monthPeriod, int dayPeriod) { Symmetry454Date start = Symmetry454Date.of(year1, month1, dom1); Symmetry454Date end = Symmetry454Date.of(year2, month2, dom2); ChronoPeriod period = Symmetry454Chronology.INSTANCE.period(yearPeriod, monthPeriod, dayPeriod); assertEquals(period, start.until(end)); } @Test(expected = UnsupportedTemporalTypeException.class) public void test_until_TemporalUnit_unsupported() { Symmetry454Date start = Symmetry454Date.of(2012, 6, 28); Symmetry454Date end = Symmetry454Date.of(2012, 7, 1); start.until(end, MINUTES); } //----------------------------------------------------------------------- // Symmetry454Date.period //----------------------------------------------------------------------- @Test public void test_plus_Period() { assertEquals(Symmetry454Date.of(2014, 8, 1), Symmetry454Date.of(2014, 5, 21).plus(Symmetry454Chronology.INSTANCE.period(0, 2, 8))); } @Test(expected = DateTimeException.class) public void test_plus_Period_ISO() { assertEquals(Symmetry454Date.of(2014, 7, 26), Symmetry454Date.of(2014, 5, 26).plus(Period.ofMonths(2))); } @Test public void test_minus_Period() { assertEquals(Symmetry454Date.of(2014, 3, 23), Symmetry454Date.of(2014, 5, 26).minus(Symmetry454Chronology.INSTANCE.period(0, 2, 3))); } @Test(expected = DateTimeException.class) public void test_minus_Period_ISO() { assertEquals(Symmetry454Date.of(2014, 3, 26), Symmetry454Date.of(2014, 5, 26).minus(Period.ofMonths(2))); } //----------------------------------------------------------------------- // Symmetry454Date.equals //----------------------------------------------------------------------- @DataProvider public static Object[][] data_equals() { return new Object[][] { {Symmetry454Date.of(2000, 1, 3), Symmetry454Date.of(2000, 1, 4), Symmetry454Date.of(2000, 2, 3), Symmetry454Date.of(2001, 1, 3)}, {Symmetry454Date.of(2000, 12, 28), Symmetry454Date.of(2000, 12, 25), Symmetry454Date.of(2001, 1, 1), Symmetry454Date.of(2001, 12, 28)}, {Symmetry454Date.of(2000, 6, 28), Symmetry454Date.of(2000, 6, 23), Symmetry454Date.of(2000, 7, 1), Symmetry454Date.of(2004, 6, 28)}, }; } @Test @UseDataProvider("data_equals") public void test_equals(Symmetry454Date a1, Symmetry454Date b, Symmetry454Date c, Symmetry454Date d) { assertTrue(a1.equals(a1)); assertFalse(a1.equals(b)); assertFalse(a1.equals(c)); assertFalse(a1.equals(d)); assertFalse(a1.equals(null)); assertFalse("".equals(a1)); } //----------------------------------------------------------------------- // Symmetry454Date.toString //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {Symmetry454Date.of(1, 1, 1), "Sym454 CE 1/01/01"}, {Symmetry454Date.of(1970, 2, 35), "Sym454 CE 1970/02/35"}, {Symmetry454Date.of(2000, 8, 35), "Sym454 CE 2000/08/35"}, {Symmetry454Date.of(1970, 12, 35), "Sym454 CE 1970/12/35"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(Symmetry454Date date, String expected) { assertEquals(expected, date.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/scale/000077500000000000000000000000001343451174100237655ustar00rootroot00000000000000threeten-extra-1.5.0/src/test/java/org/threeten/extra/scale/MockUtcRulesAlwaysLeap.java000066400000000000000000000044341343451174100312000ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; /** * Mock rules that always returns a leap second. */ public class MockUtcRulesAlwaysLeap extends UtcRules { @Override public String getName() { return "Mock"; } @Override public int getLeapSecondAdjustment(long mjDay) { return 1; // always leap } @Override public int getTaiOffset(long mjDay) { return (int) mjDay; } @Override public long[] getLeapSecondDates() { return new long[0]; } @Override public TaiInstant convertToTai(UtcInstant utcInstant) { return null; } @Override public UtcInstant convertToUtc(TaiInstant taiInstant) { return null; } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/scale/MockUtcRulesLeapOn1000.java000066400000000000000000000044771343451174100306240ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; /** * Mock rules that returns a leap second on day 1000. */ public class MockUtcRulesLeapOn1000 extends UtcRules { @Override public String getName() { return "Mock1000"; } @Override public int getLeapSecondAdjustment(long mjDay) { return (mjDay == 1000 ? 1 : 0); } @Override public int getTaiOffset(long mjDay) { return (mjDay <= 1000 ? 10 : 11); } @Override public long[] getLeapSecondDates() { return new long[] {1000}; } @Override public TaiInstant convertToTai(UtcInstant utcInstant) { return null; } @Override public UtcInstant convertToUtc(TaiInstant taiInstant) { return null; } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/scale/TestTaiInstant.java000066400000000000000000001104661343451174100275560ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.Duration; import java.time.Instant; import java.time.format.DateTimeParseException; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test TaiInstant. */ @RunWith(DataProviderRunner.class) public class TestTaiInstant { //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(TaiInstant.class)); assertTrue(Comparable.class.isAssignableFrom(TaiInstant.class)); } //----------------------------------------------------------------------- // serialization //----------------------------------------------------------------------- @Test public void test_serialization() throws Exception { TaiInstant test = TaiInstant.ofTaiSeconds(2, 3); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- // ofTaiSeconds(long,long) //----------------------------------------------------------------------- @Test public void factory_ofTaiSecondslong_long() { for (long i = -2; i <= 2; i++) { for (int j = 0; j < 10; j++) { TaiInstant t = TaiInstant.ofTaiSeconds(i, j); assertEquals(i, t.getTaiSeconds()); assertEquals(j, t.getNano()); } for (int j = -10; j < 0; j++) { TaiInstant t = TaiInstant.ofTaiSeconds(i, j); assertEquals(i - 1, t.getTaiSeconds()); assertEquals(j + 1000000000, t.getNano()); } for (int j = 999999990; j < 1000000000; j++) { TaiInstant t = TaiInstant.ofTaiSeconds(i, j); assertEquals(i, t.getTaiSeconds()); assertEquals(j, t.getNano()); } } } @Test public void factory_ofTaiSeconds_long_long_nanosNegativeAdjusted() { TaiInstant test = TaiInstant.ofTaiSeconds(2L, -1); assertEquals(1, test.getTaiSeconds()); assertEquals(999999999, test.getNano()); } @Test(expected = ArithmeticException.class) public void factory_ofTaiSeconds_long_long_tooBig() { TaiInstant.ofTaiSeconds(Long.MAX_VALUE, 1000000000); } //----------------------------------------------------------------------- // of(Instant) //----------------------------------------------------------------------- @Test public void factory_of_Instant() { TaiInstant test = TaiInstant.of(Instant.ofEpochSecond(0, 2)); assertEquals((40587L - 36204) * 24 * 60 * 60 + 10, test.getTaiSeconds()); //((1970 - 1958) * 365 + 3) * 24 * 60 * 60 + 10); assertEquals(2, test.getNano()); } @Test(expected = NullPointerException.class) public void factory_of_Instant_null() { TaiInstant.of((Instant) null); } //----------------------------------------------------------------------- // of(UtcInstant) //----------------------------------------------------------------------- @Test public void factory_of_UtcInstant() { for (int i = -1000; i < 1000; i++) { for (int j = 0; j < 10; j++) { TaiInstant test = TaiInstant.of(UtcInstant.ofModifiedJulianDay(36204 + i, j * 1000000000L + 2L)); assertEquals(i * 24 * 60 * 60 + j + 10, test.getTaiSeconds()); assertEquals(2, test.getNano()); } } } @Test(expected = NullPointerException.class) public void factory_of_UtcInstant_null() { TaiInstant.of((UtcInstant) null); } //----------------------------------------------------------------------- // parse(CharSequence) //----------------------------------------------------------------------- @Test public void factory_parse_CharSequence() { for (int i = -1000; i < 1000; i++) { for (int j = 900000000; j < 990000000; j += 10000000) { String str = i + "." + j + "s(TAI)"; TaiInstant test = TaiInstant.parse(str); assertEquals(i, test.getTaiSeconds()); assertEquals(j, test.getNano()); } } } @DataProvider public static Object[][] data_badParse() { return new Object[][] { {"A.123456789s(TAI)"}, {"123.12345678As(TAI)"}, {"123.123456789"}, {"123.123456789s"}, {"+123.123456789s(TAI)"}, {"-123.123s(TAI)"}, }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_badParse") public void factory_parse_CharSequence_invalid(String str) { TaiInstant.parse(str); } @Test(expected = NullPointerException.class) public void factory_parse_CharSequence_null() { TaiInstant.parse((String) null); } //----------------------------------------------------------------------- // withTAISeconds() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_withTAISeconds() { return new Object[][] { {0L, 12345L, 1L, 1L, 12345L}, {0L, 12345L, -1L, -1L, 12345L}, {7L, 12345L, 2L, 2L, 12345L}, {7L, 12345L, -2L, -2L, 12345L}, {-99L, 12345L, 3L, 3L, 12345L}, {-99L, 12345L, -3L, -3L, 12345L}, }; } @Test @UseDataProvider("data_withTAISeconds") public void test_withTAISeconds(long tai, long nanos, long newTai, Long expectedTai, Long expectedNanos) { TaiInstant i = TaiInstant.ofTaiSeconds(tai, nanos).withTaiSeconds(newTai); assertEquals(expectedTai.longValue(), i.getTaiSeconds()); assertEquals(expectedNanos.longValue(), i.getNano()); } //----------------------------------------------------------------------- // withNano() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_withNano() { return new Object[][] { {0L, 12345L, 1, 0L, 1L}, {7L, 12345L, 2, 7L, 2L}, {-99L, 12345L, 3, -99L, 3L}, {-99L, 12345L, 999999999, -99L, 999999999L}, {-99L, 12345L, -1, null, null}, {-99L, 12345L, 1000000000, null, null}, }; } @Test @UseDataProvider("data_withNano") public void test_withNano(long tai, long nanos, int newNano, Long expectedTai, Long expectedNanos) { TaiInstant i = TaiInstant.ofTaiSeconds(tai, nanos); if (expectedTai != null) { i = i.withNano(newNano); assertEquals(expectedTai.longValue(), i.getTaiSeconds()); assertEquals(expectedNanos.longValue(), i.getNano()); } else { try { i = i.withNano(newNano); fail(); } catch (IllegalArgumentException ex) { // expected } } } //----------------------------------------------------------------------- // plus(Duration) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {Long.MIN_VALUE, 0, Long.MAX_VALUE, 0, -1, 0}, {-4, 666666667, -4, 666666667, -7, 333333334}, {-4, 666666667, -3, 0, -7, 666666667}, {-4, 666666667, -2, 0, -6, 666666667}, {-4, 666666667, -1, 0, -5, 666666667}, {-4, 666666667, -1, 333333334, -4, 1}, {-4, 666666667, -1, 666666667, -4, 333333334}, {-4, 666666667, -1, 999999999, -4, 666666666}, {-4, 666666667, 0, 0, -4, 666666667}, {-4, 666666667, 0, 1, -4, 666666668}, {-4, 666666667, 0, 333333333, -3, 0}, {-4, 666666667, 0, 666666666, -3, 333333333}, {-4, 666666667, 1, 0, -3, 666666667}, {-4, 666666667, 2, 0, -2, 666666667}, {-4, 666666667, 3, 0, -1, 666666667}, {-4, 666666667, 3, 333333333, 0, 0}, {-3, 0, -4, 666666667, -7, 666666667}, {-3, 0, -3, 0, -6, 0}, {-3, 0, -2, 0, -5, 0}, {-3, 0, -1, 0, -4, 0}, {-3, 0, -1, 333333334, -4, 333333334}, {-3, 0, -1, 666666667, -4, 666666667}, {-3, 0, -1, 999999999, -4, 999999999}, {-3, 0, 0, 0, -3, 0}, {-3, 0, 0, 1, -3, 1}, {-3, 0, 0, 333333333, -3, 333333333}, {-3, 0, 0, 666666666, -3, 666666666}, {-3, 0, 1, 0, -2, 0}, {-3, 0, 2, 0, -1, 0}, {-3, 0, 3, 0, 0, 0}, {-3, 0, 3, 333333333, 0, 333333333}, {-2, 0, -4, 666666667, -6, 666666667}, {-2, 0, -3, 0, -5, 0}, {-2, 0, -2, 0, -4, 0}, {-2, 0, -1, 0, -3, 0}, {-2, 0, -1, 333333334, -3, 333333334}, {-2, 0, -1, 666666667, -3, 666666667}, {-2, 0, -1, 999999999, -3, 999999999}, {-2, 0, 0, 0, -2, 0}, {-2, 0, 0, 1, -2, 1}, {-2, 0, 0, 333333333, -2, 333333333}, {-2, 0, 0, 666666666, -2, 666666666}, {-2, 0, 1, 0, -1, 0}, {-2, 0, 2, 0, 0, 0}, {-2, 0, 3, 0, 1, 0}, {-2, 0, 3, 333333333, 1, 333333333}, {-1, 0, -4, 666666667, -5, 666666667}, {-1, 0, -3, 0, -4, 0}, {-1, 0, -2, 0, -3, 0}, {-1, 0, -1, 0, -2, 0}, {-1, 0, -1, 333333334, -2, 333333334}, {-1, 0, -1, 666666667, -2, 666666667}, {-1, 0, -1, 999999999, -2, 999999999}, {-1, 0, 0, 0, -1, 0}, {-1, 0, 0, 1, -1, 1}, {-1, 0, 0, 333333333, -1, 333333333}, {-1, 0, 0, 666666666, -1, 666666666}, {-1, 0, 1, 0, 0, 0}, {-1, 0, 2, 0, 1, 0}, {-1, 0, 3, 0, 2, 0}, {-1, 0, 3, 333333333, 2, 333333333}, {-1, 666666667, -4, 666666667, -4, 333333334}, {-1, 666666667, -3, 0, -4, 666666667}, {-1, 666666667, -2, 0, -3, 666666667}, {-1, 666666667, -1, 0, -2, 666666667}, {-1, 666666667, -1, 333333334, -1, 1}, {-1, 666666667, -1, 666666667, -1, 333333334}, {-1, 666666667, -1, 999999999, -1, 666666666}, {-1, 666666667, 0, 0, -1, 666666667}, {-1, 666666667, 0, 1, -1, 666666668}, {-1, 666666667, 0, 333333333, 0, 0}, {-1, 666666667, 0, 666666666, 0, 333333333}, {-1, 666666667, 1, 0, 0, 666666667}, {-1, 666666667, 2, 0, 1, 666666667}, {-1, 666666667, 3, 0, 2, 666666667}, {-1, 666666667, 3, 333333333, 3, 0}, {0, 0, -4, 666666667, -4, 666666667}, {0, 0, -3, 0, -3, 0}, {0, 0, -2, 0, -2, 0}, {0, 0, -1, 0, -1, 0}, {0, 0, -1, 333333334, -1, 333333334}, {0, 0, -1, 666666667, -1, 666666667}, {0, 0, -1, 999999999, -1, 999999999}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 1}, {0, 0, 0, 333333333, 0, 333333333}, {0, 0, 0, 666666666, 0, 666666666}, {0, 0, 1, 0, 1, 0}, {0, 0, 2, 0, 2, 0}, {0, 0, 3, 0, 3, 0}, {0, 0, 3, 333333333, 3, 333333333}, {0, 333333333, -4, 666666667, -3, 0}, {0, 333333333, -3, 0, -3, 333333333}, {0, 333333333, -2, 0, -2, 333333333}, {0, 333333333, -1, 0, -1, 333333333}, {0, 333333333, -1, 333333334, -1, 666666667}, {0, 333333333, -1, 666666667, 0, 0}, {0, 333333333, -1, 999999999, 0, 333333332}, {0, 333333333, 0, 0, 0, 333333333}, {0, 333333333, 0, 1, 0, 333333334}, {0, 333333333, 0, 333333333, 0, 666666666}, {0, 333333333, 0, 666666666, 0, 999999999}, {0, 333333333, 1, 0, 1, 333333333}, {0, 333333333, 2, 0, 2, 333333333}, {0, 333333333, 3, 0, 3, 333333333}, {0, 333333333, 3, 333333333, 3, 666666666}, {1, 0, -4, 666666667, -3, 666666667}, {1, 0, -3, 0, -2, 0}, {1, 0, -2, 0, -1, 0}, {1, 0, -1, 0, 0, 0}, {1, 0, -1, 333333334, 0, 333333334}, {1, 0, -1, 666666667, 0, 666666667}, {1, 0, -1, 999999999, 0, 999999999}, {1, 0, 0, 0, 1, 0}, {1, 0, 0, 1, 1, 1}, {1, 0, 0, 333333333, 1, 333333333}, {1, 0, 0, 666666666, 1, 666666666}, {1, 0, 1, 0, 2, 0}, {1, 0, 2, 0, 3, 0}, {1, 0, 3, 0, 4, 0}, {1, 0, 3, 333333333, 4, 333333333}, {2, 0, -4, 666666667, -2, 666666667}, {2, 0, -3, 0, -1, 0}, {2, 0, -2, 0, 0, 0}, {2, 0, -1, 0, 1, 0}, {2, 0, -1, 333333334, 1, 333333334}, {2, 0, -1, 666666667, 1, 666666667}, {2, 0, -1, 999999999, 1, 999999999}, {2, 0, 0, 0, 2, 0}, {2, 0, 0, 1, 2, 1}, {2, 0, 0, 333333333, 2, 333333333}, {2, 0, 0, 666666666, 2, 666666666}, {2, 0, 1, 0, 3, 0}, {2, 0, 2, 0, 4, 0}, {2, 0, 3, 0, 5, 0}, {2, 0, 3, 333333333, 5, 333333333}, {3, 0, -4, 666666667, -1, 666666667}, {3, 0, -3, 0, 0, 0}, {3, 0, -2, 0, 1, 0}, {3, 0, -1, 0, 2, 0}, {3, 0, -1, 333333334, 2, 333333334}, {3, 0, -1, 666666667, 2, 666666667}, {3, 0, -1, 999999999, 2, 999999999}, {3, 0, 0, 0, 3, 0}, {3, 0, 0, 1, 3, 1}, {3, 0, 0, 333333333, 3, 333333333}, {3, 0, 0, 666666666, 3, 666666666}, {3, 0, 1, 0, 4, 0}, {3, 0, 2, 0, 5, 0}, {3, 0, 3, 0, 6, 0}, {3, 0, 3, 333333333, 6, 333333333}, {3, 333333333, -4, 666666667, 0, 0}, {3, 333333333, -3, 0, 0, 333333333}, {3, 333333333, -2, 0, 1, 333333333}, {3, 333333333, -1, 0, 2, 333333333}, {3, 333333333, -1, 333333334, 2, 666666667}, {3, 333333333, -1, 666666667, 3, 0}, {3, 333333333, -1, 999999999, 3, 333333332}, {3, 333333333, 0, 0, 3, 333333333}, {3, 333333333, 0, 1, 3, 333333334}, {3, 333333333, 0, 333333333, 3, 666666666}, {3, 333333333, 0, 666666666, 3, 999999999}, {3, 333333333, 1, 0, 4, 333333333}, {3, 333333333, 2, 0, 5, 333333333}, {3, 333333333, 3, 0, 6, 333333333}, {3, 333333333, 3, 333333333, 6, 666666666}, {Long.MAX_VALUE, 0, Long.MIN_VALUE, 0, -1, 0}, }; } @Test @UseDataProvider("data_plus") public void test_plus(long seconds, int nanos, long plusSeconds, int plusNanos, long expectedSeconds, int expectedNanoOfSecond) { TaiInstant i = TaiInstant.ofTaiSeconds(seconds, nanos).plus(Duration.ofSeconds(plusSeconds, plusNanos)); assertEquals(expectedSeconds, i.getTaiSeconds()); assertEquals(expectedNanoOfSecond, i.getNano()); } @Test(expected = ArithmeticException.class) public void test_plus_overflowTooBig() { TaiInstant i = TaiInstant.ofTaiSeconds(Long.MAX_VALUE, 999999999); i.plus(Duration.ofSeconds(0, 1)); } @Test(expected = ArithmeticException.class) public void test_plus_overflowTooSmall() { TaiInstant i = TaiInstant.ofTaiSeconds(Long.MIN_VALUE, 0); i.plus(Duration.ofSeconds(-1, 999999999)); } //----------------------------------------------------------------------- // minus(Duration) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_minus() { return new Object[][] { {Long.MIN_VALUE, 0, Long.MIN_VALUE + 1, 0, -1, 0}, {-4, 666666667, -4, 666666667, 0, 0}, {-4, 666666667, -3, 0, -1, 666666667}, {-4, 666666667, -2, 0, -2, 666666667}, {-4, 666666667, -1, 0, -3, 666666667}, {-4, 666666667, -1, 333333334, -3, 333333333}, {-4, 666666667, -1, 666666667, -3, 0}, {-4, 666666667, -1, 999999999, -4, 666666668}, {-4, 666666667, 0, 0, -4, 666666667}, {-4, 666666667, 0, 1, -4, 666666666}, {-4, 666666667, 0, 333333333, -4, 333333334}, {-4, 666666667, 0, 666666666, -4, 1}, {-4, 666666667, 1, 0, -5, 666666667}, {-4, 666666667, 2, 0, -6, 666666667}, {-4, 666666667, 3, 0, -7, 666666667}, {-4, 666666667, 3, 333333333, -7, 333333334}, {-3, 0, -4, 666666667, 0, 333333333}, {-3, 0, -3, 0, 0, 0}, {-3, 0, -2, 0, -1, 0}, {-3, 0, -1, 0, -2, 0}, {-3, 0, -1, 333333334, -3, 666666666}, {-3, 0, -1, 666666667, -3, 333333333}, {-3, 0, -1, 999999999, -3, 1}, {-3, 0, 0, 0, -3, 0}, {-3, 0, 0, 1, -4, 999999999}, {-3, 0, 0, 333333333, -4, 666666667}, {-3, 0, 0, 666666666, -4, 333333334}, {-3, 0, 1, 0, -4, 0}, {-3, 0, 2, 0, -5, 0}, {-3, 0, 3, 0, -6, 0}, {-3, 0, 3, 333333333, -7, 666666667}, {-2, 0, -4, 666666667, 1, 333333333}, {-2, 0, -3, 0, 1, 0}, {-2, 0, -2, 0, 0, 0}, {-2, 0, -1, 0, -1, 0}, {-2, 0, -1, 333333334, -2, 666666666}, {-2, 0, -1, 666666667, -2, 333333333}, {-2, 0, -1, 999999999, -2, 1}, {-2, 0, 0, 0, -2, 0}, {-2, 0, 0, 1, -3, 999999999}, {-2, 0, 0, 333333333, -3, 666666667}, {-2, 0, 0, 666666666, -3, 333333334}, {-2, 0, 1, 0, -3, 0}, {-2, 0, 2, 0, -4, 0}, {-2, 0, 3, 0, -5, 0}, {-2, 0, 3, 333333333, -6, 666666667}, {-1, 0, -4, 666666667, 2, 333333333}, {-1, 0, -3, 0, 2, 0}, {-1, 0, -2, 0, 1, 0}, {-1, 0, -1, 0, 0, 0}, {-1, 0, -1, 333333334, -1, 666666666}, {-1, 0, -1, 666666667, -1, 333333333}, {-1, 0, -1, 999999999, -1, 1}, {-1, 0, 0, 0, -1, 0}, {-1, 0, 0, 1, -2, 999999999}, {-1, 0, 0, 333333333, -2, 666666667}, {-1, 0, 0, 666666666, -2, 333333334}, {-1, 0, 1, 0, -2, 0}, {-1, 0, 2, 0, -3, 0}, {-1, 0, 3, 0, -4, 0}, {-1, 0, 3, 333333333, -5, 666666667}, {-1, 666666667, -4, 666666667, 3, 0}, {-1, 666666667, -3, 0, 2, 666666667}, {-1, 666666667, -2, 0, 1, 666666667}, {-1, 666666667, -1, 0, 0, 666666667}, {-1, 666666667, -1, 333333334, 0, 333333333}, {-1, 666666667, -1, 666666667, 0, 0}, {-1, 666666667, -1, 999999999, -1, 666666668}, {-1, 666666667, 0, 0, -1, 666666667}, {-1, 666666667, 0, 1, -1, 666666666}, {-1, 666666667, 0, 333333333, -1, 333333334}, {-1, 666666667, 0, 666666666, -1, 1}, {-1, 666666667, 1, 0, -2, 666666667}, {-1, 666666667, 2, 0, -3, 666666667}, {-1, 666666667, 3, 0, -4, 666666667}, {-1, 666666667, 3, 333333333, -4, 333333334}, {0, 0, -4, 666666667, 3, 333333333}, {0, 0, -3, 0, 3, 0}, {0, 0, -2, 0, 2, 0}, {0, 0, -1, 0, 1, 0}, {0, 0, -1, 333333334, 0, 666666666}, {0, 0, -1, 666666667, 0, 333333333}, {0, 0, -1, 999999999, 0, 1}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, -1, 999999999}, {0, 0, 0, 333333333, -1, 666666667}, {0, 0, 0, 666666666, -1, 333333334}, {0, 0, 1, 0, -1, 0}, {0, 0, 2, 0, -2, 0}, {0, 0, 3, 0, -3, 0}, {0, 0, 3, 333333333, -4, 666666667}, {0, 333333333, -4, 666666667, 3, 666666666}, {0, 333333333, -3, 0, 3, 333333333}, {0, 333333333, -2, 0, 2, 333333333}, {0, 333333333, -1, 0, 1, 333333333}, {0, 333333333, -1, 333333334, 0, 999999999}, {0, 333333333, -1, 666666667, 0, 666666666}, {0, 333333333, -1, 999999999, 0, 333333334}, {0, 333333333, 0, 0, 0, 333333333}, {0, 333333333, 0, 1, 0, 333333332}, {0, 333333333, 0, 333333333, 0, 0}, {0, 333333333, 0, 666666666, -1, 666666667}, {0, 333333333, 1, 0, -1, 333333333}, {0, 333333333, 2, 0, -2, 333333333}, {0, 333333333, 3, 0, -3, 333333333}, {0, 333333333, 3, 333333333, -3, 0}, {1, 0, -4, 666666667, 4, 333333333}, {1, 0, -3, 0, 4, 0}, {1, 0, -2, 0, 3, 0}, {1, 0, -1, 0, 2, 0}, {1, 0, -1, 333333334, 1, 666666666}, {1, 0, -1, 666666667, 1, 333333333}, {1, 0, -1, 999999999, 1, 1}, {1, 0, 0, 0, 1, 0}, {1, 0, 0, 1, 0, 999999999}, {1, 0, 0, 333333333, 0, 666666667}, {1, 0, 0, 666666666, 0, 333333334}, {1, 0, 1, 0, 0, 0}, {1, 0, 2, 0, -1, 0}, {1, 0, 3, 0, -2, 0}, {1, 0, 3, 333333333, -3, 666666667}, {2, 0, -4, 666666667, 5, 333333333}, {2, 0, -3, 0, 5, 0}, {2, 0, -2, 0, 4, 0}, {2, 0, -1, 0, 3, 0}, {2, 0, -1, 333333334, 2, 666666666}, {2, 0, -1, 666666667, 2, 333333333}, {2, 0, -1, 999999999, 2, 1}, {2, 0, 0, 0, 2, 0}, {2, 0, 0, 1, 1, 999999999}, {2, 0, 0, 333333333, 1, 666666667}, {2, 0, 0, 666666666, 1, 333333334}, {2, 0, 1, 0, 1, 0}, {2, 0, 2, 0, 0, 0}, {2, 0, 3, 0, -1, 0}, {2, 0, 3, 333333333, -2, 666666667}, {3, 0, -4, 666666667, 6, 333333333}, {3, 0, -3, 0, 6, 0}, {3, 0, -2, 0, 5, 0}, {3, 0, -1, 0, 4, 0}, {3, 0, -1, 333333334, 3, 666666666}, {3, 0, -1, 666666667, 3, 333333333}, {3, 0, -1, 999999999, 3, 1}, {3, 0, 0, 0, 3, 0}, {3, 0, 0, 1, 2, 999999999}, {3, 0, 0, 333333333, 2, 666666667}, {3, 0, 0, 666666666, 2, 333333334}, {3, 0, 1, 0, 2, 0}, {3, 0, 2, 0, 1, 0}, {3, 0, 3, 0, 0, 0}, {3, 0, 3, 333333333, -1, 666666667}, {3, 333333333, -4, 666666667, 6, 666666666}, {3, 333333333, -3, 0, 6, 333333333}, {3, 333333333, -2, 0, 5, 333333333}, {3, 333333333, -1, 0, 4, 333333333}, {3, 333333333, -1, 333333334, 3, 999999999}, {3, 333333333, -1, 666666667, 3, 666666666}, {3, 333333333, -1, 999999999, 3, 333333334}, {3, 333333333, 0, 0, 3, 333333333}, {3, 333333333, 0, 1, 3, 333333332}, {3, 333333333, 0, 333333333, 3, 0}, {3, 333333333, 0, 666666666, 2, 666666667}, {3, 333333333, 1, 0, 2, 333333333}, {3, 333333333, 2, 0, 1, 333333333}, {3, 333333333, 3, 0, 0, 333333333}, {3, 333333333, 3, 333333333, 0, 0}, {Long.MAX_VALUE, 0, Long.MAX_VALUE, 0, 0, 0}, }; } @Test @UseDataProvider("data_minus") public void test_minus(long seconds, int nanos, long minusSeconds, int minusNanos, long expectedSeconds, int expectedNanoOfSecond) { TaiInstant i = TaiInstant.ofTaiSeconds(seconds, nanos).minus(Duration.ofSeconds(minusSeconds, minusNanos)); assertEquals(expectedSeconds, i.getTaiSeconds()); assertEquals(expectedNanoOfSecond, i.getNano()); } @Test(expected = ArithmeticException.class) public void test_minus_overflowTooSmall() { TaiInstant i = TaiInstant.ofTaiSeconds(Long.MIN_VALUE, 0); i.minus(Duration.ofSeconds(0, 1)); } @Test(expected = ArithmeticException.class) public void test_minus_overflowTooBig() { TaiInstant i = TaiInstant.ofTaiSeconds(Long.MAX_VALUE, 999999999); i.minus(Duration.ofSeconds(-1, 999999999)); } //----------------------------------------------------------------------- // durationUntil() //----------------------------------------------------------------------- @Test public void test_durationUntil_fifteenSeconds() { TaiInstant tai1 = TaiInstant.ofTaiSeconds(10, 0); TaiInstant tai2 = TaiInstant.ofTaiSeconds(25, 0); Duration test = tai1.durationUntil(tai2); assertEquals(15, test.getSeconds()); assertEquals(0, test.getNano()); } @Test public void test_durationUntil_twoNanos() { TaiInstant tai1 = TaiInstant.ofTaiSeconds(4, 5); TaiInstant tai2 = TaiInstant.ofTaiSeconds(4, 7); Duration test = tai1.durationUntil(tai2); assertEquals(0, test.getSeconds()); assertEquals(2, test.getNano()); } @Test public void test_durationUntil_twoNanosNegative() { TaiInstant tai1 = TaiInstant.ofTaiSeconds(4, 9); TaiInstant tai2 = TaiInstant.ofTaiSeconds(4, 7); Duration test = tai1.durationUntil(tai2); assertEquals(-1, test.getSeconds()); assertEquals(999999998, test.getNano()); } //----------------------------------------------------------------------- // toUtcInstant() //----------------------------------------------------------------------- @Test public void test_toUtcInstant() { for (int i = -1000; i < 1000; i++) { for (int j = 0; j < 10; j++) { UtcInstant expected = UtcInstant.ofModifiedJulianDay(36204 + i, j * 1000000000L + 2L); TaiInstant test = TaiInstant.ofTaiSeconds(i * 24 * 60 * 60 + j + 10, 2); assertEquals(expected, test.toUtcInstant()); } } } //----------------------------------------------------------------------- // toInstant() //----------------------------------------------------------------------- @Test public void test_toInstant() { for (int i = -1000; i < 1000; i++) { for (int j = 0; j < 10; j++) { Instant expected = Instant.ofEpochSecond(-378691200L + i * 24 * 60 * 60 + j).plusNanos(2); TaiInstant test = TaiInstant.ofTaiSeconds(i * 24 * 60 * 60 + j + 10, 2); assertEquals(expected, test.toInstant()); } } } //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test public void test_comparisons() { doTest_comparisons_TaiInstant( TaiInstant.ofTaiSeconds(-2L, 0), TaiInstant.ofTaiSeconds(-2L, 999999998), TaiInstant.ofTaiSeconds(-2L, 999999999), TaiInstant.ofTaiSeconds(-1L, 0), TaiInstant.ofTaiSeconds(-1L, 1), TaiInstant.ofTaiSeconds(-1L, 999999998), TaiInstant.ofTaiSeconds(-1L, 999999999), TaiInstant.ofTaiSeconds(0L, 0), TaiInstant.ofTaiSeconds(0L, 1), TaiInstant.ofTaiSeconds(0L, 2), TaiInstant.ofTaiSeconds(0L, 999999999), TaiInstant.ofTaiSeconds(1L, 0), TaiInstant.ofTaiSeconds(2L, 0) ); } void doTest_comparisons_TaiInstant(TaiInstant... instants) { for (int i = 0; i < instants.length; i++) { TaiInstant a = instants[i]; for (int j = 0; j < instants.length; j++) { TaiInstant b = instants[j]; if (i < j) { assertEquals(true, a.compareTo(b) < 0); assertEquals(false, a.equals(b)); } else if (i > j) { assertEquals(true, a.compareTo(b) > 0); assertEquals(false, a.equals(b)); } else { assertEquals(0, a.compareTo(b)); assertEquals(true, a.equals(b)); } } } } @Test(expected = NullPointerException.class) public void test_compareTo_ObjectNull() { TaiInstant a = TaiInstant.ofTaiSeconds(0L, 0); a.compareTo(null); } @Test(expected = ClassCastException.class) @SuppressWarnings({"unchecked", "rawtypes"}) public void test_compareToNonTaiInstant() { Comparable c = TaiInstant.ofTaiSeconds(0L, 2); c.compareTo(new Object()); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { TaiInstant test5a = TaiInstant.ofTaiSeconds(5L, 20); TaiInstant test5b = TaiInstant.ofTaiSeconds(5L, 20); TaiInstant test5n = TaiInstant.ofTaiSeconds(5L, 30); TaiInstant test6 = TaiInstant.ofTaiSeconds(6L, 20); assertEquals(true, test5a.equals(test5a)); assertEquals(true, test5a.equals(test5b)); assertEquals(false, test5a.equals(test5n)); assertEquals(false, test5a.equals(test6)); assertEquals(true, test5b.equals(test5a)); assertEquals(true, test5b.equals(test5b)); assertEquals(false, test5b.equals(test5n)); assertEquals(false, test5b.equals(test6)); assertEquals(false, test5n.equals(test5a)); assertEquals(false, test5n.equals(test5b)); assertEquals(true, test5n.equals(test5n)); assertEquals(false, test5n.equals(test6)); assertEquals(false, test6.equals(test5a)); assertEquals(false, test6.equals(test5b)); assertEquals(false, test6.equals(test5n)); assertEquals(true, test6.equals(test6)); } @Test public void test_equals_null() { TaiInstant test5 = TaiInstant.ofTaiSeconds(5L, 20); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { TaiInstant test5 = TaiInstant.ofTaiSeconds(5L, 20); assertEquals(false, test5.equals("")); } //----------------------------------------------------------------------- // hashCode() //----------------------------------------------------------------------- @Test public void test_hashCode() { TaiInstant test5a = TaiInstant.ofTaiSeconds(5L, 20); TaiInstant test5b = TaiInstant.ofTaiSeconds(5L, 20); TaiInstant test5n = TaiInstant.ofTaiSeconds(5L, 30); TaiInstant test6 = TaiInstant.ofTaiSeconds(6L, 20); assertEquals(true, test5a.hashCode() == test5a.hashCode()); assertEquals(true, test5a.hashCode() == test5b.hashCode()); assertEquals(true, test5b.hashCode() == test5b.hashCode()); assertEquals(false, test5a.hashCode() == test5n.hashCode()); assertEquals(false, test5a.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString_standard() { TaiInstant t = TaiInstant.ofTaiSeconds(123L, 123456789); assertEquals("123.123456789s(TAI)", t.toString()); } @Test public void test_toString_negative() { TaiInstant t = TaiInstant.ofTaiSeconds(-123L, 123456789); assertEquals("-123.123456789s(TAI)", t.toString()); } @Test public void test_toString_zeroDecimal() { TaiInstant t = TaiInstant.ofTaiSeconds(0L, 567); assertEquals("0.000000567s(TAI)", t.toString()); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/scale/TestUtcInstant.java000066400000000000000000000634121343451174100275720ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; import java.time.format.DateTimeParseException; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test UtcInstant. */ @RunWith(DataProviderRunner.class) public class TestUtcInstant { private static final long MJD_1972_12_30 = 41681; private static final long MJD_1972_12_31_LEAP = 41682; private static final long MJD_1973_01_01 = 41683; private static final long MJD_1973_12_31_LEAP = MJD_1972_12_31_LEAP + 365; private static final long SECS_PER_DAY = 24L * 60 * 60; private static final long NANOS_PER_SEC = 1000000000L; private static final long NANOS_PER_DAY = SECS_PER_DAY * NANOS_PER_SEC; private static final long NANOS_PER_LEAP_DAY = (SECS_PER_DAY + 1) * NANOS_PER_SEC; //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(UtcInstant.class)); assertTrue(Comparable.class.isAssignableFrom(UtcInstant.class)); } //----------------------------------------------------------------------- // serialization //----------------------------------------------------------------------- @Test public void test_serialization() throws Exception { UtcInstant test = UtcInstant.ofModifiedJulianDay(2, 3); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertEquals(test, ois.readObject()); } } //----------------------------------------------------------------------- // ofModififiedJulianDay(long,long) //----------------------------------------------------------------------- @Test public void factory_ofModifiedJulianDay_long_long() { for (long i = -2; i <= 2; i++) { for (int j = 0; j < 10; j++) { UtcInstant t = UtcInstant.ofModifiedJulianDay(i, j); assertEquals(i, t.getModifiedJulianDay()); assertEquals(j, t.getNanoOfDay()); assertEquals(false, t.isLeapSecond()); } } } @Test public void factory_ofModifiedJulianDay_long_long_startLeap() { UtcInstant t = UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, NANOS_PER_DAY); assertEquals(MJD_1972_12_31_LEAP, t.getModifiedJulianDay()); assertEquals(NANOS_PER_DAY, t.getNanoOfDay()); } @Test public void factory_ofModifiedJulianDay_long_long_endLeap() { UtcInstant t = UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, NANOS_PER_LEAP_DAY - 1); assertEquals(MJD_1972_12_31_LEAP, t.getModifiedJulianDay()); assertEquals(NANOS_PER_LEAP_DAY - 1, t.getNanoOfDay()); } @Test(expected = DateTimeException.class) public void factory_ofModifiedJulianDay_long_long_nanosNegative() { UtcInstant.ofModifiedJulianDay(MJD_1973_01_01, -1); } @Test(expected = DateTimeException.class) public void factory_ofModifiedJulianDay_long_long_nanosTooBig_notLeap() { UtcInstant.ofModifiedJulianDay(MJD_1973_01_01, NANOS_PER_DAY); } @Test(expected = DateTimeException.class) public void factory_ofModifiedJulianDay_long_long_nanosTooBig_leap() { UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, NANOS_PER_LEAP_DAY); } //----------------------------------------------------------------------- // of(Instant) //----------------------------------------------------------------------- @Test public void factory_of_Instant() { UtcInstant test = UtcInstant.of(Instant.ofEpochSecond(0, 2)); // 1970-01-01 assertEquals(40587, test.getModifiedJulianDay()); assertEquals(2, test.getNanoOfDay()); } @Test(expected = NullPointerException.class) public void factory_of_Instant_null() { UtcInstant.of((Instant) null); } //----------------------------------------------------------------------- // of(TaiInstant) //----------------------------------------------------------------------- @Test public void factory_of_TaiInstant() { for (int i = -1000; i < 1000; i++) { for (int j = 0; j < 10; j++) { UtcInstant expected = UtcInstant.ofModifiedJulianDay(36204 + i, j * NANOS_PER_SEC + 2L); TaiInstant tai = TaiInstant.ofTaiSeconds(i * SECS_PER_DAY + j + 10, 2); assertEquals(expected, UtcInstant.of(tai)); } } } @Test(expected = NullPointerException.class) public void factory_of_TaiInstant_null() { UtcInstant.of((TaiInstant) null); } //----------------------------------------------------------------------- // parse(CharSequence) //----------------------------------------------------------------------- @Test public void factory_parse_CharSequence() { assertEquals(UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, NANOS_PER_DAY - NANOS_PER_SEC), UtcInstant.parse("1972-12-31T23:59:59Z")); assertEquals(UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, NANOS_PER_DAY), UtcInstant.parse("1972-12-31T23:59:60Z")); } @DataProvider public static Object[][] data_badParse() { return new Object[][] { {""}, {"A"}, {"2012-13-01T00:00:00Z"}, // bad month }; } @Test(expected = DateTimeParseException.class) @UseDataProvider("data_badParse") public void factory_parse_CharSequence_invalid(String str) { UtcInstant.parse(str); } @Test(expected = DateTimeException.class) public void factory_parse_CharSequence_invalidLeapSecond() { UtcInstant.parse("1972-11-11T23:59:60Z"); // leap second but not leap day } @Test(expected = NullPointerException.class) public void factory_parse_CharSequence_null() { UtcInstant.parse((String) null); } //----------------------------------------------------------------------- // withModifiedJulianDay() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_withModifiedJulianDay() { return new Object[][] { {0L, 12345L, 1L, 1L, 12345L}, {0L, 12345L, -1L, -1L, 12345L}, {7L, 12345L, 2L, 2L, 12345L}, {7L, 12345L, -2L, -2L, 12345L}, {-99L, 12345L, 3L, 3L, 12345L}, {-99L, 12345L, -3L, -3L, 12345L}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY, MJD_1972_12_30, null, null}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY, MJD_1972_12_31_LEAP, MJD_1972_12_31_LEAP, NANOS_PER_DAY}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY, MJD_1973_01_01, null, null}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY, MJD_1973_12_31_LEAP, MJD_1973_12_31_LEAP, NANOS_PER_DAY}, }; } @Test @UseDataProvider("data_withModifiedJulianDay") public void test_withModifiedJulianDay(long mjd, long nanos, long newMjd, Long expectedMjd, Long expectedNanos) { UtcInstant i = UtcInstant.ofModifiedJulianDay(mjd, nanos); if (expectedMjd != null) { i = i.withModifiedJulianDay(newMjd); assertEquals(expectedMjd.longValue(), i.getModifiedJulianDay()); assertEquals(expectedNanos.longValue(), i.getNanoOfDay()); } else { try { i = i.withModifiedJulianDay(newMjd); fail(); } catch (DateTimeException ex) { // expected } } } //----------------------------------------------------------------------- // withNanoOfDay() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_withNanoOfDay() { return new Object[][] { {0L, 12345L, 1L, 0L, 1L}, {0L, 12345L, -1L, null, null}, {7L, 12345L, 2L, 7L, 2L}, {-99L, 12345L, 3L, -99L, 3L}, {MJD_1972_12_30, NANOS_PER_DAY - 1, NANOS_PER_DAY - 1, MJD_1972_12_30, NANOS_PER_DAY - 1}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY - 1, NANOS_PER_DAY - 1, MJD_1972_12_31_LEAP, NANOS_PER_DAY - 1}, {MJD_1973_01_01, NANOS_PER_DAY - 1, NANOS_PER_DAY - 1, MJD_1973_01_01, NANOS_PER_DAY - 1}, {MJD_1972_12_30, NANOS_PER_DAY - 1, NANOS_PER_DAY, null, null}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY - 1, NANOS_PER_DAY, MJD_1972_12_31_LEAP, NANOS_PER_DAY}, {MJD_1973_01_01, NANOS_PER_DAY - 1, NANOS_PER_DAY, null, null}, {MJD_1972_12_30, NANOS_PER_DAY - 1, NANOS_PER_LEAP_DAY - 1, null, null}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY - 1, NANOS_PER_LEAP_DAY - 1, MJD_1972_12_31_LEAP, NANOS_PER_LEAP_DAY - 1}, {MJD_1973_01_01, NANOS_PER_DAY - 1, NANOS_PER_LEAP_DAY - 1, null, null}, {MJD_1972_12_30, NANOS_PER_DAY - 1, NANOS_PER_LEAP_DAY, null, null}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY - 1, NANOS_PER_LEAP_DAY, null, null}, {MJD_1973_01_01, NANOS_PER_DAY - 1, NANOS_PER_LEAP_DAY, null, null}, }; } @Test @UseDataProvider("data_withNanoOfDay") public void test_withNanoOfDay(long mjd, long nanos, long newNanoOfDay, Long expectedMjd, Long expectedNanos) { UtcInstant i = UtcInstant.ofModifiedJulianDay(mjd, nanos); if (expectedMjd != null) { i = i.withNanoOfDay(newNanoOfDay); assertEquals(expectedMjd.longValue(), i.getModifiedJulianDay()); assertEquals(expectedNanos.longValue(), i.getNanoOfDay()); } else { try { i = i.withNanoOfDay(newNanoOfDay); fail(); } catch (DateTimeException ex) { // expected } } } //----------------------------------------------------------------------- // plus(Duration) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_plus() { return new Object[][] { {0, 0, -2 * SECS_PER_DAY, 5, -2, 5}, {0, 0, -1 * SECS_PER_DAY, 1, -1, 1}, {0, 0, -1 * SECS_PER_DAY, 0, -1, 0}, {0, 0, 0, -2, -1, NANOS_PER_DAY - 2}, {0, 0, 0, -1, -1, NANOS_PER_DAY - 1}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 1}, {0, 0, 0, 2, 0, 2}, {0, 0, 1, 0, 0, 1 * NANOS_PER_SEC}, {0, 0, 2, 0, 0, 2 * NANOS_PER_SEC}, {0, 0, 3, 333333333, 0, 3 * NANOS_PER_SEC + 333333333}, {0, 0, 1 * SECS_PER_DAY, 0, 1, 0}, {0, 0, 1 * SECS_PER_DAY, 1, 1, 1}, {0, 0, 2 * SECS_PER_DAY, 5, 2, 5}, {1, 0, -2 * SECS_PER_DAY, 5, -1, 5}, {1, 0, -1 * SECS_PER_DAY, 1, 0, 1}, {1, 0, -1 * SECS_PER_DAY, 0, 0, 0}, {1, 0, 0, -2, 0, NANOS_PER_DAY - 2}, {1, 0, 0, -1, 0, NANOS_PER_DAY - 1}, {1, 0, 0, 0, 1, 0}, {1, 0, 0, 1, 1, 1}, {1, 0, 0, 2, 1, 2}, {1, 0, 1, 0, 1, 1 * NANOS_PER_SEC}, {1, 0, 2, 0, 1, 2 * NANOS_PER_SEC}, {1, 0, 3, 333333333, 1, 3 * NANOS_PER_SEC + 333333333}, {1, 0, 1 * SECS_PER_DAY, 0, 2, 0}, {1, 0, 1 * SECS_PER_DAY, 1, 2, 1}, {1, 0, 2 * SECS_PER_DAY, 5, 3, 5}, }; } @Test @UseDataProvider("data_plus") public void test_plus(long mjd, long nanos, long plusSeconds, int plusNanos, long expectedMjd, long expectedNanos) { UtcInstant i = UtcInstant.ofModifiedJulianDay(mjd, nanos).plus(Duration.ofSeconds(plusSeconds, plusNanos)); assertEquals(expectedMjd, i.getModifiedJulianDay()); assertEquals(expectedNanos, i.getNanoOfDay()); } @Test(expected = ArithmeticException.class) public void test_plus_overflowTooBig() { UtcInstant i = UtcInstant.ofModifiedJulianDay(Long.MAX_VALUE, NANOS_PER_DAY - 1); i.plus(Duration.ofNanos(1)); } @Test(expected = ArithmeticException.class) public void test_plus_overflowTooSmall() { UtcInstant i = UtcInstant.ofModifiedJulianDay(Long.MIN_VALUE, 0); i.plus(Duration.ofNanos(-1)); } //----------------------------------------------------------------------- // minus(Duration) //----------------------------------------------------------------------- @DataProvider public static Object[][] data_minus() { return new Object[][] { {0, 0, 2 * SECS_PER_DAY, -5, -2, 5}, {0, 0, 1 * SECS_PER_DAY, -1, -1, 1}, {0, 0, 1 * SECS_PER_DAY, 0, -1, 0}, {0, 0, 0, 2, -1, NANOS_PER_DAY - 2}, {0, 0, 0, 1, -1, NANOS_PER_DAY - 1}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, -1, 0, 1}, {0, 0, 0, -2, 0, 2}, {0, 0, -1, 0, 0, 1 * NANOS_PER_SEC}, {0, 0, -2, 0, 0, 2 * NANOS_PER_SEC}, {0, 0, -3, -333333333, 0, 3 * NANOS_PER_SEC + 333333333}, {0, 0, -1 * SECS_PER_DAY, 0, 1, 0}, {0, 0, -1 * SECS_PER_DAY, -1, 1, 1}, {0, 0, -2 * SECS_PER_DAY, -5, 2, 5}, {1, 0, 2 * SECS_PER_DAY, -5, -1, 5}, {1, 0, 1 * SECS_PER_DAY, -1, 0, 1}, {1, 0, 1 * SECS_PER_DAY, 0, 0, 0}, {1, 0, 0, 2, 0, NANOS_PER_DAY - 2}, {1, 0, 0, 1, 0, NANOS_PER_DAY - 1}, {1, 0, 0, 0, 1, 0}, {1, 0, 0, -1, 1, 1}, {1, 0, 0, -2, 1, 2}, {1, 0, -1, 0, 1, 1 * NANOS_PER_SEC}, {1, 0, -2, 0, 1, 2 * NANOS_PER_SEC}, {1, 0, -3, -333333333, 1, 3 * NANOS_PER_SEC + 333333333}, {1, 0, -1 * SECS_PER_DAY, 0, 2, 0}, {1, 0, -1 * SECS_PER_DAY, -1, 2, 1}, {1, 0, -2 * SECS_PER_DAY, -5, 3, 5}, }; } @Test @UseDataProvider("data_minus") public void test_minus(long mjd, long nanos, long minusSeconds, int minusNanos, long expectedMjd, long expectedNanos) { UtcInstant i = UtcInstant.ofModifiedJulianDay(mjd, nanos).minus(Duration.ofSeconds(minusSeconds, minusNanos)); assertEquals(expectedMjd, i.getModifiedJulianDay()); assertEquals(expectedNanos, i.getNanoOfDay()); } @Test(expected = ArithmeticException.class) public void test_minus_overflowTooSmall() { UtcInstant i = UtcInstant.ofModifiedJulianDay(Long.MIN_VALUE, 0); i.minus(Duration.ofNanos(1)); } @Test(expected = ArithmeticException.class) public void test_minus_overflowTooBig() { UtcInstant i = UtcInstant.ofModifiedJulianDay(Long.MAX_VALUE, NANOS_PER_DAY - 1); i.minus(Duration.ofNanos(-1)); } //----------------------------------------------------------------------- // durationUntil() //----------------------------------------------------------------------- @Test public void test_durationUntil_oneDayNoLeap() { UtcInstant utc1 = UtcInstant.ofModifiedJulianDay(MJD_1972_12_30, 0); UtcInstant utc2 = UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, 0); Duration test = utc1.durationUntil(utc2); assertEquals(86400, test.getSeconds()); assertEquals(0, test.getNano()); } @Test public void test_durationUntil_oneDayLeap() { UtcInstant utc1 = UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, 0); UtcInstant utc2 = UtcInstant.ofModifiedJulianDay(MJD_1973_01_01, 0); Duration test = utc1.durationUntil(utc2); assertEquals(86401, test.getSeconds()); assertEquals(0, test.getNano()); } @Test public void test_durationUntil_oneDayLeapNegative() { UtcInstant utc1 = UtcInstant.ofModifiedJulianDay(MJD_1973_01_01, 0); UtcInstant utc2 = UtcInstant.ofModifiedJulianDay(MJD_1972_12_31_LEAP, 0); Duration test = utc1.durationUntil(utc2); assertEquals(-86401, test.getSeconds()); assertEquals(0, test.getNano()); } //----------------------------------------------------------------------- // toTaiInstant() //----------------------------------------------------------------------- @Test public void test_toTaiInstant() { for (int i = -1000; i < 1000; i++) { for (int j = 0; j < 10; j++) { UtcInstant utc = UtcInstant.ofModifiedJulianDay(36204 + i, j * NANOS_PER_SEC + 2L); TaiInstant test = utc.toTaiInstant(); assertEquals(i * SECS_PER_DAY + j + 10, test.getTaiSeconds()); assertEquals(2, test.getNano()); } } } @Test(expected = ArithmeticException.class) public void test_toTaiInstant_maxInvalid() { UtcInstant utc = UtcInstant.ofModifiedJulianDay(Long.MAX_VALUE, 0); utc.toTaiInstant(); } //----------------------------------------------------------------------- // toInstant() //----------------------------------------------------------------------- @Test public void test_toInstant() { for (int i = -1000; i < 1000; i++) { for (int j = 0; j < 10; j++) { Instant expected = Instant.ofEpochSecond(315532800 + i * SECS_PER_DAY + j).plusNanos(2); UtcInstant test = UtcInstant.ofModifiedJulianDay(44239 + i, j * NANOS_PER_SEC + 2); assertEquals(expected, test.toInstant()); } } } //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- @Test public void test_comparisons() { doTest_comparisons_UtcInstant( UtcInstant.ofModifiedJulianDay(-2L, 0), UtcInstant.ofModifiedJulianDay(-2L, NANOS_PER_DAY - 2), UtcInstant.ofModifiedJulianDay(-2L, NANOS_PER_DAY - 1), UtcInstant.ofModifiedJulianDay(-1L, 0), UtcInstant.ofModifiedJulianDay(-1L, 1), UtcInstant.ofModifiedJulianDay(-1L, NANOS_PER_DAY - 2), UtcInstant.ofModifiedJulianDay(-1L, NANOS_PER_DAY - 1), UtcInstant.ofModifiedJulianDay(0L, 0), UtcInstant.ofModifiedJulianDay(0L, 1), UtcInstant.ofModifiedJulianDay(0L, 2), UtcInstant.ofModifiedJulianDay(0L, NANOS_PER_DAY - 1), UtcInstant.ofModifiedJulianDay(1L, 0), UtcInstant.ofModifiedJulianDay(2L, 0) ); } void doTest_comparisons_UtcInstant(UtcInstant... instants) { for (int i = 0; i < instants.length; i++) { UtcInstant a = instants[i]; for (int j = 0; j < instants.length; j++) { UtcInstant b = instants[j]; if (i < j) { assertEquals(-1, a.compareTo(b)); assertEquals(false, a.equals(b)); } else if (i > j) { assertEquals(1, a.compareTo(b)); assertEquals(false, a.equals(b)); } else { assertEquals(0, a.compareTo(b)); assertEquals(true, a.equals(b)); } } } } @Test(expected = NullPointerException.class) public void test_compareTo_ObjectNull() { UtcInstant a = UtcInstant.ofModifiedJulianDay(0L, 0); a.compareTo(null); } @Test(expected = ClassCastException.class) @SuppressWarnings({"unchecked", "rawtypes"}) public void test_compareToNonUtcInstant() { Comparable c = UtcInstant.ofModifiedJulianDay(0L, 2); c.compareTo(new Object()); } //----------------------------------------------------------------------- // equals() //----------------------------------------------------------------------- @Test public void test_equals() { UtcInstant test5a = UtcInstant.ofModifiedJulianDay(5L, 20); UtcInstant test5b = UtcInstant.ofModifiedJulianDay(5L, 20); UtcInstant test5n = UtcInstant.ofModifiedJulianDay(5L, 30); UtcInstant test6 = UtcInstant.ofModifiedJulianDay(6L, 20); assertEquals(true, test5a.equals(test5a)); assertEquals(true, test5a.equals(test5b)); assertEquals(false, test5a.equals(test5n)); assertEquals(false, test5a.equals(test6)); assertEquals(true, test5b.equals(test5a)); assertEquals(true, test5b.equals(test5b)); assertEquals(false, test5b.equals(test5n)); assertEquals(false, test5b.equals(test6)); assertEquals(false, test5n.equals(test5a)); assertEquals(false, test5n.equals(test5b)); assertEquals(true, test5n.equals(test5n)); assertEquals(false, test5n.equals(test6)); assertEquals(false, test6.equals(test5a)); assertEquals(false, test6.equals(test5b)); assertEquals(false, test6.equals(test5n)); assertEquals(true, test6.equals(test6)); } @Test public void test_equals_null() { UtcInstant test5 = UtcInstant.ofModifiedJulianDay(5L, 20); assertEquals(false, test5.equals(null)); } @Test public void test_equals_otherClass() { UtcInstant test5 = UtcInstant.ofModifiedJulianDay(5L, 20); assertEquals(false, test5.equals("")); } //----------------------------------------------------------------------- // hashCode() //----------------------------------------------------------------------- @Test public void test_hashCode() { UtcInstant test5a = UtcInstant.ofModifiedJulianDay(5L, 20); UtcInstant test5b = UtcInstant.ofModifiedJulianDay(5L, 20); UtcInstant test5n = UtcInstant.ofModifiedJulianDay(5L, 30); UtcInstant test6 = UtcInstant.ofModifiedJulianDay(6L, 20); assertEquals(true, test5a.hashCode() == test5a.hashCode()); assertEquals(true, test5a.hashCode() == test5b.hashCode()); assertEquals(true, test5b.hashCode() == test5b.hashCode()); assertEquals(false, test5a.hashCode() == test5n.hashCode()); assertEquals(false, test5a.hashCode() == test6.hashCode()); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_toString() { return new Object[][] { {40587, 0, "1970-01-01T00:00:00Z"}, {40588, 1, "1970-01-02T00:00:00.000000001Z"}, {40588, 999, "1970-01-02T00:00:00.000000999Z"}, {40588, 1000, "1970-01-02T00:00:00.000001Z"}, {40588, 999000, "1970-01-02T00:00:00.000999Z"}, {40588, 1000000, "1970-01-02T00:00:00.001Z"}, {40618, 999999999, "1970-02-01T00:00:00.999999999Z"}, {40619, 1000000000, "1970-02-02T00:00:01Z"}, {40620, 60L * 1000000000L, "1970-02-03T00:01:00Z"}, {40621, 60L * 60L * 1000000000L, "1970-02-04T01:00:00Z"}, {MJD_1972_12_31_LEAP, 24L * 60L * 60L * 1000000000L - 1000000000L, "1972-12-31T23:59:59Z"}, {MJD_1972_12_31_LEAP, NANOS_PER_DAY, "1972-12-31T23:59:60Z"}, {MJD_1973_01_01, 0, "1973-01-01T00:00:00Z"}, }; } @Test @UseDataProvider("data_toString") public void test_toString(long mjd, long nod, String expected) { assertEquals(expected, UtcInstant.ofModifiedJulianDay(mjd, nod).toString()); } @Test @UseDataProvider("data_toString") public void test_toString_parse(long mjd, long nod, String str) { assertEquals(UtcInstant.ofModifiedJulianDay(mjd, nod), UtcInstant.parse(str)); } } threeten-extra-1.5.0/src/test/java/org/threeten/extra/scale/TestUtcRules.java000066400000000000000000000570231343451174100272450ustar00rootroot00000000000000/* * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of JSR-310 nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.threeten.extra.scale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.temporal.JulianFields; import java.util.Arrays; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; /** * Test SystemLeapSecondRules. */ @RunWith(DataProviderRunner.class) public class TestUtcRules { SystemUtcRules rules; @Before public void setUp() throws Exception { Constructor con = SystemUtcRules.class.getDeclaredConstructor(); con.setAccessible(true); rules = con.newInstance(); } //----------------------------------------------------------------------- @Test public void test_interfaces() { assertTrue(Serializable.class.isAssignableFrom(Duration.class)); } //----------------------------------------------------------------------- // serialize //----------------------------------------------------------------------- @Test public void test_serialization() throws Exception { SystemUtcRules test = SystemUtcRules.INSTANCE; // use real rules, not our hacked copy ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(test); } try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { assertSame(test, ois.readObject()); } } //----------------------------------------------------------------------- // getName() //----------------------------------------------------------------------- @Test public void test_getName() { assertEquals("System", rules.getName()); } //----------------------------------------------------------------------- // getLeapSecond() //----------------------------------------------------------------------- @DataProvider public static Object[][] data_leapSeconds() { return new Object[][] { {-1, 0, 10, "1858-11-16"}, {0, 0, 10, "1858-11-17"}, {1, 0, 10, "1858-11-18"}, {41316, 0, 10, "1971-12-31"}, {41317, 0, 10, "1972-01-01"}, {41318, 0, 10, "1972-01-02"}, {41497, 0, 10, "1972-06-29"}, {41498, 1, 10, "1972-06-30"}, {41499, 0, 11, "1972-07-01"}, {41500, 0, 11, "1972-07-02"}, {41681, 0, 11, "1972-12-30"}, {41682, 1, 11, "1972-12-31"}, {41683, 0, 12, "1973-01-01"}, {41684, 0, 12, "1973-01-02"}, {42046, 0, 12, "1973-12-30"}, {42047, 1, 12, "1973-12-31"}, {42048, 0, 13, "1974-01-01"}, {42049, 0, 13, "1974-01-02"}, {42411, 0, 13, "1974-12-30"}, {42412, 1, 13, "1974-12-31"}, {42413, 0, 14, "1975-01-01"}, {42414, 0, 14, "1975-01-02"}, {42776, 0, 14, "1975-12-30"}, {42777, 1, 14, "1975-12-31"}, {42778, 0, 15, "1976-01-01"}, {42779, 0, 15, "1976-01-02"}, {43142, 0, 15, "1976-12-30"}, {43143, 1, 15, "1976-12-31"}, {43144, 0, 16, "1977-01-01"}, {43145, 0, 16, "1977-01-02"}, {43507, 0, 16, "1977-12-30"}, {43508, 1, 16, "1977-12-31"}, {43509, 0, 17, "1978-01-01"}, {43510, 0, 17, "1978-01-02"}, {43872, 0, 17, "1978-12-30"}, {43873, 1, 17, "1978-12-31"}, {43874, 0, 18, "1979-01-01"}, {43875, 0, 18, "1979-01-02"}, {44237, 0, 18, "1979-12-30"}, {44238, 1, 18, "1979-12-31"}, {44239, 0, 19, "1980-01-01"}, {44240, 0, 19, "1980-01-02"}, {44784, 0, 19, "1981-06-29"}, {44785, 1, 19, "1981-06-30"}, {44786, 0, 20, "1981-07-01"}, {44787, 0, 20, "1981-07-02"}, {45149, 0, 20, "1982-06-29"}, {45150, 1, 20, "1982-06-30"}, {45151, 0, 21, "1982-07-01"}, {45152, 0, 21, "1982-07-02"}, {45514, 0, 21, "1983-06-29"}, {45515, 1, 21, "1983-06-30"}, {45516, 0, 22, "1983-07-01"}, {45517, 0, 22, "1983-07-02"}, {46245, 0, 22, "1985-06-29"}, {46246, 1, 22, "1985-06-30"}, {46247, 0, 23, "1985-07-01"}, {46248, 0, 23, "1985-07-02"}, {47159, 0, 23, "1987-12-30"}, {47160, 1, 23, "1987-12-31"}, {47161, 0, 24, "1988-01-01"}, {47162, 0, 24, "1988-01-02"}, {47890, 0, 24, "1989-12-30"}, {47891, 1, 24, "1989-12-31"}, {47892, 0, 25, "1990-01-01"}, {47893, 0, 25, "1990-01-02"}, {48255, 0, 25, "1990-12-30"}, {48256, 1, 25, "1990-12-31"}, {48257, 0, 26, "1991-01-01"}, {48258, 0, 26, "1991-01-02"}, {48802, 0, 26, "1992-06-29"}, {48803, 1, 26, "1992-06-30"}, {48804, 0, 27, "1992-07-01"}, {48805, 0, 27, "1992-07-02"}, {49167, 0, 27, "1993-06-29"}, {49168, 1, 27, "1993-06-30"}, {49169, 0, 28, "1993-07-01"}, {49170, 0, 28, "1993-07-02"}, {49532, 0, 28, "1994-06-29"}, {49533, 1, 28, "1994-06-30"}, {49534, 0, 29, "1994-07-01"}, {49535, 0, 29, "1994-07-02"}, {50081, 0, 29, "1995-12-30"}, {50082, 1, 29, "1995-12-31"}, {50083, 0, 30, "1996-01-01"}, {50084, 0, 30, "1996-01-02"}, {50628, 0, 30, "1997-06-29"}, {50629, 1, 30, "1997-06-30"}, {50630, 0, 31, "1997-07-01"}, {50631, 0, 31, "1997-07-02"}, {51177, 0, 31, "1998-12-30"}, {51178, 1, 31, "1998-12-31"}, {51179, 0, 32, "1999-01-01"}, {51180, 0, 32, "1999-01-02"}, {53734, 0, 32, "2005-12-30"}, {53735, 1, 32, "2005-12-31"}, {53736, 0, 33, "2006-01-01"}, {53737, 0, 33, "2006-01-02"}, {54830, 0, 33, "2008-12-30"}, {54831, 1, 33, "2008-12-31"}, {54832, 0, 34, "2009-01-01"}, {54833, 0, 34, "2009-01-02"}, }; } @Test @UseDataProvider("data_leapSeconds") public void test_leapSeconds(long mjd, int adjust, int offset, String checkDate) { assertEquals(LocalDate.parse(checkDate).getLong(JulianFields.MODIFIED_JULIAN_DAY), mjd); assertEquals(adjust, rules.getLeapSecondAdjustment(mjd)); assertEquals(offset, rules.getTaiOffset(mjd)); if (adjust != 0) { long[] leaps = rules.getLeapSecondDates(); Arrays.sort(leaps); assertEquals(true, Arrays.binarySearch(leaps, mjd) >= 0); } } //----------------------------------------------------------------------- // convertToUtc(TaiInstant)/convertToTai(UtcInstant) //----------------------------------------------------------------------- private static final int CURRENT_TAI_OFFSET = 37; // change this as leap secs added private static final long SECS_PER_DAY = 24 * 60 * 60L; private static final long NANOS_PER_SEC = 1000000000L; private static final long MJD_1800 = -21504L; private static final long MJD_1900 = 15020L; private static final long MJD_1958 = 36204L; private static final long MJD_1980 = 44239L; private static final long MJD_2100 = 88069L; private static final long TAI_SECS_UTC1800 = (MJD_1800 - MJD_1958) * SECS_PER_DAY + 10; private static final long TAI_SECS_UTC1900 = (MJD_1900 - MJD_1958) * SECS_PER_DAY + 10; private static final long TAI_SECS_UTC1958 = 10; private static final long TAI_SECS_UTC1980 = (MJD_1980 - MJD_1958) * SECS_PER_DAY + 19; private static final long TAI_SECS_UTC2100 = (MJD_2100 - MJD_1958) * SECS_PER_DAY + CURRENT_TAI_OFFSET; private static final long TAI_SECS_UTC2100_EXTRA_NEGATIVE_LEAP = (MJD_2100 - MJD_1958) * SECS_PER_DAY + CURRENT_TAI_OFFSET - 1; @Test public void test_convertToUtc_TaiInstant_startUtcPeriod() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1980, 0); // 1980-01-01 (19 leap secs added) UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1980, 0); for (int i = -10; i < 10; i++) { Duration duration = Duration.ofNanos(i); assertEquals(expected.plus(duration), rules.convertToUtc(tai.plus(duration))); assertEquals(tai.plus(duration), rules.convertToTai(expected.plus(duration))); // check reverse } for (int i = -10; i < 10; i++) { Duration duration = Duration.ofSeconds(i); assertEquals(expected.plus(duration), rules.convertToUtc(tai.plus(duration))); assertEquals(tai.plus(duration), rules.convertToTai(expected.plus(duration))); // check reverse } } @Test public void test_convertToUtc_TaiInstant_furtherAfterLeap() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1980 + 1, 0); // 1980-01-01 (19 leap secs added) UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1980, NANOS_PER_SEC); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_justAfterLeap() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1980, 0); // 1980-01-01 (19 leap secs added) UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1980, 0); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_inLeap() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1980 - 1, 0); // 1980-01-01 (1 second before 1980) UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1980 - 1, SECS_PER_DAY * NANOS_PER_SEC); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_justBeforeLeap() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1980 - 2, 0); // 1980-01-01 (2 seconds before 1980) UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1980 - 1, (SECS_PER_DAY - 1) * NANOS_PER_SEC); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_1800() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1800, 0); // 1800-01-01 UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1800, 0); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_1900() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1900, 0); // 1900-01-01 UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1900, 0); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_1958() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC1958, 0); // 1958-01-01 UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_1958, 0); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_convertToUtc_TaiInstant_2100() { TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC2100, 0); // 2100-01-01 UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_2100, 0); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test(expected = NullPointerException.class) public void test_convertToUtc_TaiInstant_null() { rules.convertToUtc((TaiInstant) null); } @Test(expected = NullPointerException.class) public void test_convertToTai_UtcInstant_null() { rules.convertToTai((UtcInstant) null); } //------------------------------------------------------------------------- @Test public void test_negativeLeap_justBeforeLeap() { rules.register(MJD_2100 - 1, -1); TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC2100_EXTRA_NEGATIVE_LEAP - 1, 0); // 2100-01-01 (1 second before 2100) UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_2100 - 1, (SECS_PER_DAY - 2) * NANOS_PER_SEC); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } @Test public void test_negativeLeap_justAfterLeap() { rules.register(MJD_2100 - 1, -1); TaiInstant tai = TaiInstant.ofTaiSeconds(TAI_SECS_UTC2100_EXTRA_NEGATIVE_LEAP, 0); // 2100-01-01 UtcInstant expected = UtcInstant.ofModifiedJulianDay(MJD_2100, 0); assertEquals(expected, rules.convertToUtc(tai)); assertEquals(tai, rules.convertToTai(expected)); // check reverse } //----------------------------------------------------------------------- // convertToUtc(Instant)/convertToInstant(UtcInstant) //----------------------------------------------------------------------- @Test public void test_convertToInstant_justBeforeLeap() { OffsetDateTime odt = OffsetDateTime.of(1979, 12, 31, 23, 43, 21, 0, ZoneOffset.UTC); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay(MJD_1980 - 1, (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC); assertEquals(instant, rules.convertToInstant(utc)); assertEquals(utc, rules.convertToUtc(instant)); } @Test public void test_convertToInstant_sls() { for (int i = 1; i < 1000; i++) { long utcNanos = (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i * 1000000000L; long startSls = (86401 - 1000) * NANOS_PER_SEC; long slsNanos = (utcNanos - (utcNanos - startSls) / 1000); OffsetDateTime odt = OffsetDateTime.of(1979, 12, 31, 0, 0, 0, (int) (slsNanos % NANOS_PER_SEC), ZoneOffset.UTC) .plusSeconds((int) (slsNanos / NANOS_PER_SEC)); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay( MJD_1980 - 1, (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i * NANOS_PER_SEC); assertEquals(instant, rules.convertToInstant(utc)); assertEquals(utc, rules.convertToUtc(instant)); } } @Test public void test_convertToInstant_slsMillis() { for (int i = 1; i < 1000; i++) { long utcNanos = (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i * 1000000; long startSls = (86401 - 1000) * NANOS_PER_SEC; long slsNanos = (utcNanos - (utcNanos - startSls) / 1000); OffsetDateTime odt = OffsetDateTime.of(1979, 12, 31, 23, 43, 21, (int) (slsNanos % NANOS_PER_SEC), ZoneOffset.UTC); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay( MJD_1980 - 1, (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i * 1000000); assertEquals(instant, rules.convertToInstant(utc)); assertEquals(utc, rules.convertToUtc(instant)); } } @Test public void test_convertToInstant_slsMicros() { for (int i = 1; i < 1000; i++) { long utcNanos = (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i * 1000; long startSls = (86401 - 1000) * NANOS_PER_SEC; long slsNanos = (utcNanos - (utcNanos - startSls) / 1000); OffsetDateTime odt = OffsetDateTime.of(1979, 12, 31, 23, 43, 21, (int) (slsNanos % NANOS_PER_SEC), ZoneOffset.UTC); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay( MJD_1980 - 1, (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i * 1000); assertEquals(instant, rules.convertToInstant(utc)); assertEquals(utc, rules.convertToUtc(instant)); } } @Test public void test_convertToInstant_slsNanos() { for (int i = 1; i < 5005; i++) { long utcNanos = (SECS_PER_DAY + 1 - 1000) * NANOS_PER_SEC + i; long startSls = (86401 - 1000) * NANOS_PER_SEC; long slsNanos = (utcNanos - (utcNanos - startSls) / 1000); OffsetDateTime odt = OffsetDateTime.of(1979, 12, 31, 23, 43, 21, (int) (slsNanos % NANOS_PER_SEC), ZoneOffset.UTC); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay(MJD_1980 - 1, utcNanos); assertEquals(instant, rules.convertToInstant(utc)); // not all instants can map back to the correct UTC value long reverseUtcNanos = startSls + ((slsNanos - startSls) * 1000L) / (1000L - 1); assertEquals(UtcInstant.ofModifiedJulianDay(MJD_1980 - 1, reverseUtcNanos), rules.convertToUtc(instant)); } } @Test public void test_convertToInstant_justAfterLeap() { OffsetDateTime odt = OffsetDateTime.of(1980, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay(MJD_1980, 0); assertEquals(instant, rules.convertToInstant(utc)); assertEquals(utc, rules.convertToUtc(instant)); } @Test public void test_convertToInstant_furtherAfterLeap() { OffsetDateTime odt = OffsetDateTime.of(1980, 1, 1, 0, 0, 1, 0, ZoneOffset.UTC); Instant instant = odt.toInstant(); UtcInstant utc = UtcInstant.ofModifiedJulianDay(MJD_1980, NANOS_PER_SEC); assertEquals(instant, rules.convertToInstant(utc)); assertEquals(utc, rules.convertToUtc(instant)); } //----------------------------------------------------------------------- // registerLeapSecond() //----------------------------------------------------------------------- @Test public void test_registerLeapSecond_justAfterLastDate_plusOne() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 1] + 1; rules.register(mjd, 1); long[] test = rules.getLeapSecondDates(); assertEquals(dates.length + 1, test.length); assertEquals(mjd, test[test.length - 1]); assertEquals(1, rules.getLeapSecondAdjustment(mjd)); } @Test public void test_registerLeapSecond_justAfterLastDate_minusOne() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 1] + 1; rules.register(mjd, -1); long[] test = rules.getLeapSecondDates(); assertEquals(dates.length + 1, test.length); assertEquals(mjd, test[test.length - 1]); assertEquals(-1, rules.getLeapSecondAdjustment(mjd)); } @Test public void test_registerLeapSecond_equalLastDate_sameLeap() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 1]; int adj = rules.getLeapSecondAdjustment(mjd); rules.register(mjd, adj); long[] test = rules.getLeapSecondDates(); assertEquals(true, Arrays.equals(test, dates)); assertEquals(adj, rules.getLeapSecondAdjustment(mjd)); } @Test public void test_registerLeapSecond_equalEarlierDate_sameLeap() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 2]; int adj = rules.getLeapSecondAdjustment(mjd); rules.register(mjd, adj); long[] test = rules.getLeapSecondDates(); assertEquals(true, Arrays.equals(test, dates)); assertEquals(adj, rules.getLeapSecondAdjustment(mjd)); } @Test(expected = IllegalArgumentException.class) public void test_registerLeapSecond_equalLastDate_differentLeap() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 1]; int adj = rules.getLeapSecondAdjustment(mjd); rules.register(mjd, -adj); } @Test(expected = IllegalArgumentException.class) public void test_registerLeapSecond_equalEarlierDate_differentLeap() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 2]; int adj = rules.getLeapSecondAdjustment(mjd); rules.register(mjd, -adj); } @Test(expected = IllegalArgumentException.class) public void test_registerLeapSecond_beforeLastDate() { long[] dates = rules.getLeapSecondDates(); long mjd = dates[dates.length - 1] - 1; rules.register(mjd, 1); } @Test(expected = IllegalArgumentException.class) public void test_registerLeapSecond_invalidAdjustment_zero() { rules.register(MJD_2100, 0); } @Test(expected = IllegalArgumentException.class) public void test_registerLeapSecond_invalidAdjustment_minusTwo() { rules.register(MJD_2100, -2); } @Test(expected = IllegalArgumentException.class) public void test_registerLeapSecond_invalidAdjustment_three() { rules.register(MJD_2100, 3); } //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- @Test public void test_toString() { assertEquals("UtcRules[System]", rules.toString()); } }