// moment.js // version : 1.7.2 // author : Tim Wood // license : MIT // momentjs.com (function (undefined) { /************************************ Constants ************************************/ var moment, VERSION = "1.7.2", round = Math.round, i, // internal storage for language config files languages = {}, currentLanguage = 'en', // check for nodeJS hasModule = (typeof module !== 'undefined' && module.exports), // Parameters to check for on the lang config. This list of properties // will be inherited from English if not provided in a language // definition. monthsParse is also a lang config property, but it // cannot be inherited and as such cannot be enumerated here. langConfigProperties = 'months|monthsShort|weekdays|weekdaysShort|weekdaysMin|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'), // ASP.NET json date format regex aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, // format tokens formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|.)/g, localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?)/g, // parsing tokens parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi, // parsing token regexes parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 parseTokenThreeDigits = /\d{3}/, // 000 - 999 parseTokenFourDigits = /\d{1,4}/, // 0 - 9999 parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z parseTokenT = /T/i, // T (ISO seperator) // preliminary iso regex // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/, isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', // iso time formats and regexes isoTimes = [ ['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/], ['HH:mm:ss', /T\d\d:\d\d:\d\d/], ['HH:mm', /T\d\d:\d\d/], ['HH', /T\d\d/] ], // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] parseTimezoneChunker = /([\+\-]|\d\d)/gi, // getter and setter names proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), unitMillisecondFactors = { 'Milliseconds' : 1, 'Seconds' : 1e3, 'Minutes' : 6e4, 'Hours' : 36e5, 'Days' : 864e5, 'Months' : 2592e6, 'Years' : 31536e6 }, // format function strings formatFunctions = {}, // tokens to ordinalize and pad ordinalizeTokens = 'DDD w M D d'.split(' '), paddedTokens = 'M D H h m s w'.split(' '), /* * moment.fn.format uses new Function() to create an inlined formatting function. * Results are a 3x speed boost * http://jsperf.com/momentjs-cached-format-functions * * These strings are appended into a function using replaceFormatTokens and makeFormatFunction */ formatTokenFunctions = { // a = placeholder // b = placeholder // t = the current moment being formatted // v = getValueAtKey function // o = language.ordinal function // p = leftZeroFill function // m = language.meridiem value or function M : function () { return this.month() + 1; }, MMM : function (format) { return getValueFromArray("monthsShort", this.month(), this, format); }, MMMM : function (format) { return getValueFromArray("months", this.month(), this, format); }, D : function () { return this.date(); }, DDD : function () { var a = new Date(this.year(), this.month(), this.date()), b = new Date(this.year(), 0, 1); return ~~(((a - b) / 864e5) + 1.5); }, d : function () { return this.day(); }, dd : function (format) { return getValueFromArray("weekdaysMin", this.day(), this, format); }, ddd : function (format) { return getValueFromArray("weekdaysShort", this.day(), this, format); }, dddd : function (format) { return getValueFromArray("weekdays", this.day(), this, format); }, w : function () { var a = new Date(this.year(), this.month(), this.date() - this.day() + 5), b = new Date(a.getFullYear(), 0, 4); return ~~((a - b) / 864e5 / 7 + 1.5); }, YY : function () { return leftZeroFill(this.year() % 100, 2); }, YYYY : function () { return leftZeroFill(this.year(), 4); }, a : function () { return this.lang().meridiem(this.hours(), this.minutes(), true); }, A : function () { return this.lang().meridiem(this.hours(), this.minutes(), false); }, H : function () { return this.hours(); }, h : function () { return this.hours() % 12 || 12; }, m : function () { return this.minutes(); }, s : function () { return this.seconds(); }, S : function () { return ~~(this.milliseconds() / 100); }, SS : function () { return leftZeroFill(~~(this.milliseconds() / 10), 2); }, SSS : function () { return leftZeroFill(this.milliseconds(), 3); }, Z : function () { var a = -this.zone(), b = "+"; if (a < 0) { a = -a; b = "-"; } return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2); }, ZZ : function () { var a = -this.zone(), b = "+"; if (a < 0) { a = -a; b = "-"; } return b + leftZeroFill(~~(10 * a / 6), 4); } }; function getValueFromArray(key, index, m, format) { var lang = m.lang(); return lang[key].call ? lang[key](m, format) : lang[key][index]; } function padToken(func, count) { return function (a) { return leftZeroFill(func.call(this, a), count); }; } function ordinalizeToken(func) { return function (a) { var b = func.call(this, a); return b + this.lang().ordinal(b); }; } while (ordinalizeTokens.length) { i = ordinalizeTokens.pop(); formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]); } while (paddedTokens.length) { i = paddedTokens.pop(); formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); /************************************ Constructors ************************************/ // Moment prototype object function Moment(date, isUTC, lang) { this._d = date; this._isUTC = !!isUTC; this._a = date._a || null; this._lang = lang || false; } // Duration Constructor function Duration(duration) { var data = this._data = {}, years = duration.years || duration.y || 0, months = duration.months || duration.M || 0, weeks = duration.weeks || duration.w || 0, days = duration.days || duration.d || 0, hours = duration.hours || duration.h || 0, minutes = duration.minutes || duration.m || 0, seconds = duration.seconds || duration.s || 0, milliseconds = duration.milliseconds || duration.ms || 0; // representation for dateAddRemove this._milliseconds = milliseconds + seconds * 1e3 + // 1000 minutes * 6e4 + // 1000 * 60 hours * 36e5; // 1000 * 60 * 60 // Because of dateAddRemove treats 24 hours as different from a // day when working around DST, we need to store them separately this._days = days + weeks * 7; // It is impossible translate months into days without knowing // which months you are are talking about, so we have to store // it separately. this._months = months + years * 12; // The following code bubbles up values, see the tests for // examples of what that means. data.milliseconds = milliseconds % 1000; seconds += absRound(milliseconds / 1000); data.seconds = seconds % 60; minutes += absRound(seconds / 60); data.minutes = minutes % 60; hours += absRound(minutes / 60); data.hours = hours % 24; days += absRound(hours / 24); days += weeks * 7; data.days = days % 30; months += absRound(days / 30); data.months = months % 12; years += absRound(months / 12); data.years = years; this._lang = false; } /************************************ Helpers ************************************/ function absRound(number) { if (number < 0) { return Math.ceil(number); } else { return Math.floor(number); } } // left zero fill a number // see http://jsperf.com/left-zero-filling for performance comparison function leftZeroFill(number, targetLength) { var output = number + ''; while (output.length < targetLength) { output = '0' + output; } return output; } // helper function for _.addTime and _.subtractTime function addOrSubtractDurationFromMoment(mom, duration, isAdding) { var ms = duration._milliseconds, d = duration._days, M = duration._months, currentDate; if (ms) { mom._d.setTime(+mom + ms * isAdding); } if (d) { mom.date(mom.date() + d * isAdding); } if (M) { currentDate = mom.date(); mom.date(1) .month(mom.month() + M * isAdding) .date(Math.min(currentDate, mom.daysInMonth())); } } // check if is an array function isArray(input) { return Object.prototype.toString.call(input) === '[object Array]'; } // compare two arrays, return the number of differences function compareArrays(array1, array2) { var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; for (i = 0; i < len; i++) { if (~~array1[i] !== ~~array2[i]) { diffs++; } } return diffs + lengthDiff; } // convert an array to a date. // the array should mirror the parameters below // note: all values past the year are optional and will default to the lowest possible value. // [year, month, day , hour, minute, second, millisecond] function dateFromArray(input, asUTC, hoursOffset, minutesOffset) { var i, date, forValid = []; for (i = 0; i < 7; i++) { forValid[i] = input[i] = (input[i] == null) ? (i === 2 ? 1 : 0) : input[i]; } // we store whether we used utc or not in the input array input[7] = forValid[7] = asUTC; // if the parser flagged the input as invalid, we pass the value along if (input[8] != null) { forValid[8] = input[8]; } // add the offsets to the time to be parsed so that we can have a clean array // for checking isValid input[3] += hoursOffset || 0; input[4] += minutesOffset || 0; date = new Date(0); if (asUTC) { date.setUTCFullYear(input[0], input[1], input[2]); date.setUTCHours(input[3], input[4], input[5], input[6]); } else { date.setFullYear(input[0], input[1], input[2]); date.setHours(input[3], input[4], input[5], input[6]); } date._a = forValid; return date; } // Loads a language definition into the `languages` cache. The function // takes a key and optionally values. If not in the browser and no values // are provided, it will load the language file module. As a convenience, // this function also returns the language values. function loadLang(key, values) { var i, m, parse = []; if (!values && hasModule) { values = require('./lang/' + key); } for (i = 0; i < langConfigProperties.length; i++) { // If a language definition does not provide a value, inherit // from English values[langConfigProperties[i]] = values[langConfigProperties[i]] || languages.en[langConfigProperties[i]]; } for (i = 0; i < 12; i++) { m = moment([2000, i]); parse[i] = new RegExp('^' + (values.months[i] || values.months(m, '')) + '|^' + (values.monthsShort[i] || values.monthsShort(m, '')).replace('.', ''), 'i'); } values.monthsParse = values.monthsParse || parse; languages[key] = values; return values; } // Determines which language definition to use and returns it. // // With no parameters, it will return the global language. If you // pass in a language key, such as 'en', it will return the // definition for 'en', so long as 'en' has already been loaded using // moment.lang. If you pass in a moment or duration instance, it // will decide the language based on that, or default to the global // language. function getLangDefinition(m) { var langKey = (typeof m === 'string') && m || m && m._lang || null; return langKey ? (languages[langKey] || loadLang(langKey)) : moment; } /************************************ Formatting ************************************/ function removeFormattingTokens(input) { if (input.match(/\[.*\]/)) { return input.replace(/^\[|\]$/g, ""); } return input.replace(/\\/g, ""); } function makeFormatFunction(format) { var array = format.match(formattingTokens), i, length; for (i = 0, length = array.length; i < length; i++) { if (formatTokenFunctions[array[i]]) { array[i] = formatTokenFunctions[array[i]]; } else { array[i] = removeFormattingTokens(array[i]); } } return function (mom) { var output = ""; for (i = 0; i < length; i++) { output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i]; } return output; }; } // format date using native date object function formatMoment(m, format) { var i = 5; function replaceLongDateFormatTokens(input) { return m.lang().longDateFormat[input] || input; } while (i-- && localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); } if (!formatFunctions[format]) { formatFunctions[format] = makeFormatFunction(format); } return formatFunctions[format](m); } /************************************ Parsing ************************************/ // get the regex to find the next token function getParseRegexForToken(token) { switch (token) { case 'DDDD': return parseTokenThreeDigits; case 'YYYY': return parseTokenFourDigits; case 'S': case 'SS': case 'SSS': case 'DDD': return parseTokenOneToThreeDigits; case 'MMM': case 'MMMM': case 'dd': case 'ddd': case 'dddd': case 'a': case 'A': return parseTokenWord; case 'Z': case 'ZZ': return parseTokenTimezone; case 'T': return parseTokenT; case 'MM': case 'DD': case 'YY': case 'HH': case 'hh': case 'mm': case 'ss': case 'M': case 'D': case 'd': case 'H': case 'h': case 'm': case 's': return parseTokenOneOrTwoDigits; default : return new RegExp(token.replace('\\', '')); } } // function to convert string input to date function addTimeToArrayFromToken(token, input, datePartArray, config) { var a, b; switch (token) { // MONTH case 'M' : // fall through to MM case 'MM' : datePartArray[1] = (input == null) ? 0 : ~~input - 1; break; case 'MMM' : // fall through to MMMM case 'MMMM' : for (a = 0; a < 12; a++) { if (getLangDefinition().monthsParse[a].test(input)) { datePartArray[1] = a; b = true; break; } } // if we didn't find a month name, mark the date as invalid. if (!b) { datePartArray[8] = false; } break; // DAY OF MONTH case 'D' : // fall through to DDDD case 'DD' : // fall through to DDDD case 'DDD' : // fall through to DDDD case 'DDDD' : if (input != null) { datePartArray[2] = ~~input; } break; // YEAR case 'YY' : datePartArray[0] = ~~input + (~~input > 70 ? 1900 : 2000); break; case 'YYYY' : datePartArray[0] = ~~Math.abs(input); break; // AM / PM case 'a' : // fall through to A case 'A' : config.isPm = ((input + '').toLowerCase() === 'pm'); break; // 24 HOUR case 'H' : // fall through to hh case 'HH' : // fall through to hh case 'h' : // fall through to hh case 'hh' : datePartArray[3] = ~~input; break; // MINUTE case 'm' : // fall through to mm case 'mm' : datePartArray[4] = ~~input; break; // SECOND case 's' : // fall through to ss case 'ss' : datePartArray[5] = ~~input; break; // MILLISECOND case 'S' : case 'SS' : case 'SSS' : datePartArray[6] = ~~ (('0.' + input) * 1000); break; // TIMEZONE case 'Z' : // fall through to ZZ case 'ZZ' : config.isUTC = true; a = (input + '').match(parseTimezoneChunker); if (a && a[1]) { config.tzh = ~~a[1]; } if (a && a[2]) { config.tzm = ~~a[2]; } // reverse offsets if (a && a[0] === '+') { config.tzh = -config.tzh; config.tzm = -config.tzm; } break; } // if the input is null, the date is not valid if (input == null) { datePartArray[8] = false; } } // date from string and format string function makeDateFromStringAndFormat(string, format) { // This array is used to make a Date, either with `new Date` or `Date.UTC` // We store some additional data on the array for validation // datePartArray[7] is true if the Date was created with `Date.UTC` and false if created with `new Date` // datePartArray[8] is false if the Date is invalid, and undefined if the validity is unknown. var datePartArray = [0, 0, 1, 0, 0, 0, 0], config = { tzh : 0, // timezone hour offset tzm : 0 // timezone minute offset }, tokens = format.match(formattingTokens), i, parsedInput; for (i = 0; i < tokens.length; i++) { parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0]; if (parsedInput) { string = string.slice(string.indexOf(parsedInput) + parsedInput.length); } // don't parse if its not a known token if (formatTokenFunctions[tokens[i]]) { addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config); } } // handle am pm if (config.isPm && datePartArray[3] < 12) { datePartArray[3] += 12; } // if is 12 am, change hours to 0 if (config.isPm === false && datePartArray[3] === 12) { datePartArray[3] = 0; } // return return dateFromArray(datePartArray, config.isUTC, config.tzh, config.tzm); } // date from string and array of format strings function makeDateFromStringAndArray(string, formats) { var output, inputParts = string.match(parseMultipleFormatChunker) || [], formattedInputParts, scoreToBeat = 99, i, currentDate, currentScore; for (i = 0; i < formats.length; i++) { currentDate = makeDateFromStringAndFormat(string, formats[i]); formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || []; currentScore = compareArrays(inputParts, formattedInputParts); if (currentScore < scoreToBeat) { scoreToBeat = currentScore; output = currentDate; } } return output; } // date from iso format function makeDateFromString(string) { var format = 'YYYY-MM-DDT', i; if (isoRegex.exec(string)) { for (i = 0; i < 4; i++) { if (isoTimes[i][1].exec(string)) { format += isoTimes[i][0]; break; } } return parseTokenTimezone.exec(string) ? makeDateFromStringAndFormat(string, format + ' Z') : makeDateFromStringAndFormat(string, format); } return new Date(string); } /************************************ Relative Time ************************************/ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { var rt = lang.relativeTime[string]; return (typeof rt === 'function') ? rt(number || 1, !!withoutSuffix, string, isFuture) : rt.replace(/%d/i, number || 1); } function relativeTime(milliseconds, withoutSuffix, lang) { var seconds = round(Math.abs(milliseconds) / 1000), minutes = round(seconds / 60), hours = round(minutes / 60), days = round(hours / 24), years = round(days / 365), args = seconds < 45 && ['s', seconds] || minutes === 1 && ['m'] || minutes < 45 && ['mm', minutes] || hours === 1 && ['h'] || hours < 22 && ['hh', hours] || days === 1 && ['d'] || days <= 25 && ['dd', days] || days <= 45 && ['M'] || days < 345 && ['MM', round(days / 30)] || years === 1 && ['y'] || ['yy', years]; args[2] = withoutSuffix; args[3] = milliseconds > 0; args[4] = lang; return substituteTimeAgo.apply({}, args); } /************************************ Top Level Functions ************************************/ moment = function (input, format) { if (input === null || input === '') { return null; } var date, matched; // parse Moment object if (moment.isMoment(input)) { return new Moment(new Date(+input._d), input._isUTC, input._lang); // parse string and format } else if (format) { if (isArray(format)) { date = makeDateFromStringAndArray(input, format); } else { date = makeDateFromStringAndFormat(input, format); } // evaluate it as a JSON-encoded date } else { matched = aspNetJsonRegex.exec(input); date = input === undefined ? new Date() : matched ? new Date(+matched[1]) : input instanceof Date ? input : isArray(input) ? dateFromArray(input) : typeof input === 'string' ? makeDateFromString(input) : new Date(input); } return new Moment(date); }; // creating with utc moment.utc = function (input, format) { if (isArray(input)) { return new Moment(dateFromArray(input, true), true); } // if we don't have a timezone, we need to add one to trigger parsing into utc if (typeof input === 'string' && !parseTokenTimezone.exec(input)) { input += ' +0000'; if (format) { format += ' Z'; } } return moment(input, format).utc(); }; // creating with unix timestamp (in seconds) moment.unix = function (input) { return moment(input * 1000); }; // duration moment.duration = function (input, key) { var isDuration = moment.isDuration(input), isNumber = (typeof input === 'number'), duration = (isDuration ? input._data : (isNumber ? {} : input)), ret; if (isNumber) { if (key) { duration[key] = input; } else { duration.milliseconds = input; } } ret = new Duration(duration); if (isDuration) { ret._lang = input._lang; } return ret; }; // humanizeDuration // This method is deprecated in favor of the new Duration object. Please // see the moment.duration method. moment.humanizeDuration = function (num, type, withSuffix) { return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix); }; // version number moment.version = VERSION; // default format moment.defaultFormat = isoFormat; // This function will load languages and then set the global language. If // no arguments are passed in, it will simply return the current global // language key. moment.lang = function (key, values) { var i; if (!key) { return currentLanguage; } if (values || !languages[key]) { loadLang(key, values); } if (languages[key]) { // deprecated, to get the language definition variables, use the // moment.fn.lang method or the getLangDefinition function. for (i = 0; i < langConfigProperties.length; i++) { moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]]; } moment.monthsParse = languages[key].monthsParse; currentLanguage = key; } }; // returns language data moment.langData = getLangDefinition; // compare moment object moment.isMoment = function (obj) { return obj instanceof Moment; }; // for typechecking Duration objects moment.isDuration = function (obj) { return obj instanceof Duration; }; // Set default language, other languages will inherit from English. moment.lang('en', { months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), longDateFormat : { LT : "h:mm A", L : "MM/DD/YYYY", LL : "MMMM D YYYY", LLL : "MMMM D YYYY LT", LLLL : "dddd, MMMM D YYYY LT" }, meridiem : function (hours, minutes, isLower) { if (hours > 11) { return isLower ? 'pm' : 'PM'; } else { return isLower ? 'am' : 'AM'; } }, calendar : { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[last] dddd [at] LT', sameElse : 'L' }, relativeTime : { future : "in %s", past : "%s ago", s : "a few seconds", m : "a minute", mm : "%d minutes", h : "an hour", hh : "%d hours", d : "a day", dd : "%d days", M : "a month", MM : "%d months", y : "a year", yy : "%d years" }, ordinal : function (number) { var b = number % 10; return (~~ (number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; } }); /************************************ Moment Prototype ************************************/ moment.fn = Moment.prototype = { clone : function () { return moment(this); }, valueOf : function () { return +this._d; }, unix : function () { return Math.floor(+this._d / 1000); }, toString : function () { return this._d.toString(); }, toDate : function () { return this._d; }, toArray : function () { var m = this; return [ m.year(), m.month(), m.date(), m.hours(), m.minutes(), m.seconds(), m.milliseconds(), !!this._isUTC ]; }, isValid : function () { if (this._a) { // if the parser finds that the input is invalid, it sets // the eighth item in the input array to false. if (this._a[8] != null) { return !!this._a[8]; } return !compareArrays(this._a, (this._a[7] ? moment.utc(this._a) : moment(this._a)).toArray()); } return !isNaN(this._d.getTime()); }, utc : function () { this._isUTC = true; return this; }, local : function () { this._isUTC = false; return this; }, format : function (inputString) { return formatMoment(this, inputString ? inputString : moment.defaultFormat); }, add : function (input, val) { var dur = val ? moment.duration(+val, input) : moment.duration(input); addOrSubtractDurationFromMoment(this, dur, 1); return this; }, subtract : function (input, val) { var dur = val ? moment.duration(+val, input) : moment.duration(input); addOrSubtractDurationFromMoment(this, dur, -1); return this; }, diff : function (input, val, asFloat) { var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(), zoneDiff = (this.zone() - inputMoment.zone()) * 6e4, diff = this._d - inputMoment._d - zoneDiff, year = this.year() - inputMoment.year(), month = this.month() - inputMoment.month(), date = this.date() - inputMoment.date(), output; if (val === 'months') { output = year * 12 + month + date / 30; } else if (val === 'years') { output = year + (month + date / 30) / 12; } else { output = val === 'seconds' ? diff / 1e3 : // 1000 val === 'minutes' ? diff / 6e4 : // 1000 * 60 val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60 val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24 val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7 diff; } return asFloat ? output : round(output); }, from : function (time, withoutSuffix) { return moment.duration(this.diff(time)).lang(this._lang).humanize(!withoutSuffix); }, fromNow : function (withoutSuffix) { return this.from(moment(), withoutSuffix); }, calendar : function () { var diff = this.diff(moment().sod(), 'days', true), calendar = this.lang().calendar, allElse = calendar.sameElse, format = diff < -6 ? allElse : diff < -1 ? calendar.lastWeek : diff < 0 ? calendar.lastDay : diff < 1 ? calendar.sameDay : diff < 2 ? calendar.nextDay : diff < 7 ? calendar.nextWeek : allElse; return this.format(typeof format === 'function' ? format.apply(this) : format); }, isLeapYear : function () { var year = this.year(); return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; }, isDST : function () { return (this.zone() < moment([this.year()]).zone() || this.zone() < moment([this.year(), 5]).zone()); }, day : function (input) { var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); return input == null ? day : this.add({ d : input - day }); }, startOf: function (val) { // the following switch intentionally omits break keywords // to utilize falling through the cases. switch (val.replace(/s$/, '')) { case 'year': this.month(0); /* falls through */ case 'month': this.date(1); /* falls through */ case 'day': this.hours(0); /* falls through */ case 'hour': this.minutes(0); /* falls through */ case 'minute': this.seconds(0); /* falls through */ case 'second': this.milliseconds(0); /* falls through */ } return this; }, endOf: function (val) { return this.startOf(val).add(val.replace(/s?$/, 's'), 1).subtract('ms', 1); }, sod: function () { return this.clone().startOf('day'); }, eod: function () { // end of day = start of day plus 1 day, minus 1 millisecond return this.clone().endOf('day'); }, zone : function () { return this._isUTC ? 0 : this._d.getTimezoneOffset(); }, daysInMonth : function () { return moment.utc([this.year(), this.month() + 1, 0]).date(); }, // If passed a language key, it will set the language for this // instance. Otherwise, it will return the language configuration // variables for this instance. lang : function (lang) { if (lang === undefined) { return getLangDefinition(this); } else { this._lang = lang; return this; } } }; // helper for adding shortcuts function makeGetterAndSetter(name, key) { moment.fn[name] = function (input) { var utc = this._isUTC ? 'UTC' : ''; if (input != null) { this._d['set' + utc + key](input); return this; } else { return this._d['get' + utc + key](); } }; } // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds) for (i = 0; i < proxyGettersAndSetters.length; i ++) { makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]); } // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear') makeGetterAndSetter('year', 'FullYear'); /************************************ Duration Prototype ************************************/ moment.duration.fn = Duration.prototype = { weeks : function () { return absRound(this.days() / 7); }, valueOf : function () { return this._milliseconds + this._days * 864e5 + this._months * 2592e6; }, humanize : function (withSuffix) { var difference = +this, rel = this.lang().relativeTime, output = relativeTime(difference, !withSuffix, this.lang()), fromNow = difference <= 0 ? rel.past : rel.future; if (withSuffix) { if (typeof fromNow === 'function') { output = fromNow(output); } else { output = fromNow.replace(/%s/i, output); } } return output; }, lang : moment.fn.lang }; function makeDurationGetter(name) { moment.duration.fn[name] = function () { return this._data[name]; }; } function makeDurationAsGetter(name, factor) { moment.duration.fn['as' + name] = function () { return +this / factor; }; } for (i in unitMillisecondFactors) { if (unitMillisecondFactors.hasOwnProperty(i)) { makeDurationAsGetter(i, unitMillisecondFactors[i]); makeDurationGetter(i.toLowerCase()); } } makeDurationAsGetter('Weeks', 6048e5); /************************************ Exposing Moment ************************************/ // CommonJS module is defined if (hasModule) { module.exports = moment; } /*global ender:false */ if (typeof ender === 'undefined') { // here, `this` means `window` in the browser, or `global` on the server // add `moment` as a global object via a string identifier, // for Closure Compiler "advanced" mode this['moment'] = moment; } /*global define:false */ if (typeof define === "function" && define.amd) { define("moment", [], function () { return moment; }); } }).call(this);