jdk/src/share/classes/java/time/temporal/JulianFields.java
changeset 15658 55b829ca2334
parent 15289 3ac550392e43
child 16852 60207b2b4b42
equal deleted inserted replaced
15657:c588664d547e 15658:55b829ca2334
    63 
    63 
    64 import static java.time.temporal.ChronoField.EPOCH_DAY;
    64 import static java.time.temporal.ChronoField.EPOCH_DAY;
    65 import static java.time.temporal.ChronoUnit.DAYS;
    65 import static java.time.temporal.ChronoUnit.DAYS;
    66 import static java.time.temporal.ChronoUnit.FOREVER;
    66 import static java.time.temporal.ChronoUnit.FOREVER;
    67 
    67 
    68 import java.io.InvalidObjectException;
       
    69 import java.io.Serializable;
       
    70 import java.time.DateTimeException;
    68 import java.time.DateTimeException;
    71 import java.time.LocalDate;
    69 import java.util.Collections;
    72 import java.time.format.DateTimeBuilder;
    70 import java.util.Map;
    73 
    71 
    74 /**
    72 /**
    75  * A set of date fields that provide access to Julian Days.
    73  * A set of date fields that provide access to Julian Days.
    76  * <p>
    74  * <p>
    77  * The Julian Day is a standard way of expressing date and time commonly used in the scientific community.
    75  * The Julian Day is a standard way of expressing date and time commonly used in the scientific community.
    78  * It is expressed as a decimal number of whole days where days start at midday.
    76  * It is expressed as a decimal number of whole days where days start at midday.
    79  * This class represents variations on Julian Days that count whole days from midnight.
    77  * This class represents variations on Julian Days that count whole days from midnight.
       
    78  * <p>
       
    79  * The fields are implemented relative to {@link ChronoField#EPOCH_DAY EPOCH_DAY}.
       
    80  * The fields are supported, and can be queried and set if {@code EPOCH_DAY} is available.
       
    81  * The fields work with all chronologies.
    80  *
    82  *
    81  * <h3>Specification for implementors</h3>
    83  * <h3>Specification for implementors</h3>
    82  * This is an immutable and thread-safe class.
    84  * This is an immutable and thread-safe class.
    83  *
    85  *
    84  * @since 1.8
    86  * @since 1.8
    97      * Julian Day is a well-known system that represents the count of whole days since day 0,
    99      * Julian Day is a well-known system that represents the count of whole days since day 0,
    98      * which is defined to be January 1, 4713 BCE in the Julian calendar, and -4713-11-24 Gregorian.
   100      * which is defined to be January 1, 4713 BCE in the Julian calendar, and -4713-11-24 Gregorian.
    99      * The field  has "JulianDay" as 'name', and 'DAYS' as 'baseUnit'.
   101      * The field  has "JulianDay" as 'name', and 'DAYS' as 'baseUnit'.
   100      * The field always refers to the local date-time, ignoring the offset or zone.
   102      * The field always refers to the local date-time, ignoring the offset or zone.
   101      * <p>
   103      * <p>
   102      * For date-times, 'JULIAN_DAY.doGet()' assumes the same value from
   104      * For date-times, 'JULIAN_DAY.getFrom()' assumes the same value from
   103      * midnight until just before the next midnight.
   105      * midnight until just before the next midnight.
   104      * When 'JULIAN_DAY.doWith()' is applied to a date-time, the time of day portion remains unaltered.
   106      * When 'JULIAN_DAY.adjustInto()' is applied to a date-time, the time of day portion remains unaltered.
   105      * 'JULIAN_DAY.doWith()' and 'JULIAN_DAY.doGet()' only apply to {@code Temporal} objects that
   107      * 'JULIAN_DAY.adjustInto()' and 'JULIAN_DAY.getFrom()' only apply to {@code Temporal} objects that
   106      * can be converted into {@link ChronoField#EPOCH_DAY}.
   108      * can be converted into {@link ChronoField#EPOCH_DAY}.
   107      * A {@link DateTimeException} is thrown for any other type of object.
   109      * A {@link DateTimeException} is thrown for any other type of object.
   108      * <p>
   110      * <p>
   109      * <h3>Astronomical and Scientific Notes</h3>
   111      * <h3>Astronomical and Scientific Notes</h3>
   110      * The standard astronomical definition uses a fraction to indicate the time-of-day,
   112      * The standard astronomical definition uses a fraction to indicate the time-of-day,
   127      * <p>
   129      * <p>
   128      * Julian Days are sometimes taken to imply Universal Time or UTC, but this
   130      * Julian Days are sometimes taken to imply Universal Time or UTC, but this
   129      * implementation always uses the Julian Day number for the local date,
   131      * implementation always uses the Julian Day number for the local date,
   130      * regardless of the offset or time-zone.
   132      * regardless of the offset or time-zone.
   131      */
   133      */
   132     public static final TemporalField JULIAN_DAY = new Field("JulianDay", DAYS, FOREVER, JULIAN_DAY_OFFSET);
   134     public static final TemporalField JULIAN_DAY = Field.JULIAN_DAY;
   133 
   135 
   134     /**
   136     /**
   135      * Modified Julian Day field.
   137      * Modified Julian Day field.
   136      * <p>
   138      * <p>
   137      * This is an integer-based version of the Modified Julian Day Number.
   139      * This is an integer-based version of the Modified Julian Day Number.
   138      * Modified Julian Day (MJD) is a well-known system that counts days continuously.
   140      * Modified Julian Day (MJD) is a well-known system that counts days continuously.
   139      * It is defined relative to astronomical Julian Day as  {@code MJD = JD - 2400000.5}.
   141      * It is defined relative to astronomical Julian Day as  {@code MJD = JD - 2400000.5}.
   140      * Each Modified Julian Day runs from midnight to midnight.
   142      * Each Modified Julian Day runs from midnight to midnight.
   141      * The field always refers to the local date-time, ignoring the offset or zone.
   143      * The field always refers to the local date-time, ignoring the offset or zone.
   142      * <p>
   144      * <p>
   143      * For date-times, 'MODIFIED_JULIAN_DAY.doGet()' assumes the same value from
   145      * For date-times, 'MODIFIED_JULIAN_DAY.getFrom()' assumes the same value from
   144      * midnight until just before the next midnight.
   146      * midnight until just before the next midnight.
   145      * When 'MODIFIED_JULIAN_DAY.doWith()' is applied to a date-time, the time of day portion remains unaltered.
   147      * When 'MODIFIED_JULIAN_DAY.adjustInto()' is applied to a date-time, the time of day portion remains unaltered.
   146      * 'MODIFIED_JULIAN_DAY.doWith()' and 'MODIFIED_JULIAN_DAY.doGet()' only apply to {@code Temporal} objects
   148      * 'MODIFIED_JULIAN_DAY.adjustInto()' and 'MODIFIED_JULIAN_DAY.getFrom()' only apply to {@code Temporal} objects
   147      * that can be converted into {@link ChronoField#EPOCH_DAY}.
   149      * that can be converted into {@link ChronoField#EPOCH_DAY}.
   148      * A {@link DateTimeException} is thrown for any other type of object.
   150      * A {@link DateTimeException} is thrown for any other type of object.
   149      * <p>
   151      * <p>
   150      * This implementation is an integer version of MJD with the decimal part rounded to floor.
   152      * This implementation is an integer version of MJD with the decimal part rounded to floor.
   151      * <p>
   153      * <p>
   163      * <p>
   165      * <p>
   164      * Modified Julian Days are sometimes taken to imply Universal Time or UTC, but this
   166      * Modified Julian Days are sometimes taken to imply Universal Time or UTC, but this
   165      * implementation always uses the Modified Julian Day for the local date,
   167      * implementation always uses the Modified Julian Day for the local date,
   166      * regardless of the offset or time-zone.
   168      * regardless of the offset or time-zone.
   167      */
   169      */
   168     public static final TemporalField MODIFIED_JULIAN_DAY = new Field("ModifiedJulianDay", DAYS, FOREVER, 40587L);
   170     public static final TemporalField MODIFIED_JULIAN_DAY = Field.MODIFIED_JULIAN_DAY;
   169 
   171 
   170     /**
   172     /**
   171      * Rata Die field.
   173      * Rata Die field.
   172      * <p>
   174      * <p>
   173      * Rata Die counts whole days continuously starting day 1 at midnight at the beginning of 0001-01-01 (ISO).
   175      * Rata Die counts whole days continuously starting day 1 at midnight at the beginning of 0001-01-01 (ISO).
   174      * The field always refers to the local date-time, ignoring the offset or zone.
   176      * The field always refers to the local date-time, ignoring the offset or zone.
   175      * <p>
   177      * <p>
   176      * For date-times, 'RATA_DIE.doGet()' assumes the same value from
   178      * For date-times, 'RATA_DIE.getFrom()' assumes the same value from
   177      * midnight until just before the next midnight.
   179      * midnight until just before the next midnight.
   178      * When 'RATA_DIE.doWith()' is applied to a date-time, the time of day portion remains unaltered.
   180      * When 'RATA_DIE.adjustInto()' is applied to a date-time, the time of day portion remains unaltered.
   179      * 'MODIFIED_JULIAN_DAY.doWith()' and 'RATA_DIE.doGet()' only apply to {@code Temporal} objects
   181      * 'RATA_DIE.adjustInto()' and 'RATA_DIE.getFrom()' only apply to {@code Temporal} objects
   180      * that can be converted into {@link ChronoField#EPOCH_DAY}.
   182      * that can be converted into {@link ChronoField#EPOCH_DAY}.
   181      * A {@link DateTimeException} is thrown for any other type of object.
   183      * A {@link DateTimeException} is thrown for any other type of object.
   182      */
   184      */
   183     public static final TemporalField RATA_DIE = new Field("RataDie", DAYS, FOREVER, 719163L);
   185     public static final TemporalField RATA_DIE = Field.RATA_DIE;
   184 
   186 
   185     /**
   187     /**
   186      * Restricted constructor.
   188      * Restricted constructor.
   187      */
   189      */
   188     private JulianFields() {
   190     private JulianFields() {
   189         throw new AssertionError("Not instantiable");
   191         throw new AssertionError("Not instantiable");
   190     }
   192     }
   191 
   193 
   192     /**
   194     /**
   193      * implementation of JulianFields.  Each instance is a singleton.
   195      * Implementation of JulianFields.  Each instance is a singleton.
   194      */
   196      */
   195     private static class Field implements TemporalField, Serializable {
   197     private static enum Field implements TemporalField {
       
   198         JULIAN_DAY("JulianDay", DAYS, FOREVER, JULIAN_DAY_OFFSET),
       
   199         MODIFIED_JULIAN_DAY("ModifiedJulianDay", DAYS, FOREVER, 40587L),
       
   200         RATA_DIE("RataDie", DAYS, FOREVER, 719163L);
   196 
   201 
   197         private static final long serialVersionUID = -7501623920830201812L;
   202         private static final long serialVersionUID = -7501623920830201812L;
   198 
   203 
   199         private final String name;
   204         private final transient String name;
   200         private final transient TemporalUnit baseUnit;
   205         private final transient TemporalUnit baseUnit;
   201         private final transient TemporalUnit rangeUnit;
   206         private final transient TemporalUnit rangeUnit;
   202         private final transient ValueRange range;
   207         private final transient ValueRange range;
   203         private final transient long offset;
   208         private final transient long offset;
   204 
   209 
   208             this.rangeUnit = rangeUnit;
   213             this.rangeUnit = rangeUnit;
   209             this.range = ValueRange.of(-365243219162L + offset, 365241780471L + offset);
   214             this.range = ValueRange.of(-365243219162L + offset, 365241780471L + offset);
   210             this.offset = offset;
   215             this.offset = offset;
   211         }
   216         }
   212 
   217 
   213 
       
   214         /**
       
   215          * Resolve the object from the stream to the appropriate singleton.
       
   216          * @return one of the singleton objects {@link #JULIAN_DAY},
       
   217          *     {@link #MODIFIED_JULIAN_DAY}, or {@link #RATA_DIE}.
       
   218          * @throws InvalidObjectException if the object in the stream is not one of the singletons.
       
   219          */
       
   220         private Object readResolve() throws InvalidObjectException {
       
   221             if (JULIAN_DAY.getName().equals(name)) {
       
   222                 return JULIAN_DAY;
       
   223             } else if (MODIFIED_JULIAN_DAY.getName().equals(name)) {
       
   224                 return MODIFIED_JULIAN_DAY;
       
   225             } else if (RATA_DIE.getName().equals(name)) {
       
   226                 return RATA_DIE;
       
   227             } else {
       
   228                 throw new InvalidObjectException("Not one of the singletons");
       
   229             }
       
   230         }
       
   231 
       
   232         //-----------------------------------------------------------------------
   218         //-----------------------------------------------------------------------
   233         @Override
   219         @Override
   234         public String getName() {
   220         public String getName() {
   235             return name;
   221             return name;
   236         }
   222         }
   250             return range;
   236             return range;
   251         }
   237         }
   252 
   238 
   253         //-----------------------------------------------------------------------
   239         //-----------------------------------------------------------------------
   254         @Override
   240         @Override
   255         public boolean doIsSupported(TemporalAccessor temporal) {
   241         public boolean isSupportedBy(TemporalAccessor temporal) {
   256             return temporal.isSupported(EPOCH_DAY);
   242             return temporal.isSupported(EPOCH_DAY);
   257         }
   243         }
   258 
   244 
   259         @Override
   245         @Override
   260         public ValueRange doRange(TemporalAccessor temporal) {
   246         public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
   261             if (doIsSupported(temporal) == false) {
   247             if (isSupportedBy(temporal) == false) {
   262                 throw new DateTimeException("Unsupported field: " + this);
   248                 throw new DateTimeException("Unsupported field: " + this);
   263             }
   249             }
   264             return range();
   250             return range();
   265         }
   251         }
   266 
   252 
   267         @Override
   253         @Override
   268         public long doGet(TemporalAccessor temporal) {
   254         public long getFrom(TemporalAccessor temporal) {
   269             return temporal.getLong(EPOCH_DAY) + offset;
   255             return temporal.getLong(EPOCH_DAY) + offset;
   270         }
   256         }
   271 
   257 
   272         @Override
   258         @SuppressWarnings("unchecked")
   273         public <R extends Temporal> R doWith(R temporal, long newValue) {
   259         @Override
       
   260         public <R extends Temporal> R adjustInto(R temporal, long newValue) {
   274             if (range().isValidValue(newValue) == false) {
   261             if (range().isValidValue(newValue) == false) {
   275                 throw new DateTimeException("Invalid value: " + name + " " + newValue);
   262                 throw new DateTimeException("Invalid value: " + name + " " + newValue);
   276             }
   263             }
   277             return (R) temporal.with(EPOCH_DAY, Math.subtractExact(newValue, offset));
   264             return (R) temporal.with(EPOCH_DAY, Math.subtractExact(newValue, offset));
   278         }
   265         }
   279 
   266 
   280         //-----------------------------------------------------------------------
   267         //-----------------------------------------------------------------------
   281         @Override
   268         @Override
   282         public boolean resolve(DateTimeBuilder builder, long value) {
   269         public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long value) {
   283             boolean changed = false;
   270             return Collections.<TemporalField, Long>singletonMap(EPOCH_DAY, Math.subtractExact(value, offset));
   284             changed = resolve0(JULIAN_DAY, builder, changed);
       
   285             changed = resolve0(MODIFIED_JULIAN_DAY, builder, changed);
       
   286             changed = resolve0(RATA_DIE, builder, changed);
       
   287             return changed;
       
   288         }
       
   289 
       
   290         private boolean resolve0(TemporalField field, DateTimeBuilder builder, boolean changed) {
       
   291             if (builder.containsFieldValue(field)) {
       
   292                 builder.addCalendrical(LocalDate.ofEpochDay(Math.subtractExact(builder.getFieldValue(JULIAN_DAY), JULIAN_DAY_OFFSET)));
       
   293                 builder.removeFieldValue(JULIAN_DAY);
       
   294                 changed = true;
       
   295             }
       
   296             return changed;
       
   297         }
   271         }
   298 
   272 
   299         //-----------------------------------------------------------------------
   273         //-----------------------------------------------------------------------
   300         @Override
   274         @Override
   301         public String toString() {
   275         public String toString() {
   302             return getName();
   276             return name;
   303         }
   277         }
   304     }
   278     }
   305 }
   279 }