Home Reference Source Repository

src/temporal/ChronoField.js

/**
 * @copyright (c) 2016, Philipp Thuerwaechter & Pattrick Hueper
 * @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
 * @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
 */

import {MAX_SAFE_INTEGER, MIN_SAFE_INTEGER} from '../MathUtil';

import {ChronoUnit} from './ChronoUnit';
import {TemporalField} from './TemporalField';
import {ValueRange} from './ValueRange';
import {Year} from '../Year';

/**
 * A standard set of fields.
 * <p>
 * This set of fields provide field-based access to manipulate a date, time or date-time.
 * The standard set of fields can be extended by implementing {@link TemporalField}.
 * <p>
 * These fields are intended to be applicable in multiple calendar systems.
 * For example, most non-ISO calendar systems define dates as a year, month and day,
 * just with slightly different rules.
 * The documentation of each field explains how it operates.
 * 
 * <h3>Static properties of Class {@link ChronoField}</h3>
 * 
 * ChronoField.NANO_OF_SECOND
 *
 * ChronoField.NANO_OF_DAY
 *
 * ChronoField.MICRO_OF_SECOND
 *
 * ChronoField.MICRO_OF_DAY
 *
 * ChronoField.MILLI_OF_SECOND
 *
 * ChronoField.MILLI_OF_DAY
 *
 * ChronoField.SECOND_OF_MINUTE
 *
 * ChronoField.SECOND_OF_DAY
 *
 * ChronoField.MINUTE_OF_HOUR
 *
 * ChronoField.MINUTE_OF_DAY
 *
 * ChronoField.HOUR_OF_AMPM
 *
 * ChronoField.CLOCK_HOUR_OF_AMPM
 *
 * ChronoField.HOUR_OF_DAY
 *
 * ChronoField.CLOCK_HOUR_OF_DAY
 *
 * ChronoField.AMPM_OF_DAY
 *
 * ChronoField.DAY_OF_WEEK
 *
 * ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH
 *
 * ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR
 *
 * ChronoField.DAY_OF_MONTH
 *
 * ChronoField.DAY_OF_YEAR
 *
 * ChronoField.EPOCH_DAY
 *
 * ChronoField.ALIGNED_WEEK_OF_MONTH
 *
 * ChronoField.ALIGNED_WEEK_OF_YEAR
 *
 * ChronoField.MONTH_OF_YEAR
 *
 * ChronoField.PROLEPTIC_MONTH
 *
 * ChronoField.YEAR_OF_ERA
 *
 * ChronoField.YEAR
 *
 * ChronoField.ERA
 *
 * ChronoField.INSTANT_SECONDS
 *
 * ChronoField.OFFSET_SECONDS
 *
 */
export class ChronoField extends TemporalField {

    /**
     *
     * @param {!string} name
     * @param {!number} baseUnit
     * @param {!number} rangeUnit
     * @param {!ValueRange} range
     */
    constructor(name, baseUnit, rangeUnit, range) {
        super();
        this._name = name;
        this._baseUnit = baseUnit;
        this._rangeUnit = rangeUnit;
        this._range = range;
    }

    /**
     *
     * @returns {string}
     */
    name(){
        return this._name;    
    }

    /**
     *
     * @returns {!number}
     */
    baseUnit(){
        return this._baseUnit;    
    }

    /**
     *
     * @returns {!number}
     */
    rangeUnit(){
        return this._rangeUnit;    
    }

    /**
     *
     * @returns {!ValueRange}
     */
    range(){
        return this._range;    
    }

    /**
     *
     * @returns {string}
     */
    displayName(){
        return this.toString();    
    }

    /**
     *
     * @param {number} value
     * @returns {*}
     */
    checkValidValue(value) {
        return this.range().checkValidValue(value, this.name());
    }

    /**
     * Checks if this field represents a component of a date.
     *
     * @return {boolean} true if it is a component of a date
     */
    isDateBased() {
        var dateBased =
            this === ChronoField.DAY_OF_WEEK ||
            this === ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH ||
            this === ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR ||
            this === ChronoField.DAY_OF_MONTH ||
            this === ChronoField.DAY_OF_YEAR ||
            this === ChronoField.EPOCH_DAY ||
            this === ChronoField.ALIGNED_WEEK_OF_MONTH ||
            this === ChronoField.ALIGNED_WEEK_OF_YEAR ||
            this === ChronoField.MONTH_OF_YEAR ||
            //this === ChronoField.EPOCH_MONTH ||
            this === ChronoField.YEAR_OF_ERA ||
            this === ChronoField.YEAR ||
            this === ChronoField.ERA;
        return dateBased;
    }

