From a256741275c11e57a8507571a5bc6afdf13220fd Mon Sep 17 00:00:00 2001 From: Marnus Weststrate Date: Mon, 7 Oct 2019 07:37:44 +0100 Subject: [PATCH] parseUTC - minimal implementation for parsing JSON dates from an API --- src/parseUTC/benchmark.js | 16 ++++++++++++ src/parseUTC/index.js | 52 +++++++++++++++++++++++++++++++++++++++ src/parseUTC/test.js | 51 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/parseUTC/benchmark.js create mode 100644 src/parseUTC/index.js create mode 100644 src/parseUTC/test.js diff --git a/src/parseUTC/benchmark.js b/src/parseUTC/benchmark.js new file mode 100644 index 0000000000..4183ba8313 --- /dev/null +++ b/src/parseUTC/benchmark.js @@ -0,0 +1,16 @@ +// @flow +/* eslint-env mocha */ +/* global suite, benchmark */ + +import parseUTC from '.' +import moment from 'moment' + +suite('toDate', function() { + benchmark('date-fns', function() { + return parseUTC('2014-10-25T13:46:20+00:00') + }) + + benchmark('Moment.js', function() { + return moment('2014-10-25T13:46:20+00:00') + }) +}) diff --git a/src/parseUTC/index.js b/src/parseUTC/index.js new file mode 100644 index 0000000000..579299c83c --- /dev/null +++ b/src/parseUTC/index.js @@ -0,0 +1,52 @@ +import toDate from '../toDate/index.js' + +/** + * @name parseUTC + * @category Common Helpers + * @summary Parse UTC/JSON string + * + * @description + * Converts a complete ISO date string in UTC time, the typical format for transmitting + * a date in JSON, to a JavaScript `Date` instance. + * + * This is a minimal implementation for converting dates retrieved from a JSON API to + * a `Date` instance which can be used with other functions in the `date-fns` library. + * The following formats are supported: + * + * - `2000-03-15T05:20:10.123Z`: The output of `.toISOString()` and `JSON.stringify(new Date())` + * - `2000-03-15T05:20:10Z`: Without milliseconds + * - `2000-03-15T05:20:10+00:00`: With a zero offset, the default JSON encoded format in some other languages + * - `2000-03-15T05:20:10+0000`: With a zero offset without a colon + * + * For convenience and ease of use these other input types are also supported + * via [toDate]{@link https://date-fns.org/docs/toDate}: + * + * - A `Date` instance will be cloned + * - A `number` will be treated as a timestamp + * + * Any other input type or invalid date strings will return an `Invalid Date`. + * + * @param {String|Number|Date|null} [dirtyDate] A fully formed ISO1806 date string to convert + * @returns {Date} the parsed date in the local time zone + */ +export default function index(dirtyDate) { + if (typeof dirtyDate === 'string') { + var parts = dirtyDate.match( + /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?(?:Z|\+00:?00)/ + ) + if (parts) { + return new Date( + Date.UTC( + +parts[1], + parts[2] - 1, + +parts[3], + +parts[4], + +parts[5], + +parts[6], + +(parts[7] || 0) + ) + ) + } + } + return toDate(dirtyDate) +} diff --git a/src/parseUTC/test.js b/src/parseUTC/test.js new file mode 100644 index 0000000000..28f817273d --- /dev/null +++ b/src/parseUTC/test.js @@ -0,0 +1,51 @@ +import assert from 'power-assert' +import parseUTCDate from '.' + +describe('parseUTCDate', function() { + it('parses a fully formed ISO date with Z', () => { + const date = '2000-03-15T05:20:10.123Z' + const parsedDate = parseUTCDate(date) + assert.equal(parsedDate.toISOString(), date) + }) + + it('parses a fully formed ISO date with Z without ms', () => { + const date = '2000-03-15T05:20:10Z' + const expectedDate = '2000-03-15T05:20:10.000Z' + const parsedDate = parseUTCDate(date) + assert.equal(parsedDate.toISOString(), expectedDate) + }) + + it('parses a fully formed ISO date with zero offset', () => { + const zeroOffset = '2000-03-15T05:20:10+00:00' + const expectedDate = '2000-03-15T05:20:10.000Z' + const parsedDate = parseUTCDate(zeroOffset) + assert.equal(parsedDate.toISOString(), expectedDate) + }) + + it('parses a fully formed ISO date with zero offset without colon', () => { + const zeroOffset = '2000-03-15T05:20:10+0000' + const expectedDate = '2000-03-15T05:20:10.000Z' + const parsedDate = parseUTCDate(zeroOffset) + assert.equal(parsedDate.toISOString(), expectedDate) + }) + + it('clones a date object', () => { + const date = new Date(2000, 2, 15, 5, 20, 10, 20) + const parsedDate = parseUTCDate(date) + assert.deepEqual(parsedDate, date) + assert.notEqual(parsedDate, date) + }) + + it('assumes a number is a timestamp', () => { + const date = new Date(2000, 2, 15, 5, 20, 10, 20) + const timestamp = date.getTime() + const parsedDate = parseUTCDate(timestamp) + assert.deepEqual(parsedDate, date) + }) + + it('returns an invalid date for anything else', () => { + assert.equal(parseUTCDate('').toString(), 'Invalid Date') + assert.equal(parseUTCDate('invalid').toString(), 'Invalid Date') + assert.equal(parseUTCDate('2020-10-10').toString(), 'Invalid Date') + }) +})