]> git.ipfire.org Git - thirdparty/moment.git/commitdiff
Fix a few crashes with invalid iso strings
authorIskren Chernev <iskren.chernev@gmail.com>
Tue, 29 Sep 2015 07:47:38 +0000 (00:47 -0700)
committerIskren Chernev <iskren.chernev@gmail.com>
Mon, 9 Nov 2015 02:27:40 +0000 (18:27 -0800)
src/lib/create/from-string.js
src/lib/parse/regex.js
src/lib/units/offset.js
src/test/moment/create.js
src/test/moment/is_valid.js

index be30c8a113fe28dcbb7dc1dd6bf7090935cde816..38bdca17e106f071b006910036e2572533f697d3 100644 (file)
@@ -1,4 +1,3 @@
-import { matchOffset } from '../parse/regex';
 import { configFromStringAndFormat } from './from-string-and-format';
 import { hooks } from '../utils/hooks';
 import { deprecate } from '../utils/deprecate';
@@ -6,25 +5,29 @@ import getParsingFlags from './parsing-flags';
 
 // iso 8601 regex
 // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
-var isoRegex = /^\s*((?:[+-]\d{6}|\d{4})-?(?:\d\d$|\d\d-?\d\d|W\d\d$|W\d\d-?\d|\d\d\d))(?:(T| )(\d\d(?::?\d\d(?::?\d\d(?::?[.,]\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
+var isoRegex = /^\s*((?:[+-]\d{6}|\d{4})-?(?:\d\d-?\d\d|W\d\d-?\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::?\d\d(?::?\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/;
+
+var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
 
 var isoDates = [
-    ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/, true],
-    ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/, true],
-    ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/, true],
-    ['GGGG-[W]WW', /\d{4}-W\d{2}/, true],
+    ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/, true],
+    ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/, true],
+    ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/, true],
+    ['GGGG-[W]WW', /\d{4}-W\d\d/, true, false],
     ['YYYY-DDD', /\d{4}-\d{3}/, true],
-    ['YYYYYYMMDD', /[+-]\d{6}\d{2}\d{2}/, false],
-    ['YYYYMMDD', /\d{4}\d{2}\d{2}/, false],
+    ['YYYY-MM', /\d{4}-\d\d/, true, false],
+    ['YYYYYYMMDD', /[+-]\d{10}/, false],
+    ['YYYYMMDD', /\d{8}/, false],
     // YYYYMM is NOT allowed by the standard
-    ['GGGG[W]WWE', /\d{4}W\d{2}\d/, false],
-    ['GGGG[W]WW', /\d{4}W\d{2}/, false],
-    ['YYYYDDD', /\d{4}\d{3}/, false]
+    ['GGGG[W]WWE', /\d{4}W\d{3}/, false],
+    ['GGGG[W]WW', /\d{4}W\d{2}/, false, false],
+    ['YYYYDDD', /\d{7}/, false]
 ];
 
 // iso time formats and regexes
 var isoTimes = [
     ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/, true],
+    ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/, true],
     ['HH:mm:ss', /\d\d:\d\d:\d\d/, true],
     ['HH:mm', /\d\d:\d\d/, true],
     ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/, false],
