001/* 002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos 003 * 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions are met: 008 * 009 * * Redistributions of source code must retain the above copyright notice, 010 * this list of conditions and the following disclaimer. 011 * 012 * * Redistributions in binary form must reproduce the above copyright notice, 013 * this list of conditions and the following disclaimer in the documentation 014 * and/or other materials provided with the distribution. 015 * 016 * * Neither the name of JSR-310 nor the names of its contributors 017 * may be used to endorse or promote products derived from this software 018 * without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 031 */ 032package org.threeten.bp.temporal; 033 034import static org.threeten.bp.temporal.ChronoField.EPOCH_DAY; 035import static org.threeten.bp.temporal.ChronoUnit.DAYS; 036import static org.threeten.bp.temporal.ChronoUnit.FOREVER; 037 038import org.threeten.bp.DateTimeException; 039import org.threeten.bp.LocalDate; 040import org.threeten.bp.format.DateTimeBuilder; 041import org.threeten.bp.jdk8.Jdk8Methods; 042 043/** 044 * A set of date fields that provide access to Julian Days. 045 * <p> 046 * The Julian Day is a standard way of expressing date and time commonly used in the scientific community. 047 * It is expressed as a decimal number of whole days where days start at midday. 048 * This class represents variations on Julian Days that count whole days from midnight. 049 * 050 * <h3>Specification for implementors</h3> 051 * This is an immutable and thread-safe class. 052 */ 053public final class JulianFields { 054 055 /** 056 * Julian Day field. 057 * <p> 058 * This is an integer-based version of the Julian Day Number. 059 * Julian Day is a well-known system that represents the count of whole days since day 0, 060 * which is defined to be January 1, 4713 BCE in the Julian calendar, and -4713-11-24 Gregorian. 061 * The field has "JulianDay" as 'name', and 'DAYS' as 'baseUnit'. 062 * The field always refers to the local date-time, ignoring the offset or zone. 063 * <p> 064 * For date-times, 'JULIAN_DAY.doGet()' assumes the same value from 065 * midnight until just before the next midnight. 066 * When 'JULIAN_DAY.doWith()' is applied to a date-time, the time of day portion remains unaltered. 067 * 'JULIAN_DAY.doWith()' and 'JULIAN_DAY.doGet()' only apply to {@code Temporal} objects that 068 * can be converted into {@link ChronoField#EPOCH_DAY}. 069 * A {@link DateTimeException} is thrown for any other type of object. 070 * <p> 071 * <h3>Astronomical and Scientific Notes</h3> 072 * The standard astronomical definition uses a fraction to indicate the time-of-day, 073 * thus 3.25 would represent the time 18:00, since days start at midday. 074 * This implementation uses an integer and days starting at midnight. 075 * The integer value for the Julian Day Number is the astronomical Julian Day value at midday 076 * of the date in question. 077 * This amounts to the astronomical Julian Day, rounded to an integer {@code JDN = floor(JD + 0.5)}. 078 * <p> 079 * <pre> 080 * | ISO date | Julian Day Number | Astronomical Julian Day | 081 * | 1970-01-01T00:00 | 2,440,588 | 2,440,587.5 | 082 * | 1970-01-01T06:00 | 2,440,588 | 2,440,587.75 | 083 * | 1970-01-01T12:00 | 2,440,588 | 2,440,588.0 | 084 * | 1970-01-01T18:00 | 2,440,588 | 2,440,588.25 | 085 * | 1970-01-02T00:00 | 2,440,589 | 2,440,588.5 | 086 * | 1970-01-02T06:00 | 2,440,589 | 2,440,588.75 | 087 * | 1970-01-02T12:00 | 2,440,589 | 2,440,589.0 | 088 * </pre> 089 * <p> 090 * Julian Days are sometimes taken to imply Universal Time or UTC, but this 091 * implementation always uses the Julian Day number for the local date, 092 * regardless of the offset or time-zone. 093 */ 094 public static final TemporalField JULIAN_DAY = Field.JULIAN_DAY; 095 /** 096 * Modified Julian Day field. 097 * <p> 098 * This is an integer-based version of the Modified Julian Day Number. 099 * Modified Julian Day (MJD) is a well-known system that counts days continuously. 100 * It is defined relative to astronomical Julian Day as {@code MJD = JD - 2400000.5}. 101 * Each Modified Julian Day runs from midnight to midnight. 102 * The field always refers to the local date-time, ignoring the offset or zone. 103 * <p> 104 * For date-times, 'MODIFIED_JULIAN_DAY.doGet()' assumes the same value from 105 * midnight until just before the next midnight. 106 * When 'MODIFIED_JULIAN_DAY.doWith()' is applied to a date-time, the time of day portion remains unaltered. 107 * 'MODIFIED_JULIAN_DAY.doWith()' and 'MODIFIED_JULIAN_DAY.doGet()' only apply to {@code Temporal} objects 108 * that can be converted into {@link ChronoField#EPOCH_DAY}. 109 * A {@link DateTimeException} is thrown for any other type of object. 110 * <p> 111 * This implementation is an integer version of MJD with the decimal part rounded to floor. 112 * <p> 113 * <h3>Astronomical and Scientific Notes</h3> 114 * <pre> 115 * | ISO date | Modified Julian Day | Decimal MJD | 116 * | 1970-01-01T00:00 | 40,587 | 40,587.0 | 117 * | 1970-01-01T06:00 | 40,587 | 40,587.25 | 118 * | 1970-01-01T12:00 | 40,587 | 40,587.5 | 119 * | 1970-01-01T18:00 | 40,587 | 40,587.75 | 120 * | 1970-01-02T00:00 | 40,588 | 40,588.0 | 121 * | 1970-01-02T06:00 | 40,588 | 40,588.25 | 122 * | 1970-01-02T12:00 | 40,588 | 40,588.5 | 123 * </pre> 124 * <p> 125 * Modified Julian Days are sometimes taken to imply Universal Time or UTC, but this 126 * implementation always uses the Modified Julian Day for the local date, 127 * regardless of the offset or time-zone. 128 */ 129 public static final TemporalField MODIFIED_JULIAN_DAY = Field.MODIFIED_JULIAN_DAY; 130 /** 131 * Rata Die field. 132 * <p> 133 * Rata Die counts whole days continuously starting day 1 at midnight at the beginning of 0001-01-01 (ISO). 134 * The field always refers to the local date-time, ignoring the offset or zone. 135 * <p> 136 * For date-times, 'RATA_DIE.doGet()' assumes the same value from 137 * midnight until just before the next midnight. 138 * When 'RATA_DIE.doWith()' is applied to a date-time, the time of day portion remains unaltered. 139 * 'MODIFIED_JULIAN_DAY.doWith()' and 'RATA_DIE.doGet()' only apply to {@code Temporal} objects 140 * that can be converted into {@link ChronoField#EPOCH_DAY}. 141 * A {@link DateTimeException} is thrown for any other type of object. 142 */ 143 public static final TemporalField RATA_DIE = Field.RATA_DIE; 144 145 /** 146 * Hidden implementation. 147 */ 148 private static enum Field implements TemporalField { 149 /** 150 * Julian Day field. 151 */ 152 // 719163L + 1721425L = 2440588L 153 JULIAN_DAY("JulianDay", DAYS, FOREVER, 2440588L), 154 /** 155 * Modified Julian Day field. 156 */ 157 // 719163L - 678576L = 40587L 158 MODIFIED_JULIAN_DAY("ModifiedJulianDay", DAYS, FOREVER, 40587L), 159 /** 160 * Rata Die field. 161 */ 162 RATA_DIE("RataDie", DAYS, FOREVER, 719163L), 163 // lots of others Truncated,Lilian, ANSI COBOL (also dotnet related), Excel? 164 ; 165 166 private final String name; 167 private final TemporalUnit baseUnit; 168 private final TemporalUnit rangeUnit; 169 private final ValueRange range; 170 private final long offset; 171 172 private Field(String name, TemporalUnit baseUnit, TemporalUnit rangeUnit, long offset) { 173 this.name = name; 174 this.baseUnit = baseUnit; 175 this.rangeUnit = rangeUnit; 176 this.range = ValueRange.of(-365243219162L + offset, 365241780471L + offset); 177 this.offset = offset; 178 } 179 180 //----------------------------------------------------------------------- 181 @Override 182 public String getName() { 183 return name; 184 } 185 186 @Override 187 public TemporalUnit getBaseUnit() { 188 return baseUnit; 189 } 190 191 @Override 192 public TemporalUnit getRangeUnit() { 193 return rangeUnit; 194 } 195 196 @Override 197 public ValueRange range() { 198 return range; 199 } 200 201 @Override 202 public int compare(TemporalAccessor temporal1, TemporalAccessor temporal2) { 203 return Long.compare(temporal1.getLong(this), temporal2.getLong(this)); 204 } 205 206 //----------------------------------------------------------------------- 207 @Override 208 public boolean doIsSupported(TemporalAccessor temporal) { 209 return temporal.isSupported(EPOCH_DAY); 210 } 211 212 @Override 213 public ValueRange doRange(TemporalAccessor temporal) { 214 if (doIsSupported(temporal) == false) { 215 throw new DateTimeException("Unsupported field: " + this); 216 } 217 return range(); 218 } 219 220 @Override 221 public long doGet(TemporalAccessor temporal) { 222 return temporal.getLong(EPOCH_DAY) + offset; 223 } 224 225 @Override 226 public <R extends Temporal> R doWith(R dateTime, long newValue) { 227 if (range().isValidValue(newValue) == false) { 228 throw new DateTimeException("Invalid value: " + name + " " + newValue); 229 } 230 return (R) dateTime.with(EPOCH_DAY, Jdk8Methods.safeSubtract(newValue, offset)); 231 } 232 233 //----------------------------------------------------------------------- 234 @Override 235 public boolean resolve(DateTimeBuilder builder, long value) { 236 boolean changed = false; 237 changed = resolve0(JULIAN_DAY, builder, changed); 238 changed = resolve0(MODIFIED_JULIAN_DAY, builder, changed); 239 changed = resolve0(RATA_DIE, builder, changed); 240 return changed; 241 } 242 243 private boolean resolve0(Field field, DateTimeBuilder builder, boolean changed) { 244 if (builder.containsFieldValue(field)) { 245 builder.addCalendrical(LocalDate.ofEpochDay(Jdk8Methods.safeSubtract(builder.getFieldValue(JULIAN_DAY), JULIAN_DAY.offset))); 246 builder.removeFieldValue(JULIAN_DAY); 247 changed = true; 248 } 249 return changed; 250 } 251 252 //----------------------------------------------------------------------- 253 @Override 254 public String toString() { 255 return getName(); 256 } 257 258 } 259}