Skip to content

Commit

Permalink
Add parseJSON function (#1463)
Browse files Browse the repository at this point in the history
Add `parseJSON` - minimal implementation for parsing dates formatted with `toJSON`.
  • Loading branch information
marnusw authored and kossnocorp committed Oct 22, 2019
1 parent 10ac162 commit 83e7328
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/fp/index.js
Expand Up @@ -196,6 +196,7 @@ export { default as min } from './min/index.js'
export { default as parse } from './parse/index.js'
export { default as parseISO } from './parseISO/index.js'
export { default as parseISOWithOptions } from './parseISOWithOptions/index.js'
export { default as parseJSON } from './parseJSON/index.js'
export { default as parseWithOptions } from './parseWithOptions/index.js'
export {
default as roundToNearestMinutes
Expand Down
1 change: 1 addition & 0 deletions src/fp/index.js.flow
Expand Up @@ -238,6 +238,7 @@ declare module.exports: {
parse: CurriedFn3<Date | number, string, string, Date>,
parseISO: CurriedFn1<string, Date>,
parseISOWithOptions: CurriedFn2<Object, string, Date>,
parseJSON: CurriedFn1<string | number | Date, Date>,
parseWithOptions: CurriedFn4<Object, Date | number, string, string, Date>,
roundToNearestMinutes: CurriedFn1<Date | number, Date>,
roundToNearestMinutesWithOptions: CurriedFn2<Object, Date | number, Date>,
Expand Down
4 changes: 4 additions & 0 deletions src/fp/parseJSON/index.d.ts
@@ -0,0 +1,4 @@
// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.

import { parseJSON } from 'date-fns/fp'
export default parseJSON
8 changes: 8 additions & 0 deletions src/fp/parseJSON/index.js
@@ -0,0 +1,8 @@
// This file is generated automatically by `scripts/build/fp.js`. Please, don't change it.

import fn from '../../parseJSON/index.js'
import convertToFP from '../_lib/convertToFP/index.js'

var parseJSON = convertToFP(fn, 1)

export default parseJSON
40 changes: 40 additions & 0 deletions src/fp/parseJSON/index.js.flow
@@ -0,0 +1,40 @@
// @flow
// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.

export type Interval = {
start: Date | number,
end: Date | number
}

export type Locale = {
formatDistance: Function,
formatRelative: Function,
localize: {
ordinalNumber: Function,
era: Function,
quarter: Function,
month: Function,
day: Function,
dayPeriod: Function
},
formatLong: Object,
date: Function,
time: Function,
dateTime: Function,
match: {
ordinalNumber: Function,
era: Function,
quarter: Function,
month: Function,
day: Function,
dayPeriod: Function
},
options?: {
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7
}
}

type CurriedFn1<A, R> = <A>(a: A) => R

declare module.exports: CurriedFn1<string | number | Date, Date>
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -167,6 +167,7 @@ export { default as max } from './max/index.js'
export { default as min } from './min/index.js'
export { default as parse } from './parse/index.js'
export { default as parseISO } from './parseISO/index.js'
export { default as parseJSON } from './parseJSON/index.js'
export {
default as roundToNearestMinutes
} from './roundToNearestMinutes/index.js'
Expand Down
2 changes: 2 additions & 0 deletions src/index.js.flow
Expand Up @@ -508,6 +508,8 @@ declare module.exports: {
}
) => Date,

parseJSON: (argument: string | number | Date) => Date,

roundToNearestMinutes: (
date: Date | number,
options?: {
Expand Down
16 changes: 16 additions & 0 deletions src/parseJSON/benchmark.js
@@ -0,0 +1,16 @@
// @flow
/* eslint-env mocha */
/* global suite, benchmark */

import parseJSON from '.'
import moment from 'moment'

suite('toDate', function() {
benchmark('date-fns', function() {
return parseJSON('2014-10-25T13:46:20+00:00')
})

benchmark('Moment.js', function() {
return moment('2014-10-25T13:46:20+00:00')
})
})
4 changes: 4 additions & 0 deletions src/parseJSON/index.d.ts
@@ -0,0 +1,4 @@
// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.

import { parseJSON } from 'date-fns'
export default parseJSON
59 changes: 59 additions & 0 deletions src/parseJSON/index.js
@@ -0,0 +1,59 @@
import toDate from '../toDate/index.js'

/**
* @name parseJSON
* @category Common Helpers
* @summary Parse a JSON date 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} argument A fully formed ISO1806 date string to convert
* @returns {Date} the parsed date in the local time zone
* @throws {TypeError} 1 argument required
*/
export default function parseJSON(argument) {
if (arguments.length < 1) {
throw new TypeError(
'1 argument required, but only ' + arguments.length + ' present'
)
}

if (typeof argument === 'string') {
var parts = argument.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(argument)
}
38 changes: 38 additions & 0 deletions src/parseJSON/index.js.flow
@@ -0,0 +1,38 @@
// @flow
// This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.

export type Interval = {
start: Date | number,
end: Date | number
}

export type Locale = {
formatDistance: Function,
formatRelative: Function,
localize: {
ordinalNumber: Function,
era: Function,
quarter: Function,
month: Function,
day: Function,
dayPeriod: Function
},
formatLong: Object,
date: Function,
time: Function,
dateTime: Function,
match: {
ordinalNumber: Function,
era: Function,
quarter: Function,
month: Function,
day: Function,
dayPeriod: Function
},
options?: {
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7
}
}

declare module.exports: (argument: string | number | Date) => Date
54 changes: 54 additions & 0 deletions src/parseJSON/test.js
@@ -0,0 +1,54 @@
// @flow
/* eslint-env mocha */

import assert from 'power-assert'
import parseJSON from '.'

describe('parseJSON', function() {
it('parses a fully formed ISO date with Z', () => {
const date = '2000-03-15T05:20:10.123Z'
const parsedDate = parseJSON(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 = parseJSON(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 = parseJSON(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 = parseJSON(zeroOffset)
assert.equal(parsedDate.toISOString(), expectedDate)
})

it('clones a date object', () => {
const date = new Date(2000, 2, 15, 5, 20, 10, 20)
const parsedDate = parseJSON(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 = parseJSON(timestamp)
assert.deepEqual(parsedDate, date)
})

it('returns an invalid date for anything else', () => {
assert.equal(parseJSON('').toString(), 'Invalid Date')
assert.equal(parseJSON('invalid').toString(), 'Invalid Date')
assert.equal(parseJSON('2020-10-10').toString(), 'Invalid Date')
})
})

0 comments on commit 83e7328

Please sign in to comment.