From: Isaac Cambron Date: Fri, 27 Sep 2013 20:49:37 +0000 (-0400) Subject: tests and a working isDSTShifted X-Git-Tag: 2.3.0~7^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4525a1cefb1bd3175e63477607fe2ebc8828de7c;p=thirdparty%2Fmoment.git tests and a working isDSTShifted --- diff --git a/moment.js b/moment.js index de4f44566..107fbc061 100644 --- a/moment.js +++ b/moment.js @@ -495,10 +495,14 @@ empty : false, unusedTokens : [], trailingInput : '', + skippedInput : [], overflowMonthOk : false, dstShifted : false, overflow : -2, - charsLeftOver: 0 + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + userInvalidated : false }; } @@ -507,7 +511,9 @@ m._isValid = !isNaN(m._d.getTime()) && m._pf.overflow < 0 && !m._pf.empty && - !m._pf.invalidMonth; + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.userInvalidated; if (m._strict) { m._isValid = m._isValid && @@ -1047,7 +1053,7 @@ // This array is used to make a Date, either with `new Date` or `Date.UTC` var lang = getLangDefinition(config._l), string = '' + config._i, - i, parsedInput, tokens, regex, token, + i, parsedInput, tokens, token, skipped, stringLength = string.length, totalParsedInputLength = 0; @@ -1055,12 +1061,12 @@ for (i = 0; i < tokens.length; i++) { token = tokens[i]; - regex = getParseRegexForToken(token, config); - if (config._strict) { - regex = new RegExp("^" + regex.source, regex.ignoreCase ? "i" : ""); - } - parsedInput = (regex.exec(string) || [])[0]; + parsedInput = (getParseRegexForToken(token, config).exec(string) || [])[0]; if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.skippedInput.push(skipped); + } string = string.slice(string.indexOf(parsedInput) + parsedInput.length); totalParsedInputLength += parsedInput.length; } @@ -1069,8 +1075,14 @@ if (parsedInput) { config._pf.empty = false; } + else { + config._pf.unusedTokens.push(token); + } addTimeToArrayFromToken(token, parsedInput, config); } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } } // add remaining unparsed input length to the string @@ -1264,10 +1276,8 @@ initializeParsingFlags(config); } - if (input === null || - (typeof input === 'string' && - input.replace(/^\s+|\s+$/g, '') === '')) { - return moment.invalid(); + if (input === null) { + return moment.invalid({nullInput: true}); } if (typeof input === 'string') { @@ -1421,9 +1431,14 @@ return normalizeUnits(units); }; - moment.invalid = function () { + moment.invalid = function (flags) { var m = moment.utc(NaN); - m._isValid = false; + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } return m; }; @@ -1483,7 +1498,7 @@ if (this._a) { //this is best-effort. What if it's a valid overflow AND DST-shifted? - return !this._pf.overflowMonthOk && !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()); + return this.isValid() && !this._pf.overflowMonthOk && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; } return false; diff --git a/test/moment.parsing_flags.js b/test/moment.parsing_flags.js new file mode 100644 index 000000000..1b716c708 --- /dev/null +++ b/test/moment.parsing_flags.js @@ -0,0 +1,181 @@ +var moment = require('../../moment'), + flags = function () { + return moment.apply(null, arguments).parsingFlags(); + }; + +exports.parsing_flags = { + + 'overflow with array' : function (test) { + + //months + test.equal(flags([2010, 0]).overflow, -1, 'month 0 valid'); + test.equal(flags([2010, 1]).overflow, -1, 'month 1 valid'); + test.equal(flags([2010, -1]).overflow, 1, 'month -1 invalid'); + test.equal(flags([2100, 12]).overflow, 1, 'month 12 invalid'); + + //days + test.equal(flags([2010, 1, 16]).overflow, -1, 'date valid'); + test.equal(flags([2010, 1, -1]).overflow, 2, 'date -1 invalid'); + test.equal(flags([2010, 1, 0]).overflow, 2, 'date 0 invalid'); + test.equal(flags([2010, 1, 32]).overflow, 2, 'date 32 invalid'); + test.equal(flags([2012, 1, 29]).overflow, -1, 'date leap year valid'); + test.equal(flags([2010, 1, 29]).overflow, 2, 'date leap year invalid'); + + //hours + test.equal(flags([2010, 1, 1, 8]).overflow, -1, 'hour valid'); + test.equal(flags([2010, 1, 1, 0]).overflow, -1, 'hour 0 valid'); + test.equal(flags([2010, 1, 1, -1]).overflow, 3, 'hour -1 invalid'); + test.equal(flags([2010, 1, 1, 24]).overflow, 3, 'hour 24 invalid'); + + //minutes + test.equal(flags([2010, 1, 1, 8, 15]).overflow, -1, 'minute valid'); + test.equal(flags([2010, 1, 1, 8, 0]).overflow, -1, 'minute 0 valid'); + test.equal(flags([2010, 1, 1, 8, -1]).overflow, 4, 'minute -1 invalid'); + test.equal(flags([2010, 1, 1, 8, 60]).overflow, 4, 'minute 60 invalid'); + + //seconds + test.equal(flags([2010, 1, 1, 8, 15, 12]).overflow, -1, 'second valid'); + test.equal(flags([2010, 1, 1, 8, 15, 0]).overflow, -1, 'second 0 valid'); + test.equal(flags([2010, 1, 1, 8, 15, -1]).overflow, 5, 'second -1 invalid'); + test.equal(flags([2010, 1, 1, 8, 15, 60]).overflow, 5, 'second 60 invalid'); + + //milliseconds + test.equal(flags([2010, 1, 1, 8, 15, 12, 345]).overflow, -1, 'millisecond valid'); + test.equal(flags([2010, 1, 1, 8, 15, 12, 0]).overflow, -1, 'millisecond 0 valid'); + test.equal(flags([2010, 1, 1, 8, 15, 12, -1]).overflow, 6, 'millisecond -1 invalid'); + test.equal(flags([2010, 1, 1, 8, 15, 12, 1000]).overflow, 6, 'millisecond 1000 invalid'); + + test.done(); + }, + + 'overflow without format' : function (test) { + + //months + test.equal(flags('2001-01', 'YYYY-MM').overflow, -1, 'month 1 valid'); + test.equal(flags('2001-12', 'YYYY-MM').overflow, -1, 'month 12 valid'); + test.equal(flags('2001-13', 'YYYY-MM').overflow, 1, 'month 13 invalid'); + + //days + test.equal(flags('2010-01-16', 'YYYY-MM-DD').overflow, -1, 'date 16 valid'); + test.equal(flags('2010-01-0', 'YYYY-MM-DD').overflow, 2, 'date 0 invalid'); + test.equal(flags('2010-01-32', 'YYYY-MM-DD').overflow, 2, 'date 32 invalid'); + test.equal(flags('2012-02-29', 'YYYY-MM-DD').overflow, -1, 'date leap year valid'); + test.equal(flags('2010-02-29', 'YYYY-MM-DD').overflow, 2, 'date leap year invalid'); + + //days of the year + test.equal(flags('2010 300', 'YYYY DDDD').overflow, -1, 'day 300 of year valid'); + test.equal(flags('2010 365', 'YYYY DDDD').overflow, -1, 'day 365 of year valid'); + test.equal(flags('2010 366', 'YYYY DDDD').overflow, 2, 'day 366 of year invalid'); + test.equal(flags('2012 364', 'YYYY DDDD').overflow, -1, 'day 364 of leap year valid'); + test.equal(flags('2012 365', 'YYYY DDDD').overflow, 2, 'day 365 of leap year invalid'); + + //hours + test.equal(flags('08', 'HH').overflow, -1, 'hour valid'); + test.equal(flags('00', 'HH').overflow, -1, 'hour 0 valid'); + test.equal(flags('24', 'HH').overflow, 3, 'hour 24 invalid'); + + //minutes + test.equal(flags('08:15', 'HH:mm').overflow, -1, 'minute valid'); + test.equal(flags('08:00', 'HH:mm').overflow, -1, 'minute 0 valid'); + test.equal(flags('08:60', 'HH:mm').overflow, 4, 'minute 60 invalid'); + + //seconds + test.equal(flags('08:15:12', 'HH:mm:ss').overflow, -1, 'second valid'); + test.equal(flags('08:15:00', 'HH:mm:ss').overflow, -1, 'second 0 valid'); + test.equal(flags('08:15:60', 'HH:mm:ss').overflow, 5, 'second 60 invalid'); + + //milliseconds + test.equal(flags('08:15:12:345', 'HH:mm:ss:SSSS').overflow, -1, 'millisecond valid'); + test.equal(flags('08:15:12:000', 'HH:mm:ss:SSSS').overflow, -1, 'millisecond 0 valid'); + + //this is OK because we don't match the last digit, so it's 100 ms + test.equal(flags('08:15:12:1000', 'HH:mm:ss:SSSS').overflow, -1, 'millisecond 1000 actually valid'); + + test.done(); + }, + + 'extra tokens' : function (test) { + + test.deepEqual(flags('1982-05-25', 'YYYY-MM-DD').unusedTokens, [], 'nothing extra'); + test.deepEqual(flags('1982-05', 'YYYY-MM-DD').unusedTokens, ['DD'], 'extra formatting token'); + test.deepEqual(flags('1982', 'YYYY-MM-DD').unusedTokens, ['MM', 'DD'], 'multiple extra formatting tokens'); + test.deepEqual(flags('1982-05', 'YYYY-MM-').unusedTokens, [], 'extra non-formatting token'); + test.deepEqual(flags('1982-05-', 'YYYY-MM-DD').unusedTokens, ['DD'], 'non-extra non-formatting token'); + test.deepEqual(flags('1982 05 1982', 'YYYY-MM-DD').unusedTokens, [], 'different non-formatting token'); + + test.done(); + }, + + 'extra tokens strict' : function (test) { + + test.deepEqual(flags('1982-05-25', 'YYYY-MM-DD', true).unusedTokens, [], 'nothing extra'); + test.deepEqual(flags('1982-05', 'YYYY-MM-DD', true).unusedTokens, ['-', 'DD'], 'extra formatting token'); + test.deepEqual(flags('1982', 'YYYY-MM-DD', true).unusedTokens, ['-', 'MM', '-', 'DD'], 'multiple extra formatting tokens'); + test.deepEqual(flags('1982-05', 'YYYY-MM-', true).unusedTokens, ['-'], 'extra non-formatting token'); + test.deepEqual(flags('1982-05-', 'YYYY-MM-DD', true).unusedTokens, ['DD'], 'non-extra non-formatting token'); + test.deepEqual(flags('1982 05 1982', 'YYYY-MM-DD', true).unusedTokens, ['-', '-'], 'different non-formatting token'); + + test.done(); + }, + + 'trailing output' : function (test) { + test.equal(flags('1982-05-25', 'YYYY-MM-DD').trailingInput, '', 'normal input'); + test.equal(flags('1982-05-25 this is more stuff', 'YYYY-MM-DD').trailingInput, ' this is more stuff', 'trailing nonsense'); + test.equal(flags('1982-05-25 09:30', 'YYYY-MM-DD').trailingInput, ' 09:30', 'trailing legit-looking input'); + test.equal(flags('1982-05-25 some junk', 'YYYY-MM-DD [some junk]').trailingInput, '', 'junk that actually gets matched'); + + test.done(); + }, + + 'skipped input' : function (test) { + test.deepEqual(flags('1982-05-25', 'YYYY-MM-DD').skippedInput, [], 'normal input'); + test.deepEqual(flags('stuff at beginning 1982-05-25', 'YYYY-MM-DD').skippedInput, ['stuff at beginning '], 'leading junk'); + test.deepEqual(flags('1982 junk 05 more junk25', 'YYYY-MM-DD').skippedInput, [' junk ', ' more junk'], 'interstitial junk'); + + test.done(); + }, + + 'skipped input strict' : function (test) { + test.deepEqual(flags('1982-05-25', 'YYYY-MM-DD').skippedInput, [], 'normal input'); + test.deepEqual(flags('stuff at beginning 1982-05-25', 'YYYY-MM-DD').skippedInput, ['stuff at beginning '], 'leading junk'); + test.deepEqual(flags('1982 junk 05 more junk25', 'YYYY-MM-DD').skippedInput, [' junk ', ' more junk'], 'interstitial junk'); + + test.done(); + }, + + 'chars left over' : function (test) { + test.equal(flags('1982-05-25', 'YYYY-MM-DD').charsLeftOver, 0, 'normal input'); + test.equal(flags('1982-05-25 this is more stuff', 'YYYY-MM-DD').charsLeftOver, ' this is more stuff'.length, 'trailing nonsense'); + test.equal(flags('1982-05-25 09:30', 'YYYY-MM-DD').charsLeftOver, ' 09:30'.length, 'trailing legit-looking input'); + test.equal(flags('stuff at beginning 1982-05-25', 'YYYY-MM-DD').charsLeftOver, 'stuff at beginning '.length, 'leading junk'); + test.equal(flags('1982 junk 05 more junk25', 'YYYY-MM-DD').charsLeftOver, [' junk ', ' more junk'].join('').length, 'interstitial junk'); + test.equal(flags('stuff at beginning 1982 junk 05 more junk25', 'YYYY-MM-DD').charsLeftOver, ['stuff at beginning ', ' junk ', ' more junk'].join('').length, 'leading and interstitial junk'); + + test.done(); + }, + + 'empty' : function (test) { + test.equal(flags('1982-05-25', 'YYYY-MM-DD').empty, false, 'normal input'); + test.equal(flags('nothing here', 'YYYY-MM-DD').empty, true, 'pure garbage'); + test.equal(flags('junk but has the number 2000 in it', 'YYYY-MM-DD').empty, false, 'only mostly garbage'); + test.equal(flags('', 'YYYY-MM-DD').empty, true, 'empty string'); + test.equal(flags('', 'YYYY-MM-DD').empty, true, 'blank string'); + + test.done(); + }, + + 'null' : function (test) { + test.equal(flags('1982-05-25', 'YYYY-MM-DD').nullInput, false, 'normal input'); + test.equal(flags(null).nullInput, true, 'just null'); + test.equal(flags(null, 'YYYY-MM-DD').nullInput, true, 'null with format'); + + test.done(); + }, + + 'invalid month' : function (test) { + test.equal(flags('1982 May', 'YYYY MMMM').invalidMonth, null, 'normal input'); + test.equal(flags('1982 Laser', 'YYYY MMMM').invalidMonth, 'Laser', 'bad month name'); + + test.done(); + } +}; diff --git a/test/moment/create.js b/test/moment/create.js index 34b5ce5f0..03adba342 100644 --- a/test/moment/create.js +++ b/test/moment/create.js @@ -306,7 +306,7 @@ exports.create = { }, "string with array of formats" : function (test) { - test.expect(16); + test.expect(19); test.equal(moment('11-02-1999', ['MM-DD-YYYY', 'DD-MM-YYYY']).format('MM DD YYYY'), '11 02 1999', 'switching month and day'); test.equal(moment('02-11-1999', ['MM/DD/YYYY', 'YYYY MM DD', 'MM-DD-YYYY']).format('MM DD YYYY'), '02 11 1999', 'year last'); @@ -323,6 +323,10 @@ exports.create = { test.equal(moment('02-01-2000', ['DD/MM/YYYY', 'MM/DD/YYYY']).format('MM DD YYYY'), '01 02 2000', 'either can be a month, day first format'); test.equal(moment('11-02-10', ['MM/DD/YY', 'YY MM DD', 'DD-MM-YY']).format('MM DD YYYY'), '02 11 2010', 'all unparsed substrings have influence on format penalty'); + test.equal(moment('11-02-10', ['MM-DD-YY HH:mm', 'YY MM DD']).format('MM DD YYYY'), '02 10 2011', 'prefer formats without extra tokens'); + test.equal(moment('11-02-10 junk', ['MM-DD-YY', 'YY.MM.DD junk']).format('MM DD YYYY'), '02 10 2011', 'prefer formats that dont result in extra characters'); + test.equal(moment('11-22-10', ['YY-MM-DD', 'YY-DD-MM']).format('MM DD YYYY'), '10 22 2011', 'prefer valid results'); + test.equal(moment('11-02-10', ['MM.DD.YY', 'DD-MM-YY']).format('MM DD YYYY'), '02 11 2010', 'escape RegExp special characters on comparing'); test.equal(moment('13-14-98', ['DD MM YY', 'DD MM YYYY'])._f, 'DD MM YY', 'use two digit year'); @@ -509,11 +513,12 @@ exports.create = { }, "strict parsing" : function (test) { - test.expect(10); + test.expect(11); test.equal(moment("2012-05", "YYYY-MM", true).format("YYYY-MM"), "2012-05", "parse correct string"); test.equal(moment(" 2012-05", "YYYY-MM", true).isValid(), false, "fail on extra whitespace"); test.equal(moment("foo 2012-05", "[foo] YYYY-MM", true).format('YYYY-MM'), "2012-05", "handle fixed text"); test.equal(moment("2012 05", "YYYY-MM", true).isValid(), false, "fail on different separator"); + test.equal(moment("2012 05", "YYYY MM DD", true).isValid(), false, "fail on too many tokens"); test.equal(moment("05 30 2010", ["DD MM YYYY", "MM DD YYYY"], true).format("MM DD YYYY"), "05 30 2010", "array with bad date"); test.equal(moment("05 30 2010", ["", "MM DD YYYY"], true).format("MM DD YYYY"), "05 30 2010", "array with invalid format"); diff --git a/test/moment/invalid.js b/test/moment/invalid.js index be764c37f..494a4890d 100644 --- a/test/moment/invalid.js +++ b/test/moment/invalid.js @@ -4,6 +4,25 @@ exports.invalid = { "invalid" : function (test) { var m = moment.invalid(); test.equals(m.isValid(), false); + test.equals(m.parsingFlags().userInvalidated, true); + test.ok(isNaN(m.valueOf())); + test.done(); + }, + + "invalid with existing flag" : function (test) { + var m = moment.invalid({invalidMonth : 'whatchamacallit'}); + test.equals(m.isValid(), false); + test.equals(m.parsingFlags().userInvalidated, false); + test.equals(m.parsingFlags().invalidMonth, 'whatchamacallit'); + test.ok(isNaN(m.valueOf())); + test.done(); + }, + + "invalid with custom flag" : function (test) { + var m = moment.invalid({tooBusyWith : 'reiculating splines'}); + test.equals(m.isValid(), false); + test.equals(m.parsingFlags().userInvalidated, false); + test.equals(m.parsingFlags().tooBusyWith, 'reiculating splines'); test.ok(isNaN(m.valueOf())); test.done(); } diff --git a/test/moment/is_valid.js b/test/moment/is_valid.js index ead45fd6b..99fc2c134 100644 --- a/test/moment/is_valid.js +++ b/test/moment/is_valid.js @@ -2,15 +2,9 @@ var moment = require("../../moment"); exports.is_valid = { "array bad month" : function (test) { - var underflow = moment([2010, -1]), - overflow = moment([2100, 12]); - - test.expect(4); - test.equal(underflow.isValid(), false, 'month -1 invalid'); - test.equal(underflow.parsingFlags().overflow, 1, 'month -1 overflow'); - - test.equal(overflow.isValid(), false, 'month 12 invalid'); - test.equal(overflow.parsingFlags().overflow, 1, 'month 12 invalid'); + test.expect(2); + test.equal(moment([2010, -1]).isValid(), false, 'month -1 invalid'); + test.equal(moment([2100, 12]).isValid(), false, 'month 12 invalid'); test.done(); }, @@ -35,12 +29,11 @@ exports.is_valid = { ], i, m; - test.expect(tests.length * 2); + test.expect(tests.length); for (i in tests) { m = tests[i]; test.equal(m.isValid(), false); - test.equal(m.parsingFlags().overflow, 2); } test.done(); @@ -234,5 +227,26 @@ exports.is_valid = { test.equal(moment(" ", "X").isValid(), false, 'string space'); test.done(); - } + }, + + "empty" : function (test) { + test.equal(moment(null).isValid(), false, 'null'); + test.equal(moment('').isValid(), false, 'empty string'); + test.equal(moment(' ').isValid(), false, 'empty when trimmed'); + + test.equal(moment(null, 'YYYY').isValid(), false, 'format + null'); + test.equal(moment('', 'YYYY').isValid(), false, 'format + empty string'); + test.equal(moment(' ', 'YYYY').isValid(), false, 'format + empty when trimmed'); + test.done(); + }, + + "oddball permissiveness" : function (test) { + //https://github.com/moment/moment/issues/1128 + test.ok(moment("2010-10-3199", ["MM/DD/YYYY", "MM-DD-YYYY", "YYYY-MM-DD"]).isValid()); + + //https://github.com/moment/moment/issues/1122 + test.ok(moment("3:25", ["h:mma", "hh:mma", "H:mm", "HH:mm"]).isValid()); + + test.done() + }, };