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

org.threeten.extra.scale.TaiInstant Maven / Gradle / Ivy

Go to download

Additional functionality that enhances JSR-310 dates and times in Java SE 8 and later

There is a newer version: 1.8.0
Show newest version
/*
 * 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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy