├── .gitmodules
├── CONTRIBUTING.md
├── iso8601.min.js
├── tests
├── index.html
└── test.js
├── iso8601.js
└── README.md
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "tests/qunit"]
2 | path = tests/qunit
3 | url = https://github.com/jquery/qunit.git
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Are you about to submit a pull request to make the master branch “comply” with ISO-8601?
2 |
3 | Stop stop stop stop stop stop! **Read the README!** ES5 does not implement the full ISO-8601 standard!
4 | Use the [LAX branch](https://github.com/csnover/js-iso8601/tree/lax)!
5 |
6 | # Anything else
7 |
8 | Contribute away!
9 |
--------------------------------------------------------------------------------
/iso8601.min.js:
--------------------------------------------------------------------------------
1 | /** https://github.com/csnover/js-iso8601 */(function(n,f){var u=n.parse,c=[1,4,5,6,7,10,11];n.parse=function(t){var i,o,a=0;if(o=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(t)){for(var v=0,r;r=c[v];++v)o[r]=+o[r]||0;o[2]=(+o[2]||1)-1,o[3]=+o[3]||1,o[8]!=="Z"&&o[9]!==f&&(a=o[10]*60+o[11],o[9]==="+"&&(a=0-a)),i=n.UTC(o[1],o[2],o[3],o[4],o[5]+a,o[6],o[7])}else i=u?u(t):NaN;return i}})(Date)
2 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ES5 ISO 8601 Test Suite
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/iso8601.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Date.parse with progressive enhancement for ISO 8601
3 | * © 2011 Colin Snover
4 | * Released under MIT license.
5 | */
6 | (function (Date, undefined) {
7 | var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
8 | Date.parse = function (date) {
9 | var timestamp, struct, minutesOffset = 0;
10 |
11 | // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
12 | // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
13 | // implementations could be faster
14 | // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
15 | if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
16 | // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
17 | for (var i = 0, k; (k = numericKeys[i]); ++i) {
18 | struct[k] = +struct[k] || 0;
19 | }
20 |
21 | // allow undefined days and months
22 | struct[2] = (+struct[2] || 1) - 1;
23 | struct[3] = +struct[3] || 1;
24 |
25 | if (struct[8] !== 'Z' && struct[9] !== undefined) {
26 | minutesOffset = struct[10] * 60 + struct[11];
27 |
28 | if (struct[9] === '+') {
29 | minutesOffset = 0 - minutesOffset;
30 | }
31 | }
32 |
33 | timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
34 | }
35 | else {
36 | timestamp = origParse ? origParse(date) : NaN;
37 | }
38 |
39 | return timestamp;
40 | };
41 | }(Date));
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Universal JavaScript Date.parse for ISO 8601
2 | ============================================
3 |
4 | ECMAScript revision 5 adds native support for simplified [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601) dates in the
5 | `Date.parse` method, but some browsers currently on the market (Safari 5-, IE 8-, Firefox 3.6-) do not support it. This
6 | is a simple shim for Date.parse that adds support for parsing ES5 simplified ISO 8601 strings to all browsers.
7 |
8 | **If you are attempting to parse date strings coming from non-ES5-conformant backends, please consider using the
9 | [non-conformant edition](https://github.com/csnover/js-iso8601/tree/lax).**
10 |
11 | Caveats
12 | -------
13 |
14 | 1. This library strictly implements the simplified ISO 8601 date time string format specified in the
15 | [ES5 Errata](http://wiki.ecmascript.org/doku.php?id=es3.1:es3.1_proposal_working_draft) (§15.9.1.15) and will *not*
16 | parse ISO 8601 variants that would otherwise be considered valid under ISO 8601:2004(E).
17 | 2. Creating a new date using the `new Date(dateString)` form will not be fixed by this shim.
18 | 3. ES5 §15.9.4.2 states that parsing for Date Time String Format must occur prior to any implementation-specific date
19 | format parsing; therefore, this JavaScript implementation will always be used in lieu of native browser support
20 | for parsing the simplified ISO 8601 date format.
21 |
22 | Unit tests
23 | ----------
24 |
25 | By default, the unit tests are configured only to test the JavaScript fallback portion of the script, which means
26 | your browser’s native Date.parse implementation will not be used. Add “?useNativeDateParse” to the URL to run unit
27 | tests that will use the browser’s native implementation (for browser compliance testing purposes).
28 |
29 | **Note:** You must checkout using `git clone --recursive` in order for unit tests to function.
30 |
31 |
32 | License
33 | -------
34 |
35 | © 2011 Colin Snover. [MIT Licensed](http://www.opensource.org/licenses/mit-license.php).
36 |
--------------------------------------------------------------------------------
/tests/test.js:
--------------------------------------------------------------------------------
1 | var sixHours = 6 * 60 * 60 * 1000,
2 | sixHoursThirty = sixHours + 30 * 60 * 1000;
3 |
4 | test('date-part', 16, function () {
5 | strictEqual(Date.parse('1970-01-01'), Date.UTC(1970, 0, 1, 0, 0, 0, 0), 'Unix epoch');
6 |
7 | strictEqual(Date.parse('2001'), Date.UTC(2001, 0, 1, 0, 0, 0, 0), '2001');
8 | strictEqual(Date.parse('2001-02'), Date.UTC(2001, 1, 1, 0, 0, 0, 0), '2001-02');
9 | strictEqual(Date.parse('2001-02-03'), Date.UTC(2001, 1, 3, 0, 0, 0, 0), '2001-02-03');
10 |
11 | strictEqual(Date.parse('-002001'), Date.UTC(-2001, 0, 1, 0, 0, 0, 0), '-002001');
12 | strictEqual(Date.parse('-002001-02'), Date.UTC(-2001, 1, 1, 0, 0, 0, 0), '-002001-02');
13 | strictEqual(Date.parse('-002001-02-03'), Date.UTC(-2001, 1, 3, 0, 0, 0, 0), '-002001-02-03');
14 |
15 | strictEqual(Date.parse('+010000-02'), Date.UTC(10000, 1, 1, 0, 0, 0, 0), '+010000-02');
16 | strictEqual(Date.parse('+010000-02-03'), Date.UTC(10000, 1, 3, 0, 0, 0, 0), '+010000-02-03');
17 | strictEqual(Date.parse('-010000-02'), Date.UTC(-10000, 1, 1, 0, 0, 0, 0), '-010000-02');
18 | strictEqual(Date.parse('-010000-02-03'), Date.UTC(-10000, 1, 3, 0, 0, 0, 0), '-010000-02-03');
19 |
20 | // 2 digit years incorrectly parsed as though they were prefixed with 19
21 | /* disabled, since no native implementations get this right either
22 | strictEqual(Date.parse('0099-12-31'), +new Date(100, 0, 1, 0, 0, 0, 0) - 1000 * 60 * 60 * 24, '0099-12-31');
23 | strictEqual(Date.parse('0099-12-31T00:00Z'), Date.UTC(100, 0, 1, 0, 0, 0, 0) - 1000 * 60 * 60 * 24, '0099-12-31T00:00Z');
24 | */
25 |
26 | ok(isNaN(Date.parse('asdf')), 'invalid YYYY (non-digits)');
27 | ok(isNaN(Date.parse('1970-as-df')), 'invalid YYYY-MM-DD (non-digits)');
28 | ok(isNaN(Date.parse('1970-01-')), 'invalid YYYY-MM- (extra hyphen)');
29 | ok(isNaN(Date.parse('19700101')), 'invalid YYYY-MM-DD (missing hyphens)');
30 | ok(isNaN(Date.parse('197001')), 'ambiguous YYYY-MM/YYYYYY (missing plus/minus or hyphen)');
31 |
32 | // TODO: Test for invalid YYYYMM and invalid YYYYY?
33 | });
34 |
35 | test('date-time', 31, function () {
36 | strictEqual(Date.parse('2001-02-03T04:05'), Date.UTC(2001, 1, 3, 4, 5, 0, 0), '2001-02-03T04:05');
37 | strictEqual(Date.parse('2001-02-03T04:05:06'), Date.UTC(2001, 1, 3, 4, 5, 6, 0), '2001-02-03T04:05:06');
38 | strictEqual(Date.parse('2001-02-03T04:05:06.007'), Date.UTC(2001, 1, 3, 4, 5, 6, 7), '2001-02-03T04:05:06.007');
39 |
40 | strictEqual(Date.parse('2001-02-03T04:05Z'), Date.UTC(2001, 1, 3, 4, 5, 0, 0), '2001-02-03T04:05Z');
41 | strictEqual(Date.parse('2001-02-03T04:05:06Z'), Date.UTC(2001, 1, 3, 4, 5, 6, 0), '2001-02-03T04:05:06Z');
42 | strictEqual(Date.parse('2001-02-03T04:05:06.007Z'), Date.UTC(2001, 1, 3, 4, 5, 6, 7), '2001-02-03T04:05:06.007Z');
43 |
44 | strictEqual(Date.parse('2001-02-03T04:05-00:00'), Date.UTC(2001, 1, 3, 4, 5, 0, 0), '2001-02-03T04:05-00:00');
45 | strictEqual(Date.parse('2001-02-03T04:05:06-00:00'), Date.UTC(2001, 1, 3, 4, 5, 6, 0), '2001-02-03T04:05:06-00:00');
46 | strictEqual(Date.parse('2001-02-03T04:05:06.007-00:00'), Date.UTC(2001, 1, 3, 4, 5, 6, 7), '2001-02-03T04:05:06.007-00:00');
47 |
48 | strictEqual(Date.parse('2001-02-03T04:05+00:00'), Date.UTC(2001, 1, 3, 4, 5, 0, 0), '2001-02-03T04:05+00:00');
49 | strictEqual(Date.parse('2001-02-03T04:05:06+00:00'), Date.UTC(2001, 1, 3, 4, 5, 6, 0), '2001-02-03T04:05:06+00:00');
50 | strictEqual(Date.parse('2001-02-03T04:05:06.007+00:00'), Date.UTC(2001, 1, 3, 4, 5, 6, 7), '2001-02-03T04:05:06.007+00:00');
51 |
52 | strictEqual(Date.parse('2001-02-03T04:05-06:30'), Date.UTC(2001, 1, 3, 4, 5, 0, 0) + sixHoursThirty, '2001-02-03T04:05-06:30');
53 | strictEqual(Date.parse('2001-02-03T04:05:06-06:30'), Date.UTC(2001, 1, 3, 4, 5, 6, 0) + sixHoursThirty, '2001-02-03T04:05:06-06:30');
54 | strictEqual(Date.parse('2001-02-03T04:05:06.007-06:30'), Date.UTC(2001, 1, 3, 4, 5, 6, 7) + sixHoursThirty, '2001-02-03T04:05:06.007-06:30');
55 |
56 | strictEqual(Date.parse('2001-02-03T04:05+06:30'), Date.UTC(2001, 1, 3, 4, 5, 0, 0) - sixHoursThirty, '2001-02-03T04:05+06:30');
57 | strictEqual(Date.parse('2001-02-03T04:05:06+06:30'), Date.UTC(2001, 1, 3, 4, 5, 6, 0) - sixHoursThirty, '2001-02-03T04:05:06+06:30');
58 | strictEqual(Date.parse('2001-02-03T04:05:06.007+06:30'), Date.UTC(2001, 1, 3, 4, 5, 6, 7) - sixHoursThirty, '2001-02-03T04:05:06.007+06:30');
59 |
60 | strictEqual(Date.parse('2001T04:05:06.007'), Date.UTC(2001, 0, 1, 4, 5, 6, 7), '2001T04:05:06.007');
61 | strictEqual(Date.parse('2001-02T04:05:06.007'), Date.UTC(2001, 1, 1, 4, 5, 6, 7), '2001-02T04:05:06.007');
62 | strictEqual(Date.parse('2001-02-03T04:05:06.007'), Date.UTC(2001, 1, 3, 4, 5, 6, 7), '2001-02-03T04:05:06.007');
63 | strictEqual(Date.parse('2001-02-03T04:05:06.007-06:30'), Date.UTC(2001, 1, 3, 4, 5, 6, 7) + sixHoursThirty, '2001-02-03T04:05:06.007-06:30');
64 |
65 | strictEqual(Date.parse('-010000T04:05'), Date.UTC(-10000, 0, 1, 4, 5, 0, 0), '-010000T04:05');
66 | strictEqual(Date.parse('-010000-02T04:05'), Date.UTC(-10000, 1, 1, 4, 5, 0, 0), '-010000-02T04:05');
67 | strictEqual(Date.parse('-010000-02-03T04:05'), Date.UTC(-10000, 1, 3, 4, 5, 0, 0), '-010000-02-03T04:05');
68 |
69 | ok(isNaN(Date.parse('1970-01-01 00:00:00')), 'invalid date-time (missing T)');
70 | ok(isNaN(Date.parse('1970-01-01T00:00:00.000000')), 'invalid date-time (too many characters in millisecond part)');
71 | ok(isNaN(Date.parse('1970-01-01T00:00:00,000')), 'invalid date-time (comma instead of dot)');
72 | ok(isNaN(Date.parse('1970-01-01T00:00:00+0630')), 'invalid date-time (missing colon in timezone part)');
73 | ok(isNaN(Date.parse('1970-01-01T0000')), 'invalid date-time (missing colon in time part)');
74 | ok(isNaN(Date.parse('1970-01-01T00:00.000')), 'invalid date-time (msec with missing seconds)');
75 |
76 | // TODO: DRY
77 | });
--------------------------------------------------------------------------------