    /**
     * Checks if this field represents a component of a time.
     *
     * @return {boolean} true if it is a component of a time
     */
    isTimeBased() {
        var timeBased =
            this === ChronoField.NANO_OF_SECOND     ||
            this === ChronoField.NANO_OF_DAY        ||
            this === ChronoField.MICRO_OF_SECOND    ||
            this === ChronoField.MICRO_OF_DAY       ||
            this === ChronoField.MILLI_OF_SECOND    ||
            this === ChronoField.MILLI_OF_DAY       ||
            this === ChronoField.SECOND_OF_MINUTE   ||
            this === ChronoField.SECOND_OF_DAY      ||
            this === ChronoField.MINUTE_OF_HOUR     ||
            this === ChronoField.MINUTE_OF_DAY      ||
            this === ChronoField.HOUR_OF_AMPM       ||
            this === ChronoField.CLOCK_HOUR_OF_AMPM ||
            this === ChronoField.HOUR_OF_DAY        ||
            this === ChronoField.CLOCK_HOUR_OF_DAY  ||
            this === ChronoField.AMPM_OF_DAY;
        return timeBased;
    }

    /**
     * Get the range of valid values for this field using the temporal object to
     * refine the result.
     * <p>
     * This uses the temporal object to find the range of valid values for the field.
     * This is similar to {@link #range()}, however this method refines the result
     * using the temporal. For example, if the field is {@code DAY_OF_MONTH} the
     * {@code range} method is not accurate as there are four possible month lengths,
     * 28, 29, 30 and 31 days. Using this method with a date allows the range to be
     * accurate, returning just one of those four options.
     * <p>
     * There are two equivalent ways of using this method.
     * The first is to invoke this method directly.
     * The second is to use {@link TemporalAccessor#range(TemporalField)}:
     * <pre>
     *   // these two lines are equivalent, but the second approach is recommended
     *   temporal = thisField.rangeRefinedBy(temporal);
     *   temporal = temporal.range(thisField);
     * </pre>
     * It is recommended to use the second approach, {@code range(TemporalField)},
     * as it is a lot clearer to read in code.
     * <p>
     * Implementations should perform any queries or calculations using the fields
     * available in {@link ChronoField}.
     * If the field is not supported a {@code DateTimeException} must be thrown.
     *
     * @param {!TemporalAccessor} temporal - the temporal object used to refine the result, not null
     * @return {ValueRange} the range of valid values for this field, not null
     * @throws DateTimeException if the range for the field cannot be obtained
     */
    rangeRefinedBy(temporal) {
        return temporal.range(this);
    }

    /**
     * Checks that the specified value is valid and fits in an {@code int}.
     * <p>
     * This validates that the value is within the outer range of valid values
     * returned by {@link #range()}.
     * It also checks that all valid values are within the bounds of an {@code int}.
     * <p>
     * This method checks against the range of the field in the ISO-8601 calendar system.
     * This range may be incorrect for other calendar systems.
     * Use {@link Chronology#range(ChronoField)} to access the correct range
     * for a different calendar system.
     *
     * @param {number} value - the value to check
     * @return {number} the value that was passed in
     */
    checkValidIntValue(value) {
        return this.range().checkValidIntValue(value, this);
    }

    /**
     *
     * @param {TemporalAccessor} temporal
     * @returns {number}
     */
    getFrom(temporal) {
        return temporal.getLong(this);
    }

    /**
     *
     * @returns {string}
     */
    toString(){
        return this.name();
    }

    /**
     *
     * @param {*} other
     * @returns {boolean}
     */
    equals(other){
        return this === other;
    }
}

