Skip to content

Commit 6834899

Browse files
committed
Introduce new ISO Date scalars (#380)
* Introduce new ISO Date scalars * Upgrade to Node 14 * Add mocks and missing exports * Add Timestamp * acknowledgement * Add missing typeDef
1 parent 062034e commit 6834899

29 files changed

+2467
-176
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
node_version: [10, 12, 13]
17+
node_version: [10, 12, 14]
1818
steps:
1919
- name: Checkout Master
2020
uses: actions/checkout@v1
@@ -38,7 +38,7 @@ jobs:
3838
- name: Use Node
3939
uses: actions/setup-node@v1
4040
with:
41-
node-version: '13.x'
41+
node-version: '14.x'
4242
- name: Install Dependencies using Yarn
4343
run: yarn install
4444
- name: Build

README.md

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -435,12 +435,63 @@ common in defining schemas or interfaces to data.
435435

436436
## The Types
437437

438+
### Date
439+
440+
A date string, such as 2007-12-03, compliant with the `full-date` format outlined in section 5.6 of the [RFC 3339](./rfc3339.txt) profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
441+
442+
This scalar is a description of the date, as used for birthdays for example. It cannot represent an instant on the time-line.
443+
444+
**Result Coercion**
445+
446+
Javascript Date instances are coerced to an RFC 3339 compliant date string. Invalid Date instances raise a field error.
447+
448+
**Input Coercion**
449+
450+
When expected as an input type, only RFC 3339 compliant date strings are accepted. All other input values raise a query error indicating an incorrect type.
451+
452+
### Time
453+
454+
A time string at UTC, such as 10:15:30Z, compliant with the `full-time` format outlined in section 5.6 of the [RFC 3339](./rfc3339.txt) profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
455+
456+
This scalar is a description of a time instant such as the opening bell of the New York Stock Exchange for example. It cannot represent an exact instant on the time-line.
457+
458+
This scalar ignores leap seconds (thereby assuming that a minute constitutes of 59 seconds), in this respect it diverges from the RFC 3339 profile.
459+
460+
Where an RFC 3339 compliant time string has a time-zone other than UTC, it is shifted to UTC. For example, the time string "14:10:20+01:00" is shifted to "13:10:20Z".
461+
462+
**Result Coercion**
463+
464+
Javascript Date instances are coerced to an RFC 3339 compliant time string by extracting the UTC time part. Invalid Date instances raise a field error.
465+
466+
**Input Coercion**
467+
468+
When expected as an input type, only RFC 3339 compliant time strings are accepted. All other input values raise a query error indicating an incorrect type.
469+
438470
### DateTime
439471

440-
Use real JavaScript Dates for GraphQL fields. Currently you can use a String or an Int (e.g., a
441-
timestamp in milliseconds) to represent a date/time. This scalar makes it easy to be explicit about
442-
the type and have a real JavaScript Date returned that the client can use _without_ doing the
443-
inevitable parsing or conversion themselves.
472+
A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the [RFC 3339](./rfc3339.txt) profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
473+
474+
This scalar is a description of an exact instant on the time-line such as the instant that a user account was created.
475+
476+
This scalar ignores leap seconds (thereby assuming that a minute constitutes of 59 seconds). In this respect it diverges from the RFC 3339 profile.
477+
478+
Where an RFC 3339 compliant date-time string has a time-zone other than UTC, it is shifted to UTC. For example, the date-time string "2016-01-01T14:10:20+01:00" is shifted to "2016-01-01T13:10:20Z".
479+
480+
**Result Coercion**
481+
482+
JavaScript Date instances and Unix timestamps (represented as 32-bit signed integers) are coerced to RFC 3339 compliant date-time strings. Invalid Date instances raise a field error.
483+
484+
**Input Coercion**
485+
486+
When expected as an input type, only RFC 3339 compliant date-time strings are accepted. All other input values raise a query error indicating an incorrect type.
487+
488+
> Taken from [graphql-iso-date](https://github.com/excitement-engineer/graphql-iso-date/)
489+
490+
### Timestamp
491+
492+
The javascript `Date` as integer. Type represents date and time as number of milliseconds from start of UNIX epoch.
493+
494+
> Taken from [GraphQLTimestamp.js](https://gist.github.com/langpavel/b30f3d507a47713b0c6e89016e4e9eb7)
444495
445496
### UtcOffset
446497

@@ -534,7 +585,7 @@ The [GraphQL spec](https://facebook.github.io/graphql/#sec-Int) limits its Int t
534585

535586
```
536587
GraphQLError: Argument "num" has invalid value 9007199254740990.
537-
Expected type "Int", found 9007199254740990.
588+
Expected value of type ""Int"", found 9007199254740990.
538589
```
539590

540591
> Based on [graphql-bigint](https://github.com/stems/graphql-bigint)
@@ -597,18 +648,18 @@ A field whose value is an [ISO-4217 currency](https://en.wikipedia.org/wiki/ISO_
597648

598649
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
599650

600-
> Uses [graphql-type-json](https://github.com/taion/graphql-type-json)
651+
> Based on [graphql-type-json](https://github.com/taion/graphql-type-json)
601652
602653
### JSONObject
603654

604655
The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
605656

657+
> Based on [graphql-type-json](https://github.com/taion/graphql-type-json)
658+
606659
### Byte
607660

608661
The `Byte` scalar type represents byte value as specified by [NodeJS Buffer type](https://nodejs.org/api/buffer.html)
609662

610-
> Uses [graphql-type-json](https://github.com/taion/graphql-type-json)
611-
612663
### IBAN
613664

614665
Includes IBAN specifications for the following countries:
@@ -747,3 +798,5 @@ It was created and maintained by the company `ok-grow`.
747798
We, The Guild, took over the maintaince of that library [later on](https://medium.com/the-guild/the-guild-is-taking-over-maintenance-of-merge-graphql-schemas-so-lets-talk-about-graphql-schema-46246557a225).
748799

749800
We also like to say thank you to [@adriano-di-giovanni](https://github.com/adriano-di-giovanni) for being extremely generous and giving us the `graphql-scalars` name on npm which was previously owned by his own [library](https://github.com/adriano-di-giovanni/graphql-scalars).
801+
802+
And thanks to [excitement-engineer](https://github.com/excitement-engineer) for [graphql-iso-date](https://github.com/excitement-engineer/graphql-iso-date), [stems](https://github.com/stems) for [graphql-bigint](https://github.com/stems/graphql-bigint), [taion](https://github.com/taion) for [graphql-type-json](https://github.com/taion/graphql-type-json), [langpavel](https://github.com/langpavel) for [GraphQLTimestamp.js](https://gist.github.com/langpavel/b30f3d507a47713b0c6e89016e4e9eb7)

bundle-test/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"bundlesize": [
1111
{
1212
"path": "./dist/index.js",
13-
"maxSize": "98 kB",
13+
"maxSize": "150 kB",
1414
"compression": "none"
1515
}
1616
],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"husky": "4.2.5",
6060
"jest": "26.0.1",
6161
"lint-staged": "10.2.11",
62+
"mockdate": "3.0.2",
6263
"prettier": "2.0.5",
6364
"semver": "7.3.2",
6465
"ts-jest": "26.1.0",

src/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import * as resolvers from './resolvers';
22
import * as mocks from './mocks';
33

44
export {
5+
Date as DateTypeDefinition,
6+
Time as TimeTypeDefinition,
57
DateTime as DateTimeTypeDefinition,
8+
Timestamp as TimestampTypeDefinition,
69
UtcOffset as UtcOffsetTypeDefinition,
710
EmailAddress as EmailAddressTypeDefinition,
811
NegativeFloat as NegativeFloatTypeDefinition,
@@ -45,7 +48,10 @@ export {
4548
export { default as typeDefs } from './typeDefs';
4649

4750
export {
51+
Date as DateResolver,
52+
Time as TimeResolver,
4853
DateTime as DateTimeResolver,
54+
Timestamp as TimestampResolver,
4955
UtcOffset as UtcOffsetResolver,
5056
EmailAddress as EmailAddressResolver,
5157
NegativeFloat as NegativeFloatResolver,
@@ -86,7 +92,10 @@ export {
8692
} from './resolvers';
8793

8894
export {
95+
Date as GraphQLDate,
96+
Time as GraphQLTime,
8997
DateTime as GraphQLDateTime,
98+
Timestamp as GraphQLTimestamp,
9099
UtcOffset as GraphQLUtcOffset,
91100
EmailAddress as GraphQLEmailAddress,
92101
NegativeFloat as GraphQLNegativeFloat,
@@ -129,7 +138,10 @@ export {
129138
export { resolvers };
130139

131140
export {
141+
Date as DateMock,
142+
Time as TimeMock,
132143
DateTime as DateTimeMock,
144+
Timestamp as TimestampMock,
133145
UtcOffset as UtcOffsetMock,
134146
EmailAddress as EmailAddressMock,
135147
NegativeFloat as NegativeFloatMock,

src/mocks.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const BigIntMock = () => BigInt(Number.MAX_SAFE_INTEGER);
22
const ByteMock = () => new Uint8Array([1988, 1981, 1965, 1963, 1959, 1955]);
3-
export const DateTime = () => new Date();
3+
const DateMock = () => '2007-12-03';
4+
export const Time = () => '10:15:30Z';
5+
export const DateTime = () => '2007-12-03T10:15:30Z';
6+
export const Timestamp = () => 1592577642;
47
export const UtcOffset = () => '+03:00';
58
export const EmailAddress = () => '[email protected]';
69
export const NegativeFloat = () => -123.45;
@@ -83,6 +86,7 @@ export const IBAN = () => 'NL55INGB4789170233';
8386
export const Void = (): null => null;
8487

8588
export {
89+
DateMock as Date,
8690
URLMock as URL,
8791
NonNegativeInt as UnsignedInt,
8892
NonNegativeFloat as UnsignedFloat,

src/resolvers/DateTime.ts

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/resolvers/Timestamp.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { GraphQLScalarType, Kind, ValueNode } from 'graphql';
2+
3+
// Taken from https://gist.github.com/langpavel/b30f3d507a47713b0c6e89016e4e9eb7
4+
5+
function serializeDate(value: any) {
6+
if (value instanceof Date) {
7+
return value.getTime();
8+
} else if (typeof value === 'number') {
9+
return Math.trunc(value);
10+
} else if (typeof value === 'string') {
11+
return Date.parse(value);
12+
}
13+
return null;
14+
}
15+
16+
function parseDate(value: any) {
17+
if (value === null) {
18+
return null;
19+
}
20+
21+
try {
22+
return new Date(value);
23+
} catch (err) {
24+
return null;
25+
}
26+
}
27+
28+
function parseDateFromLiteral(ast: ValueNode) {
29+
if (ast.kind === Kind.INT) {
30+
const num = parseInt(ast.value, 10);
31+
return new Date(num);
32+
} else if (ast.kind === Kind.STRING) {
33+
return parseDate(ast.value);
34+
}
35+
return null;
36+
}
37+
38+
export default new GraphQLScalarType({
39+
name: 'Timestamp',
40+
description:
41+
'The javascript `Date` as integer. Type represents date and time ' +
42+
'as number of milliseconds from start of UNIX epoch.',
43+
serialize: serializeDate,
44+
parseValue: parseDate,
45+
parseLiteral: parseDateFromLiteral,
46+
});

src/resolvers/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import DateTime from './DateTime';
1+
import Date from './iso-date/Date';
2+
import Time from './iso-date/Time';
3+
import DateTime from './iso-date/DateTime';
4+
import Timestamp from './Timestamp';
25
import UtcOffset from './UtcOffset';
36
import NonPositiveInt from './NonPositiveInt';
47
import PositiveInt from './PositiveInt';
@@ -43,7 +46,10 @@ const NonNegativeFloatResolver = NonNegativeFloatFactory('NonNegativeFloat');
4346
const UnsignedFloatResolver = NonNegativeFloatFactory('UnsignedFloat');
4447

4548
export {
49+
Date,
50+
Time,
4651
DateTime,
52+
Timestamp,
4753
UtcOffset,
4854
NonPositiveInt,
4955
PositiveInt,

0 commit comments

Comments
 (0)