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

com.altoros.temporal.iso8601.ZuluDateTime Maven / Gradle / Ivy

package com.altoros.temporal.iso8601;

import java.io.Serializable;
import java.time.Clock;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.ValueRange;
import java.util.Objects;

import com.altoros.temporal.MillisecTimeSource;
import com.altoros.temporal.iso8601.gson.GsonTypeAdapterZuluDateTime;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.gson.annotations.JsonAdapter;
import org.litesoft.annotations.NotNull;
import org.litesoft.annotations.Nullable;
import org.litesoft.annotations.Significant;
import org.litesoft.annotations.SignificantOrNull;

/**
 * This class provides the appropriate support for handling ISO8601 formatted DateTimes which are always based on
 * UTC/Zulu Time Zone and when rendered to a String (JSON) will always produce Millisec resolution.
 * 

* The methods of this class are a mixture of those found in LocalDateTime & Instant. *

* Notes:
*

  • The LocalDateTime produced by toLocalDateTime() is assumed to remain in the UTC 'time zone'.
  • *
  • Since the math methods are NOT supported directly, it is recommended to convert instances of this class into * a LocalDateTime, manipulate that, and then convert the changed LocalDateTime back into a new ZuluDateTime.
  • *
  • Should an instance of this class be created (e.g. Java Serialization) without the appropriate internal values, all references to the methods * of this instance will return values as if it was initialized with: -999080706-05-04T03:02:01.000Z
  • */ @SuppressWarnings({"WeakerAccess", "unused"}) @JsonAdapter(GsonTypeAdapterZuluDateTime.class) public final class ZuluDateTime implements Comparable, TemporalAccessor, Serializable { public static final ZoneOffset UTC_ZONE_OFFSET = ZoneOffset.UTC; public static final ZoneId UTC_ZONE_ID = ZoneId.ofOffset( "UTC", UTC_ZONE_OFFSET ); public static final LocalDateTime INVALID_INITIALIZATION_INDICATOR_LOCAL_DATE_TIME_VALUE = LocalDateTime .of( -999080706, 5, 4, 3, 2, 1 ); // -999080706-05-04T03:02:01.000Z public static final Instant INVALID_INITIALIZATION_INDICATOR_INSTANT_VALUE = Instant .ofEpochSecond( INVALID_INITIALIZATION_INDICATOR_LOCAL_DATE_TIME_VALUE.toEpochSecond( UTC_ZONE_OFFSET ) ); public static final DateTimeFormatter RENDERING_ISO_8601_3_FACTIONAL_SEC = new DateTimeFormatterBuilder() .appendInstant( 3 ) .toFormatter(); /** * Serialization version. */ private static final long serialVersionUID = 1L; private final Instant mInstant; private transient volatile LocalDateTime mLocalDateTime; // Don't Serialize - Note: 'volatile' supports near Singleton /** * Create a ZuluDateTime from the required Instant (note: since ZuluDateTime's resolution is only to the Millisec, the Instant may get truncated). * * @param pInstant not null */ public ZuluDateTime( @NotNull Instant pInstant ) { mInstant = Objects.requireNonNull( pInstant, "Instant" ).truncatedTo( ChronoUnit.MILLIS ); } /** * Create a ZuluDateTime from the optional Clock. *

    * This will query the specified clock to obtain the current time. *

    * * @param pClock if null the Clock.systemUTC() will be used * * @return not null */ public static ZuluDateTime now( @Nullable Clock pClock ) { return now( MillisecTimeSource.from( pClock ) ); } /** * Create a ZuluDateTime from the optional Clock. *

    * This will query the specified clock to obtain the current time. *

    * * @param pMillisecTimeSource not null * * @return not null */ public static ZuluDateTime now( @NotNull MillisecTimeSource pMillisecTimeSource ) { return new ZuluDateTime( Instant.ofEpochMilli( Objects.requireNonNull( pMillisecTimeSource, "MillisecTimeSource" ).currentTimeMillis() ) ); } /** * Create a ZuluDateTime from the text such as {@code 2007-12-03T10:15:30.00Z} (note: since ZuluDateTime's resolution is only to the Millisec, some portion of the fractional second may be lost). *

    * The text is parsed using {@link DateTimeFormatter#ISO_INSTANT}. * * @param pText the text to parse, must be significant after trimming * * @return not null * * @throws DateTimeParseException if the text cannot be parsed */ @NotNull public static ZuluDateTime parse( @Significant CharSequence pText ) { return new ZuluDateTime( Instant.parse( significant( pText ) ) ); } // ------------ vvv of field constructors (See LocalDateTime) of & to vvv ------------ public static ZuluDateTime of( int year, Month month, int dayOfMonth, int hour, int minute ) { return ofLocalDateTime( LocalDateTime.of( year, month, dayOfMonth, hour, minute ) ); } public static ZuluDateTime of( int year, Month month, int dayOfMonth, int hour, int minute, int second ) { return ofLocalDateTime( LocalDateTime.of( year, month, dayOfMonth, hour, minute, second ) ); } public static ZuluDateTime of( int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond ) { return ofLocalDateTime( LocalDateTime.of( year, month, dayOfMonth, hour, minute, second, nanoOfSecond ) ); } public static ZuluDateTime of( int year, int month, int dayOfMonth, int hour, int minute ) { return ofLocalDateTime( LocalDateTime.of( year, month, dayOfMonth, hour, minute ) ); } public static ZuluDateTime of( int year, int month, int dayOfMonth, int hour, int minute, int second ) { return ofLocalDateTime( LocalDateTime.of( year, month, dayOfMonth, hour, minute, second ) ); } public static ZuluDateTime of( int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond ) { return ofLocalDateTime( LocalDateTime.of( year, month, dayOfMonth, hour, minute, second, nanoOfSecond ) ); } public static ZuluDateTime of( LocalDate date, LocalTime time ) { return ofLocalDateTime( LocalDateTime.of( date, time ) ); } // ------------ ^^^ of field constructors (See LocalDateTime) of & to ^^^ ------------ // ----------------------------- vvv JsonCreator & JsonValue vvv --------------------- @JsonCreator @Nullable public static ZuluDateTime jsonCreator( String pText ) { if ( pText != null ) { pText = pText.trim(); if ( !pText.isEmpty() ) { return ofString( pText ); } } return null; } @JsonValue @SignificantOrNull public String jsonValue() { return renderJson(); } // ----------------------------- ^^^ JsonCreator & JsonValue ^^^ --------------------- private String renderJson() { return (mInstant == null) ? null : RENDERING_ISO_8601_3_FACTIONAL_SEC.format( mInstant ); } // --------------------------------- vvv of & to vvv --------------------------------- /** * See parse(...)! */ @NotNull public static ZuluDateTime ofString( @Significant String pText ) { return parse( pText ); } @Override @Significant public String toString() { return renderJson(); } @NotNull public static ZuluDateTime ofInstant( @NotNull Instant pInstant ) { return new ZuluDateTime( pInstant ); } @NotNull public Instant toInstant() { return (mInstant != null) ? mInstant : INVALID_INITIALIZATION_INDICATOR_INSTANT_VALUE; } @NotNull public static ZuluDateTime ofLocalDateTime( @NotNull LocalDateTime pDateTime ) { return ofInstant( Objects.requireNonNull( pDateTime, "DateTime" ).toInstant( UTC_ZONE_OFFSET ) ); } @NotNull public LocalDateTime toLocalDateTime() { LocalDateTime zLocalDateTime = mLocalDateTime; if ( zLocalDateTime == null ) { if ( mInstant == null ) { return INVALID_INITIALIZATION_INDICATOR_LOCAL_DATE_TIME_VALUE; } mLocalDateTime = zLocalDateTime = LocalDateTime.ofInstant( mInstant, UTC_ZONE_OFFSET ); } return zLocalDateTime; } @NotNull public static ZuluDateTime ofOffsetDateTime( @NotNull OffsetDateTime pDateTime ) { return ofInstant( Objects.requireNonNull( pDateTime, "DateTime" ).toInstant() ); } @NotNull public OffsetDateTime toOffsetDateTime() { return OffsetDateTime.of( toLocalDateTime(), UTC_ZONE_OFFSET ); } @NotNull public static ZuluDateTime ofZonedDateTime( @NotNull ZonedDateTime pDateTime ) { return ofInstant( Objects.requireNonNull( pDateTime, "DateTime" ).toInstant() ); } @NotNull public ZonedDateTime toZonedDateTime() { return ZonedDateTime.of( toLocalDateTime(), UTC_ZONE_ID ); } // --------------------------------- ^^^ of & to ^^^ --------------------------------- // -------------------- vvv Field Accessors (See LocalDateTime) vvv ------------------ public LocalDate toLocalDate() { return toLocalDateTime().toLocalDate(); } public int getYear() { return toLocalDateTime().getYear(); } public int getMonthValue() { return toLocalDateTime().getMonthValue(); } public Month getMonth() { return toLocalDateTime().getMonth(); } public int getDayOfMonth() { return toLocalDateTime().getDayOfMonth(); } public int getDayOfYear() { return toLocalDateTime().getDayOfYear(); } public DayOfWeek getDayOfWeek() { return toLocalDateTime().getDayOfWeek(); } public LocalTime toLocalTime() { return toLocalDateTime().toLocalTime(); } public int getHour() { return toLocalDateTime().getHour(); } public int getMinute() { return toLocalDateTime().getMinute(); } public int getSecond() { return toLocalDateTime().getSecond(); } public int getNano() { return toLocalDateTime().getNano(); } // -------------------- ^^^ Field Accessors (See LocalDateTime) ^^^ ------------------ // ----------------------------- vvv TemporalAccessor vvv ---------------------------- @Override public boolean isSupported( TemporalField pTemporalField ) { return toLocalDateTime().isSupported( pTemporalField ); } @Override public long getLong( TemporalField pTemporalField ) { return toLocalDateTime().getLong( pTemporalField ); } @Override public ValueRange range( TemporalField pTemporalField ) { return toLocalDateTime().range( pTemporalField ); } @Override public int get( TemporalField pTemporalField ) { return toLocalDateTime().get( pTemporalField ); } @Override public R query( TemporalQuery pTemporalQuery ) { return toLocalDateTime().query( pTemporalQuery ); } // ----------------------------- ^^^ TemporalAccessor ^^^ ---------------------------- // ------------------- vvv is... (Relative) (see LocalDateTime) vvv ------------------ public boolean isAfter( @NotNull ZuluDateTime them ) { return this.toInstant().isAfter( notNullInstant( them ) ); } public boolean isBefore( @NotNull ZuluDateTime them ) { return this.toInstant().isBefore( notNullInstant( them ) ); } public boolean isEqual( @Nullable ZuluDateTime them ) { return equals( them ); } // ------------------- ^^^ is... (Relative) (see LocalDateTime) vvv ------------------ @NotNull public ZoneOffset getOffset() { return UTC_ZONE_OFFSET; } @NotNull public ZoneId getZone() { return UTC_ZONE_ID; } @Override public int compareTo( @NotNull ZuluDateTime them ) { return this.toInstant().compareTo( notNullInstant( them ) ); } @Override public int hashCode() { return Objects.hash( mInstant ); } @Override public boolean equals( Object them ) { return (this == them) || ((them instanceof ZuluDateTime) && equals( (ZuluDateTime)them )); // Left to Right } public boolean equals( ZuluDateTime them ) { return (this == them) || ((them != null) && Objects.equals( this.mInstant, them.mInstant )); } @NotNull private static Instant notNullInstant( @Nullable ZuluDateTime them ) { return Objects.requireNonNull( them, "them" ).toInstant(); } private static CharSequence significant( CharSequence pText ) { if ( pText != null ) { pText = trimTrailing( trimLeading( pText ) ); if ( pText.length() != 0 ) { return pText; } } throw new IllegalArgumentException( "Insignificant Provided Text: " + format( pText ) ); } private static CharSequence trimLeading( CharSequence pText ) { while ( (pText.length() != 0) && isWhiteSpace( pText, 0 ) ) { pText = pText.subSequence( 1, pText.length() ); } return pText; } private static CharSequence trimTrailing( CharSequence pText ) { for ( int zLast = pText.length() - 1; (zLast != -1) && isWhiteSpace( pText, zLast ); zLast-- ) { pText = pText.subSequence( 1, zLast ); } return pText; } private static boolean isWhiteSpace( CharSequence pText, int pAt ) { return Character.isWhitespace( pText.charAt( pAt ) ); } private static String format( CharSequence pText ) { return (pText == null) ? "null" : ("\"" + pText + "\""); } }





    © 2015 - 2025 Weber Informatics LLC | Privacy Policy