/** * GregorianCalendar class for KISSY. * @ignore * @author yiminghe@gmail.com */ KISSY.add('date/gregorian', function (S, defaultLocale, Utils, Const, undefined) { var toInt = parseInt; /** * GregorianCalendar class. * * - no arguments: * Constructs a default GregorianCalendar using the current time * in the default time zone with the default locale. * - one argument timezoneOffset: * Constructs a GregorianCalendar based on the current time * in the given timezoneOffset with the default locale. * - one argument locale: * Constructs a GregorianCalendar * based on the current time in the default time zone with the given locale. * - two arguments * Constructs a GregorianCalendar based on the current time in the given time zone with the given locale. * - zone - the given time zone. * - aLocale - the given locale. * * - 3 to 6 arguments: * Constructs a GregorianCalendar with the given date and time set for the default time zone with the default locale. * - year - the value used to set the YEAR calendar field in the calendar. * - month - the value used to set the MONTH calendar field in the calendar. Month value is 0-based. e.g., * 0 for January. * - dayOfMonth - the value used to set the DAY_OF_MONTH calendar field in the calendar. * - hourOfDay - the value used to set the HOUR_OF_DAY calendar field in the calendar. * - minute - the value used to set the MINUTE calendar field in the calendar. * - second - the value used to set the SECONDS calendar field in the calendar. * * * @class KISSY.Date.Gregorian */ function GregorianCalendar(timezoneOffset, locale) { var args = S.makeArray(arguments); if (S.isObject(timezoneOffset)) { locale = timezoneOffset; timezoneOffset = locale.timezoneOffset; } else if (args.length >= 3) { timezoneOffset = locale = null; } locale = locale || 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 = 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; if (arguments.length >= 3) { this.set.apply(this, args); } } S.mix(GregorianCalendar, Const); S.mix(GregorianCalendar, { /** * 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, isLeapYear = Utils.isLeapYear, 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 // DAY_OF_WEEK_IN_MONTH ]; GregorianCalendar.prototype = { constructor: GregorianCalendar, /** * 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 KISSY.Date.Gregorian */ isLeapYear: function () { return isLeapYear(this.getYear()); }, /** * Return local info for current date instance * @returns {Object} */ getLocale: function () { 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 (field) { if (MIN_VALUES[field] !== undefined) { return MIN_VALUES[field]; } var fields = this.fields; if (field === WEEK_OF_MONTH) { var cal = new GregorianCalendar(fields[YEAR], 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 (field) { if (MAX_VALUES[field] !== undefined) { return MAX_VALUES[field]; } var value, fields = this.fields; switch (field) { case DAY_OF_MONTH: value = getMonthLength(fields[YEAR], fields[MONTH]); break; case WEEK_OF_YEAR: var endOfYear = new GregorianCalendar(fields[YEAR], GregorianCalendar.DECEMBER, 31); value = endOfYear.get(WEEK_OF_YEAR); if (value == 1) { value = 52; } break; case WEEK_OF_MONTH: var endOfMonth = new GregorianCalendar(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; } 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 (field) { return this.fields[field] !== undefined; }, /** * Converts the time value (millisecond offset from the Epoch) * to calendar field values. * @protected */ computeFields: function () { 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 () { if (!this.isSet(YEAR)) { throw new Error('year must be set for KISSY GregorianCalendar'); } var fields = this.fields; var year = fields[YEAR]; 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 () { if (this.time === undefined) { this.computeTime(); } if (!this.fieldsComputed) { this.computeFields(); } }, getFixedDate: function () { 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 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)) { var 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; 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 { 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 KISSY.Date.Gregorian * @returns {Number} the current time as UTC milliseconds from the epoch. */ getTime: function () { 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 (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 (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 (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 KISSY 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 * KISSY.use('date/gregorian',function(S, GregorianCalendar){ * var 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 (field, amount) { if (!amount) { return; } 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'); break; } 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 (value, amount, min, max) { 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 * var 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 (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; } }, /** * 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 (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; } }, /** * get current date instance's timezone offset * @returns {Number} */ getTimezoneOffset: function () { return this.timezoneOffset; }, /** * set current date instance's timezone offset */ 'setTimezoneOffset': function (timezoneOffset) { if (this.timezoneOffset != timezoneOffset) { this.fieldsComputed = undefined; this.timezoneOffset = timezoneOffset; } }, /** * set first day of week for current date instance */ 'setFirstDayOfWeek': function (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 () { 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 (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 () { 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 () { 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.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 () { 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 (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 {KISSY.Date.Gregorian} */ clone: function () { if (this.time === undefined) { this.computeTime(); } var cal = new GregorianCalendar(this.timezoneOffset, this.locale); 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 {KISSY.Date.Gregorian} obj the object to compare with. * @returns {boolean} true if this object is equal to obj; false otherwise. */ equals: function (obj) { return this.getTime() == obj.getTime() && this.firstDayOfWeek == obj.firstDayOfWeek && this.timezoneOffset == obj.timezoneOffset && this.minimalDaysInFirstWeek == obj.minimalDaysInFirstWeek; }, /** * 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 (field) { if (field === undefined) { this.field = []; } else { this.fields[field] = undefined; } this.time = undefined; this.fieldsComputed = false; } }; var GregorianCalendarProto = GregorianCalendar.prototype; if ('@DEBUG@') { // for idea GregorianCalendarProto.getDayOfMonth = GregorianCalendarProto.getHourOfDay = GregorianCalendarProto.getWeekOfYear = GregorianCalendarProto.getWeekOfMonth = GregorianCalendarProto.getDayOfYear = GregorianCalendarProto.getDayOfWeek = GregorianCalendarProto.getDayOfWeekInMonth = S.noop; GregorianCalendarProto.addDayOfMonth = GregorianCalendarProto.addMonth = GregorianCalendarProto.addYear = GregorianCalendarProto.addMinutes = GregorianCalendarProto.addSeconds = GregorianCalendarProto.addMilliSeconds = GregorianCalendarProto.addHourOfDay = GregorianCalendarProto.addWeekOfYear = GregorianCalendarProto.addWeekOfMonth = GregorianCalendarProto.addDayOfYear = GregorianCalendarProto.addDayOfWeek = GregorianCalendarProto.addDayOfWeekInMonth = S.noop; GregorianCalendarProto.isSetDayOfMonth = GregorianCalendarProto.isSetMonth = GregorianCalendarProto.isSetYear = GregorianCalendarProto.isSetMinutes = GregorianCalendarProto.isSetSeconds = GregorianCalendarProto.isSetMilliSeconds = GregorianCalendarProto.isSetHourOfDay = GregorianCalendarProto.isSetWeekOfYear = GregorianCalendarProto.isSetWeekOfMonth = GregorianCalendarProto.isSetDayOfYear = GregorianCalendarProto.isSetDayOfWeek = GregorianCalendarProto.isSetDayOfWeekInMonth = S.noop; GregorianCalendarProto.setDayOfMonth = GregorianCalendarProto.setHourOfDay = GregorianCalendarProto.setWeekOfYear = GregorianCalendarProto.setWeekOfMonth = GregorianCalendarProto.setDayOfYear = GregorianCalendarProto.setDayOfWeek = GregorianCalendarProto.setDayOfWeekInMonth = S.noop; GregorianCalendarProto.rollDayOfMonth = GregorianCalendarProto.rollMonth = GregorianCalendarProto.rollYear = GregorianCalendarProto.rollMinutes = GregorianCalendarProto.rollSeconds = GregorianCalendarProto.rollMilliSeconds = GregorianCalendarProto.rollHourOfDay = GregorianCalendarProto.rollWeekOfYear = GregorianCalendarProto.rollWeekOfMonth = GregorianCalendarProto.rollDayOfYear = GregorianCalendarProto.rollDayOfWeek = GregorianCalendarProto.rollDayOfWeekInMonth = S.noop; } S.each(fields, function (f, index) { if (f) { GregorianCalendarProto['get' + f] = function () { return this.get(index); }; GregorianCalendarProto['isSet' + f] = function () { return this.isSet(index); }; GregorianCalendarProto['set' + f] = function (v) { return this.set(index, v); }; GregorianCalendarProto['add' + f] = function (v) { return this.add(index, v); }; GregorianCalendarProto['roll' + f] = function (v) { return this.roll(index, v); }; } }); // ------------------- private start 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 getMonthLength(year, month) { return isLeapYear(year) ? LEAP_MONTH_LENGTH[month] : MONTH_LENGTH[month]; } function getYearLength(year) { return isLeapYear(year) ? 366 : 365; } 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; } function getDayOfWeekDateOnOrBefore(fixedDate, dayOfWeek) { // 1.1.1 is monday // one week has 7 days return fixedDate - mod(fixedDate - dayOfWeek, 7); } // ------------------- private end return GregorianCalendar; }, { requires: ['i18n!date', './gregorian/utils', './gregorian/const'] }); /* http://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html TODO - day saving time - i18n - julian calendar */