master
Raw Download raw file
   1// moment.js
   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);