/**
|
* @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
|
* @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
|
* @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
|
*/
|
|
import { assert, requireNonNull, requireInstance } from './assert';
|
import { MathUtil } from './MathUtil';
|
|
import { ChronoField } from './temporal/ChronoField';
|
import { ChronoUnit } from './temporal/ChronoUnit';
|
import { DateTimeException, IllegalArgumentException, UnsupportedTemporalTypeException } from './errors';
|
import { IsoChronology } from './chrono/IsoChronology';
|
import { TemporalAccessor } from './temporal/TemporalAccessor';
|
import { TemporalQueries } from './temporal/TemporalQueries';
|
|
/**
|
* A month-of-year, such as 'July'.
|
*
|
* {@link Month} is representing the 12 months of the year -
|
* January, February, March, April, May, June, July, August, September, October,
|
* November and December.
|
*
|
* In addition to the textual name, each month-of-year has an `int` value.
|
* The `int` value follows normal usage and the ISO-8601 standard,
|
* from 1 (January) to 12 (December). It is recommended that applications use the static values defined by this class
|
* rather than the `int` value to ensure code clarity.
|
*
|
* This class represents a common concept that is found in many calendar systems.
|
* As such, this class may be used by any calendar system that has the month-of-year
|
* concept defined exactly equivalent to the ISO-8601 calendar system.
|
*
|
* ### Static properties of Class {@link Month}
|
*
|
* Month.JANUARY, Month.FEBRUARY, Month.MARCH, Month.APRIL, Month.MAY, Month.JUNE,
|
* Month.JULY, Month.AUGUST, Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER, Month.DECEMBER
|
*
|
*/
|
export class Month extends TemporalAccessor {
|
|
/**
|
*
|
* @param {number} ordinal
|
* @param {string} name
|
* @private
|
*/
|
constructor(value, name){
|
super();
|
this._value = MathUtil.safeToInt(value);
|
this._name = name;
|
}
|
|
/**
|
*
|
* @return {number} gets the value
|
*/
|
value() {
|
return this._value;
|
}
|
|
/**
|
*
|
* @returns {number}
|
*/
|
ordinal(){
|
return this._value - 1;
|
}
|
|
/**
|
*
|
* @returns {string}
|
*/
|
name(){
|
return this._name;
|
}
|
|
/**
|
* Gets the textual representation, such as 'Jan' or 'December'.
|
*
|
* This returns the textual name used to identify the month-of-year.
|
* The parameters control the length of the returned text and the locale.
|
*
|
* If no textual mapping is found then the numeric value (see {@link getValue}) is returned.
|
*
|
* @param {TextStyle} style - the length of the text required, not null
|
* @param {Locale} locale - the locale to use, not null
|
* @return {string} the text value of the day-of-week, not null
|
*/
|
// eslint-disable-next-line no-unused-vars
|
displayName(style, locale) {
|
// TODO:
|
throw new IllegalArgumentException('Pattern using (localized) text not implemented yet!');
|
}
|
|
/**
|
* Checks if the specified field is supported.
|
*
|
* This checks if this month-of-year can be queried for the specified field.
|
* If false, then calling the range (see {@link range}) and
|
* get (see {@link get}) methods will throw an exception.
|
*
|
* If the field is MONTH_OF_YEAR (see {@link ChronoField#MONTH_OF_YEAR}) then
|
* this method returns true.
|
* All other {@link ChronoField} instances will return false.
|
*
|
* If the field is not a {@link ChronoField}, then the result of this method
|
* is obtained by invoking {@link TemporalField.isSupportedBy}
|
* passing `this` as the argument.
|
* Whether the field is supported is determined by the field.
|
*
|
* @param {TemporalField} field - the field to check, null returns false
|
* @return {boolean} true if the field is supported on this month-of-year, false if not
|
*/
|
isSupported(field) {
|
if (null === field) {
|
return false;
|
}
|
if (field instanceof ChronoField) {
|
return field === ChronoField.MONTH_OF_YEAR;
|
}
|
return field != null && field.isSupportedBy(this);
|
}
|
|
/**
|
* Gets the value of the specified field from this month-of-year as an `int`.
|
*
|
* This queries this month 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 MONTH_OF_YEAR (see {@link ChronoField#MONTH_OF_YEAR}) then the
|
* value of the month-of-year, from 1 to 12, will be returned.
|
* All other {@link ChronoField} instances will throw an {@link UnsupportedTemporalTypeException}.
|
*
|
* If the field is not a {@link ChronoField}, then the result of this method
|
* is obtained by invoking {@link TemporalField.getFrom}
|
* passing `this` as the argument. Whether the value can be obtained,
|
* and what the value represents, is determined by the field.
|
*
|
* @param {TemporalField} field - the field to get, not null
|
* @return {Number} the value for the field, within the valid range of values
|
* @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 `int`
|
* @throws ArithmeticException if numeric overflow occurs
|
*/
|
get(field) {
|
if (field === ChronoField.MONTH_OF_YEAR) {
|
return this.value();
|
}
|
return this.range(field).checkValidIntValue(this.getLong(field), field);
|
}
|
|
/**
|
* Gets the value of the specified field from this month-of-year as a `long`.
|
*
|
* This queries this month 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 MONTH_OF_YEAR (see {@link ChronoField#MONTH_OF_YEAR}) then the
|
* value of the month-of-year, from 1 to 12, will be returned.
|
* All other {@link ChronoField} instances will throw an {@link UnsupportedTemporalTypeException}.
|
*
|
* If the field is not a {@link ChronoField}, then the result of this method
|
* is obtained by invoking {@link TemporalField.getFrom}
|
* passing `this` as the argument. Whether the value can be obtained,
|
* and what the value represents, is determined by the field.
|
*
|
* @param {TemporalField} field - the field to get, not null
|
* @return {Number} 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
|
*/
|
getLong(field) {
|
if (field === ChronoField.MONTH_OF_YEAR) {
|
return this.value();
|
} else if (field instanceof ChronoField) {
|
throw new UnsupportedTemporalTypeException(`Unsupported field: ${field}`);
|
}
|
return field.getFrom(this);
|
}
|
|
/**
|
* Returns the month-of-year that is the specified number of months after this one.
|
*
|
* The calculation rolls around the end of the year from December to January.
|
* The specified period may be negative.
|
*
|
* This instance is immutable and unaffected by this method call.
|
*
|
* @param {number} months - the months to add, positive or negative
|
* @return {Month} the resulting month, not null
|
*/
|
plus(months) {
|
const amount = MathUtil.intMod(months, 12) + 12; // + 12 to make sure negative arguments are positive, the total is "corrected" by the next % 12
|
let newMonthVal = MathUtil.intMod((this.value() + amount), 12);
|
/* December is 12, not 0, but 12 % 12 = 0 */
|
newMonthVal = newMonthVal === 0 ? 12 : newMonthVal;
|
return Month.of(newMonthVal);
|
}
|
|
/**
|
* Returns the month-of-year that is the specified number of months before this one.
|
*
|
* The calculation rolls around the start of the year from January to December.
|
* The specified period may be negative.
|
*
|
* This instance is immutable and unaffected by this method call.
|
*
|
* @param {number} months - the months to subtract, positive or negative
|
* @return {Month} the resulting month, not null
|
*/
|
minus(months) {
|
return this.plus(-1 * MathUtil.intMod(months, 12));
|
}
|
|
/**
|
* Gets the length of this month in days.
|
*
|
* This takes a flag to determine whether to return the length for a leap year or not.
|
*
|
* February has 28 days in a standard year and 29 days in a leap year.
|
* April, June, September and November have 30 days.
|
* All other months have 31 days.
|
*
|
* @param {boolean} leapYear - true if the length is required for a leap year
|
* @return {number} the length of this month in days, from 28 to 31
|
*/
|
length(leapYear) {
|
switch (this) {
|
case Month.FEBRUARY:
|
return (leapYear ? 29 : 28);
|
case Month.APRIL:
|
case Month.JUNE:
|
case Month.SEPTEMBER:
|
case Month.NOVEMBER:
|
return 30;
|
default:
|
return 31;
|
}
|
}
|
|
/**
|
* Gets the minimum length of this month in days.
|
*
|
* February has a minimum length of 28 days.
|
* April, June, September and November have 30 days.
|
* All other months have 31 days.
|
*
|
* @return {number} the minimum length of this month in days, from 28 to 31
|
*/
|
minLength() {
|
switch (this) {
|
case Month.FEBRUARY:
|
return 28;
|
case Month.APRIL:
|
case Month.JUNE:
|
case Month.SEPTEMBER:
|
case Month.NOVEMBER:
|
return 30;
|
default:
|
return 31;
|
}
|
}
|
|
/**
|
* Gets the maximum length of this month in days.
|
*
|
* February has a maximum length of 29 days.
|
* April, June, September and November have 30 days.
|
* All other months have 31 days.
|
*
|
* @return {number} the maximum length of this month in days, from 29 to 31
|
*/
|
maxLength() {
|
switch (this) {
|
case Month.FEBRUARY:
|
return 29;
|
case Month.APRIL:
|
case Month.JUNE:
|
case Month.SEPTEMBER:
|
case Month.NOVEMBER:
|
return 30;
|
default:
|
return 31;
|
}
|
}
|
|
/**
|
* Gets the day-of-year corresponding to the first day of this month.
|
*
|
* This returns the day-of-year that this month begins on, using the leap
|
* year flag to determine the length of February.
|
*
|
* @param {boolean} leapYear - true if the length is required for a leap year
|
* @return {number} the day of year corresponding to the first day of this month, from 1 to 336
|
*/
|
firstDayOfYear(leapYear) {
|
const leap = leapYear ? 1 : 0;
|
switch (this) {
|
case Month.JANUARY:
|
return 1;
|
case Month.FEBRUARY:
|
return 32;
|
case Month.MARCH:
|
return 60 + leap;
|
case Month.APRIL:
|
return 91 + leap;
|
case Month.MAY:
|
return 121 + leap;
|
case Month.JUNE:
|
return 152 + leap;
|
case Month.JULY:
|
return 182 + leap;
|
case Month.AUGUST:
|
return 213 + leap;
|
case Month.SEPTEMBER:
|
return 244 + leap;
|
case Month.OCTOBER:
|
return 274 + leap;
|
case Month.NOVEMBER:
|
return 305 + leap;
|
case Month.DECEMBER:
|
default:
|
return 335 + leap;
|
}
|
}
|
|
/**
|
* Gets the month corresponding to the first month of this quarter.
|
*
|
* The year can be divided into four quarters.
|
* This method returns the first month of the quarter for the base month.
|
* January, February and March return January.
|
* April, May and June return April.
|
* July, August and September return July.
|
* October, November and December return October.
|
*
|
* @return {Month} the first month of the quarter corresponding to this month, not null
|
*/
|
firstMonthOfQuarter() {
|
switch (this) {
|
case Month.JANUARY:
|
case Month.FEBRUARY:
|
case Month.MARCH:
|
return Month.JANUARY;
|
case Month.APRIL:
|
case Month.MAY:
|
case Month.JUNE:
|
return Month.APRIL;
|
case Month.JULY:
|
case Month.AUGUST:
|
case Month.SEPTEMBER:
|
return Month.JULY;
|
case Month.OCTOBER:
|
case Month.NOVEMBER:
|
case Month.DECEMBER:
|
default:
|
return Month.OCTOBER;
|
}
|
}
|
|
/**
|
* Queries this month-of-year using the specified query.
|
*
|
* This queries this month-of-year using the specified query strategy object.
|
* The {@link 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} method on the
|
* specified query passing `this` as the argument.
|
*
|
* @param {TemporalQuery} 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)
|
*/
|
query(query) {
|
assert(query != null, 'query() parameter must not be null', DateTimeException);
|
if (query === TemporalQueries.chronology()) {
|
return IsoChronology.INSTANCE;
|
} else if (query === TemporalQueries.precision()) {
|
return ChronoUnit.MONTHS;
|
}
|
return super.query(query);
|
}
|
|
|
|
/**
|
* toString implementation... in JDK this is inherited from the Enum class
|
*
|
* @return {String}
|
*/
|
toString() {
|
switch (this) {
|
case Month.JANUARY:
|
return 'JANUARY';
|
case Month.FEBRUARY:
|
return 'FEBRUARY';
|
case Month.MARCH:
|
return 'MARCH';
|
case Month.APRIL:
|
return 'APRIL';
|
case Month.MAY:
|
return 'MAY';
|
case Month.JUNE:
|
return 'JUNE';
|
case Month.JULY:
|
return 'JULY';
|
case Month.AUGUST:
|
return 'AUGUST';
|
case Month.SEPTEMBER:
|
return 'SEPTEMBER';
|
case Month.OCTOBER:
|
return 'OCTOBER';
|
case Month.NOVEMBER:
|
return 'NOVEMBER';
|
case Month.DECEMBER:
|
return 'DECEMBER';
|
default:
|
return `unknown Month, value: ${this.value()}`;
|
}
|
}
|
|
/**
|
* toJSON() use by JSON.stringify
|
* delegates to toString()
|
*
|
* @return {string}
|
*/
|
toJSON() {
|
return this.toString();
|
}
|
|
/**
|
* Adjusts the specified temporal object to have this month-of-year.
|
*
|
* This returns a temporal object of the same observable type as the input
|
* with the month-of-year changed to be the same as this.
|
*
|
* The adjustment is equivalent to using {@link Temporal#with}
|
* passing {@link ChronoField#MONTH_OF_YEAR} as the field.
|
* If the specified temporal object does not use the ISO calendar system then
|
* a {@link DateTimeException} is thrown.
|
*
|
* In most cases, it is clearer to reverse the calling pattern by using
|
* {@link Temporal#with}:
|
* <pre>
|
* // these two lines are equivalent, but the second approach is recommended
|
* temporal = thisMonth.adjustInto(temporal);
|
* temporal = temporal.with(thisMonth);
|
* </pre>
|
*
|
* For example, given a date in May, the following are output:
|
* <pre>
|
* dateInMay.with(JANUARY); // four months earlier
|
* dateInMay.with(APRIL); // one months earlier
|
* dateInMay.with(MAY); // same date
|
* dateInMay.with(JUNE); // one month later
|
* dateInMay.with(DECEMBER); // seven months later
|
* </pre>
|
*
|
* This instance is immutable and unaffected by this method call.
|
*
|
* @param {Temporal} temporal - the target object to be adjusted, not null
|
* @return {Temporal} the adjusted object, not null
|
* @throws DateTimeException if unable to make the adjustment
|
* @throws ArithmeticException if numeric overflow occurs
|
*/
|
adjustInto(temporal) {
|
/* we support only ISO for now
|
if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) === false) {
|
throw new DateTimeException('Adjustment only supported on ISO date-time');
|
}
|
*/
|
return temporal.with(ChronoField.MONTH_OF_YEAR, this.value());
|
}
|
|
/**
|
* Compares this Month to another Month.
|
*
|
* The comparison is based on the value of the Month.
|
* It is "consistent with equals", as defined by {@link Comparable}.
|
*
|
* @param {Month} other the other year to compare to, not null
|
* @return {number} the comparator value, negative if less, positive if greater
|
*/
|
compareTo(other) {
|
requireNonNull(other, 'other');
|
requireInstance(other, Month, 'other');
|
return this._value - other._value;
|
}
|
|
/**
|
*
|
* @returns {boolean}
|
*/
|
equals(other){
|
return this === other;
|
}
|
|
/**
|
*
|
* @param {string} name
|
* @returns {Month}
|
*/
|
static valueOf(name) {
|
let ordinal = 0;
|
for(ordinal; ordinal < MONTHS.length; ordinal++){
|
if(MONTHS[ordinal].name() === name){
|
break;
|
}
|
}
|
return Month.of(ordinal+1);
|
}
|
|
|
/**
|
* replacement for enum values
|
* @return {Month[]}
|
*/
|
static values(){
|
return MONTHS.slice();
|
}
|
|
/**
|
*
|
* @param {number} month
|
* @return {Month} not null
|
**/
|
static of(month) {
|
if (month < 1 || month > 12) {
|
assert(false, `Invalid value for MonthOfYear: ${month}`, DateTimeException);
|
}
|
return MONTHS[month-1];
|
}
|
|
/**
|
* Obtains an instance of {@link Month} from a temporal object.
|
*
|
* This obtains a month based on the specified temporal.
|
* A {@link TemporalAccessor} represents an arbitrary set of date and time information,
|
* which this factory converts to an instance of {@link Month}.
|
*
|
* The conversion extracts the MONTH_OF_YEAR (see {@link ChronoField#MONTH_OF_YEAR}) field.
|
* The extraction is only permitted if the temporal object has an ISO
|
* chronology, or can be converted to a {@link LocalDate}.
|
*
|
* This method matches the signature of the functional interface {@link TemporalQuery}
|
* allowing it to be used in queries via method reference, {@link Month::from}.
|
*
|
* @param {TemporalAccessor} temporal the temporal object to convert, not null
|
* @return {Month} the month-of-year, not null
|
* @throws DateTimeException if unable to convert to a {@link Month}
|
*/
|
static from(temporal) {
|
if (temporal instanceof Month) {
|
return temporal;
|
}
|
try {
|
/* only ISO for now
|
if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
|
temporal = LocalDate.from(temporal);
|
}*/
|
return Month.of(temporal.get(ChronoField.MONTH_OF_YEAR));
|
} catch (ex) {
|
throw new DateTimeException(`Unable to obtain Month from TemporalAccessor: ${
|
temporal} of type ${temporal && temporal.constructor != null ? temporal.constructor.name : ''}`, ex);
|
}
|
}
|
}
|
|
let MONTHS;
|
|
export function _init() {
|
Month.JANUARY = new Month(1, 'JANUARY');
|
Month.FEBRUARY = new Month(2, 'FEBRUARY');
|
Month.MARCH = new Month(3, 'MARCH');
|
Month.APRIL = new Month(4, 'APRIL');
|
Month.MAY = new Month(5, 'MAY');
|
Month.JUNE = new Month(6, 'JUNE');
|
Month.JULY = new Month(7, 'JULY');
|
Month.AUGUST = new Month(8, 'AUGUST');
|
Month.SEPTEMBER = new Month(9, 'SEPTEMBER');
|
Month.OCTOBER = new Month(10, 'OCTOBER');
|
Month.NOVEMBER = new Month(11, 'NOVEMBER');
|
Month.DECEMBER = new Month(12, 'DECEMBER');
|
|
MONTHS = [
|
Month.JANUARY, Month.FEBRUARY, Month.MARCH, Month.APRIL, Month.MAY, Month.JUNE,
|
Month.JULY, Month.AUGUST, Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER, Month.DECEMBER
|
];
|
}
|