gregorian-calendar
Version:
date time utils ported from java
1,349 lines (1,196 loc) • 38.6 kB
JavaScript
/*
* GregorianCalendar class
* @ignore
* @author yiminghe@gmail.com
*/
'use strict';
var toInt = parseInt;
var Utils = require('./utils');
var defaultLocale = require('./locale/en_US');
var Const = require('./const');
/*
* GregorianCalendar class.
*
* - no arguments:
* Constructs a default GregorianCalendar using the current time
* in the default time zone with the default locale.
* - one argument locale:
* Constructs a GregorianCalendar
* based on the current time in the default time zone with the given locale.
*
* @class Date.Gregorian
*/
function GregorianCalendar(loc) {
var locale = loc || defaultLocale;
this.locale = locale;
this.fields = [];
/*
* The currently set time for this date.
* @protected
* @type Number|undefined
*/
this.time = undefined;
/*
* The timezoneOffset in minutes used by this date.
* @type Number
* @protected
*/
this.timezoneOffset = locale.timezoneOffset;
/*
* The first day of the week
* @type Number
* @protected
*/
this.firstDayOfWeek = locale.firstDayOfWeek;
/*
* The number of days required for the first week in a month or year,
* with possible values from 1 to 7.
* @@protected
* @type Number
*/
this.minimalDaysInFirstWeek = locale.minimalDaysInFirstWeek;
this.fieldsComputed = false;
}
Utils.mix(GregorianCalendar, Const);
Utils.mix(GregorianCalendar, {
Utils: Utils,
defaultLocale: defaultLocale,
/*
* Determines if the given year is a leap year.
* Returns true if the given year is a leap year. To specify BC year numbers,
* 1 - year number must be given. For example, year BC 4 is specified as -3.
* @param {Number} year the given year.
* @returns {Boolean} true if the given year is a leap year; false otherwise.
* @static
* @method
*/
isLeapYear: Utils.isLeapYear,
/*
* Enum indicating year field of date
* @type Number
*/
YEAR: 1,
/*
* Enum indicating month field of date
* @type Number
*/
MONTH: 2,
/*
* Enum indicating the day of the month
* @type Number
*/
DAY_OF_MONTH: 3,
/*
* Enum indicating the hour (24).
* @type Number
*/
HOUR_OF_DAY: 4,
/*
* Enum indicating the minute of the day
* @type Number
*/
MINUTES: 5,
/*
* Enum indicating the second of the day
* @type Number
*/
SECONDS: 6,
/*
* Enum indicating the millisecond of the day
* @type Number
*/
MILLISECONDS: 7,
/*
* Enum indicating the week number within the current year
* @type Number
*/
WEEK_OF_YEAR: 8,
/*
* Enum indicating the week number within the current month
* @type Number
*/
WEEK_OF_MONTH: 9,
/*
* Enum indicating the day of the day number within the current year
* @type Number
*/
DAY_OF_YEAR: 10,
/*
* Enum indicating the day of the week
* @type Number
*/
DAY_OF_WEEK: 11,
/*
* Enum indicating the day of the ordinal number of the day of the week
* @type Number
*/
DAY_OF_WEEK_IN_MONTH: 12,
/*
* Enum indicating am
* @type Number
*/
AM: 0,
/*
* Enum indicating pm
* @type Number
*/
PM: 1
});
var FIELDS = ['', 'Year', 'Month', 'DayOfMonth', 'HourOfDay', 'Minutes', 'Seconds', 'Milliseconds', 'WeekOfYear', 'WeekOfMonth', 'DayOfYear', 'DayOfWeek', 'DayOfWeekInMonth'];
var YEAR = GregorianCalendar.YEAR;
var MONTH = GregorianCalendar.MONTH;
var DAY_OF_MONTH = GregorianCalendar.DAY_OF_MONTH;
var HOUR_OF_DAY = GregorianCalendar.HOUR_OF_DAY;
var MINUTE = GregorianCalendar.MINUTES;
var SECONDS = GregorianCalendar.SECONDS;
var MILLISECONDS = GregorianCalendar.MILLISECONDS;
var DAY_OF_WEEK_IN_MONTH = GregorianCalendar.DAY_OF_WEEK_IN_MONTH;
var DAY_OF_YEAR = GregorianCalendar.DAY_OF_YEAR;
var DAY_OF_WEEK = GregorianCalendar.DAY_OF_WEEK;
var WEEK_OF_MONTH = GregorianCalendar.WEEK_OF_MONTH;
var WEEK_OF_YEAR = GregorianCalendar.WEEK_OF_YEAR;
var MONTH_LENGTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // 0-based
var LEAP_MONTH_LENGTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // 0-based
var ONE_SECOND = 1000;
var ONE_MINUTE = 60 * ONE_SECOND;
var ONE_HOUR = 60 * ONE_MINUTE;
var ONE_DAY = 24 * ONE_HOUR;
var ONE_WEEK = ONE_DAY * 7;
var EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian)
var mod = Utils.mod;
var _isLeapYear = Utils.isLeapYear;
var floorDivide = Math.floor;
var MIN_VALUES = [undefined, 1, // YEAR
GregorianCalendar.JANUARY, // MONTH
1, // DAY_OF_MONTH
0, // HOUR_OF_DAY
0, // MINUTE
0, // SECONDS
0, // MILLISECONDS
1, // WEEK_OF_YEAR
undefined, // WEEK_OF_MONTH
1, // DAY_OF_YEAR
GregorianCalendar.SUNDAY, // DAY_OF_WEEK
1];
// DAY_OF_WEEK_IN_MONTH
var MAX_VALUES = [undefined, 292278994, // YEAR
GregorianCalendar.DECEMBER, // MONTH
undefined, // DAY_OF_MONTH
23, // HOUR_OF_DAY
59, // MINUTE
59, // SECONDS
999, // MILLISECONDS
undefined, // WEEK_OF_YEAR
undefined, // WEEK_OF_MONTH
undefined, // DAY_OF_YEAR
GregorianCalendar.SATURDAY, // DAY_OF_WEEK
undefined];
// ------------------- private start
// DAY_OF_WEEK_IN_MONTH
function getMonthLength(year, month) {
return _isLeapYear(year) ? LEAP_MONTH_LENGTH[month] : MONTH_LENGTH[month];
}
function getYearLength(year) {
return _isLeapYear(year) ? 366 : 365;
}
function adjustDayOfMonth(self) {
var fields = self.fields;
var year = fields[YEAR];
var month = fields[MONTH];
var monthLen = getMonthLength(year, month);
var dayOfMonth = fields[DAY_OF_MONTH];
if (dayOfMonth > monthLen) {
self.set(DAY_OF_MONTH, monthLen);
}
}
function getDayOfWeekDateOnOrBefore(fixedDate, dayOfWeek) {
// 1.1.1 is monday
// one week has 7 days
return fixedDate - mod(fixedDate - dayOfWeek, 7);
}
function getWeekNumber(self, fixedDay1, fixedDate) {
var fixedDay1st = getDayOfWeekDateOnOrBefore(fixedDay1 + 6, self.firstDayOfWeek);
var nDays = fixedDay1st - fixedDay1;
if (nDays >= self.minimalDaysInFirstWeek) {
fixedDay1st -= 7;
}
var normalizedDayOfPeriod = fixedDate - fixedDay1st;
return floorDivide(normalizedDayOfPeriod / 7) + 1;
}
// ------------------- private end
GregorianCalendar.prototype = {
constructor: GregorianCalendar,
isGregorianCalendar: 1,
/*
* Determines if current year is a leap year.
* Returns true if the given year is a leap year. To specify BC year numbers,
* 1 - year number must be given. For example, year BC 4 is specified as -3.
* @returns {Boolean} true if the given year is a leap year; false otherwise.
* @method
* @member Date.Gregorian
*/
isLeapYear: function isLeapYear() {
return _isLeapYear(this.getYear());
},
/*
* Return local info for current date instance
* @returns {Object}
*/
getLocale: function getLocale() {
return this.locale;
},
/*
* Returns the minimum value for
* the given calendar field of this GregorianCalendar instance.
* The minimum value is defined as the smallest value
* returned by the get method for any possible time value,
* taking into consideration the current values of the getFirstDayOfWeek,
* getMinimalDaysInFirstWeek.
* @param field the calendar field.
* @returns {Number} the minimum value for the given calendar field.
*/
getActualMinimum: function getActualMinimum(field) {
if (MIN_VALUES[field] !== undefined) {
return MIN_VALUES[field];
}
if (field === WEEK_OF_MONTH) {
var cal = this.clone();
cal.clear();
cal.set(this.fields[YEAR], this.fields[MONTH], 1);
return cal.get(WEEK_OF_MONTH);
}
throw new Error('minimum value not defined!');
},
/*
* Returns the maximum value for the given calendar field
* of this GregorianCalendar instance.
* The maximum value is defined as the largest value returned
* by the get method for any possible time value, taking into consideration
* the current values of the getFirstDayOfWeek, getMinimalDaysInFirstWeek methods.
* @param field the calendar field.
* @returns {Number} the maximum value for the given calendar field.
*/
getActualMaximum: function getActualMaximum(field) {
if (MAX_VALUES[field] !== undefined) {
return MAX_VALUES[field];
}
var value = undefined;
var fields = this.fields;
switch (field) {
case DAY_OF_MONTH:
value = getMonthLength(fields[YEAR], fields[MONTH]);
break;
case WEEK_OF_YEAR:
var endOfYear = this.clone();
endOfYear.clear();
endOfYear.set(fields[YEAR], GregorianCalendar.DECEMBER, 31);
value = endOfYear.get(WEEK_OF_YEAR);
if (value === 1) {
value = 52;
}
break;
case WEEK_OF_MONTH:
var endOfMonth = this.clone();
endOfMonth.clear();
endOfMonth.set(fields[YEAR], fields[MONTH], getMonthLength(fields[YEAR], fields[MONTH]));
value = endOfMonth.get(WEEK_OF_MONTH);
break;
case DAY_OF_YEAR:
value = getYearLength(fields[YEAR]);
break;
case DAY_OF_WEEK_IN_MONTH:
value = toInt((getMonthLength(fields[YEAR], fields[MONTH]) - 1) / 7) + 1;
break;
default:
break;
}
if (value === undefined) {
throw new Error('maximum value not defined!');
}
return value;
},
/*
* Determines if the given calendar field has a value set,
* including cases that the value has been set by internal fields calculations
* triggered by a get method call.
* @param field the calendar field to be cleared.
* @returns {boolean} true if the given calendar field has a value set; false otherwise.
*/
isSet: function isSet(field) {
return this.fields[field] !== undefined;
},
/*
* Converts the time value (millisecond offset from the Epoch)
* to calendar field values.
* @protected
*/
computeFields: function computeFields() {
var time = this.time;
var timezoneOffset = this.timezoneOffset * ONE_MINUTE;
var fixedDate = toInt(timezoneOffset / ONE_DAY);
var timeOfDay = timezoneOffset % ONE_DAY;
fixedDate += toInt(time / ONE_DAY);
timeOfDay += time % ONE_DAY;
if (timeOfDay >= ONE_DAY) {
timeOfDay -= ONE_DAY;
fixedDate++;
} else {
while (timeOfDay < 0) {
timeOfDay += ONE_DAY;
fixedDate--;
}
}
fixedDate += EPOCH_OFFSET;
var date = Utils.getGregorianDateFromFixedDate(fixedDate);
var year = date.year;
var fields = this.fields;
fields[YEAR] = year;
fields[MONTH] = date.month;
fields[DAY_OF_MONTH] = date.dayOfMonth;
fields[DAY_OF_WEEK] = date.dayOfWeek;
if (timeOfDay !== 0) {
fields[HOUR_OF_DAY] = toInt(timeOfDay / ONE_HOUR);
var r = timeOfDay % ONE_HOUR;
fields[MINUTE] = toInt(r / ONE_MINUTE);
r %= ONE_MINUTE;
fields[SECONDS] = toInt(r / ONE_SECOND);
fields[MILLISECONDS] = r % ONE_SECOND;
} else {
fields[HOUR_OF_DAY] = fields[MINUTE] = fields[SECONDS] = fields[MILLISECONDS] = 0;
}
var fixedDateJan1 = Utils.getFixedDate(year, GregorianCalendar.JANUARY, 1);
var dayOfYear = fixedDate - fixedDateJan1 + 1;
var fixDateMonth1 = fixedDate - date.dayOfMonth + 1;
fields[DAY_OF_YEAR] = dayOfYear;
fields[DAY_OF_WEEK_IN_MONTH] = toInt((date.dayOfMonth - 1) / 7) + 1;
var weekOfYear = getWeekNumber(this, fixedDateJan1, fixedDate);
// 本周没有足够的时间在当前年
if (weekOfYear === 0) {
// If the date belongs to the last week of the
// previous year, use the week number of "12/31" of
// the "previous" year.
var fixedDec31 = fixedDateJan1 - 1;
var prevJan1 = fixedDateJan1 - getYearLength(year - 1);
weekOfYear = getWeekNumber(this, prevJan1, fixedDec31);
} else
// 本周是年末最后一周,可能有足够的时间在新的一年
if (weekOfYear >= 52) {
var nextJan1 = fixedDateJan1 + getYearLength(year);
var nextJan1st = getDayOfWeekDateOnOrBefore(nextJan1 + 6, this.firstDayOfWeek);
var nDays = nextJan1st - nextJan1;
// 本周有足够天数在新的一年
if (nDays >= this.minimalDaysInFirstWeek &&
// 当天确实在本周,weekOfYear === 53 时是不需要这个判断
fixedDate >= nextJan1st - 7) {
weekOfYear = 1;
}
}
fields[WEEK_OF_YEAR] = weekOfYear;
fields[WEEK_OF_MONTH] = getWeekNumber(this, fixDateMonth1, fixedDate);
this.fieldsComputed = true;
},
/*
* Converts calendar field values to the time value
* (millisecond offset from the Epoch).
* @protected
*/
computeTime: function computeTime() {
var year = undefined;
var fields = this.fields;
if (this.isSet(YEAR)) {
year = fields[YEAR];
} else {
year = new Date().getFullYear();
}
var timeOfDay = 0;
if (this.isSet(HOUR_OF_DAY)) {
timeOfDay += fields[HOUR_OF_DAY];
}
timeOfDay *= 60;
timeOfDay += fields[MINUTE] || 0;
timeOfDay *= 60;
timeOfDay += fields[SECONDS] || 0;
timeOfDay *= 1000;
timeOfDay += fields[MILLISECONDS] || 0;
var fixedDate = 0;
fields[YEAR] = year;
fixedDate = fixedDate + this.getFixedDate();
// millis represents local wall-clock time in milliseconds.
var millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
millis -= this.timezoneOffset * ONE_MINUTE;
this.time = millis;
this.computeFields();
},
/*
* Fills in any unset fields in the calendar fields. First,
* the computeTime() method is called if the time value (millisecond offset from the Epoch)
* has not been calculated from calendar field values.
* Then, the computeFields() method is called to calculate all calendar field values.
* @protected
*/
complete: function complete() {
if (this.time === undefined) {
this.computeTime();
}
if (!this.fieldsComputed) {
this.computeFields();
}
},
getFixedDate: function getFixedDate() {
var self = this;
var fields = self.fields;
var firstDayOfWeekCfg = self.firstDayOfWeek;
var year = fields[YEAR];
var month = GregorianCalendar.JANUARY;
if (self.isSet(MONTH)) {
month = fields[MONTH];
if (month > GregorianCalendar.DECEMBER) {
year += toInt(month / 12);
month %= 12;
} else if (month < GregorianCalendar.JANUARY) {
year += floorDivide(month / 12);
month = mod(month, 12);
}
}
// Get the fixed date since Jan 1, 1 (Gregorian). We are on
// the first day of either `month' or January in 'year'.
var fixedDate = Utils.getFixedDate(year, month, 1);
var firstDayOfWeek = undefined;
var dayOfWeek = self.firstDayOfWeek;
if (self.isSet(DAY_OF_WEEK)) {
dayOfWeek = fields[DAY_OF_WEEK];
}
if (self.isSet(MONTH)) {
if (self.isSet(DAY_OF_MONTH)) {
fixedDate += fields[DAY_OF_MONTH] - 1;
} else {
if (self.isSet(WEEK_OF_MONTH)) {
firstDayOfWeek = getDayOfWeekDateOnOrBefore(fixedDate + 6, firstDayOfWeekCfg);
// If we have enough days in the first week, then
// move to the previous week.
if (firstDayOfWeek - fixedDate >= self.minimalDaysInFirstWeek) {
firstDayOfWeek -= 7;
}
if (dayOfWeek !== firstDayOfWeekCfg) {
firstDayOfWeek = getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6, dayOfWeek);
}
fixedDate = firstDayOfWeek + 7 * (fields[WEEK_OF_MONTH] - 1);
} else {
var dowim = undefined;
if (self.isSet(DAY_OF_WEEK_IN_MONTH)) {
dowim = fields[DAY_OF_WEEK_IN_MONTH];
} else {
dowim = 1;
}
var lastDate = 7 * dowim;
if (dowim < 0) {
lastDate = getMonthLength(year, month) + 7 * (dowim + 1);
}
fixedDate = getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1, dayOfWeek);
}
}
} else {
// We are on the first day of the year.
if (self.isSet(DAY_OF_YEAR)) {
fixedDate += fields[DAY_OF_YEAR] - 1;
} else if (self.isSet(WEEK_OF_YEAR)) {
firstDayOfWeek = getDayOfWeekDateOnOrBefore(fixedDate + 6, firstDayOfWeekCfg);
// If we have enough days in the first week, then move
// to the previous week.
if (firstDayOfWeek - fixedDate >= self.minimalDaysInFirstWeek) {
firstDayOfWeek -= 7;
}
if (dayOfWeek !== firstDayOfWeekCfg) {
firstDayOfWeek = getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6, dayOfWeek);
}
fixedDate = firstDayOfWeek + 7 * (fields[WEEK_OF_YEAR] - 1);
}
}
return fixedDate;
},
/*
* Returns this Calendar's time value in milliseconds
* @member Date.Gregorian
* @returns {Number} the current time as UTC milliseconds from the epoch.
*/
getTime: function getTime() {
if (this.time === undefined) {
this.computeTime();
}
return this.time;
},
/*
* Sets this Calendar's current time from the given long value.
* @param time the new time in UTC milliseconds from the epoch.
*/
setTime: function setTime(time) {
this.time = time;
this.fieldsComputed = false;
this.complete();
},
/*
* Returns the value of the given calendar field.
* @param field the given calendar field.
* @returns {Number} the value for the given calendar field.
*/
get: function get(field) {
this.complete();
return this.fields[field];
},
/*
* Returns the year of the given calendar field.
* @method getYear
* @returns {Number} the year for the given calendar field.
*/
/*
* Returns the month of the given calendar field.
* @method getMonth
* @returns {Number} the month for the given calendar field.
*/
/*
* Returns the day of month of the given calendar field.
* @method getDayOfMonth
* @returns {Number} the day of month for the given calendar field.
*/
/*
* Returns the hour of day of the given calendar field.
* @method getHourOfDay
* @returns {Number} the hour of day for the given calendar field.
*/
/*
* Returns the minute of the given calendar field.
* @method getMinute
* @returns {Number} the minute for the given calendar field.
*/
/*
* Returns the second of the given calendar field.
* @method getSecond
* @returns {Number} the second for the given calendar field.
*/
/*
* Returns the millisecond of the given calendar field.
* @method getMilliSecond
* @returns {Number} the millisecond for the given calendar field.
*/
/*
* Returns the week of year of the given calendar field.
* @method getWeekOfYear
* @returns {Number} the week of year for the given calendar field.
*/
/*
* Returns the week of month of the given calendar field.
* @method getWeekOfMonth
* @returns {Number} the week of month for the given calendar field.
*/
/*
* Returns the day of year of the given calendar field.
* @method getDayOfYear
* @returns {Number} the day of year for the given calendar field.
*/
/*
* Returns the day of week of the given calendar field.
* @method getDayOfWeek
* @returns {Number} the day of week for the given calendar field.
*/
/*
* Returns the day of week in month of the given calendar field.
* @method getDayOfWeekInMonth
* @returns {Number} the day of week in month for the given calendar field.
*/
/*
* Sets the given calendar field to the given value.
* @param field the given calendar field.
* @param v the value to be set for the given calendar field.
*/
set: function set(field, v) {
var len = arguments.length;
if (len === 2) {
this.fields[field] = v;
} else if (len < MILLISECONDS + 1) {
for (var i = 0; i < len; i++) {
this.fields[YEAR + i] = arguments[i];
}
} else {
throw new Error('illegal arguments for GregorianCalendar set');
}
this.time = undefined;
},
/*
* Set the year of the given calendar field.
* @method setYear
*/
/*
* Set the month of the given calendar field.
* @method setMonth
*/
/*
* Set the day of month of the given calendar field.
* @method setDayOfMonth
*/
/*
* Set the hour of day of the given calendar field.
* @method setHourOfDay
*/
/*
* Set the minute of the given calendar field.
* @method setMinute
*/
/*
* Set the second of the given calendar field.
* @method setSecond
*/
/*
* Set the millisecond of the given calendar field.
* @method setMilliSecond
*/
/*
* Set the week of year of the given calendar field.
* @method setWeekOfYear
*/
/*
* Set the week of month of the given calendar field.
* @method setWeekOfMonth
*/
/*
* Set the day of year of the given calendar field.
* @method setDayOfYear
*/
/*
* Set the day of week of the given calendar field.
* @method setDayOfWeek
*/
/*
* Set the day of week in month of the given calendar field.
* @method setDayOfWeekInMonth
*/
/*
* add for specified field based on two rules:
*
* - Add rule 1. The value of field after the call minus the value of field before the
* call is amount, modulo any overflow that has occurred in field
* Overflow occurs when a field value exceeds its range and,
* as a result, the next larger field is incremented or
* decremented and the field value is adjusted back into its range.
*
* - Add rule 2. If a smaller field is expected to be invariant,
* but it is impossible for it to be equal to its
* prior value because of changes in its minimum or maximum after
* field is changed, then its value is adjusted to be as close
* as possible to its expected value. A smaller field represents a
* smaller unit of time. HOUR_OF_DAY is a smaller field than
* DAY_OF_MONTH. No adjustment is made to smaller fields
* that are not expected to be invariant. The calendar system
* determines what fields are expected to be invariant.
*
*
* @example
* use('date/gregorian',function(S, GregorianCalendar){
* const d = new GregorianCalendar();
* d.set(2012, GregorianCalendar.JANUARY, 31);
* d.add(Gregorian.MONTH,1);
* // 2012-2-29
* document.writeln('<p>'+d.getYear()+'-'+d.getMonth()+'-'+d.getDayOfWeek())
* d.add(Gregorian.MONTH,12);
* // 2013-2-28
* document.writeln('<p>'+d.getYear()+'-'+d.getMonth()+'-'+d.getDayOfWeek())
* });
*
* @param field the calendar field.
* @param {Number} amount he amount of date or time to be added to the field.
*/
add: function add(field, a) {
if (!a) {
return;
}
var amount = a;
var self = this;
var fields = self.fields;
// computer and retrieve original value
var value = self.get(field);
if (field === YEAR) {
value += amount;
self.set(YEAR, value);
adjustDayOfMonth(self);
} else if (field === MONTH) {
value += amount;
var yearAmount = floorDivide(value / 12);
value = mod(value, 12);
if (yearAmount) {
self.set(YEAR, fields[YEAR] + yearAmount);
}
self.set(MONTH, value);
adjustDayOfMonth(self);
} else {
switch (field) {
case HOUR_OF_DAY:
amount *= ONE_HOUR;
break;
case MINUTE:
amount *= ONE_MINUTE;
break;
case SECONDS:
amount *= ONE_SECOND;
break;
case MILLISECONDS:
break;
case WEEK_OF_MONTH:
case WEEK_OF_YEAR:
case DAY_OF_WEEK_IN_MONTH:
amount *= ONE_WEEK;
break;
case DAY_OF_WEEK:
case DAY_OF_YEAR:
case DAY_OF_MONTH:
amount *= ONE_DAY;
break;
default:
throw new Error('illegal field for add');
}
self.setTime(self.time + amount);
}
},
/*
* add the year of the given calendar field.
* @method addYear
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the month of the given calendar field.
* @method addMonth
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the day of month of the given calendar field.
* @method addDayOfMonth
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the hour of day of the given calendar field.
* @method addHourOfDay
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the minute of the given calendar field.
* @method addMinute
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the second of the given calendar field.
* @method addSecond
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the millisecond of the given calendar field.
* @method addMilliSecond
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the week of year of the given calendar field.
* @method addWeekOfYear
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the week of month of the given calendar field.
* @method addWeekOfMonth
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the day of year of the given calendar field.
* @method addDayOfYear
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the day of week of the given calendar field.
* @method addDayOfWeek
* @param {Number} amount the signed amount to add to field.
*/
/*
* add the day of week in month of the given calendar field.
* @method addDayOfWeekInMonth
* @param {Number} amount the signed amount to add to field.
*/
/*
* Get rolled value for the field
* @protected
*/
getRolledValue: function getRolledValue(value, a, min, max) {
var amount = a;
var diff = value - min;
var range = max - min + 1;
amount %= range;
return min + (diff + amount + range) % range;
},
/*
* Adds a signed amount to the specified calendar field without changing larger fields.
* A negative roll amount means to subtract from field without changing
* larger fields. If the specified amount is 0, this method performs nothing.
*
*
*
* @example
* const d = new GregorianCalendar();
* d.set(1999, GregorianCalendar.AUGUST, 31);
* // 1999-4-30
* // Tuesday June 1, 1999
* d.set(1999, GregorianCalendar.JUNE, 1);
* d.add(Gregorian.WEEK_OF_MONTH,-1); // === d.add(Gregorian.WEEK_OF_MONTH,
* d.get(Gregorian.WEEK_OF_MONTH));
* // 1999-06-29
*
*
* @param field the calendar field.
* @param {Number} amount the signed amount to add to field.
*/
roll: function roll(field, amount) {
if (!amount) {
return;
}
var self = this;
// computer and retrieve original value
var value = self.get(field);
var min = self.getActualMinimum(field);
var max = self.getActualMaximum(field);
value = self.getRolledValue(value, amount, min, max);
self.set(field, value);
// consider compute time priority
switch (field) {
case MONTH:
adjustDayOfMonth(self);
break;
default:
// other fields are set already when get
self.updateFieldsBySet(field);
break;
}
},
/*
* keep field stable.
*
* 2015-09-29 setMonth 2 vs rollSetMonth 2
*
*/
rollSet: function rollSet(field, v) {
this.set(field, v);
switch (field) {
case MONTH:
adjustDayOfMonth(this);
break;
default:
// other fields are set already when get
this.updateFieldsBySet(field);
break;
}
},
/*
* roll the year of the given calendar field.
* @method rollYear
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the month of the given calendar field.
* @param {Number} amount the signed amount to add to field.
* @method rollMonth
*/
/*
* roll the day of month of the given calendar field.
* @method rollDayOfMonth
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the hour of day of the given calendar field.
* @method rollHourOfDay
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the minute of the given calendar field.
* @method rollMinute
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the second of the given calendar field.
* @method rollSecond
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the millisecond of the given calendar field.
* @method rollMilliSecond
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the week of year of the given calendar field.
* @method rollWeekOfYear
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the week of month of the given calendar field.
* @method rollWeekOfMonth
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the day of year of the given calendar field.
* @method rollDayOfYear
* @param {Number} amount the signed amount to add to field.
*/
/*
* roll the day of week of the given calendar field.
* @method rollDayOfWeek
* @param {Number} amount the signed amount to add to field.
*/
/*
* remove other priority fields when call getFixedDate
* precondition: other fields are all set or computed
* @protected
*/
updateFieldsBySet: function updateFieldsBySet(field) {
var fields = this.fields;
switch (field) {
case WEEK_OF_MONTH:
fields[DAY_OF_MONTH] = undefined;
break;
case DAY_OF_YEAR:
fields[MONTH] = undefined;
break;
case DAY_OF_WEEK:
fields[DAY_OF_MONTH] = undefined;
break;
case WEEK_OF_YEAR:
fields[DAY_OF_YEAR] = undefined;
fields[MONTH] = undefined;
break;
default:
break;
}
},
/*
* get current date instance's timezone offset
* @returns {Number}
*/
getTimezoneOffset: function getTimezoneOffset() {
return this.timezoneOffset;
},
/*
* set current date instance's timezone offset
*/
setTimezoneOffset: function setTimezoneOffset(timezoneOffset) {
if (this.timezoneOffset !== timezoneOffset) {
this.fieldsComputed = undefined;
this.timezoneOffset = timezoneOffset;
}
},
/*
* set first day of week for current date instance
*/
setFirstDayOfWeek: function setFirstDayOfWeek(firstDayOfWeek) {
if (this.firstDayOfWeek !== firstDayOfWeek) {
this.firstDayOfWeek = firstDayOfWeek;
this.fieldsComputed = false;
}
},
/*
* Gets what the first day of the week is; e.g., SUNDAY in the U.S., MONDAY in France.
* @returns {Number} the first day of the week.
*/
getFirstDayOfWeek: function getFirstDayOfWeek() {
return this.firstDayOfWeek;
},
/*
* Sets what the minimal days required in the first week of the year are; For example,
* if the first week is defined as one that contains the first day of the first month of a year,
* call this method with value 1.
* If it must be a full week, use value 7.
* @param minimalDaysInFirstWeek the given minimal days required in the first week of the year.
*/
setMinimalDaysInFirstWeek: function setMinimalDaysInFirstWeek(minimalDaysInFirstWeek) {
if (this.minimalDaysInFirstWeek !== minimalDaysInFirstWeek) {
this.minimalDaysInFirstWeek = minimalDaysInFirstWeek;
this.fieldsComputed = false;
}
},
/*
* Gets what the minimal days required in the first week of the year are; e.g.,
* if the first week is defined as one that contains the first day of the first month of a year,
* this method returns 1.
* If the minimal days required must be a full week, this method returns 7.
* @returns {Number} the minimal days required in the first week of the year.
*/
getMinimalDaysInFirstWeek: function getMinimalDaysInFirstWeek() {
return this.minimalDaysInFirstWeek;
},
/*
* Returns the number of weeks in the week year
* represented by this GregorianCalendar.
*
* For example, if this GregorianCalendar's date is
* December 31, 2008 with the ISO
* 8601 compatible setting, this method will return 53 for the
* period: December 29, 2008 to January 3, 2010
* while getActualMaximum(WEEK_OF_YEAR) will return
* 52 for the period: December 31, 2007 to December 28, 2008.
*
* @return {Number} the number of weeks in the week year.
*/
getWeeksInWeekYear: function getWeeksInWeekYear() {
var weekYear = this.getWeekYear();
if (weekYear === this.get(YEAR)) {
return this.getActualMaximum(WEEK_OF_YEAR);
}
// Use the 2nd week for calculating the max of WEEK_OF_YEAR
var gc = this.clone();
gc.clear();
gc.setWeekDate(weekYear, 2, this.get(DAY_OF_WEEK));
return gc.getActualMaximum(WEEK_OF_YEAR);
},
/*
* Returns the week year represented by this GregorianCalendar.
* The dates in the weeks between 1 and the
* maximum week number of the week year have the same week year value
* that may be one year before or after the calendar year value.
*
* @return {Number} the week year represented by this GregorianCalendar.
*/
getWeekYear: function getWeekYear() {
var year = this.get(YEAR); // implicitly complete
var weekOfYear = this.get(WEEK_OF_YEAR);
var month = this.get(MONTH);
if (month === GregorianCalendar.JANUARY) {
if (weekOfYear >= 52) {
--year;
}
} else if (month === GregorianCalendar.DECEMBER) {
if (weekOfYear === 1) {
++year;
}
}
return year;
},
/*
* Sets this GregorianCalendar to the date given by the date specifiers - weekYear,
* weekOfYear, and dayOfWeek. weekOfYear follows the WEEK_OF_YEAR numbering.
* The dayOfWeek value must be one of the DAY_OF_WEEK values: SUNDAY to SATURDAY.
*
* @param weekYear the week year
* @param weekOfYear the week number based on weekYear
* @param dayOfWeek the day of week value
*/
setWeekDate: function setWeekDate(weekYear, weekOfYear, dayOfWeek) {
if (dayOfWeek < GregorianCalendar.SUNDAY || dayOfWeek > GregorianCalendar.SATURDAY) {
throw new Error('invalid dayOfWeek: ' + dayOfWeek);
}
var fields = this.fields;
// To avoid changing the time of day fields by date
// calculations, use a clone with the GMT time zone.
var gc = this.clone();
gc.clear();
gc.setTimezoneOffset(0);
gc.set(YEAR, weekYear);
gc.set(WEEK_OF_YEAR, 1);
gc.set(DAY_OF_WEEK, this.getFirstDayOfWeek());
var days = dayOfWeek - this.getFirstDayOfWeek();
if (days < 0) {
days += 7;
}
days += 7 * (weekOfYear - 1);
if (days !== 0) {
gc.add(DAY_OF_YEAR, days);
} else {
gc.complete();
}
fields[YEAR] = gc.get(YEAR);
fields[MONTH] = gc.get(MONTH);
fields[DAY_OF_MONTH] = gc.get(DAY_OF_MONTH);
this.complete();
},
/*
* Creates and returns a copy of this object.
* @returns {Date.Gregorian}
*/
clone: function clone() {
if (this.time === undefined) {
this.computeTime();
}
var cal = new GregorianCalendar(this.locale);
cal.setTimezoneOffset(cal.getTimezoneOffset());
cal.setFirstDayOfWeek(cal.getFirstDayOfWeek());
cal.setMinimalDaysInFirstWeek(cal.getMinimalDaysInFirstWeek());
cal.setTime(this.time);
return cal;
},
/*
* Compares this GregorianCalendar to the specified Object.
* The result is true if and only if the argument is a GregorianCalendar object
* that represents the same time value (millisecond offset from the Epoch)
* under the same Calendar parameters and Gregorian change date as this object.
* @param {Date.Gregorian} obj the object to compare with.
* @returns {boolean} true if this object is equal to obj; false otherwise.
*/
equals: function equals(obj) {
return this.getTime() === obj.getTime() && this.firstDayOfWeek === obj.firstDayOfWeek && this.timezoneOffset === obj.timezoneOffset && this.minimalDaysInFirstWeek === obj.minimalDaysInFirstWeek;
},
compareToDay: function compareToDay(d2) {
var d1Year = this.getYear();
var d2Year = d2.getYear();
var d1Month = this.getMonth();
var d2Month = d2.getMonth();
var d1Day = this.getDayOfMonth();
var d2Day = d2.getDayOfMonth();
if (d1Year !== d2Year) {
return d1Year - d2Year;
}
if (d1Month !== d2Month) {
return d1Month - d2Month;
}
return d1Day - d2Day;
},
/*
* Sets all the calendar field values or specified field and the time value
* (millisecond offset from the Epoch) of this Calendar undefined.
* This means that isSet() will return false for all the calendar fields,
* and the date and time calculations will treat the fields as if they had never been set.
* @param [field] the calendar field to be cleared.
*/
clear: function clear(field) {
if (field === undefined) {
this.field = [];
} else {
this.fields[field] = undefined;
}
this.time = undefined;
this.fieldsComputed = false;
},
toString: function toString() {
// for debug
var v = this;
return '[GregorianCalendar]: ' + v.getYear() + '/' + v.getMonth() + '/' + v.getDayOfMonth() + ' ' + v.getHourOfDay() + ':' + v.getMinutes() + ':' + v.getSeconds();
}
};
var GregorianCalendarProto = GregorianCalendar.prototype;
Utils.each(FIELDS, function (f, index) {
if (f) {
GregorianCalendarProto['get' + f] = function get() {
return this.get(index);
};
GregorianCalendarProto['isSet' + f] = function isSet() {
return this.isSet(index);
};
GregorianCalendarProto['set' + f] = function set(v) {
return this.set(index, v);
};
GregorianCalendarProto['add' + f] = function add(v) {
return this.add(index, v);
};
GregorianCalendarProto['roll' + f] = function roll(v) {
return this.roll(index, v);
};
GregorianCalendarProto['rollSet' + f] = function rollSet(v) {
return this.rollSet(index, v);
};
}
});
module.exports = GregorianCalendar;
/*
http://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html
TODO
- day saving time
- i18n
- julian calendar
*/