/* * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file: * * Copyright (c) 2007-2012, 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 build.tools.tzdb; import static build.tools.tzdb.ChronoField.HOUR_OF_DAY; import static build.tools.tzdb.ChronoField.MINUTE_OF_HOUR; import static build.tools.tzdb.ChronoField.SECOND_OF_MINUTE; import static build.tools.tzdb.ChronoField.SECOND_OF_DAY; import java.util.Objects; /** * A time without time-zone in the ISO-8601 calendar system, * such as {@code 10:15:30}. * */ final class LocalTime { /** * The minimum supported {@code LocalTime}, '00:00'. * This is the time of midnight at the start of the day. */ public static final LocalTime MIN; /** * The minimum supported {@code LocalTime}, '23:59:59.999999999'. * This is the time just before midnight at the end of the day. */ public static final LocalTime MAX; /** * The time of midnight at the start of the day, '00:00'. */ public static final LocalTime MIDNIGHT; /** * The time of noon in the middle of the day, '12:00'. */ public static final LocalTime NOON; /** * Constants for the local time of each hour. */ private static final LocalTime[] HOURS = new LocalTime[24]; static { for (int i = 0; i < HOURS.length; i++) { HOURS[i] = new LocalTime(i, 0, 0); } MIDNIGHT = HOURS[0]; NOON = HOURS[12]; MIN = HOURS[0]; MAX = new LocalTime(23, 59, 59); } /** * Hours per day. */ static final int HOURS_PER_DAY = 24; /** * Minutes per hour. */ static final int MINUTES_PER_HOUR = 60; /** * Minutes per day. */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY; /** * Seconds per minute. */ static final int SECONDS_PER_MINUTE = 60; /** * Seconds per hour. */ static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; /** * Seconds per day. */ static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; /** * Milliseconds per day. */ static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L; /** * Microseconds per day. */ static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L; /** * The hour. */ private final byte hour; /** * The minute. */ private final byte minute; /** * The second. */ private final byte second; /** * Obtains an instance of {@code LocalTime} from an hour and minute. * <p> * The second and nanosecond fields will be set to zero by this factory method. * <p> * This factory may return a cached value, but applications must not rely on this. * * @param hour the hour-of-day to represent, from 0 to 23 * @param minute the minute-of-hour to represent, from 0 to 59 * @return the local time, not null * @throws DateTimeException if the value of any field is out of range */ public static LocalTime of(int hour, int minute) { HOUR_OF_DAY.checkValidValue(hour); if (minute == 0) { return HOURS[hour]; // for performance } MINUTE_OF_HOUR.checkValidValue(minute); return new LocalTime(hour, minute, 0); } /** * Obtains an instance of {@code LocalTime} from an hour, minute and second. * <p> * The nanosecond field will be set to zero by this factory method. * <p> * This factory may return a cached value, but applications must not rely on this. * * @param hour the hour-of-day to represent, from 0 to 23 * @param minute the minute-of-hour to represent, from 0 to 59 * @param second the second-of-minute to represent, from 0 to 59 * @return the local time, not null * @throws DateTimeException if the value of any field is out of range */ public static LocalTime of(int hour, int minute, int second) { HOUR_OF_DAY.checkValidValue(hour); if ((minute | second) == 0) { return HOURS[hour]; // for performance } MINUTE_OF_HOUR.checkValidValue(minute); SECOND_OF_MINUTE.checkValidValue(second); return new LocalTime(hour, minute, second); } /** * Obtains an instance of {@code LocalTime} from a second-of-day value. * <p> * This factory may return a cached value, but applications must not rely on this. * * @param secondOfDay the second-of-day, from {@code 0} to {@code 24 * 60 * 60 - 1} * @return the local time, not null * @throws DateTimeException if the second-of-day value is invalid */ public static LocalTime ofSecondOfDay(int secondOfDay) { SECOND_OF_DAY.checkValidValue(secondOfDay); int hours = secondOfDay / SECONDS_PER_HOUR; secondOfDay -= hours * SECONDS_PER_HOUR; int minutes = secondOfDay / SECONDS_PER_MINUTE; secondOfDay -= minutes * SECONDS_PER_MINUTE; return create(hours, minutes, secondOfDay); } /** * Creates a local time from the hour, minute, second and nanosecond fields. * <p> * This factory may return a cached value, but applications must not rely on this. * * @param hour the hour-of-day to represent, validated from 0 to 23 * @param minute the minute-of-hour to represent, validated from 0 to 59 * @param second the second-of-minute to represent, validated from 0 to 59 * @return the local time, not null */ private static LocalTime create(int hour, int minute, int second) { if ((minute | second) == 0) { return HOURS[hour]; } return new LocalTime(hour, minute, second); } /** * Constructor, previously validated. * * @param hour the hour-of-day to represent, validated from 0 to 23 * @param minute the minute-of-hour to represent, validated from 0 to 59 * @param second the second-of-minute to represent, validated from 0 to 59 */ private LocalTime(int hour, int minute, int second) { this.hour = (byte) hour; this.minute = (byte) minute; this.second = (byte) second; } /** * Gets the hour-of-day field. * * @return the hour-of-day, from 0 to 23 */ public int getHour() { return hour; } /** * Gets the minute-of-hour field. * * @return the minute-of-hour, from 0 to 59 */ public int getMinute() { return minute; } /** * Gets the second-of-minute field. * * @return the second-of-minute, from 0 to 59 */ public int getSecond() { return second; } /** * Returns a copy of this {@code LocalTime} with the specified period in seconds added. * <p> * This adds the specified number of seconds to this time, returning a new time. * The calculation wraps around midnight. * <p> * This instance is immutable and unaffected by this method call. * * @param secondstoAdd the seconds to add, may be negative * @return a {@code LocalTime} based on this time with the seconds added, not null */ public LocalTime plusSeconds(long secondstoAdd) { if (secondstoAdd == 0) { return this; } int sofd = hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; int newSofd = ((int) (secondstoAdd % SECONDS_PER_DAY) + sofd + SECONDS_PER_DAY) % SECONDS_PER_DAY; if (sofd == newSofd) { return this; } int newHour = newSofd / SECONDS_PER_HOUR; int newMinute = (newSofd / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR; int newSecond = newSofd % SECONDS_PER_MINUTE; return create(newHour, newMinute, newSecond); } /** * Returns a copy of this {@code LocalTime} with the specified period in seconds subtracted. * <p> * This subtracts the specified number of seconds from this time, returning a new time. * The calculation wraps around midnight. * <p> * This instance is immutable and unaffected by this method call. * * @param secondsToSubtract the seconds to subtract, may be negative * @return a {@code LocalTime} based on this time with the seconds subtracted, not null */ public LocalTime minusSeconds(long secondsToSubtract) { return plusSeconds(-(secondsToSubtract % SECONDS_PER_DAY)); } /** * Extracts the time as seconds of day, * from {@code 0} to {@code 24 * 60 * 60 - 1}. * * @return the second-of-day equivalent to this time */ public int toSecondOfDay() { int total = hour * SECONDS_PER_HOUR; total += minute * SECONDS_PER_MINUTE; total += second; return total; } /** * Compares this {@code LocalTime} to another time. * <p> * The comparison is based on the time-line position of the local times within a day. * It is "consistent with equals", as defined by {@link Comparable}. * * @param other the other time to compare to, not null * @return the comparator value, negative if less, positive if greater * @throws NullPointerException if {@code other} is null */ public int compareTo(LocalTime other) { int cmp = Integer.compare(hour, other.hour); if (cmp == 0) { cmp = Integer.compare(minute, other.minute); if (cmp == 0) { cmp = Integer.compare(second, other.second); } } return cmp; } /** * Checks if this time is equal to another time. * <p> * The comparison is based on the time-line position of the time within a day. * <p> * Only objects of type {@code LocalTime} are compared, other types return false. * To compare the date of two {@code TemporalAccessor} instances, use * {@link ChronoField#NANO_OF_DAY} as a comparator. * * @param obj the object to check, null returns false * @return true if this is equal to the other time */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof LocalTime) { LocalTime other = (LocalTime) obj; return hour == other.hour && minute == other.minute && second == other.second; } return false; } /** * A hash code for this time. * * @return a suitable hash code */ @Override public int hashCode() { long sod = toSecondOfDay(); return (int) (sod ^ (sod >>> 32)); } }