├── .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 |

ES5 ISO 8601 Test Suite

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 | }); --------------------------------------------------------------------------------