com.helger.commons.datetime.XMLOffsetTime Maven / Gradle / Ivy
Show all versions of ph-commons Show documentation
/*
* Copyright (C) 2014-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.helger.commons.datetime;
import static java.time.temporal.ChronoField.NANO_OF_DAY;
import static java.time.temporal.ChronoField.OFFSET_SECONDS;
import static java.time.temporal.ChronoUnit.NANOS;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
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.TemporalQueries;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.time.zone.ZoneRules;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import com.helger.commons.CGlobal;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.equals.EqualsHelper;
import com.helger.commons.hashcode.HashCodeGenerator;
/**
* A version of {@link OffsetTime} that has an optional {@link ZoneOffset}
*
* @since 10.1
*/
@Immutable
public class XMLOffsetTime implements Temporal, TemporalAdjuster, Comparable , Serializable
{
/**
* The minimum supported {@code XMLOffsetTime}, '00:00:00+18:00'. This is the
* time of midnight at the start of the day in the maximum offset (larger
* offsets are earlier on the time-line). This combines {@link LocalTime#MIN}
* and {@link ZoneOffset#MAX}. This could be used by an application as a "far
* past" date.
*/
public static final XMLOffsetTime MIN = XMLOffsetTime.of (LocalTime.MIN, ZoneOffset.MAX);
/**
* The maximum supported {@code XMLOffsetTime}, '23:59:59.999999999-18:00'.
* This is the time just before midnight at the end of the day in the minimum
* offset (larger negative offsets are later on the time-line). This combines
* {@link LocalTime#MAX} and {@link ZoneOffset#MIN}. This could be used by an
* application as a "far future" date.
*/
public static final XMLOffsetTime MAX = XMLOffsetTime.of (LocalTime.MAX, ZoneOffset.MIN);
/**
* The local date-time.
*/
private final LocalTime m_aTime;
/**
* The offset from UTC/Greenwich.
*/
private final ZoneOffset m_aOffset;
/**
* Obtains the current time 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 time. The offset will be calculated
* from 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 time using the system clock and default time-zone, not
* null
*/
@Nonnull
public static XMLOffsetTime now ()
{
return now (Clock.systemDefaultZone ());
}
/**
* Obtains the current time from the system clock in the specified time-zone.
*
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the
* current time. Specifying the time-zone avoids dependence on the default
* time-zone. The offset will be calculated from the specified 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 time using the system clock, not null
*/
@Nonnull
public static XMLOffsetTime now (final ZoneId zone)
{
return now (Clock.system (zone));
}
/**
* Obtains the current time from the specified clock.
*
* This will query the specified clock to obtain the current time. The offset
* will be calculated from the time-zone in the clock.
*
* 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 time, not null
*/
@Nonnull
public static XMLOffsetTime now (@Nonnull final Clock clock)
{
Objects.requireNonNull (clock, "clock");
final Instant now = clock.instant (); // called once
return ofInstant (now, clock.getZone ().getRules ().getOffset (now));
}
/**
* Obtains an instance of {@code XMLOffsetTime} from a local time and no
* offset.
*
* @param time
* the local time, not null
* @return the offset time and never null
* @since 11.0.0
*/
@Nonnull
public static XMLOffsetTime of (@Nonnull final LocalTime time)
{
return new XMLOffsetTime (time, null);
}
/**
* Obtains an instance of {@code XMLOffsetTime} from a local time and an
* offset.
*
* @param time
* the local time, not null
* @param offset
* the zone offset, not null
* @return the offset time and never null
*/
@Nonnull
public static XMLOffsetTime of (@Nonnull final LocalTime time, @Nullable final ZoneOffset offset)
{
return new XMLOffsetTime (time, offset);
}
@Nonnull
public static XMLOffsetTime of (@Nonnull final OffsetTime ofsTime)
{
return new XMLOffsetTime (ofsTime.toLocalTime (), ofsTime.getOffset ());
}
/**
* Obtains an instance of {@code XMLOffsetTime} from an hour, minute, second
* and nanosecond.
*
* This creates an offset time with the four specified fields.
*
* This method exists primarily for writing test cases. Non test-code will
* typically use other methods to create an offset time. {@code LocalTime} has
* two additional convenience variants of the equivalent factory method taking
* fewer arguments. They are not provided here to reduce the footprint of the
* API.
*
* @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
* @param nanoOfSecond
* the nano-of-second to represent, from 0 to 999,999,999
* @param offset
* the zone offset, not null
* @return the offset time, not null
* @throws DateTimeException
* if the value of any field is out of range
*/
@Nonnull
public static XMLOffsetTime of (final int hour,
final int minute,
final int second,
final int nanoOfSecond,
@Nullable final ZoneOffset offset)
{
return new XMLOffsetTime (LocalTime.of (hour, minute, second, nanoOfSecond), offset);
}
/**
* Obtains an instance of {@code XMLOffsetTime} from an {@code Instant} and
* zone ID.
*
* This creates an offset time with the same instant as that specified.
* Finding the offset from UTC/Greenwich is simple as there is only one valid
* offset for each instant.
*
* The date component of the instant is dropped during the conversion. This
* means that the conversion can never fail due to the instant being out of
* the valid range of dates.
*
* @param instant
* the instant to create the time from, not null
* @param zone
* the time-zone, which may be an offset, not null
* @return the offset time, not null
*/
@Nonnull
public static XMLOffsetTime ofInstant (@Nonnull final Instant instant, @Nonnull final ZoneId zone)
{
Objects.requireNonNull (instant, "instant");
Objects.requireNonNull (zone, "zone");
final ZoneRules rules = zone.getRules ();
final ZoneOffset offset = rules.getOffset (instant);
// overflow caught later
final long localSecond = instant.getEpochSecond () + offset.getTotalSeconds ();
final int secsOfDay = Math.floorMod (localSecond, CGlobal.SECONDS_PER_DAY);
final LocalTime time = LocalTime.ofNanoOfDay (secsOfDay * CGlobal.NANOSECONDS_PER_SECOND + instant.getNano ());
return new XMLOffsetTime (time, offset);
}
/**
* Obtains an instance of {@code XMLOffsetTime} from a temporal object.
*
* This obtains an offset time 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 XMLOffsetTime}.
*
* The conversion extracts and combines the {@code ZoneOffset} and the
* {@code LocalTime} from the temporal object. Implementations are permitted
* to perform optimizations such as accessing those fields that are equivalent
* to the relevant objects.
*
* This method matches the signature of the functional interface
* {@link TemporalQuery} allowing it to be used as a query via method
* reference, {@code XMLOffsetTime::from}.
*
* @param temporal
* the temporal object to convert, not null
* @return the offset time, not null
* @throws DateTimeException
* if unable to convert to an {@code XMLOffsetTime}
*/
@Nonnull
public static XMLOffsetTime from (@Nonnull final TemporalAccessor temporal)
{
if (temporal instanceof XMLOffsetTime)
return (XMLOffsetTime) temporal;
try
{
final LocalTime time = LocalTime.from (temporal);
ZoneOffset offset;
try
{
offset = ZoneOffset.from (temporal);
}
catch (final DateTimeException ex)
{
offset = null;
}
return new XMLOffsetTime (time, offset);
}
catch (final DateTimeException ex)
{
throw new DateTimeException ("Unable to obtain XMLOffsetTime from TemporalAccessor: " +
temporal +
" of type " +
temporal.getClass ().getName (),
ex);
}
}
/**
* Obtains an instance of {@code XMLOffsetTime} from a text string such as
* {@code 10:15:30+01:00}.
*
* The string must represent a valid time and is parsed using
* {@link java.time.format.DateTimeFormatter#ISO_TIME}.
*
* @param text
* the text to parse such as "10:15:30+01:00", not null
* @return the parsed local time, not null
* @throws DateTimeParseException
* if the text cannot be parsed
*/
@Nonnull
public static XMLOffsetTime parse (@Nonnull final CharSequence text)
{
return parse (text, DateTimeFormatter.ISO_TIME);
}
/**
* Obtains an instance of {@code XMLOffsetTime} from a text string using a
* specific formatter.
*
* The text is parsed using the formatter, returning a time.
*
* @param text
* the text to parse, not null
* @param formatter
* the formatter to use, not null
* @return the parsed offset time, not null
* @throws DateTimeParseException
* if the text cannot be parsed
*/
@Nonnull
public static XMLOffsetTime parse (@Nonnull final CharSequence text, @Nonnull final DateTimeFormatter formatter)
{
Objects.requireNonNull (formatter, "formatter");
return formatter.parse (text, XMLOffsetTime::from);
}
/**
* Constructor.
*
* @param time
* the local time, not null
* @param offset
* the zone offset, maybe null
*/
protected XMLOffsetTime (@Nonnull final LocalTime time, @Nullable final ZoneOffset offset)
{
m_aTime = Objects.requireNonNull (time, "time");
m_aOffset = offset;
}
/**
* Returns a new time based on this one, returning {@code this} where
* possible.
*
* @param time
* the time to create with, not null
* @param offset
* the zone offset to create with, maybe null
* @return {@code this} or the newly created value
*/
@Nonnull
protected XMLOffsetTime with (@Nonnull final LocalTime time, @Nullable final ZoneOffset offset)
{
if (m_aTime == time && EqualsHelper.equals (m_aOffset, offset))
return this;
return new XMLOffsetTime (time, offset);
}
/**
* Checks if the specified field is supported.
*
* This checks if this time 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 NANO_OF_SECOND}
*
- {@code NANO_OF_DAY}
*
- {@code MICRO_OF_SECOND}
*
- {@code MICRO_OF_DAY}
*
- {@code MILLI_OF_SECOND}
*
- {@code MILLI_OF_DAY}
*
- {@code SECOND_OF_MINUTE}
*
- {@code SECOND_OF_DAY}
*
- {@code MINUTE_OF_HOUR}
*
- {@code MINUTE_OF_DAY}
*
- {@code HOUR_OF_AMPM}
*
- {@code CLOCK_HOUR_OF_AMPM}
*
- {@code HOUR_OF_DAY}
*
- {@code CLOCK_HOUR_OF_DAY}
*
- {@code AMPM_OF_DAY}
*
- {@code OFFSET_SECONDS}
*
* 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 time, false if not
*/
@Override
public boolean isSupported (@Nullable final TemporalField field)
{
if (field instanceof ChronoField)
return field.isTimeBased () || field == OFFSET_SECONDS;
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
* offset-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 NANOS}
*
- {@code MICROS}
*
- {@code MILLIS}
*
- {@code SECONDS}
*
- {@code MINUTES}
*
- {@code HOURS}
*
- {@code HALF_DAYS}
*
* 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 // override for Javadoc
public boolean isSupported (@Nullable final TemporalUnit unit)
{
if (unit instanceof ChronoUnit)
return unit.isTimeBased ();
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 time 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
@Nonnull
public ValueRange range (@Nonnull final TemporalField field)
{
if (field instanceof ChronoField)
{
if (field == OFFSET_SECONDS)
return field.range ();
return m_aTime.range (field);
}
return field.rangeRefinedBy (this);
}
/**
* Gets the value of the specified field from this time as an {@code int}.
*
* This queries this time for the value of 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 time, except {@code NANO_OF_DAY} and
* {@code MICRO_OF_DAY} which are too large to fit in an {@code int} and throw
* a {@code DateTimeException}. 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 // override for Javadoc
public int get (@Nonnull final TemporalField field)
{
return Temporal.super.get (field);
}
/**
* Gets the value of the specified field from this time as a {@code long}.
*
* This queries this time for the value of 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 time. 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 (@Nonnull final TemporalField field)
{
if (field instanceof ChronoField)
{
if (field == OFFSET_SECONDS)
return m_aOffset != null ? m_aOffset.getTotalSeconds () : 0;
return m_aTime.getLong (field);
}
return field.getFrom (this);
}
/**
* Gets the zone offset, such as '+01:00'.
*
* This is the offset of the local time from UTC/Greenwich.
*
* @return the zone offset, maybe null
*/
@Nullable
public ZoneOffset getOffset ()
{
return m_aOffset;
}
/**
* @return true
if this date has a zone offset,
* false
if not.
*/
public boolean hasOffset ()
{
return m_aOffset != null;
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified offset
* ensuring that the result has the same local time.
*
* This method returns an object with the same {@code LocalTime} and the
* specified {@code ZoneOffset}. No calculation is needed or performed. For
* example, if this time represents {@code 10:30+02:00} and the offset
* specified is {@code +03:00}, then this method will return
* {@code 10:30+03:00}.
*
* To take into account the difference between the offsets, and adjust the
* time fields, use {@link #withOffsetSameInstant}.
*
* This instance is immutable and unaffected by this method call.
*
* @param offset
* the zone offset to change to, not null
* @return an {@code XMLOffsetTime} based on this time with the requested
* offset, not null
*/
@Nonnull
public XMLOffsetTime withOffsetSameLocal (@Nullable final ZoneOffset offset)
{
return EqualsHelper.equals (offset, m_aOffset) ? this : new XMLOffsetTime (m_aTime, offset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified offset
* ensuring that the result is at the same instant on an implied day.
*
* This method returns an object with the specified {@code ZoneOffset} and a
* {@code LocalTime} adjusted by the difference between the two offsets. This
* will result in the old and new objects representing the same instant on an
* implied day. This is useful for finding the local time in a different
* offset. For example, if this time represents {@code 10:30+02:00} and the
* offset specified is {@code +03:00}, then this method will return
* {@code 11:30+03:00}.
*
* To change the offset without adjusting the local time use
* {@link #withOffsetSameLocal}.
*
* This instance is immutable and unaffected by this method call.
*
* @param offset
* the zone offset to change to, not null
* @return an {@code XMLOffsetTime} based on this time with the requested
* offset, not null
*/
@Nonnull
public XMLOffsetTime withOffsetSameInstant (@Nullable final ZoneOffset offset)
{
if (EqualsHelper.equals (offset, m_aOffset))
return this;
final int difference = (offset != null ? offset.getTotalSeconds () : 0) - (m_aOffset != null ? m_aOffset.getTotalSeconds () : 0);
final LocalTime adjusted = m_aTime.plusSeconds (difference);
return new XMLOffsetTime (adjusted, offset);
}
/**
* Gets the {@code LocalTime} part of this date-time.
*
* This returns a {@code LocalTime} with the same hour, minute, second and
* nanosecond as this date-time.
*
* @return the time part of this date-time, not null
*/
@Nonnull
public LocalTime toLocalTime ()
{
return m_aTime;
}
/**
* Gets the hour-of-day field.
*
* @return the hour-of-day, from 0 to 23
*/
public int getHour ()
{
return m_aTime.getHour ();
}
/**
* Gets the minute-of-hour field.
*
* @return the minute-of-hour, from 0 to 59
*/
public int getMinute ()
{
return m_aTime.getMinute ();
}
/**
* Gets the second-of-minute field.
*
* @return the second-of-minute, from 0 to 59
*/
public int getSecond ()
{
return m_aTime.getSecond ();
}
/**
* Gets the nano-of-second field.
*
* @return the nano-of-second, from 0 to 999,999,999
*/
public int getNano ()
{
return m_aTime.getNano ();
}
/**
* Returns an adjusted copy of this time.
*
* This returns an {@code XMLOffsetTime}, based on this one, with the time
* 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 hour
* field. A more complex adjuster might set the time to the last hour of the
* day.
*
* The classes {@link LocalTime} and {@link ZoneOffset} implement
* {@code TemporalAdjuster}, thus this method can be used to change the time
* or offset:
*
*
* result = offsetTime.with (time);
* result = offsetTime.with (offset);
*
*
* 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 an {@code XMLOffsetTime} 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
@Nonnull
public XMLOffsetTime with (@Nonnull final TemporalAdjuster adjuster)
{
// optimizations
if (adjuster instanceof LocalTime)
return with ((LocalTime) adjuster, m_aOffset);
if (adjuster instanceof ZoneOffset)
return with (m_aTime, (ZoneOffset) adjuster);
if (adjuster instanceof XMLOffsetTime)
return (XMLOffsetTime) adjuster;
return (XMLOffsetTime) adjuster.adjustInto (this);
}
/**
* Returns a copy of this time with the specified field set to a new value.
*
* This returns an {@code XMLOffsetTime}, based on this one, with the value
* for the specified field changed. This can be used to change any supported
* field, such as the hour, minute or second. 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 {@code OFFSET_SECONDS} field will return a time with the specified
* offset. The local time is unaltered. If the new offset value is outside the
* valid range then a {@code DateTimeException} will be thrown.
*
* The other {@link #isSupported(TemporalField) supported fields} will behave
* as per the matching method on {@link LocalTime#with(TemporalField, long)}
* LocalTime}. In this case, the offset is not part of the calculation and
* will be unchanged.
*
* 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 an {@code XMLOffsetTime} 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
@Nonnull
public XMLOffsetTime with (@Nonnull final TemporalField field, final long newValue)
{
if (field instanceof ChronoField)
{
if (field == OFFSET_SECONDS)
{
final ChronoField f = (ChronoField) field;
return with (m_aTime, ZoneOffset.ofTotalSeconds (f.checkValidIntValue (newValue)));
}
return with (m_aTime.with (field, newValue), m_aOffset);
}
return field.adjustInto (this, newValue);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the hour-of-day altered.
*
* The offset does not affect the calculation and will be the same in the
* result.
*
* This instance is immutable and unaffected by this method call.
*
* @param hour
* the hour-of-day to set in the result, from 0 to 23
* @return an {@code XMLOffsetTime} based on this time with the requested
* hour, not null
* @throws DateTimeException
* if the hour value is invalid
*/
@Nonnull
public XMLOffsetTime withHour (final int hour)
{
return with (m_aTime.withHour (hour), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the minute-of-hour
* altered.
*
* The offset does not affect the calculation and will be the same in the
* result.
*
* This instance is immutable and unaffected by this method call.
*
* @param minute
* the minute-of-hour to set in the result, from 0 to 59
* @return an {@code XMLOffsetTime} based on this time with the requested
* minute, not null
* @throws DateTimeException
* if the minute value is invalid
*/
@Nonnull
public XMLOffsetTime withMinute (final int minute)
{
return with (m_aTime.withMinute (minute), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the second-of-minute
* altered.
*
* The offset does not affect the calculation and will be the same in the
* result.
*
* This instance is immutable and unaffected by this method call.
*
* @param second
* the second-of-minute to set in the result, from 0 to 59
* @return an {@code XMLOffsetTime} based on this time with the requested
* second, not null
* @throws DateTimeException
* if the second value is invalid
*/
@Nonnull
public XMLOffsetTime withSecond (final int second)
{
return with (m_aTime.withSecond (second), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the nano-of-second
* altered.
*
* The offset does not affect the calculation and will be the same in the
* result.
*
* This instance is immutable and unaffected by this method call.
*
* @param nanoOfSecond
* the nano-of-second to set in the result, from 0 to 999,999,999
* @return an {@code XMLOffsetTime} based on this time with the requested
* nanosecond, not null
* @throws DateTimeException
* if the nanos value is invalid
*/
@Nonnull
public XMLOffsetTime withNano (final int nanoOfSecond)
{
return with (m_aTime.withNano (nanoOfSecond), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the time truncated.
*
* Truncation returns a copy of the original time with fields smaller than the
* specified unit set to zero. For example, truncating with the
* {@link ChronoUnit#MINUTES minutes} unit will set the second-of-minute and
* nano-of-second field to zero.
*
* The unit must have a {@linkplain TemporalUnit#getDuration() duration} that
* divides into the length of a standard day without remainder. This includes
* all supplied time units on {@link ChronoUnit} and {@link ChronoUnit#DAYS
* DAYS}. Other units throw an exception.
*
* The offset does not affect the calculation and will be the same in the
* result.
*
* This instance is immutable and unaffected by this method call.
*
* @param unit
* the unit to truncate to, not null
* @return an {@code XMLOffsetTime} based on this time with the time
* truncated, not null
* @throws DateTimeException
* if unable to truncate
* @throws UnsupportedTemporalTypeException
* if the unit is not supported
*/
@Nonnull
public XMLOffsetTime truncatedTo (@Nonnull final TemporalUnit unit)
{
return with (m_aTime.truncatedTo (unit), m_aOffset);
}
/**
* Returns a copy of this time with the specified amount added.
*
* This returns an {@code XMLOffsetTime}, based on this one, with the
* specified amount added. The amount is typically {@link Duration} 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 an {@code XMLOffsetTime} based on this time with the addition made,
* not null
* @throws DateTimeException
* if the addition cannot be made
* @throws ArithmeticException
* if numeric overflow occurs
*/
@Override
@Nonnull
public XMLOffsetTime plus (@Nonnull final TemporalAmount amountToAdd)
{
return (XMLOffsetTime) amountToAdd.addTo (this);
}
/**
* Returns a copy of this time with the specified amount added.
*
* This returns an {@code XMLOffsetTime}, 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 by
* {@link LocalTime#plus(long, TemporalUnit)}. The offset is not part of the
* calculation and will be unchanged in the result.
*
* 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 an {@code XMLOffsetTime} based on this time 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
@Nonnull
public XMLOffsetTime plus (final long amountToAdd, @Nonnull final TemporalUnit unit)
{
if (unit instanceof ChronoUnit)
return with (m_aTime.plus (amountToAdd, unit), m_aOffset);
return unit.addTo (this, amountToAdd);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* hours added.
*
* This adds the specified number of hours to this time, returning a new time.
* The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param hours
* the hours to add, may be negative
* @return an {@code XMLOffsetTime} based on this time with the hours added,
* not null
*/
@Nonnull
public XMLOffsetTime plusHours (final long hours)
{
return with (m_aTime.plusHours (hours), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* minutes added.
*
* This adds the specified number of minutes to this time, returning a new
* time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param minutes
* the minutes to add, may be negative
* @return an {@code XMLOffsetTime} based on this time with the minutes added,
* not null
*/
@Nonnull
public XMLOffsetTime plusMinutes (final long minutes)
{
return with (m_aTime.plusMinutes (minutes), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* seconds added.
*
* This adds the specified number of seconds to this time, returning a new
* time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param seconds
* the seconds to add, may be negative
* @return an {@code XMLOffsetTime} based on this time with the seconds added,
* not null
*/
@Nonnull
public XMLOffsetTime plusSeconds (final long seconds)
{
return with (m_aTime.plusSeconds (seconds), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* nanoseconds added.
*
* This adds the specified number of nanoseconds to this time, returning a new
* time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param nanos
* the nanos to add, may be negative
* @return an {@code XMLOffsetTime} based on this time with the nanoseconds
* added, not null
*/
@Nonnull
public XMLOffsetTime plusNanos (final long nanos)
{
return with (m_aTime.plusNanos (nanos), m_aOffset);
}
/**
* Returns a copy of this time with the specified amount subtracted.
*
* This returns an {@code XMLOffsetTime}, based on this one, with the
* specified amount subtracted. The amount is typically {@link Duration} 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 an {@code XMLOffsetTime} based on this time with the subtraction
* made, not null
* @throws DateTimeException
* if the subtraction cannot be made
* @throws ArithmeticException
* if numeric overflow occurs
*/
@Override
@Nonnull
public XMLOffsetTime minus (@Nonnull final TemporalAmount amountToSubtract)
{
return (XMLOffsetTime) amountToSubtract.subtractFrom (this);
}
/**
* Returns a copy of this time with the specified amount subtracted.
*
* This returns an {@code XMLOffsetTime}, 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 an {@code XMLOffsetTime} based on this time 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
@Nonnull
public XMLOffsetTime minus (final long amountToSubtract, @Nonnull final TemporalUnit unit)
{
return amountToSubtract == Long.MIN_VALUE ? plus (Long.MAX_VALUE, unit).plus (1, unit) : plus (-amountToSubtract, unit);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* hours subtracted.
*
* This subtracts the specified number of hours from this time, returning a
* new time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param hours
* the hours to subtract, may be negative
* @return an {@code XMLOffsetTime} based on this time with the hours
* subtracted, not null
*/
@Nonnull
public XMLOffsetTime minusHours (final long hours)
{
return with (m_aTime.minusHours (hours), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* minutes subtracted.
*
* This subtracts the specified number of minutes from this time, returning a
* new time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param minutes
* the minutes to subtract, may be negative
* @return an {@code XMLOffsetTime} based on this time with the minutes
* subtracted, not null
*/
@Nonnull
public XMLOffsetTime minusMinutes (final long minutes)
{
return with (m_aTime.minusMinutes (minutes), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* seconds subtracted.
*
* This subtracts the specified number of seconds from this time, returning a
* new time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param seconds
* the seconds to subtract, may be negative
* @return an {@code XMLOffsetTime} based on this time with the seconds
* subtracted, not null
*/
@Nonnull
public XMLOffsetTime minusSeconds (final long seconds)
{
return with (m_aTime.minusSeconds (seconds), m_aOffset);
}
/**
* Returns a copy of this {@code XMLOffsetTime} with the specified number of
* nanoseconds subtracted.
*
* This subtracts the specified number of nanoseconds from this time,
* returning a new time. The calculation wraps around midnight.
*
* This instance is immutable and unaffected by this method call.
*
* @param nanos
* the nanos to subtract, may be negative
* @return an {@code XMLOffsetTime} based on this time with the nanoseconds
* subtracted, not null
*/
@Nonnull
public XMLOffsetTime minusNanos (final long nanos)
{
return with (m_aTime.minusNanos (nanos), m_aOffset);
}
@Nonnull
protected ZoneOffset getOffsetOrDefault ()
{
ZoneOffset ret = m_aOffset;
if (ret == null)
{
// We don't have a date to reasonable determine a time zone
ret = ZoneOffset.UTC;
}
return ret;
}
@Nonnull
protected ZoneOffset getOffsetOrDefault (@Nonnull final LocalDateTime aAt)
{
ZoneOffset ret = m_aOffset;
if (ret == null)
{
// Find the match
ret = PDTConfig.getDefaultZoneId ().getRules ().getOffset (aAt);
}
return ret;
}
/**
* Queries this time using the specified query.
*
* This queries this time 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
@Nonnull
public R query (@Nonnull final TemporalQuery query)
{
if (query == TemporalQueries.offset () || query == TemporalQueries.zone ())
return (R) getOffsetOrDefault ();
if (query == TemporalQueries.zoneId () || query == TemporalQueries.chronology () || query == TemporalQueries.localDate ())
return null;
if (query == TemporalQueries.localTime ())
return (R) m_aTime;
if (query == TemporalQueries.precision ())
return (R) NANOS;
// inline TemporalAccessor.super.query(query) as an optimization
// non-JDK classes are not permitted to make this optimization
return query.queryFrom (this);
}
/**
* Adjusts the specified temporal object to have the same offset and time as
* this object.
*
* This returns a temporal object of the same observable type as the input
* with the offset and time changed to be the same as this.
*
* The adjustment is equivalent to using
* {@link Temporal#with(TemporalField, long)} twice, passing
* {@link ChronoField#NANO_OF_DAY} and {@link ChronoField#OFFSET_SECONDS} as
* the fields.
*
* 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 = thisOffsetTime.adjustInto (temporal);
* temporal = temporal.with (thisOffsetTime);
*
*
* 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
@Nonnull
public Temporal adjustInto (@Nonnull final Temporal temporal)
{
return temporal.with (NANO_OF_DAY, m_aTime.toNanoOfDay ()).with (OFFSET_SECONDS, m_aOffset != null ? m_aOffset.getTotalSeconds () : 0);
}
/**
* Calculates the amount of time until another time in terms of the specified
* unit.
*
* This calculates the amount of time between two {@code XMLOffsetTime}
* objects in terms of a single {@code TemporalUnit}. The start and end points
* are {@code this} and the specified time. The result will be negative if the
* end is before the start. For example, the amount in hours between two times
* can be calculated using {@code startTime.until(endTime, HOURS)}.
*
* The {@code Temporal} passed to this method is converted to a
* {@code XMLOffsetTime} using {@link #from(TemporalAccessor)}. If the offset
* differs between the two times, then the specified end time is normalized to
* have the same offset as this time.
*
* The calculation returns a whole number, representing the number of complete
* units between the two times. For example, the amount in hours between
* 11:30Z and 13:29Z will only be one hour as it is one minute short of two
* hours.
*
* 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, MINUTES);
* amount = MINUTES.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 NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
* {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS} 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 time, exclusive, which is converted to an
* {@code XMLOffsetTime}, not null
* @param unit
* the unit to measure the amount in, not null
* @return the amount of time between this time and the end time
* @throws DateTimeException
* if the amount cannot be calculated, or the end temporal cannot be
* converted to an {@code XMLOffsetTime}
* @throws UnsupportedTemporalTypeException
* if the unit is not supported
* @throws ArithmeticException
* if numeric overflow occurs
*/
@Override
public long until (@Nonnull final Temporal endExclusive, @Nonnull final TemporalUnit unit)
{
final XMLOffsetTime end = XMLOffsetTime.from (endExclusive);
if (unit instanceof ChronoUnit)
{
// no overflow
final long nanosUntil = end.toEpochNano () - toEpochNano ();
switch ((ChronoUnit) unit)
{
case NANOS:
return nanosUntil;
case MICROS:
return nanosUntil / 1_000;
case MILLIS:
return nanosUntil / 1_000_000;
case SECONDS:
return nanosUntil / CGlobal.NANOSECONDS_PER_SECOND;
case MINUTES:
return nanosUntil / CGlobal.NANOSECONDS_PER_MINUTE;
case HOURS:
return nanosUntil / CGlobal.NANOSECONDS_PER_HOUR;
case HALF_DAYS:
return nanosUntil / (12 * CGlobal.NANOSECONDS_PER_HOUR);
}
throw new UnsupportedTemporalTypeException ("Unsupported unit: " + unit);
}
return unit.between (this, end);
}
/**
* Formats this time using the specified formatter.
*
* This time will be passed to the formatter to produce a string.
*
* @param formatter
* the formatter to use, not null
* @return the formatted time string, not null
* @throws DateTimeException
* if an error occurs during printing
*/
@Nonnull
public String format (@Nonnull final DateTimeFormatter formatter)
{
Objects.requireNonNull (formatter, "formatter");
return formatter.format (this);
}
/**
* Combines this time with a date to create an {@code OffsetDateTime}.
*
* This returns an {@code OffsetDateTime} formed from this time and the
* specified date. All possible combinations of date and time are valid.
*
* @param date
* the date to combine with, not null
* @return the offset date-time formed from this time and the specified date,
* not null
*/
@Nonnull
public OffsetDateTime atDate (@Nonnull final LocalDate date)
{
final LocalDateTime aLDT = date.atTime (m_aTime);
return OffsetDateTime.of (aLDT, getOffsetOrDefault (aLDT));
}
/**
* Combines this time with a date to create an {@code XMLOffsetDateTime}.
*
* This returns an {@code OffsetDateTime} formed from this time and the
* specified date. All possible combinations of date and time are valid.
*
* @param date
* the date to combine with, not null
* @return the offset date-time formed from this time and the specified date,
* not null
*/
@Nonnull
public XMLOffsetDateTime atXMLDate (@Nonnull final LocalDate date)
{
return XMLOffsetDateTime.of (date, m_aTime, m_aOffset);
}
/**
* Converts this time to epoch nanos based on 1970-01-01Z.
*
* @return the epoch nanos value
*/
protected long toEpochNano ()
{
final long nod = m_aTime.toNanoOfDay ();
final long offsetNanos = m_aOffset != null ? m_aOffset.getTotalSeconds () * CGlobal.NANOSECONDS_PER_SECOND : 0;
return nod - offsetNanos;
}
/**
* Compares this {@code XMLOffsetTime} to another time.
*
* The comparison is based first on the UTC equivalent instant, then on the
* local time. It is "consistent with equals", as defined by
* {@link Comparable}.
*
* For example, the following is the comparator order:
*
* - {@code 10:30+01:00}
* - {@code 11:00+01:00}
* - {@code 12:00+02:00}
* - {@code 11:30+01:00}
* - {@code 12:00+01:00}
* - {@code 12:30+01:00}
*
* Values #2 and #3 represent the same instant on the time-line. When two
* values represent the same instant, the local time is compared to
* distinguish them. This step is needed to make the ordering consistent with
* {@code equals()}.
*
* To compare the underlying local time of two {@code TemporalAccessor}
* instances, use {@link ChronoField#NANO_OF_DAY} as a comparator.
*
* @param o
* 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
*/
@Override
public int compareTo (@Nonnull final XMLOffsetTime o)
{
if (EqualsHelper.equals (m_aOffset, o.m_aOffset))
return m_aTime.compareTo (o.m_aTime);
int ret = Long.compare (toEpochNano (), o.toEpochNano ());
if (ret == 0)
ret = m_aTime.compareTo (o.m_aTime);
return ret;
}
/**
* Checks if the instant of this {@code XMLOffsetTime} is after that of the
* specified time applying both times to a common date.
*
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the instant of the time. This is equivalent to converting
* both times to an instant using the same date and comparing the instants.
*
* @param other
* the other time to compare to, not null
* @return true if this is after the instant of the specified time
*/
public boolean isAfter (@Nonnull final XMLOffsetTime other)
{
return toEpochNano () > other.toEpochNano ();
}
/**
* Checks if the instant of this {@code XMLOffsetTime} is before that of the
* specified time applying both times to a common date.
*
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the instant of the time. This is equivalent to converting
* both times to an instant using the same date and comparing the instants.
*
* @param other
* the other time to compare to, not null
* @return true if this is before the instant of the specified time
*/
public boolean isBefore (@Nonnull final XMLOffsetTime other)
{
return toEpochNano () < other.toEpochNano ();
}
/**
* Checks if the instant of this {@code XMLOffsetTime} is equal to that of the
* specified time applying both times to a common date.
*
* This method differs from the comparison in {@link #compareTo} and
* {@link #equals} in that it only compares the instant of the time. This is
* equivalent to converting both times to an instant using the same date and
* comparing the instants.
*
* @param other
* the other time to compare to, not null
* @return true if this is equal to the instant of the specified time
*/
public boolean isEqual (@Nonnull final XMLOffsetTime other)
{
return toEpochNano () == other.toEpochNano ();
}
@Nullable
public OffsetTime toOffsetTime ()
{
return OffsetTime.of (m_aTime, getOffsetOrDefault ());
}
/**
* Checks if this time is equal to another time.
*
* The comparison is based on the local-time and the offset. To compare for
* the same instant on the time-line, use {@link #isEqual(XMLOffsetTime)}.
*
* Only objects of type {@code XMLOffsetTime} are compared, other types return
* false. To compare the underlying local time of two {@code TemporalAccessor}
* instances, use {@link ChronoField#NANO_OF_DAY} as a comparator.
*
* @param o
* the object to check, null returns false
* @return true if this is equal to the other time
*/
@Override
public boolean equals (final Object o)
{
if (this == o)
return true;
if (o == null || !getClass ().equals (o.getClass ()))
return false;
final XMLOffsetTime other = (XMLOffsetTime) o;
return m_aTime.equals (other.m_aTime) && EqualsHelper.equals (m_aOffset, other.m_aOffset);
}
/**
* A hash code for this time.
*
* @return a suitable hash code
*/
@Override
public int hashCode ()
{
return new HashCodeGenerator (this).append (m_aTime).append (m_aOffset).getHashCode ();
}
// Don't use "getAsString" for compatibility with the rest of the Java DT API
@Nonnull
@Nonempty
@Deprecated (forRemoval = false)
public String getAsString ()
{
return toString ();
}
/**
* Outputs this time as a {@code String}, such as {@code 10:15:30+01:00}.
*
* The output will be one of the following ISO-8601 formats:
*
* - {@code HH:mm}
* - {@code HH:mm:ss}
* - {@code HH:mm:ss.SSS}
* - {@code HH:mm:ss.SSSSSS}
* - {@code HH:mm:ss.SSSSSSSSS}
* - {@code HH:mmXXXXX}
* - {@code HH:mm:ssXXXXX}
* - {@code HH:mm:ss.SSSXXXXX}
* - {@code HH:mm:ss.SSSSSSXXXXX}
* - {@code HH:mm:ss.SSSSSSSSSXXXXX}
*
* The format used will be the shortest that outputs the full value of the
* time where the omitted parts are implied to be zero.
*
* @return a string representation of this time, not null
*/
@Override
public String toString ()
{
return m_aTime.toString () + (m_aOffset != null ? m_aOffset.toString () : "");
}
}