@@ -41,26 +44,41 @@ export function configFromISO(config) {
     var i, l,
         string = config._i,
         match = isoRegex.exec(string),
-        extendedDate, extendedTime;
+        extendedDate, extendedTime, allowTime, dateFormat, timeFormat, tzFormat;
 
     if (match) {
         getParsingFlags(config).iso = true;
 
         for (i = 0, l = isoDates.length; i < l; i++) {
             if (isoDates[i][1].exec(match[1])) {
-                config._f = isoDates[i][0];
+                dateFormat = isoDates[i][0];
                 extendedDate = isoDates[i][2];
+                allowTime = isoDates[i][3] !== false;
                 break;
             }
         }
-        for (i = 0, l = isoTimes.length; i < l; i++) {
-            if (isoTimes[i][1].exec(match[3])) {
-                // match[2] should be 'T' or space
-                config._f += (match[2] || ' ') + isoTimes[i][0];
-                extendedTime = isoTimes[i][2];
-                break;
+        if (dateFormat == null) {
+            config._isValid = false;
+            return;
+        }
+        if (match[3] != null) {
+            for (i = 0, l = isoTimes.length; i < l; i++) {
+                if (isoTimes[i][1].exec(match[3])) {
+                    // match[2] should be 'T' or space
+                    timeFormat = (match[2] || ' ') + isoTimes[i][0];
+                    extendedTime = isoTimes[i][2];
+                    break;
+                }
+            }
+            if (timeFormat == null) {
+                config._isValid = false;
+                return;
             }
         }
+        if (!allowTime && timeFormat != null) {
+            config._isValid = false;
+            return;
+        }
         if (extendedDate != null &&
                 extendedTime != null &&
                 extendedDate !== extendedTime) {
@@ -68,10 +86,15 @@ export function configFromISO(config) {
             config._isValid = false;
             return;
         }
-        matchOffset.lastIndex = 0;
-        if (matchOffset.exec(match[4])) {
-            config._f += 'Z';
+        if (match[4] != null) {
+            if (tzRegex.exec(match[4])) {
+                tzFormat = 'Z';
+            } else {
+                config._isValid = false;
+                return;
+            }
         }
+        config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
         configFromStringAndFormat(config);
     } else {
         config._isValid = false;
index 9e7d6788c925ca84e9c7132fd35734354f32a09f..b1ace27634cf524198ef7259f970c2bc890ec525 100644 (file)
@@ -12,6 +12,7 @@ export var matchUnsigned  = /\d+/;           //       0 - inf
 export var matchSigned    = /[+-]?\d+/;      //    -inf - inf
 
 export var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
+export var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
 
 export var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
 
index 5d0bf520632257b84cb5406766ebb5221d0d48fd..3d21f6aa4aca62f45d1dd06a8388fd0048aeaec3 100644 (file)
@@ -3,7 +3,7 @@ import { createDuration } from '../duration/create';
 import { addSubtract } from '../moment/add-subtract';
 import { isMoment, copyConfig } from '../moment/constructor';
 import { addFormatToken } from '../format/format';
-import { addRegexToken, matchOffset } from '../parse/regex';
+import { addRegexToken, matchOffset, matchShortOffset } from '../parse/regex';
 import { addParseToken } from '../parse/token';
 import { createLocal } from '../create/local';
 import { prepareConfig } from '../create/from-anything';
@@ -32,11 +32,11 @@ offset('ZZ', '');
 
 // PARSING
 
-addRegexToken('Z',  matchOffset);
-addRegexToken('ZZ', matchOffset);
+addRegexToken('Z',  matchShortOffset);
+addRegexToken('ZZ', matchShortOffset);
 addParseToken(['Z', 'ZZ'], function (input, array, config) {
     config._useUTC = true;
-    config._tzm = offsetFromString(input);
+    config._tzm = offsetFromString(matchShortOffset, input);
 });
 
 // HELPERS
@@ -46,8 +46,8 @@ addParseToken(['Z', 'ZZ'], function (input, array, config) {
 // '-1530'  > ['-15', '30']
 var chunkOffset = /([\+\-]|\d\d)/gi;
 
-function offsetFromString(string) {
-    var matches = ((string || '').match(matchOffset) || []);
+function offsetFromString(matcher, string) {
+    var matches = ((string || '').match(matcher) || []);
     var chunk   = matches[matches.length - 1] || [];
     var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
     var minutes = +(parts[1] * 60) + toInt(parts[2]);
@@ -102,7 +102,7 @@ export function getSetOffset (input, keepLocalTime) {
     }
     if (input != null) {
         if (typeof input === 'string') {
-            input = offsetFromString(input);
+            input = offsetFromString(matchShortOffset, input);
         }
         if (Math.abs(input) < 16) {
             input = input * 60;
@@ -164,7 +164,7 @@ export function setOffsetToParsedOffset () {
     if (this._tzm) {
         this.utcOffset(this._tzm);
     } else if (typeof this._i === 'string') {
-        this.utcOffset(offsetFromString(this._i));
+        this.utcOffset(offsetFromString(matchOffset, this._i));
     }
     return this;
 }
index a3260c17376e2f7da720cf0601cdbd91c90e323b..ad9757d48c5fdc73cff6d8ccefe6765391f33727 100644 (file)
@@ -424,6 +424,7 @@ test('parsing iso', function (assert) {
     tz2 = tz.replace(':', ''),
     tz3 = tz2.slice(0, 3),
     formats = [
+        ['2011-10',                       '2011-10-01T00:00:00.000' + tz],
         ['2011-10-08',                    '2011-10-08T00:00:00.000' + tz],
         ['2011-10-08T18',                 '2011-10-08T18:00:00.000' + tz],
         ['2011-10-08T18:04',              '2011-10-08T18:04:00.000' + tz],
@@ -581,6 +582,15 @@ test('parsing iso', function (assert) {
     }
 });
 
+test('non iso 8601 strings', function (assert) {
+    assert.ok(!moment('2015-10T10:15', moment.ISO_8601).isValid(), 'incomplete date with time');
+    assert.ok(!moment('2015-W10T10:15', moment.ISO_8601).isValid(), 'incomplete week date with time');
+    assert.ok(!moment('201510', moment.ISO_8601).isValid(), 'basic YYYYMM is not allowed');
+    assert.ok(!moment('2015W10T1015', moment.ISO_8601).isValid(), 'incomplete week date with time (basic)');
+    assert.ok(!moment('2015-10-08T1015', moment.ISO_8601).isValid(), 'mixing extended and basic format');
+    assert.ok(!moment('20151008T10:15', moment.ISO_8601).isValid(), 'mixing basic and extended format');
+});
+
 test('parsing iso week year/week/weekday', function (assert) {
     assert.equal(moment.utc('2007-W01').format(), '2007-01-01T00:00:00+00:00', '2008 week 1 (1st Jan Mon)');
     assert.equal(moment.utc('2008-W01').format(), '2007-12-31T00:00:00+00:00', '2008 week 1 (1st Jan Tue)');
index 07999a0d9556da31353fb97a6c25bda0572ada76..02ef46b8c4a10412ea300dc1732362013758558f 100644 (file)
@@ -90,8 +90,8 @@ test('invalid string iso 8601', function (assert) {
     ], i;
 
     for (i = 0; i < tests.length; i++) {
-        assert.equal(moment(tests[i]).isValid(), false, tests[i] + ' should be invalid');
-        assert.equal(moment.utc(tests[i]).isValid(), false, tests[i] + ' should be invalid');
+        assert.equal(moment(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid');
+        assert.equal(moment.utc(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid');
     }
 });
 
@@ -108,8 +108,8 @@ test('invalid string iso 8601 + timezone', function (assert) {
     ], i;
 
     for (i = 0; i < tests.length; i++) {
-        assert.equal(moment(tests[i]).isValid(), false, tests[i] + ' should be invalid');
-        assert.equal(moment.utc(tests[i]).isValid(), false, tests[i] + ' should be invalid');
+        assert.equal(moment(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid');
+        assert.equal(moment.utc(tests[i], moment.ISO_8601).isValid(), false, tests[i] + ' should be invalid');
     }
 });