export function _init() {

    ChronoField.NANO_OF_SECOND = new ChronoField('NanoOfSecond', ChronoUnit.NANOS, ChronoUnit.SECONDS, ValueRange.of(0, 999999999));

    ChronoField.NANO_OF_DAY = new ChronoField('NanoOfDay', ChronoUnit.NANOS, ChronoUnit.DAYS, ValueRange.of(0, 86400 * 1000000000 - 1));

    ChronoField.MICRO_OF_SECOND = new ChronoField('MicroOfSecond', ChronoUnit.MICROS, ChronoUnit.SECONDS, ValueRange.of(0, 999999));

    ChronoField.MICRO_OF_DAY = new ChronoField('MicroOfDay', ChronoUnit.MICROS, ChronoUnit.DAYS, ValueRange.of(0, 86400 * 1000000 - 1));

    ChronoField.MILLI_OF_SECOND = new ChronoField('MilliOfSecond', ChronoUnit.MILLIS, ChronoUnit.SECONDS, ValueRange.of(0, 999));

    ChronoField.MILLI_OF_DAY = new ChronoField('MilliOfDay', ChronoUnit.MILLIS, ChronoUnit.DAYS, ValueRange.of(0, 86400 * 1000 - 1));

    ChronoField.SECOND_OF_MINUTE = new ChronoField('SecondOfMinute', ChronoUnit.SECONDS, ChronoUnit.MINUTES, ValueRange.of(0, 59));

    ChronoField.SECOND_OF_DAY = new ChronoField('SecondOfDay', ChronoUnit.SECONDS, ChronoUnit.DAYS, ValueRange.of(0, 86400 - 1));

    ChronoField.MINUTE_OF_HOUR = new ChronoField('MinuteOfHour', ChronoUnit.MINUTES, ChronoUnit.HOURS, ValueRange.of(0, 59));

    ChronoField.MINUTE_OF_DAY = new ChronoField('MinuteOfDay', ChronoUnit.MINUTES, ChronoUnit.DAYS, ValueRange.of(0, (24 * 60) - 1));

    ChronoField.HOUR_OF_AMPM = new ChronoField('HourOfAmPm', ChronoUnit.HOURS, ChronoUnit.HALF_DAYS, ValueRange.of(0, 11));

    ChronoField.CLOCK_HOUR_OF_AMPM = new ChronoField('ClockHourOfAmPm', ChronoUnit.HOURS, ChronoUnit.HALF_DAYS, ValueRange.of(1, 12));

    ChronoField.HOUR_OF_DAY = new ChronoField('HourOfDay', ChronoUnit.HOURS, ChronoUnit.DAYS, ValueRange.of(0, 23));

    ChronoField.CLOCK_HOUR_OF_DAY = new ChronoField('ClockHourOfDay', ChronoUnit.HOURS, ChronoUnit.DAYS, ValueRange.of(1, 24));

    ChronoField.AMPM_OF_DAY = new ChronoField('AmPmOfDay', ChronoUnit.HALF_DAYS, ChronoUnit.DAYS, ValueRange.of(0, 1));

    ChronoField.DAY_OF_WEEK = new ChronoField('DayOfWeek', ChronoUnit.DAYS, ChronoUnit.WEEKS, ValueRange.of(1, 7));

    ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH = new ChronoField('AlignedDayOfWeekInMonth', ChronoUnit.DAYS, ChronoUnit.WEEKS, ValueRange.of(1, 7));

    ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR = new ChronoField('AlignedDayOfWeekInYear', ChronoUnit.DAYS, ChronoUnit.WEEKS, ValueRange.of(1, 7));

    ChronoField.DAY_OF_MONTH = new ChronoField('DayOfMonth', ChronoUnit.DAYS, ChronoUnit.MONTHS, ValueRange.of(1, 28, 31), 'day');

    ChronoField.DAY_OF_YEAR = new ChronoField('DayOfYear', ChronoUnit.DAYS, ChronoUnit.YEARS, ValueRange.of(1, 365, 366));

    ChronoField.EPOCH_DAY = new ChronoField('EpochDay', ChronoUnit.DAYS, ChronoUnit.FOREVER, ValueRange.of(Math.floor(Year.MIN_VALUE * 365.25), Math.floor(Year.MAX_VALUE * 365.25)));

    ChronoField.ALIGNED_WEEK_OF_MONTH = new ChronoField('AlignedWeekOfMonth', ChronoUnit.WEEKS, ChronoUnit.MONTHS, ValueRange.of(1, 4, 5));

    ChronoField.ALIGNED_WEEK_OF_YEAR = new ChronoField('AlignedWeekOfYear', ChronoUnit.WEEKS, ChronoUnit.YEARS, ValueRange.of(1, 53));

    ChronoField.MONTH_OF_YEAR = new ChronoField('MonthOfYear', ChronoUnit.MONTHS, ChronoUnit.YEARS, ValueRange.of(1, 12), 'month');

    ChronoField.PROLEPTIC_MONTH = new ChronoField('ProlepticMonth', ChronoUnit.MONTHS, ChronoUnit.FOREVER, ValueRange.of(Year.MIN_VALUE * 12, Year.MAX_VALUE * 12 + 11));

    ChronoField.YEAR_OF_ERA = new ChronoField('YearOfEra', ChronoUnit.YEARS, ChronoUnit.FOREVER, ValueRange.of(1, Year.MAX_VALUE, Year.MAX_VALUE + 1));

    ChronoField.YEAR = new ChronoField('Year', ChronoUnit.YEARS, ChronoUnit.FOREVER, ValueRange.of(Year.MIN_VALUE, Year.MAX_VALUE), 'year');

    ChronoField.ERA = new ChronoField('Era', ChronoUnit.ERAS, ChronoUnit.FOREVER, ValueRange.of(0, 1));

    ChronoField.INSTANT_SECONDS = new ChronoField('InstantSeconds', ChronoUnit.SECONDS, ChronoUnit.FOREVER, ValueRange.of(MIN_SAFE_INTEGER, MAX_SAFE_INTEGER));

    ChronoField.OFFSET_SECONDS = new ChronoField('OffsetSeconds', ChronoUnit.SECONDS, ChronoUnit.FOREVER, ValueRange.of(-18 * 3600, 18 * 3600));

}