hjg
2024-07-09 30304784e82d4bba24121328da8eb8490aec4f4f
提交 | 用户 | 时间
58d006 1 // moment.js
A 2 // version : 2.0.0
3 // author : Tim Wood
4 // license : MIT
5 // momentjs.com
6
7 (function (undefined) {
8
9     /************************************
10      Constants
11      ************************************/
12
13     var moment,
14         VERSION = "2.0.0",
15         round = Math.round, i,
16     // internal storage for language config files
17         languages = {},
18
19     // check for nodeJS
20         hasModule = (typeof module !== 'undefined' && module.exports),
21
22     // ASP.NET json date format regex
23         aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
24
25     // format tokens
26         formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,
27         localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
28
29     // parsing tokens
30         parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
31
32     // parsing token regexes
33         parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
34         parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
35         parseTokenThreeDigits = /\d{3}/, // 000 - 999
36         parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
37         parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
38         parseTokenWord = /[0-9]*[a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF]+\s*?[\u0600-\u06FF]+/i, // any word (or two) characters or numbers including two word month in arabic.
39         parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
40         parseTokenT = /T/i, // T (ISO seperator)
41         parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
42
43     // preliminary iso regex
44     // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
45         isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
46         isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
47
48     // iso time formats and regexes
49         isoTimes = [
50             ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
51             ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
52             ['HH:mm', /(T| )\d\d:\d\d/],
53             ['HH', /(T| )\d\d/]
54         ],
55
56     // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
57         parseTimezoneChunker = /([\+\-]|\d\d)/gi,
58
59     // getter and setter names
60         proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
61         unitMillisecondFactors = {
62             'Milliseconds' : 1,
63             'Seconds' : 1e3,
64             'Minutes' : 6e4,
65             'Hours' : 36e5,
66             'Days' : 864e5,
67             'Months' : 2592e6,
68             'Years' : 31536e6
69         },
70
71     // format function strings
72         formatFunctions = {},
73
74     // tokens to ordinalize and pad
75         ordinalizeTokens = 'DDD w W M D d'.split(' '),
76         paddedTokens = 'M D H h m s w W'.split(' '),
77
78         formatTokenFunctions = {
79             M    : function () {
80                 return this.month() + 1;
81             },
82             MMM  : function (format) {
83                 return this.lang().monthsShort(this, format);
84             },
85             MMMM : function (format) {
86                 return this.lang().months(this, format);
87             },
88             D    : function () {
89                 return this.date();
90             },
91             DDD  : function () {
92                 return this.dayOfYear();
93             },
94             d    : function () {
95                 return this.day();
96             },
97             dd   : function (format) {
98                 return this.lang().weekdaysMin(this, format);
99             },
100             ddd  : function (format) {
101                 return this.lang().weekdaysShort(this, format);
102             },
103             dddd : function (format) {
104                 return this.lang().weekdays(this, format);
105             },
106             w    : function () {
107                 return this.week();
108             },
109             W    : function () {
110                 return this.isoWeek();
111             },
112             YY   : function () {
113                 return leftZeroFill(this.year() % 100, 2);
114             },
115             YYYY : function () {
116                 return leftZeroFill(this.year(), 4);
117             },
118             YYYYY : function () {
119                 return leftZeroFill(this.year(), 5);
120             },
121             a    : function () {
122                 return this.lang().meridiem(this.hours(), this.minutes(), true);
123             },
124             A    : function () {
125                 return this.lang().meridiem(this.hours(), this.minutes(), false);
126             },
127             H    : function () {
128                 return this.hours();
129             },
130             h    : function () {
131                 return this.hours() % 12 || 12;
132             },
133             m    : function () {
134                 return this.minutes();
135             },
136             s    : function () {
137                 return this.seconds();
138             },
139             S    : function () {
140                 return ~~(this.milliseconds() / 100);
141             },
142             SS   : function () {
143                 return leftZeroFill(~~(this.milliseconds() / 10), 2);
144             },
145             SSS  : function () {
146                 return leftZeroFill(this.milliseconds(), 3);
147             },
148             Z    : function () {
149                 var a = -this.zone(),
150                     b = "+";
151                 if (a < 0) {
152                     a = -a;
153                     b = "-";
154                 }
155                 return b + leftZeroFill(~~(a / 60), 2) + ":" + leftZeroFill(~~a % 60, 2);
156             },
157             ZZ   : function () {
158                 var a = -this.zone(),
159                     b = "+";
160                 if (a < 0) {
161                     a = -a;
162                     b = "-";
163                 }
164                 return b + leftZeroFill(~~(10 * a / 6), 4);
165             },
166             X    : function () {
167                 return this.unix();
168             }
169         };
170
171     function padToken(func, count) {
172         return function (a) {
173             return leftZeroFill(func.call(this, a), count);
174         };
175     }
176     function ordinalizeToken(func) {
177         return function (a) {
178             return this.lang().ordinal(func.call(this, a));
179         };
180     }
181
182     while (ordinalizeTokens.length) {
183         i = ordinalizeTokens.pop();
184         formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i]);
185     }
186     while (paddedTokens.length) {
187         i = paddedTokens.pop();
188         formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
189     }
190     formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
191
192
193     /************************************
194      Constructors
195      ************************************/
196
197     function Language() {
198
199     }
200
201     // Moment prototype object
202     function Moment(config) {
203         extend(this, config);
204     }
205
206     // Duration Constructor
207     function Duration(duration) {
208         var data = this._data = {},
209             years = duration.years || duration.year || duration.y || 0,
210             months = duration.months || duration.month || duration.M || 0,
211             weeks = duration.weeks || duration.week || duration.w || 0,
212             days = duration.days || duration.day || duration.d || 0,
213             hours = duration.hours || duration.hour || duration.h || 0,
214             minutes = duration.minutes || duration.minute || duration.m || 0,
215             seconds = duration.seconds || duration.second || duration.s || 0,
216             milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0;
217
218         // representation for dateAddRemove
219         this._milliseconds = milliseconds +
220             seconds * 1e3 + // 1000
221             minutes * 6e4 + // 1000 * 60
222             hours * 36e5; // 1000 * 60 * 60
223         // Because of dateAddRemove treats 24 hours as different from a
224         // day when working around DST, we need to store them separately
225         this._days = days +
226             weeks * 7;
227         // It is impossible translate months into days without knowing
228         // which months you are are talking about, so we have to store
229         // it separately.
230         this._months = months +
231             years * 12;
232
233         // The following code bubbles up values, see the tests for
234         // examples of what that means.
235         data.milliseconds = milliseconds % 1000;
236         seconds += absRound(milliseconds / 1000);
237
238         data.seconds = seconds % 60;
239         minutes += absRound(seconds / 60);
240
241         data.minutes = minutes % 60;
242         hours += absRound(minutes / 60);
243
244         data.hours = hours % 24;
245         days += absRound(hours / 24);
246
247         days += weeks * 7;
248         data.days = days % 30;
249
250         months += absRound(days / 30);
251
252         data.months = months % 12;
253         years += absRound(months / 12);
254
255         data.years = years;
256     }
257
258
259     /************************************
260      Helpers
261      ************************************/
262
263
264     function extend(a, b) {
265         for (var i in b) {
266             if (b.hasOwnProperty(i)) {
267                 a[i] = b[i];
268             }
269         }
270         return a;
271     }
272
273     function absRound(number) {
274         if (number < 0) {
275             return Math.ceil(number);
276         } else {
277             return Math.floor(number);
278         }
279     }
280
281     // left zero fill a number
282     // see http://jsperf.com/left-zero-filling for performance comparison
283     function leftZeroFill(number, targetLength) {
284         var output = number + '';
285         while (output.length < targetLength) {
286             output = '0' + output;
287         }
288         return output;
289     }
290
291     // helper function for _.addTime and _.subtractTime
292     function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
293         var ms = duration._milliseconds,
294             d = duration._days,
295             M = duration._months,
296             currentDate;
297
298         if (ms) {
299             mom._d.setTime(+mom + ms * isAdding);
300         }
301         if (d) {
302             mom.date(mom.date() + d * isAdding);
303         }
304         if (M) {
305             currentDate = mom.date();
306             mom.date(1)
307                 .month(mom.month() + M * isAdding)
308                 .date(Math.min(currentDate, mom.daysInMonth()));
309         }
310     }
311
312     // check if is an array
313     function isArray(input) {
314         return Object.prototype.toString.call(input) === '[object Array]';
315     }
316
317     // compare two arrays, return the number of differences
318     function compareArrays(array1, array2) {
319         var len = Math.min(array1.length, array2.length),
320             lengthDiff = Math.abs(array1.length - array2.length),
321             diffs = 0,
322             i;
323         for (i = 0; i < len; i++) {
324             if (~~array1[i] !== ~~array2[i]) {
325                 diffs++;
326             }
327         }
328         return diffs + lengthDiff;
329     }
330
331
332     /************************************
333      Languages
334      ************************************/
335
336
337     Language.prototype = {
338         set : function (config) {
339             var prop, i;
340             for (i in config) {
341                 prop = config[i];
342                 if (typeof prop === 'function') {
343                     this[i] = prop;
344                 } else {
345                     this['_' + i] = prop;
346                 }
347             }
348         },
349
350         _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
351         months : function (m) {
352             return this._months[m.month()];
353         },
354
355         _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
356         monthsShort : function (m) {
357             return this._monthsShort[m.month()];
358         },
359
360         monthsParse : function (monthName) {
361             var i, mom, regex, output;
362
363             if (!this._monthsParse) {
364                 this._monthsParse = [];
365             }
366
367             for (i = 0; i < 12; i++) {
368                 // make the regex if we don't have it already
369                 if (!this._monthsParse[i]) {
370                     mom = moment([2000, i]);
371                     regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
372                     this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
373                 }
374                 // test the regex
375                 if (this._monthsParse[i].test(monthName)) {
376                     return i;
377                 }
378             }
379         },
380
381         _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
382         weekdays : function (m) {
383             return this._weekdays[m.day()];
384         },
385
386         _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
387         weekdaysShort : function (m) {
388             return this._weekdaysShort[m.day()];
389         },
390
391         _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
392         weekdaysMin : function (m) {
393             return this._weekdaysMin[m.day()];
394         },
395
396         _longDateFormat : {
397             LT : "h:mm A",
398             L : "MM/DD/YYYY",
399             LL : "MMMM D YYYY",
400             LLL : "MMMM D YYYY LT",
401             LLLL : "dddd, MMMM D YYYY LT"
402         },
403         longDateFormat : function (key) {
404             var output = this._longDateFormat[key];
405             if (!output && this._longDateFormat[key.toUpperCase()]) {
406                 output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
407                     return val.slice(1);
408                 });
409                 this._longDateFormat[key] = output;
410             }
411             return output;
412         },
413
414         meridiem : function (hours, minutes, isLower) {
415             if (hours > 11) {
416                 return isLower ? 'pm' : 'PM';
417             } else {
418                 return isLower ? 'am' : 'AM';
419             }
420         },
421
422         _calendar : {
423             sameDay : '[Today at] LT',
424             nextDay : '[Tomorrow at] LT',
425             nextWeek : 'dddd [at] LT',
426             lastDay : '[Yesterday at] LT',
427             lastWeek : '[last] dddd [at] LT',
428             sameElse : 'L'
429         },
430         calendar : function (key, mom) {
431             var output = this._calendar[key];
432             return typeof output === 'function' ? output.apply(mom) : output;
433         },
434
435         _relativeTime : {
436             future : "in %s",
437             past : "%s ago",
438             s : "a few seconds",
439             m : "a minute",
440             mm : "%d minutes",
441             h : "an hour",
442             hh : "%d hours",
443             d : "a day",
444             dd : "%d days",
445             M : "a month",
446             MM : "%d months",
447             y : "a year",
448             yy : "%d years"
449         },
450         relativeTime : function (number, withoutSuffix, string, isFuture) {
451             var output = this._relativeTime[string];
452             return (typeof output === 'function') ?
453                 output(number, withoutSuffix, string, isFuture) :
454                 output.replace(/%d/i, number);
455         },
456         pastFuture : function (diff, output) {
457             var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
458             return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
459         },
460
461         ordinal : function (number) {
462             return this._ordinal.replace("%d", number);
463         },
464         _ordinal : "%d",
465
466         preparse : function (string) {
467             return string;
468         },
469
470         postformat : function (string) {
471             return string;
472         },
473
474         week : function (mom) {
475             return weekOfYear(mom, this._week.dow, this._week.doy);
476         },
477         _week : {
478             dow : 0, // Sunday is the first day of the week.
479             doy : 6  // The week that contains Jan 1st is the first week of the year.
480         }
481     };
482
483     // Loads a language definition into the `languages` cache.  The function
484     // takes a key and optionally values.  If not in the browser and no values
485     // are provided, it will load the language file module.  As a convenience,
486     // this function also returns the language values.
487     function loadLang(key, values) {
488         values.abbr = key;
489         if (!languages[key]) {
490             languages[key] = new Language();
491         }
492         languages[key].set(values);
493         return languages[key];
494     }
495
496     // Determines which language definition to use and returns it.
497     //
498     // With no parameters, it will return the global language.  If you
499     // pass in a language key, such as 'en', it will return the
500     // definition for 'en', so long as 'en' has already been loaded using
501     // moment.lang.
502     function getLangDefinition(key) {
503         if (!key) {
504             return moment.fn._lang;
505         }
506         if (!languages[key] && hasModule) {
507             require('./lang/' + key);
508         }
509         return languages[key];
510     }
511
512
513     /************************************
514      Formatting
515      ************************************/
516
517
518     function removeFormattingTokens(input) {
519         if (input.match(/\[.*\]/)) {
520             return input.replace(/^\[|\]$/g, "");
521         }
522         return input.replace(/\\/g, "");
523     }
524
525     function makeFormatFunction(format) {
526         var array = format.match(formattingTokens), i, length;
527
528         for (i = 0, length = array.length; i < length; i++) {
529             if (formatTokenFunctions[array[i]]) {
530                 array[i] = formatTokenFunctions[array[i]];
531             } else {
532                 array[i] = removeFormattingTokens(array[i]);
533             }
534         }
535
536         return function (mom) {
537             var output = "";
538             for (i = 0; i < length; i++) {
539                 output += typeof array[i].call === 'function' ? array[i].call(mom, format) : array[i];
540             }
541             return output;
542         };
543     }
544
545     // format date using native date object
546     function formatMoment(m, format) {
547         var i = 5;
548
549         function replaceLongDateFormatTokens(input) {
550             return m.lang().longDateFormat(input) || input;
551         }
552
553         while (i-- && localFormattingTokens.test(format)) {
554             format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
555         }
556
557         if (!formatFunctions[format]) {
558             formatFunctions[format] = makeFormatFunction(format);
559         }
560
561         return formatFunctions[format](m);
562     }
563
564
565     /************************************
566      Parsing
567      ************************************/
568
569
570         // get the regex to find the next token
571     function getParseRegexForToken(token) {
572         switch (token) {
573             case 'DDDD':
574                 return parseTokenThreeDigits;
575             case 'YYYY':
576                 return parseTokenFourDigits;
577             case 'YYYYY':
578                 return parseTokenSixDigits;
579             case 'S':
580             case 'SS':
581             case 'SSS':
582             case 'DDD':
583                 return parseTokenOneToThreeDigits;
584             case 'MMM':
585             case 'MMMM':
586             case 'dd':
587             case 'ddd':
588             case 'dddd':
589             case 'a':
590             case 'A':
591                 return parseTokenWord;
592             case 'X':
593                 return parseTokenTimestampMs;
594             case 'Z':
595             case 'ZZ':
596                 return parseTokenTimezone;
597             case 'T':
598                 return parseTokenT;
599             case 'MM':
600             case 'DD':
601             case 'YY':
602             case 'HH':
603             case 'hh':
604             case 'mm':
605             case 'ss':
606             case 'M':
607             case 'D':
608             case 'd':
609             case 'H':
610             case 'h':
611             case 'm':
612             case 's':
613                 return parseTokenOneOrTwoDigits;
614             default :
615                 return new RegExp(token.replace('\\', ''));
616         }
617     }
618
619     // function to convert string input to date
620     function addTimeToArrayFromToken(token, input, config) {
621         var a, b,
622             datePartArray = config._a;
623
624         switch (token) {
625             // MONTH
626             case 'M' : // fall through to MM
627             case 'MM' :
628                 datePartArray[1] = (input == null) ? 0 : ~~input - 1;
629                 break;
630             case 'MMM' : // fall through to MMMM
631             case 'MMMM' :
632                 a = getLangDefinition(config._l).monthsParse(input);
633                 // if we didn't find a month name, mark the date as invalid.
634                 if (a != null) {
635                     datePartArray[1] = a;
636                 } else {
637                     config._isValid = false;
638                 }
639                 break;
640             // DAY OF MONTH
641             case 'D' : // fall through to DDDD
642             case 'DD' : // fall through to DDDD
643             case 'DDD' : // fall through to DDDD
644             case 'DDDD' :
645                 if (input != null) {
646                     datePartArray[2] = ~~input;
647                 }
648                 break;
649             // YEAR
650             case 'YY' :
651                 datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
652                 break;
653             case 'YYYY' :
654             case 'YYYYY' :
655                 datePartArray[0] = ~~input;
656                 break;
657             // AM / PM
658             case 'a' : // fall through to A
659             case 'A' :
660                 config._isPm = ((input + '').toLowerCase() === 'pm');
661                 break;
662             // 24 HOUR
663             case 'H' : // fall through to hh
664             case 'HH' : // fall through to hh
665             case 'h' : // fall through to hh
666             case 'hh' :
667                 datePartArray[3] = ~~input;
668                 break;
669             // MINUTE
670             case 'm' : // fall through to mm
671             case 'mm' :
672                 datePartArray[4] = ~~input;
673                 break;
674             // SECOND
675             case 's' : // fall through to ss
676             case 'ss' :
677                 datePartArray[5] = ~~input;
678                 break;
679             // MILLISECOND
680             case 'S' :
681             case 'SS' :
682             case 'SSS' :
683                 datePartArray[6] = ~~ (('0.' + input) * 1000);
684                 break;
685             // UNIX TIMESTAMP WITH MS
686             case 'X':
687                 config._d = new Date(parseFloat(input) * 1000);
688                 break;
689             // TIMEZONE
690             case 'Z' : // fall through to ZZ
691             case 'ZZ' :
692                 config._useUTC = true;
693                 a = (input + '').match(parseTimezoneChunker);
694                 if (a && a[1]) {
695                     config._tzh = ~~a[1];
696                 }
697                 if (a && a[2]) {
698                     config._tzm = ~~a[2];
699                 }
700                 // reverse offsets
701                 if (a && a[0] === '+') {
702                     config._tzh = -config._tzh;
703                     config._tzm = -config._tzm;
704                 }
705                 break;
706         }
707
708         // if the input is null, the date is not valid
709         if (input == null) {
710             config._isValid = false;
711         }
712     }
713
714     // convert an array to a date.
715     // the array should mirror the parameters below
716     // note: all values past the year are optional and will default to the lowest possible value.
717     // [year, month, day , hour, minute, second, millisecond]
718     function dateFromArray(config) {
719         var i, date, input = [];
720
721         if (config._d) {
722             return;
723         }
724
725         for (i = 0; i < 7; i++) {
726             config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
727         }
728
729         // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
730         input[3] += config._tzh || 0;
731         input[4] += config._tzm || 0;
732
733         date = new Date(0);
734
735         if (config._useUTC) {
736             date.setUTCFullYear(input[0], input[1], input[2]);
737             date.setUTCHours(input[3], input[4], input[5], input[6]);
738         } else {
739             date.setFullYear(input[0], input[1], input[2]);
740             date.setHours(input[3], input[4], input[5], input[6]);
741         }
742
743         config._d = date;
744     }
745
746     // date from string and format string
747     function makeDateFromStringAndFormat(config) {
748         // This array is used to make a Date, either with `new Date` or `Date.UTC`
749         var tokens = config._f.match(formattingTokens),
750             string = config._i,
751             i, parsedInput;
752
753         config._a = [];
754
755         for (i = 0; i < tokens.length; i++) {
756             parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
757             if (parsedInput) {
758                 string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
759             }
760             // don't parse if its not a known token
761             if (formatTokenFunctions[tokens[i]]) {
762                 addTimeToArrayFromToken(tokens[i], parsedInput, config);
763             }
764         }
765         // handle am pm
766         if (config._isPm && config._a[3] < 12) {
767             config._a[3] += 12;
768         }
769         // if is 12 am, change hours to 0
770         if (config._isPm === false && config._a[3] === 12) {
771             config._a[3] = 0;
772         }
773         // return
774         dateFromArray(config);
775     }
776
777     // date from string and array of format strings
778     function makeDateFromStringAndArray(config) {
779         var tempConfig,
780             tempMoment,
781             bestMoment,
782
783             scoreToBeat = 99,
784             i,
785             currentDate,
786             currentScore;
787
788         while (config._f.length) {
789             tempConfig = extend({}, config);
790             tempConfig._f = config._f.pop();
791             makeDateFromStringAndFormat(tempConfig);
792             tempMoment = new Moment(tempConfig);
793
794             if (tempMoment.isValid()) {
795                 bestMoment = tempMoment;
796                 break;
797             }
798
799             currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
800
801             if (currentScore < scoreToBeat) {
802                 scoreToBeat = currentScore;
803                 bestMoment = tempMoment;
804             }
805         }
806
807         extend(config, bestMoment);
808     }
809
810     // date from iso format
811     function makeDateFromString(config) {
812         var i,
813             string = config._i;
814         if (isoRegex.exec(string)) {
815             config._f = 'YYYY-MM-DDT';
816             for (i = 0; i < 4; i++) {
817                 if (isoTimes[i][1].exec(string)) {
818                     config._f += isoTimes[i][0];
819                     break;
820                 }
821             }
822             if (parseTokenTimezone.exec(string)) {
823                 config._f += " Z";
824             }
825             makeDateFromStringAndFormat(config);
826         } else {
827             config._d = new Date(string);
828         }
829     }
830
831     function makeDateFromInput(config) {
832         var input = config._i,
833             matched = aspNetJsonRegex.exec(input);
834
835         if (input === undefined) {
836             config._d = new Date();
837         } else if (matched) {
838             config._d = new Date(+matched[1]);
839         } else if (typeof input === 'string') {
840             makeDateFromString(config);
841         } else if (isArray(input)) {
842             config._a = input.slice(0);
843             dateFromArray(config);
844         } else {
845             config._d = input instanceof Date ? new Date(+input) : new Date(input);
846         }
847     }
848
849
850     /************************************
851      Relative Time
852      ************************************/
853
854
855         // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
856     function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
857         return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
858     }
859
860     function relativeTime(milliseconds, withoutSuffix, lang) {
861         var seconds = round(Math.abs(milliseconds) / 1000),
862             minutes = round(seconds / 60),
863             hours = round(minutes / 60),
864             days = round(hours / 24),
865             years = round(days / 365),
866             args = seconds < 45 && ['s', seconds] ||
867                 minutes === 1 && ['m'] ||
868                 minutes < 45 && ['mm', minutes] ||
869                 hours === 1 && ['h'] ||
870                 hours < 22 && ['hh', hours] ||
871                 days === 1 && ['d'] ||
872                 days <= 25 && ['dd', days] ||
873                 days <= 45 && ['M'] ||
874                 days < 345 && ['MM', round(days / 30)] ||
875                 years === 1 && ['y'] || ['yy', years];
876         args[2] = withoutSuffix;
877         args[3] = milliseconds > 0;
878         args[4] = lang;
879         return substituteTimeAgo.apply({}, args);
880     }
881
882
883     /************************************
884      Week of Year
885      ************************************/
886
887
888         // firstDayOfWeek       0 = sun, 6 = sat
889         //                      the day of the week that starts the week
890         //                      (usually sunday or monday)
891         // firstDayOfWeekOfYear 0 = sun, 6 = sat
892         //                      the first week is the week that contains the first
893         //                      of this day of the week
894         //                      (eg. ISO weeks use thursday (4))
895     function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
896         var end = firstDayOfWeekOfYear - firstDayOfWeek,
897             daysToDayOfWeek = firstDayOfWeekOfYear - mom.day();
898
899
900         if (daysToDayOfWeek > end) {
901             daysToDayOfWeek -= 7;
902         }
903
904         if (daysToDayOfWeek < end - 7) {
905             daysToDayOfWeek += 7;
906         }
907
908         return Math.ceil(moment(mom).add('d', daysToDayOfWeek).dayOfYear() / 7);
909     }
910
911
912     /************************************
913      Top Level Functions
914      ************************************/
915
916     function makeMoment(config) {
917         var input = config._i,
918             format = config._f;
919
920         if (input === null || input === '') {
921             return null;
922         }
923
924         if (typeof input === 'string') {
925             config._i = input = getLangDefinition().preparse(input);
926         }
927
928         if (moment.isMoment(input)) {
929             config = extend({}, input);
930             config._d = new Date(+input._d);
931         } else if (format) {
932             if (isArray(format)) {
933                 makeDateFromStringAndArray(config);
934             } else {
935                 makeDateFromStringAndFormat(config);
936             }
937         } else {
938             makeDateFromInput(config);
939         }
940
941         return new Moment(config);
942     }
943
944     moment = function (input, format, lang) {
945         return makeMoment({
946             _i : input,
947             _f : format,
948             _l : lang,
949             _isUTC : false
950         });
951     };
952
953     // creating with utc
954     moment.utc = function (input, format, lang) {
955         return makeMoment({
956             _useUTC : true,
957             _isUTC : true,
958             _l : lang,
959             _i : input,
960             _f : format
961         });
962     };
963
964     // creating with unix timestamp (in seconds)
965     moment.unix = function (input) {
966         return moment(input * 1000);
967     };
968
969     // duration
970     moment.duration = function (input, key) {
971         var isDuration = moment.isDuration(input),
972             isNumber = (typeof input === 'number'),
973             duration = (isDuration ? input._data : (isNumber ? {} : input)),
974             ret;
975
976         if (isNumber) {
977             if (key) {
978                 duration[key] = input;
979             } else {
980                 duration.milliseconds = input;
981             }
982         }
983
984         ret = new Duration(duration);
985
986         if (isDuration && input.hasOwnProperty('_lang')) {
987             ret._lang = input._lang;
988         }
989
990         return ret;
991     };
992
993     // version number
994     moment.version = VERSION;
995
996     // default format
997     moment.defaultFormat = isoFormat;
998
999     // This function will load languages and then set the global language.  If
1000     // no arguments are passed in, it will simply return the current global
1001     // language key.
1002     moment.lang = function (key, values) {
1003         var i;
1004
1005         if (!key) {
1006             return moment.fn._lang._abbr;
1007         }
1008         if (values) {
1009             loadLang(key, values);
1010         } else if (!languages[key]) {
1011             getLangDefinition(key);
1012         }
1013         moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
1014     };
1015
1016     // returns language data
1017     moment.langData = function (key) {
1018         if (key && key._lang && key._lang._abbr) {
1019             key = key._lang._abbr;
1020         }
1021         return getLangDefinition(key);
1022     };
1023
1024     // compare moment object
1025     moment.isMoment = function (obj) {
1026         return obj instanceof Moment;
1027     };
1028
1029     // for typechecking Duration objects
1030     moment.isDuration = function (obj) {
1031         return obj instanceof Duration;
1032     };
1033
1034
1035     /************************************
1036      Moment Prototype
1037      ************************************/
1038
1039
1040     moment.fn = Moment.prototype = {
1041
1042         clone : function () {
1043             return moment(this);
1044         },
1045
1046         valueOf : function () {
1047             return +this._d;
1048         },
1049
1050         unix : function () {
1051             return Math.floor(+this._d / 1000);
1052         },
1053
1054         toString : function () {
1055             return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
1056         },
1057
1058         toDate : function () {
1059             return this._d;
1060         },
1061
1062         toJSON : function () {
1063             return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
1064         },
1065
1066         toArray : function () {
1067             var m = this;
1068             return [
1069                 m.year(),
1070                 m.month(),
1071                 m.date(),
1072                 m.hours(),
1073                 m.minutes(),
1074                 m.seconds(),
1075                 m.milliseconds()
1076             ];
1077         },
1078
1079         isValid : function () {
1080             if (this._isValid == null) {
1081                 if (this._a) {
1082                     this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray());
1083                 } else {
1084                     this._isValid = !isNaN(this._d.getTime());
1085                 }
1086             }
1087             return !!this._isValid;
1088         },
1089
1090         utc : function () {
1091             this._isUTC = true;
1092             return this;
1093         },
1094
1095         local : function () {
1096             this._isUTC = false;
1097             return this;
1098         },
1099
1100         format : function (inputString) {
1101             var output = formatMoment(this, inputString || moment.defaultFormat);
1102             return this.lang().postformat(output);
1103         },
1104
1105         add : function (input, val) {
1106             var dur;
1107             // switch args to support add('s', 1) and add(1, 's')
1108             if (typeof input === 'string') {
1109                 dur = moment.duration(+val, input);
1110             } else {
1111                 dur = moment.duration(input, val);
1112             }
1113             addOrSubtractDurationFromMoment(this, dur, 1);
1114             return this;
1115         },
1116
1117         subtract : function (input, val) {
1118             var dur;
1119             // switch args to support subtract('s', 1) and subtract(1, 's')
1120             if (typeof input === 'string') {
1121                 dur = moment.duration(+val, input);
1122             } else {
1123                 dur = moment.duration(input, val);
1124             }
1125             addOrSubtractDurationFromMoment(this, dur, -1);
1126             return this;
1127         },
1128
1129         diff : function (input, units, asFloat) {
1130             var that = this._isUTC ? moment(input).utc() : moment(input).local(),
1131                 zoneDiff = (this.zone() - that.zone()) * 6e4,
1132                 diff, output;
1133
1134             if (units) {
1135                 // standardize on singular form
1136                 units = units.replace(/s$/, '');
1137             }
1138
1139             if (units === 'year' || units === 'month') {
1140                 diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
1141                 output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
1142                 output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff;
1143                 if (units === 'year') {
1144                     output = output / 12;
1145                 }
1146             } else {
1147                 diff = (this - that) - zoneDiff;
1148                 output = units === 'second' ? diff / 1e3 : // 1000
1149                     units === 'minute' ? diff / 6e4 : // 1000 * 60
1150                         units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
1151                             units === 'day' ? diff / 864e5 : // 1000 * 60 * 60 * 24
1152                                 units === 'week' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
1153                                     diff;
1154             }
1155             return asFloat ? output : absRound(output);
1156         },
1157
1158         from : function (time, withoutSuffix) {
1159             return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
1160         },
1161
1162         fromNow : function (withoutSuffix) {
1163             return this.from(moment(), withoutSuffix);
1164         },
1165
1166         calendar : function () {
1167             var diff = this.diff(moment().startOf('day'), 'days', true),
1168                 format = diff < -6 ? 'sameElse' :
1169                     diff < -1 ? 'lastWeek' :
1170                         diff < 0 ? 'lastDay' :
1171                             diff < 1 ? 'sameDay' :
1172                                 diff < 2 ? 'nextDay' :
1173                                     diff < 7 ? 'nextWeek' : 'sameElse';
1174             return this.format(this.lang().calendar(format, this));
1175         },
1176
1177         isLeapYear : function () {
1178             var year = this.year();
1179             return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
1180         },
1181
1182         isDST : function () {
1183             return (this.zone() < moment([this.year()]).zone() ||
1184                 this.zone() < moment([this.year(), 5]).zone());
1185         },
1186
1187         day : function (input) {
1188             var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
1189             return input == null ? day :
1190                 this.add({ d : input - day });
1191         },
1192
1193         startOf: function (units) {
1194             units = units.replace(/s$/, '');
1195             // the following switch intentionally omits break keywords
1196             // to utilize falling through the cases.
1197             switch (units) {
1198                 case 'year':
1199                     this.month(0);
1200                 /* falls through */
1201                 case 'month':
1202                     this.date(1);
1203                 /* falls through */
1204                 case 'week':
1205                 case 'day':
1206                     this.hours(0);
1207                 /* falls through */
1208                 case 'hour':
1209                     this.minutes(0);
1210                 /* falls through */
1211                 case 'minute':
1212                     this.seconds(0);
1213                 /* falls through */
1214                 case 'second':
1215                     this.milliseconds(0);
1216                 /* falls through */
1217             }
1218
1219             // weeks are a special case
1220             if (units === 'week') {
1221                 this.day(0);
1222             }
1223
1224             return this;
1225         },
1226
1227         endOf: function (units) {
1228             return this.startOf(units).add(units.replace(/s?$/, 's'), 1).subtract('ms', 1);
1229         },
1230
1231         isAfter: function (input, units) {
1232             units = typeof units !== 'undefined' ? units : 'millisecond';
1233             return +this.clone().startOf(units) > +moment(input).startOf(units);
1234         },
1235
1236         isBefore: function (input, units) {
1237             units = typeof units !== 'undefined' ? units : 'millisecond';
1238             return +this.clone().startOf(units) < +moment(input).startOf(units);
1239         },
1240
1241         isSame: function (input, units) {
1242             units = typeof units !== 'undefined' ? units : 'millisecond';
1243             return +this.clone().startOf(units) === +moment(input).startOf(units);
1244         },
1245
1246         zone : function () {
1247             return this._isUTC ? 0 : this._d.getTimezoneOffset();
1248         },
1249
1250         daysInMonth : function () {
1251             return moment.utc([this.year(), this.month() + 1, 0]).date();
1252         },
1253
1254         dayOfYear : function (input) {
1255             var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
1256             return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
1257         },
1258
1259         isoWeek : function (input) {
1260             var week = weekOfYear(this, 1, 4);
1261             return input == null ? week : this.add("d", (input - week) * 7);
1262         },
1263
1264         week : function (input) {
1265             var week = this.lang().week(this);
1266             return input == null ? week : this.add("d", (input - week) * 7);
1267         },
1268
1269         // If passed a language key, it will set the language for this
1270         // instance.  Otherwise, it will return the language configuration
1271         // variables for this instance.
1272         lang : function (key) {
1273             if (key === undefined) {
1274                 return this._lang;
1275             } else {
1276                 this._lang = getLangDefinition(key);
1277                 return this;
1278             }
1279         }
1280     };
1281
1282     // helper for adding shortcuts
1283     function makeGetterAndSetter(name, key) {
1284         moment.fn[name] = moment.fn[name + 's'] = function (input) {
1285             var utc = this._isUTC ? 'UTC' : '';
1286             if (input != null) {
1287                 this._d['set' + utc + key](input);
1288                 return this;
1289             } else {
1290                 return this._d['get' + utc + key]();
1291             }
1292         };
1293     }
1294
1295     // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
1296     for (i = 0; i < proxyGettersAndSetters.length; i ++) {
1297         makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
1298     }
1299
1300     // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
1301     makeGetterAndSetter('year', 'FullYear');
1302
1303     // add plural methods
1304     moment.fn.days = moment.fn.day;
1305     moment.fn.weeks = moment.fn.week;
1306     moment.fn.isoWeeks = moment.fn.isoWeek;
1307
1308     /************************************
1309      Duration Prototype
1310      ************************************/
1311
1312
1313     moment.duration.fn = Duration.prototype = {
1314         weeks : function () {
1315             return absRound(this.days() / 7);
1316         },
1317
1318         valueOf : function () {
1319             return this._milliseconds +
1320                 this._days * 864e5 +
1321                 this._months * 2592e6;
1322         },
1323
1324         humanize : function (withSuffix) {
1325             var difference = +this,
1326                 output = relativeTime(difference, !withSuffix, this.lang());
1327
1328             if (withSuffix) {
1329                 output = this.lang().pastFuture(difference, output);
1330             }
1331
1332             return this.lang().postformat(output);
1333         },
1334
1335         lang : moment.fn.lang
1336     };
1337
1338     function makeDurationGetter(name) {
1339         moment.duration.fn[name] = function () {
1340             return this._data[name];
1341         };
1342     }
1343
1344     function makeDurationAsGetter(name, factor) {
1345         moment.duration.fn['as' + name] = function () {
1346             return +this / factor;
1347         };
1348     }
1349
1350     for (i in unitMillisecondFactors) {
1351         if (unitMillisecondFactors.hasOwnProperty(i)) {
1352             makeDurationAsGetter(i, unitMillisecondFactors[i]);
1353             makeDurationGetter(i.toLowerCase());
1354         }
1355     }
1356
1357     makeDurationAsGetter('Weeks', 6048e5);
1358
1359
1360     /************************************
1361      Default Lang
1362      ************************************/
1363
1364
1365         // Set default language, other languages will inherit from English.
1366     moment.lang('en', {
1367         ordinal : function (number) {
1368             var b = number % 10,
1369                 output = (~~ (number % 100 / 10) === 1) ? 'th' :
1370                     (b === 1) ? 'st' :
1371                         (b === 2) ? 'nd' :
1372                             (b === 3) ? 'rd' : 'th';
1373             return number + output;
1374         }
1375     });
1376
1377
1378     /************************************
1379      Exposing Moment
1380      ************************************/
1381
1382
1383     // CommonJS module is defined
1384     if (hasModule) {
1385         module.exports = moment;
1386     }
1387     /*global ender:false */
1388     if (typeof ender === 'undefined') {
1389         // here, `this` means `window` in the browser, or `global` on the server
1390         // add `moment` as a global object via a string identifier,
1391         // for Closure Compiler "advanced" mode
1392         this['moment'] = moment;
1393     }
1394     /*global define:false */
1395     if (typeof define === "function" && define.amd) {
1396         define("moment", [], function () {
1397             return moment;
1398         });
1399     }
1400 }).call(this);