Skip to content

Initial proposal for the experimental GraphQLDateTime scalar #557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

excitement-engineer
Copy link
Contributor

This PR contains an initial proposal for a graphQL DateTime scalar following the discussion in issue #550. Let me know what you think:)

The scalar has the following properties:

  • It follows the ISO 8601 date specification.
  • All dates are represented in UTC.
  • It serializes javascript Date instances into ISO 8601 date strings in the format YYYY-MM-DDThh:mm:ss.sssZ.
  • It parses date strings into javascript Date instances. The following formats are supported for the date strings:
'YYYY',
'YYYY-MM',
'YYYY-MM-DD',
'YYYYMMDD',
'YYYY-MM-DDThhZ',
'YYYY-MM-DDThh:mmZ',
'YYYY-MM-DDThhmmZ',
'YYYY-MM-DDThh:mm:ssZ',
'YYYY-MM-DDThhmmssZ',
'YYYY-MM-DDThh:mm:ss.sssZ',
'YYYY-MM-DDThhmmss.sssZ',
'YYYY-Www',
'YYYYWww',
'YYYY-Www-D',
'YYYYWwwD',
'YYYY-DDD',
'YYYYDDD'
  • I had to include moment as a dependency in order to perform all the date operations.

The list of supported date formats was retrieved from wikipedia.

On wikipedia they mentioned that ±YYYYY is used "To represent years before 0000 or after 9999, the standard also permits the expansion of the year representation but only by prior agreement between the sender and the receiver.". From this description I concluded that this format is not in the standard ISO 8601 spec so I did not include it in the scalar.

@stubailo
Copy link

stubailo commented Nov 8, 2016

Is there any way for a client to know that something is a Date without needing to import the schema ahead of time? I suppose ID is already a precedent for a type that is indistinguishable from String.

Would it be better to serialize as something like:

{
  "type": "DateTime",
  "value": "ISO date here"
}

I guess REST APIs often just return dates as ISO strings so this isn't really necessary, but I wonder what people's thoughts are.

@excitement-engineer
Copy link
Contributor Author

What would be your use-case for giving the client this information?

I could imagine a cool use case where graphql client libraries such as apollo and relay could parse a DateTime scalar and automatically convert it to the native date type. For example, a swift client could automatically create and return NSDate instances. This would prevent developers from having to convert string dates to native date types themselves.

@langpavel
Copy link
Contributor

Citing myself from #550:

History show us that there is no Date without (Time or Timezone)...

Most bad thing is date without explicit timezone.

All debates around with or without timezone are problematic because we all have local perception of spoken (=> written into form => stored in DB) time.

For me only acceptable format is timestamp expressed as number of (milli)seconds from (UNIX) big bang.

So, consider real number as one possible input for coercion..

I have something for this theme here: GraphQLTimestamp

@langpavel
Copy link
Contributor

BTW @excitement-engineer I must appreciate your intention but I really don't like adding new dependencies to reference implementation.

Reason: It introduces uncertainty to this reference implementation

package.json Outdated
@@ -36,7 +36,8 @@
"prepublish": ". ./resources/prepublish.sh"
},
"dependencies": {
"iterall": "1.0.2"
"iterall": "1.0.2",
"moment": "^2.15.2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think depending on the moment library for this one experimental feature is probably not what we want. Is it possible to get the same behavior with just a little bit of specific code atop the built-in Date object (accepting that super old versions of Node and browsers might warp behavior)?

For example, could we just use Date's toISOString() instead of converting it through moment first? And could we use Date.parse() which claims to parse ISO format, but protect it from parsing the other formats with a validating regex?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely agree with you on this one! I had some doubts about whether to include moment as a dependency because it is only used for a very small proportion of the code. There are some features of moment that I used:

  • Checking for a valid date is very easy using the isValid() function. I ran into the problem that the invalid date 2015-02-29 was regarded as a valid date in the Date object (because days are numbered from 1-31).
  • Checking for correctly formatted date strings is very easy in moment (you can specify an array of valid date formats). I initially tried using a single validating regex to validate all the supported date formats but it quickly became too complex to understand.

I will try to overcome these problems:) I will try to write multiple easy to understand validating regex for verifying that the date string being parsed is in ISO date format and remove the moment dependency.

I think that the important thing when doing these operations that the code remains readable and understandable. So I will make sure to write code this way:)

name: 'DateTime',
description: 'An ISO-8601 encoded UTC date string.',
serialize(value: mixed): string {
if (!(value instanceof Date)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think serialize should be more accepting of input than just asserting Date instances. For example, iso date strings should probably also be supported here.

Also, I'm curious how convenient we should try to be for unix timestamps? It's really common for datetimes to actually be stored as unix times in databases, and I fear that if we don't make it easy to represent these as DateTime that they'll be lazily be represented as Int instead. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! We should promote the use of this scalar by making it very convenient to link to any dateTime representation that are used by the underlying systems: Date, ISO string or unix timestamp. I will include support for all of these!

Thanks for the great tips!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the PR in the weekend:)

@leebyron
Copy link
Contributor

leebyron commented Nov 9, 2016

Is there any way for a client to know that something is a Date without needing to import the schema ahead of time?

@stubailo, there isn't a way for a client to know anything without looking at the schema, so I'm not sure it makes sense to try to create new wrapper types for something like this. If the alternative is using the String type and happening to send ISO dates, that doesn't leave a client any worse or better off. You should think of this purely as an improvement to the schema (which means improvement to validation and error messages as well). So if you're building a schema-less client, then such a change should be invisible.

Most bad thing is date without explicit timezone.
For me only acceptable format is timestamp expressed as number of (milli)seconds from (UNIX) big bang.

@langpavel I disagree. Unix timestamps are great for capturing exact machine time for the present day, but terrible for human time that we often have to represent in products. The datetime ISO standard was designed to represent the real ambiguity that needs to exist to represent a full range of times described by humans when talking about relative times, events, and things that will happen in the future.

We have this a similar kind of DateTime representation at Facebook for our Events/Calendar product, and use it all the time. For example:

  • What time is Happy Hour? It's 5 o'clock. What timezone? Whichever you're standing in. What's the unix timestamp for that? There isn't one.
  • When does Christmas start? December 25th just after midnight. What year? Whichever year you're in now. What timezone? Whatever timezone you'll be in during christmas. What's the unix timestamp for that? Undefined!
  • You're planning on retiring in 2050? What's the unix timestamp for that? Not enough bits to represent.

Not to say that unix timestamps aren't great or aren't suitable for APIs. When your API is depicting moments by machines and for machines, unix time can be a great choice. But if your API is depicting human times, then you need something like ISO time.

@langpavel
Copy link
Contributor

langpavel commented Nov 10, 2016

@leebyron Really nice examples, thanks for them!
But as always when talking about date and time it brings more question then answers.
Does this mean if GraphQL will support DateTime it should support Timestamp too (like databases do)?

@leebyron
Copy link
Contributor

That's a great question, I'm not sure! I think part of what evaluating this PR should entail is how this kind of scalar should be introduced. If it should be considered a candidate for addition to the spec, then it should be included in this reference implementation. If not, then perhaps we fold this out into another npm module that just makes it easy to get common behavior.

@stubailo
Copy link

I'd say one way to resolve the question of Date/DateTime/Timestamp is to map the type onto what it's most likely to turn into on the client, since GraphQL seems to be client-focused. In JavaScript, I'd suggest that Date is the most common way to represent dates. Is there an equivalent in Swift/Obj-C/Java?

@excitement-engineer
Copy link
Contributor Author

excitement-engineer commented Nov 12, 2016

I have updated the PR:

  • Removed the 'moment' dependency.
  • Used javascript Date for parsing ISO date strings. The formats that Date can parse is more limited than was possible with moment (see this document for the supported formats). This limitation has forced me to reduce the supported formats. However, I think that this is not such a problem because it makes the API simpler will make it easier for other languages to implement this scalar if this were to be included in the spec. The following formats are supported:
YYYY
YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.sssZ
  • The Date class in javascript assumes that each month has 31 days. This means that dates like 30 February are considered 'valid' dates. I think that the GraphQL spec should not include this behaviour but rather throw an error when an invalid date is being parsed or serialized. I wrote some custom logic to handle this extra validation.
  • The serialize function now accepts date string, unix timestamps and Date instances.
    • Unix timestamps are defined in the code as: the number of seconds since January 1st 1970 however the javascript Date class defines it as the number of milliseconds since January 1st 1970. Therefore, I wrote a simple conversion from seconds to milliseconds to overcome this issue. Do you agree with the definition of a Unix timestamp that is used in this scalar or should we use milliseconds?
    • When serializing a date string a check is performed to see whether it is a valid ISO date. If this check passes then the date string is simply returned. This means that if 2016-01 is passed as input then it is also returned by the serialize function. This causes an inconsistency in the date format returned by the serialize function; the format YYYY-MM-DDThh:mm:ss.sssZ is returned when a Date or timestamp is serialized, for date string any of the supported formats may be returned. What do you guys think, do we always want to output a consistent format or is the current behaviour ok?
  • We have discussed adding a Unix timestamp as a possible input for the parseValue function. Should we support this in the scalar? I like the consistency of the current implementation because a client making requests and receiving responses from a GraphQL layer will only ever have to deal with ISO 8601 compliant date strings; adding a timestamp will break this consistency. Furthermore, I am wondering how many valid use cases there are for client applications sending timestamps to a graphQL layer, I have never represented dates as Unix timestamps on a client so I can't overlook how useful such a feature would be.

Curious to hear your comments on the updates:)

@excitement-engineer
Copy link
Contributor Author

excitement-engineer commented Nov 12, 2016

Travis has failed to build because node version 4 and 0.12 consider `2016-01-01T24:00Z as an invalid date format while version 6 and 7 don't. I have updated the code to restrict the hour range from 0 to 23 to prevent these inconsistencies.

@excitement-engineer
Copy link
Contributor Author

@stubailo you can use NSDate for this in obj-c and LocalDateTime in java. Would be great if the graphql clients could map straight to these types, would safe the developer a lot of hassle in performing conversions:)

@OlegIlyenko
Copy link
Contributor

OlegIlyenko commented Nov 14, 2016

@excitement-engineer @stubailo on JVM it's a bit more involving... LocalDateTime and ZonedDateTime would be indeed a good choice on java 8, but AFAIK graphql-java is still keeping compatibility with java 6. this means that they only can use java.util.Date. Plus there is also a java.util.Calendar class which some people prefer.

In my experience though, java.util.Date is not the best choice to represent the date, so most project are actually using joda-time lib (at least on java < 8). I guess the standard GraphQL type can be expressed in terms of java.util.Date, but in most of the project people will still need to covert it to something else :)

For the moment being, I decided to just document it in sangria. So people need to implement it themselves.

On the other hand, some data transfer formats naively support dates (like Amazon Ion). For this, i implemented a mechanism to communicate these capabilities of a data format to a scalar type, so that it can output raw date objects (without any conversion to string), but only when data format supports it. (this is why you see if (caps.contains(DateSupport)) in a DateTime scalar type definition)

Also, considering all of the patterns you have described:

YYYY
YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.sssZ

In my opinion, it would not be correct to represent them with a single data type. For example in java 8 java.time package defines following classes to represent things you have described with patterns:

  • Year
  • YearMonth
  • LocalDate
  • ZonedDateTime

I personally would model them in the same way in my GraphQL schema. In fact this is what I do in my projects. For example, I have following GraphQL types in my schema:

  • DateTime - date with timezone in ISO calendar system
  • Date - just date without a timezone in ISO calendar system
  • Time - time without a timezone in ISO calendar system

(I don't use Year or YearMonth yet)

@excitement-engineer
Copy link
Contributor Author

I have used the 'java.time' package in java and it works quite well! I like the separation between the different date/time formats. It enforces a stronger contract when it comes to date/time representations.

@leebyron may I ask what conventions Facebook's GraphQL API maintains for representing dates/times, do you use a single scalar or several (for representing Date, Time, DateTime, YearMonth etc. individually)?

@excitement-engineer
Copy link
Contributor Author

Any updates on the status of this PR?

@jamesgorman2
Copy link
Contributor

@excitement-engineer pending and answer from Lee, we deal with mixed LocalDates, YearMonths, Times and DateTimes in our internal service-side representation and being able to express the difference between them in our schema would be useful in cross-timezone work.*

We are doing financial reporting and we have a lot of transactions with timezones that are colated. The result of that colation is then timezone-less, ie everything that is reported for 2016-11-27 is the same regardless of your location around the world and 2016-11-27 is always 2016-11-27 regardless of location.

We use time with TZ to manage daily jobs at the local time.

I'm more or less with Oleg (and think Oleg's mention of Java8 is apropos, especially the Date -> joda -> java.time journey) and my gut feeling is you want with TZ:

  • DateTime - a date + time with minute-or-greater resolution at UTC
  • Time - a time with minute-or-greater resolution at UTC

and without TZ:

  • LocalDate - a date with day-of-month resolution and no TZ
  • LocalTime - a time with minute-or-greater resolution and no TZ
  • Year
  • YearMonth

I'd be explicit about the LocalDate and LocalTime to mark that they are zone-less. This does mean that Time and LocalTime are effectively indistinguishable from a data perspective but you are documenting the expected behaviour w.r.t. localisation - this is particularly important for read-write values.

The reason to put Year and YearMonth is similar - you can represent these as, eg, start of year and start of month (so 2016 -> 2016-01-01) but this is more like having hh -> hh:00, which is not generally accepted (and not in the ISO) than hh:mm -> hh:mm:00.

I've deliberately left off LocalDateTime since they make everyone sad except when you figure out how to get rid of them. Someone might have a good justification for having this type but I don't.

This all begs the question of whether this should go into the spec and then the reference implementation. To do this well I think it needs to be quite heavy-weight. I like the idea of it being in the spec, but I'm not sure what the maintainers position on it's weight is. One suggestion is to fit a strong-typed version into your graphql-iso-date lib in the interim.

tl;dr - strong typing of date-time objects makes an API easier to understand. Open question on how much to stuff into the graphql spec.

* we also have LocalDateTime in data we're receiving but get a timezone into it as soon as it hits our system.

@excitement-engineer
Copy link
Contributor Author

Thanks for your thoughts @jamesgorman2 ! I really appreciate it your input:)

The reason to put Year and YearMonth is similar - you can represent these as, eg, start of year and start of month (so 2016 -> 2016-01-01)

tl;dr - strong typing of date-time objects makes an API easier to understand

I agree with you:)

Java does a great job of distinguishing between these two (and other date types) ! Having very specific types (compared to a single type for representing all dates) makes an API much easier to understand and prevents confusion from implicit assumptions about date representations in my opinion.

An issue I ran into with the current implementation in this PR is that the DateTime scalar always serializes to the full ISO string YYYY-MM-DDTHH:mm:ss.sssZ which cannot be interpreted without context about what the datetime represent (is it a date. year, month,...??). As an example, the string 2016-01-01T00:00:00.000Z can be interpreted in different ways:

  • Year 2016
  • Year month 2016-01
  • Date 2016-01-01
  • DateTime

It seems that java initially also started out with a single class (java.util.Date) for representing date/time and them moved to the current setup of multiple classes in the time package!

An idea would be to update the date/time representation in GraphQL to the following:

GraphQLYear

Serializes into string YYYY from:

  • A javascript Date
  • A string in one of the allowed ISO formats.

Parsed from string in one of the allowed ISO formats.

Allowed ISO formats

YYYY
YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.sssZ

GraphQLYearMonth

Serializes into string YYYY-MM from:

  • A javascript Date
  • A string in one of the allowed ISO formats.

Parsed from string in one of the allowed ISO formats.

Allowed ISO formats

YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.sssZ

GraphQLDate

Serializes into string YYYY-MM-DD from:

  • A javascript Date
  • A string in one of the allowed ISO formats.

Parsed from string in one of the allowed ISO formats.

Allowed ISO formats

YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.sssZ

GraphQLDateTime

Serializes into string YYYY-MM-DDThh:mm:ss.SSSZ from:

  • A javascript Date
  • A string in one of the allowed ISO formats.
  • A unix timestamp

Parsed from string in one of the allowed ISO formats.

Allowed ISO formats

YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.sssZ

GraphQLTime

Serializes into string hh:mm:ss.SSS from:

  • A javascript Date
  • A string in one of the allowed ISO formats.

Parsed from string in one of the allowed ISO formats.

Allowed ISO formats

hh:mm
hh:mm:ss
hh:mm:ss.sss

What do you guys think of such a representation? Is this a better API compared to the one currently implemented in this PR?

@jamesgorman2
Copy link
Contributor

On the whole I think this is coming along. The general direction feels right but still needs tightening up. After a bit of reflection I've come to:

  • you probably need to step back from the API and work on the schema spec first.
    • Determine which ISO 8601 fragments are in and which are out, eg, do we include duration?
    • Talk about the semantics of each type rather than the format.
    • For each type determine the coercion behaviour as per the scalar spec. This is where we introduce formatting rules.
    • once all this is done we can work out how to make JS conform.
    • something like https://gist.github.com/jamesgorman2/2d41107fd6b9d9ff08ea63ba23b65ffa, it's verbose but should help make things clearer
  • when talking about coercing a DateTime to a (Local)Date etc you should specify whether it should use the current locale or UTC (for 2016-01-01T04:00+11/2015-12-31T19:00, which year should this convert to).
  • using Date for a TZ-less date may be confusing since the JS Date type has TZ data, hence the suggestion for LocalDate.
  • having a LocalTime and and OffsetTime (to use Java8 names) as distinct types means that you can do LocalDate+LocalTime -> LocalDateTime, not great but at least I know I have to interpret it into a TZ, and LocalDate+OffsetTime -> OffsetDate (or Date) and I've an exact instant.

@excitement-engineer
Copy link
Contributor Author

Great idea on the spec @jamesgorman2! I am gonna be on holiday next week but I will work on it when I get back:)

The datetime formats I specified above are in UTC; in ISO 8601 the Z indicates that it is in UTC.

I think that the GraphQL datetime should be timezoneless, it should be in UTC. Refer to earlier by comment @leebyron on why adding timezone information is not a good idea. As a reference, the Github GraphQL api also uses UTC for their DateTime scalar.

The advantage of using UTC is that clients can add the relevant timezone information depending on the timezone that they are in. You don't want timezone information of your server bleeding into your GraphQL layer.

@jamesgorman2
Copy link
Contributor

No worries @excitement-engineer. For the on-the-wire-format I'm less concerned about UTC/non-UTC. Active TZ of a datetime is signal but not super important.*

I think we're using the word timezoneless a little differently - I'm referring to temporal objects with no timezone info (cf Lee's Christmas and Happy Hour examples), contrasted with temporal objects with a timezone regardless of whether the it has been shifted from the local TZ to UTC or not. For rich data applications being able to use both is important and being able to talk about which one is coming down the wire is since there is a big difference semantically between shifting a DateTime beween UTC/+0000 to +1100 and appending +1100 to a LocalDateTime.**

In the former the absolute time is maintained but the rendered time is changed. 2016-05-07T12:00Z becomes 2016-05-07T23:00+1100 and 2016-05-07T07:00-0500, but all represent the same instant.

In the latter an absolute time is created from a relative time. 2016-05-07T12:00 becomes 2016-05-07T12:00+1100 and 2016-05-07T12:00-0500 which are different instants.

Beyond this, there needs to be a spec around how a -zoned or -zoneless temporal object is coerced into other types. I prefer permissive input, so I can send a JS Date with a non UTC timezone to something expecting a LocalDate and know that it will always truncate in a particular way (which was my point above about non-UTC DateTimes).

Hopefully this help firm up the definitions around timezoned vs -zoneless. I'm happy to use other words for these but I think the distinction is critical.

Enjoy your holiday Dirk-Jan.

* we use a lot of postgres which has a timestamp with timezone type, but we test using H2 which doesn't so we work in UTC at rest, and convert to specific TZs for running our accounts.
** I've deliberately avoided using add here since it is ambiguous in its meaning between these two cases.

@excitement-engineer
Copy link
Contributor Author

Thanks for your explanation @jamesgorman2! We were talking about different things but now I think that we are on the same level:)

I had some time on the plane yesterday so I wrote up an initial version of what the specification could look like. Note that the text in the final draft should be more elaborate, this initial version in just to get the main concepts across.

Please provide some feedback on this initial spec so that we can make it better and more complete. I'd like to hear thoughts on whether:

  • all the necessary types are included
  • some types are still missing
  • there are types that do not add any significant additional value and are not needed

Furthermore, I have not included duration in the spec proposal yet. Is this something that we would like to support? If anybody has any use cases for duration please write them down in this thread, this will help in deciding whether to include it or not:)

@excitement-engineer
Copy link
Contributor Author

excitement-engineer commented Dec 4, 2016

ISO 8601 Date/Time Scalar Proposal

This document contains a proposal for adding ISO 8601 encoded Date/Time scalars to the GraphQL specification. The goal is to create a standardized way of representing Date/Time in GraphQL.

The GraphQL scalar types defined below use the ISO 8601 standard for representing dates as string. Below is a list containing all the Date/Time formats supported by the various scalar types together with their meaning. Note, each of the different scalars can be coerced from different subsets of formats in the list below, the supported formats are indicated for each specific scalar type.

YYYY
YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThhZ
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.SSSZ
YYYY-MM-DDThh
YYYY-MM-DDThh:mm
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ss.SSS
hhZ
hh:mmZ
hh:mm:ssZ
hh:mm:ss.SSSZ
hh
hh:mm
hh:mm:ss
hh:mm:ss.SSS

Where:

  • [MM] indicates a two-digit month of the year, 01 through 12.
  • [DD] indicates a two digit day, 01 through 31
  • [hh] refers to a zero-padded hour between 00 and 24 (where 24 is only used to denote midnight at the end of a calendar day).
  • [mm] refers to a zero-padded minute between 00 and 59.
  • [ss] refers to a zero-padded second between 00 and 60 (where 60 is only used to denote an added leap second).
  • [SSS] refers to a zero padded millisecond between 000 and 999.

A note on coercion:

All of the Scalars defined below can be coerced (both for input/result) from a date/time string formatted according to a list of supported formats. This list is a subset of the formats in the list above. A field error should be raised for strings that do not conform to one of the supported formats.

A note on the Native date object/type

In the sections below a reference is made to the "native date object/type". The "native" type refers to the standard date type of the language that GraphQL is implemented in. So in Java this could be LocalDate, LocalDateTime etc. and in Javascript this could be Date.

GraphQLLocalDate

Used for representing specific dates without timezone in format YYYY-MM-DD such as 2016-02-01. This scalar can be used to represent someone's birthdate.

Input coercion:

Coerced from date-string in one of the supported formats.

Result Coercion:

Returns date-string in format YYYY-MM-DD from:

  • Relevant native date object/type
  • Date-string in one of the supported formats

Supported formats:

The following formats are supported for input/result coercion. Note, all the formats are truncated to YYYY-MM-DD by removing time/timezone information. So, the date-string "2016-01-01T00:00Z" is truncated to "2016-01-01" for example.

YYYY-MM-DD
YYYY-MM-DDThh
YYYY-MM-DDThh:mm
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ss.SSS
YYYY-MM-DDThhZ
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.SSSZ

GraphQLDateTime

Used to represent a UTC encoded date-time in format YYYY-MM-DDThh:mm:ss.SSSZ such as 2016-04-14T10:34:11.000Z. This scalar can be used to represent an instantaneous point on the time-line such as the exact instance that a user created his/her account for example.

Input coercion:

Coerced from date-time-string in one of the supported formats.

Result Coercion:

Returns date-time-string in format YYYY-MM-DDThh:mm:ss.SSSZ from:

  • Relevant native date object/type
  • Date-time-string in one of the supported formats
  • A Unix timestamp

Supported formats:

The following formats are supported for input/result coercion. Note, formats with a less than millisecond precision are "zeroed" to millisecond precision. So, the date-time-string "2016-01-01T10Z" is "zeroed" to "2016-01-01T10:00:00.000Z" for example.

YYYY-MM-DDThhZ
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.SSSZ

GraphQLLocalDateTime

Used to represent a date-time without a timezone in format YYYY-MM-DDThh:mm:ss.SSS. Represents a point in time regardless of timezone. This could be used to represent the exact time that new years occurs for example:2017-01-01T00:00:00.000.

Input coercion:

Coerced from a date-time-string in one of the supported formats

Result Coercion:

Returns string in format YYYY-MM-DDThh:mm:ss.SSS from:

  • Relevant native date object/type
  • Date-time-string in one of the supported formats
  • Unix timestamp (integer)

Supported formats:

The following formats are supported for input/result coercion. Note, formats with a less than millisecond precision are "zeroed" to millisecond precision. So, the date-time-string "2016-01-01T10" is "zeroed" to "2016-01-01T10:00:00.000" for example.

YYYY-MM-DDThh
YYYY-MM-DDThh:mm
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ss.SSS

GraphQLLocalTime

Represents a time without a time-zone in format hh:mm:ss.SSS such as 10:34:02.001. This could be used to represent store opening and closing times for example.

Input coercion:

Coerced from time-string in format specified below

Result Coercion:

Returns string in format hh:mm:ss.SSS from:

  • Relevant native date object/type
  • time-string in format specified below

Supported formats:

The following formats are supported for input/result coercion. Note, formats with a less than millisecond precision are "zeroed" to millisecond precision. So, the time-string "10:20" is "zeroed" to "10:00:00.000" for example.

hh
hh:mm
hh:mm:ss
hh:mm:ss.SSS

GraphQLTime

Represents a UTC encoded time in the format hh:mm:ss.SSSZ such as 10:34:02.001Z, represents a specific time point in a day such as the precise time that the US stock exchange opens every day.

Input coercion:

Coerced from a time-string in one of the supported formats.

Result Coercion:

Returns string in format hh:mm:ss.SSS from:

  • Relevant native date object/type
  • A time-string in one of the supported formats

Supported formats:

The following formats are supported for input/result coercion. Note, formats with a less than millisecond precision are "zeroed". So the time-string "10" is "zeroed" to "10:00:00.000" for example.

hh
hh:mm
hh:mm:ss
hh:mm:ss.SSS

GraphQLYear

Used to represent a year in format YYYY such as 2016.

Input coercion:

Coerced from:

  • A date-string in one of the supported formats
  • A 4 digit integer. An integer with less/more digits should raise a field error.

Result Coercion:

Returns string in format YYYY from:

  • Relevant native date object/type
  • A date-string in one of the supported formats
  • A 4 digit integer. An integer with less/more digits should raise a field error.

Supported formats:

The following formats are supported for input/result coercion. Note, all the formats are truncated to YYYY. So, the date-string "2016-01-01T00:00Z" is truncated to "2016" for example.

YYYY
YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.SSSZ
YYYY-MM-DDThh:mm
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ss.SSS

GraphQLYearMonth

Used to represent a year-month combination in format YYYY-MM. This scalar can be used for storing credit card expiry dates for example.

Input coercion:

Coerced from a date-string in one of the supported formats.

Result Coercion:

Returns string in format YYYY-MM from:

  • Relevant native date object/type
  • A date-string in one of the supported formats

Supported formats:

The following formats are supported for input/result coercion. Note, all the formats are truncated to YYYY-MM. So, the date-string "2016-01-01T00:00Z" is truncated to "2016-01" for example.

YYYY-MM
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.SSSZ
YYYY-MM-DDThh:mm
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ss.SSS

GraphQLMonthDay

Used to represent a specific month-day pair in format --MM-DD. This scalar can be used to represent recurring annual dates such as a person's birthday for example.

Input coercion:

Coerced from a date-string in one of the supported formats.

Result Coercion:

Returns string in format --MM-DD from:

  • Relevant native date object/type
  • A date-string in one of the supported formats

Supported formats:

The following formats are supported for input/result coercion. Note, all the formats are truncated to --MM-DD by removing the year/time components. So, the date-string "2016-01-04T00:00Z" is truncated to "--01-04" for example.

--MM-DD
YYYY-MM-DD,
YYYY-MM-DDThh:mmZ
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss.SSSZ
YYYY-MM-DDThh:mm
YYYY-MM-DDThh:mm:ss
YYYY-MM-DDThh:mm:ss.SSS

@excitement-engineer
Copy link
Contributor Author

@leebyron I am really curious to hear your opinion and feedback on the proposal:) Do these scalars cover the use cases that need to be supported for facebook's GraphQL API?

@leebyron
Copy link
Contributor

My immediate reaction is that this is the wrong balance between simplicity and usefulness, I think having people pick between many different time formats will result in mistakes and confusion and there will be a bunch of collisions where these slightly incompatible date formats are used in overlapping ways - like a variable provided or extensions of interfaces

@leebyron
Copy link
Contributor

The most important question that needs to be answered though is if a date scalar should be added to the GraphQL specification - since graphql.js is a reference implementation of the spec, it would probably be confusing to add additional scalars that were not described by the spec.

Adding to the spec is a double-edged sword. It reduces the amount of things that you can do in user space, but in exchange you get guarantees about what to expect when you encounter it in any arbitrary (spec compliant) GraphQL server.

That means if DateTime is to be a specified scalar, there needs to be consensus on exactly how it should work, because once added to the spec there is no room for a type with that name to be different per server.

If we choose not to specify time scalars, then this work can still be useful as a separate npm library for use by anyone who agrees with these opinions on how date time should work, however if not specified then servers could be free to disagree and define scalar types with the same names that have different behavior.

Also I should point out that if we were to write specification for DateTime, it would not remove your ability to use additional types like MonthYear in your servers if that was the right choice for your API.

@smolinari
Copy link

smolinari commented Dec 11, 2016

That means if DateTime is to be a specified scalar, there needs to be consensus on exactly how it should work, because once added to the spec there is no room for a type with that name to be different per server.

If we look at this from an introspection perspective and global APIs, how could a client developer know how to handle a dateTime value she is being sent? In other words, if we look at it from the client's/ customer perspective, how would she ingest this dateTime value to then know how to properly format it, to present it to her client/ customer/ viewer?

When it comes to public APIs, will this "freedom of choice" of not putting such types in the spec always work? Public APIs need a specification to go by, so that any ingested data can be consistently used. If a client dev sees a "dateTime" type value, she should know it is of a certain format and will always be that format and also what the reference time is.

Going off on a tangent, if you think about it, dateTime or Time values are always compounds (which others have pointed out too, in different ways). Time values are always a reference value and a formatting value compounded together. In other words, a time value cannot be a scalar value really, if you go by the definition of a scalar. Or put another way, there is no way to represent a time value, without a format and a reference! 😄

Looking at a just made public API, I went to the Github GraphiQL Explorer and looked for a dateTime value and found one under "User". If you click on it, you get this description:

An ISO-8601 encoded UTC date string.

Simple enough. 😄 I know exactly what I need to do with it. Is there anything more really needed?

Scott

@excitement-engineer
Copy link
Contributor Author

Thanks for the awesome feedback @jamesgorman2, I really appreciate it!

I have updated the proposal according to your feedback and reduced the number of scalars to only include the more fundamental ones such as date, time and date-time.

Note, the coercion behaviour can be defined once a decision is made on whether any of these types are worthy of adding to the specification and in what form.

Reference: This documentation has been heavily adapted from the documentation on the java.time package. This documentation was used as a starting point.

  • LocalDate: A date without a time-zone in the ISO-8601 calendar system, such as 2007-12-03.
  • LocalTime: A time without a time-zone in the ISO-8601 calendar system, such as 10:15:30.000.
  • Time: A time at UTC in the ISO-8601 calendar system, such as 10:15:30.000Z.
  • LocalDateTime: A date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.000.
  • DateTime: A date-time at UTC in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.000Z.

LocalDate

A date without a time-zone in the ISO-8601 calendar system, such as 2007-12-03.

LocalDate is a representation of a date, viewed as year-month-day.

This scalar does not represent a time or time-zone. Instead, it is a description of the date, as used for birthdays for example. It cannot represent an instant on the time-line.

LocalDate is encoded as a string in the format YYYY-MM-DD where YYYY indicates a four-digit year, 0000 through 9999. MM indicates a two-digit month of the year, 01 through 12. DD indicates a two-digit day of that month, 01 through 31.

For example, the value "2nd of January 2015" is encoded as string "2015-01-02".

LocalTime

A time without a time-zone in the ISO-8601 calendar system, such as 10:15:30.000.

LocalTime is a representation of a time, viewed as hour-minute-second. Time is represented to millisecond precision. Where a time does not have millisecond resolution, any missing units are implied to be zero.

This scalar does not represent a date or time-zone. Instead, it is a description of the local time as seen on a wall clock for example. It cannot represent an instant on the time-line.

LocalTime is encoded as a string in the format hh:ss:mm:ss.sss where hh indicates a two-digit hour, 00 through 24. mm indicates a two-digit minute, 00 through 59. ss indicates a two-digit second, 00 through 60 (where 60 is only used to denote an added leap second). .sss indicates a fractional second in millisecond precision, .000 through .999.

The value "14:10:20.987" is encoded as string "14:10:20.987". A time with less than millisecond precision such as "14:10" is encoded as "14:10:00.000".

Time

A time at UTC in the ISO-8601 calendar system, such as 10:15:30.000Z.

Time is a representation of a time instant, viewed as hour-minute-second at UTC. A time instant is represented to millisecond precision. Where an instant does not have millisecond resolution, any missing units are implied to be zero. Where an instant has a time-zone other than UTC, it is shifted to UTC.

Time does not represents a date. Instead, it is a description of a time instant such as the opening bell of the New York Stock Exchange for example. It cannot represent an instant on the time-line. By representing an instant as a date-time at UTC, it allows the local time at which the instant occurs to be derived for each time-zone.

Time is encoded as a string in the format hh:ss:mm:ss.sssZ where hh indicates a two-digit hour, 00 through 24. mm indicates a two-digit minute, 00 through 59. ss indicates a two-digit second, 00 through 60 (where 60 is only used to denote an added leap second). .sss indicates a fractional second in millisecond precision, .000 through .999. Z indicates that the time is at UTC.

The value "14:10:20.987 at UTC" is encoded as string "14:10:20.987Z". A time instant with less than millisecond precision such as "14:10 at UTC" is encoded as "14:10:00.000Z". A time instant with a time-zone other than UTC such as "14:10:20.987 at UTC +1 hour" is encoded as "13:10:20.987Z".

LocalDateTime

A date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.000.

LocalDateTime is a representation of a full date with a local time, viewed as a combination of LocalDate and LocalTime.

This scalar does not represent a time-zone. Instead, it is a description of the date, as used for birthdays for example, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line.

LocalDateTime is encoded encoded as a concatenation of the string encoding of LocalDate and LocalTime in the format <LocalDate>T<LocalTime>, where T represents a delimiter separating the date and time.

For example, the value "2nd December 2009 at 14:10.20.987" is encoded as string "2009-12-02T14:10.20.987".

DateTime

A date-time at UTC in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.000Z.

DateTime is a representation of a full date with a time at UTC, viewed as a combination of LocalDate and Time.

DateTime represents an exact instant on the time-line to millisecond precision. It is description of the instant that a user account was created for example. By representing an instant as a date-time at UTC it allows the local date-time at which the instant occurs to be derived for each time-zone.

DateTime is encoded as a concatenation of the string encoding of LocalDate and Time in the format <LocalDate>T<Time>, where T represents a delimiter separating the date and time.

For example, the value "2nd December 2009, 14:10.20.987 at UTC" is encoded as string "2009-12-02T14:10.20.987Z".

@martinheidegger
Copy link

@excitement-engineer A lot of work has come into this PR. Wow!

When reading over the comments I was missing a few points that I would like to add:

Making sure that the timestamps are somehow correct are not always easy. This is even worse between client ←→ server. So: even if a timezone formatted value arrives at the graphql server instance the syncing might be off.

User Example: If the timestamp between server is out-of-sync, an application that alerts the user might alert the user too early or too late.

Out of this very problem, Local* types might be even more complex to deal with. Since the user just entered the text, any graphql implementation would need to "assume" the timezone from somewhere else. It might be worth specifying a "request header" with an "timezone-of-request" field that allows to treat the LocalDate accordingly.

There is another tangent to this: Servers - particularly relays - would need to sync timestamps as well. In that case I think it might be worth considering an introspection API that allows to "find out" what the current server time is. For this to be implemented I would think that an ISO string with accurate timezone might be actually the better idea!

{
  __time {
     timeZone: string
     isoTime: string
     time: string
     utc: int
  }
}

where the timeZone refers to an identifier like Asia/Tokyo, timeOffset gives the current timeoffset in hhmm, isoTime gives new Date().toISOString(), time gives the current time but not for the UTC timezone but the actual time offset: 2009-12-02T14:10.20.987+0000 and finally utc the time in ms since 1970 UTC. This way a variety of calculation could be done to sync (and display) the current state of the GraphQLAPI.

This sort of API would imho. be useful in the Relay and graphql standard and because it is used here: the type to implement it: DateTime? Has imho. a place in graphql while I am not quite sure the same is the case for Date, Time or the other Types discussed in this PR.

@excitement-engineer
Copy link
Contributor Author

Hey @martinheidegger thanks for contributing to the thread!

I personally do not have any experience syncing timestamps between client-server and server-server so I will defer my opinion to someone with more experience on this issue so that they can evaluate your proposal.

@excitement-engineer
Copy link
Contributor Author

excitement-engineer commented Jan 29, 2017

I'm currently busy upgrading the graphql-iso-date library with support for some of these date/time scalars. Implementing this stuff gave me so insights on ways to deal with dates/times.

Following your earlier comment @leebyron I decided to implement these scalars based on the RFC 3339 ISO 8601 profile. This profile is concise and easy to understand, this makes it easy for people use in their graphQL schemas but also decreases the implementation overhead compared to the full ISO 8601 standard which is very extensive. I like the idea of using the profile for the DateTime here as well!

Below is a description of the DateTime that I ended up implementing. I would love to hear feedback:)

One issue I ran into was dealing with leap seconds. Leap seconds cannot be predicted far into the future which creates a nightmare for software because it needs to be updated whenever new leap seconds are announced. Javascript Dates ignore leap seconds for example. I therefore decided to ignore leap seconds as well. What do you guys think, should we take leap seconds into account?

DateTime

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 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.

This scalar is a description of an exact instant on the time-line such as the instant that a user account was created for example.

This scalar ignores leap seconds (thereby assuming that a minute constitutes of 59 seconds), in this respect it diverges from the RFC 3339 profile.

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.987+01:00" is shifted to "2016-01-01T13:10:20.987Z".

Result Coercion

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.

Input Coercion

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.

@ghostganz
Copy link

Any reasons to exclude zoned or offset times? For my use-case the time zone is important, so converting to UTC means losing information.

@excitement-engineer
Copy link
Contributor Author

@ghostganz I tried to keep the API as simple as possible, there is a very simple contract that is upheld here: You always get UTC!

Any opinions on how we should go about this from the others, is UTC too restrictive?

@martinheidegger
Copy link

@excitement-engineer the timezone show in which timezone a server operates, at least in theory you could use that to identify where the db is running.

@langpavel
Copy link
Contributor

langpavel commented Feb 27, 2017

@martijnwalraven Even for small locally running projects I'm using and enforcing UTC. I'm almost sure most of experienced admins using UTC on server..

@excitement-engineer
Copy link
Contributor Author

@martinheidegger @langpavel Yes you are right, it would not be good to expose the timezone of a server in an API! But I don't think that's what @ghostganz 's meant with his comment

I interpreted @ghostganz 's use case as follows: suppose a client makes a request and sends the timezone where the request was made in the ISO date-time string as input for the API. In the current setup the scalar automatically shifts this date-time string to UTC thereby losing the timezone offset info. Perhaps there are some use cases where the server needs this timezone info.

@smolinari
Copy link

@ghostganz - could you please explain your use case?

Scott

@langpavel
Copy link
Contributor

langpavel commented Feb 27, 2017

@excitement-engineer @ghostganz Yes, there are use cases where timezone info is helpful, but I'm still not sure this should be part of GraphQL. Thinking about proper storage types for time is deep app specific. This discussion is out of range of GraphQL specification and should be finally closed I think. Everybody can create own type and/or publish module which cover most widely used formats of representing datetime values across RFC/ISO specifications.

In GraphQL it can always ends up as Int or String or ObjectType. Hey field can have arguments, so you can ask for your timezone and server gives you computed value or if nothing given, you should ask for original timezone. Implementer should be responsible for choosing fitting representation, not API layer — it cannot!

@ghostganz
Copy link

ghostganz commented Feb 27, 2017

In my use-case we originally used local time in ISO 8601, with no specified time zone. This was nicely human readable etc., but broke down once the clients were abroad. We realized every 5 o'clock implicitly meant 5 o'clock in CET.
Moving to UTC and hard-wiring CET into the clients would be the quick-fix, but it made no sense when the 8601 standard really allowed us to say what we meant: 5 o'clock in CET.
We now can do both explicit times in CET and client generated offsets ("five minutes ago") no matter where the client is.

The time zone of the server or database itself has no relevance here. For us the time zone is data. This is why there are time zones in the 8601 standard in the first place.

(Or really, 5 o'clock in +0100)

@smolinari
Copy link

smolinari commented Feb 28, 2017

@ghostganz - If you send a time value over the wire, it will either be the UTC value, or it will be a time value with the offset already calculated into it. So, according to this proposal, you should either get the "Z" at the end, or not.

As you said, the user's time offset is data, but it should be stored separately from the time value/ reference.

Think about it this way. When do you store a user's offset (the time zone) and when do you store time values? They are two separate occurrences or at least they should be. 😄

Scott

@ghostganz
Copy link

@smolinari No, the user's offset is not data, but the time zones of dates in our responses are.

Let me re-phrase:

In our use-case an article can be published 5 o'clock CET and should be displayed as "published 5 o'clock" no matter where the user is. But sending as local time won't do, because the client can't display a relative time ("published 10 minutes ago") unless it knows the time zone of the article.
Using UTC would force us to hard-code CET into the client and calculate the times from that.

@smolinari
Copy link

smolinari commented Feb 28, 2017

@ghostganz

should be displayed as "published 5 o'clock" no matter where the user is.

So if I am in mountain time and it is 4:10 pm, I'd see 5 o'clock in the future as the articles published time?

But sending as local time won't do, because the client can't display a relative time ("published 10 minutes ago") unless it knows the time zone of the article.

If you save the published time as UTC and you know each user's offset plus the local time (which you can get the offset from too), you can easily calculate "published 10 minutes ago" without a time zone. That is why time values are usually saved in Zulu time. In fact, trying to calculate times with time zones is much more complicated, especially when DST comes into the picture.

Scott

@martinheidegger
Copy link

While generally true, the ISO time format has its advantages, one being the timezones the other being that you never run into integer overflows.

@excitement-engineer
Copy link
Contributor Author

@langpavel In my opinion there is some value in providing a good default option for representing date-time that covers the vast majority of use cases. This will give clients standard behaviour w.r.t. date-time and will prevent people from incorrectly representing date-time as a GraphQLString or GraphQLInt (timestamps) for example.

In addition, date-time representations is something that comes up so often in APIs that there is a place for a standard representation.

Of course it is impossible to cover every possible use case with this scalar, in this case the developer can implement their own custom solution that satisfies their specific use case.

@langpavel
Copy link
Contributor

langpavel commented Feb 28, 2017

@excitement-engineer. will prevent people from incorrectly representing date-time as a GraphQLString or GraphQLInt Don't think so. If you look at APIs around you, you will see what craziness can people create 😈 And, if you enforce them to one good way, will be mostly the bad one. So let people think about, they will start looking around and then can possibly find and pick more fitting, all opt-in.

So, conclusion:

GraphQL does not include data type for representing Date and/or Time values, but you can choose from multiple custom data types.. (enumeration here...)

And yes, It is possible to implement universal multi-format, with localization custom object types which can represent any date-time value, may be this library becomes well ported across languages BUT no any unnecessary byte in this reference implementation and every custom server (client)!

At last, every time implementation is opinionated and simply there is no best representation across internet.

@thebergamo
Copy link

Support or not support any types of date formats to create an universal date/time format will be a mess, in my opinion. 😛

Any of the formats choosed will not fit the use case for someone. But keep it simple, will help to put this to the specification based on comments of @leebyron. If we use the ISO format, we can all the benefits of timezones and whatever you want, and it can be stringified, with this information, you can use at the server any library to parse this information to your favorite format.

We keep the timezone and ability to convert to any format using ISO dates, and in mind this is a reference spec, we need (again) to keep it simple, let's give the responsibility to decide how to use the "date" information to the client and server.

@langpavel
Copy link
Contributor

Again and again, this is evergreen 🙂
I hope that GraphQL will not have support for data types which are not exact by definition. Dates are not, time is not, DateTime cannot be scalar!

For me, timestamp is enough — in millis, but for atom which catching fancy foton from neighbours, well he don't incept time in same way… 🙂

Joking, but not much. This is not obvious at first spot. But similarities — to the hell with fractals 😆 — yes; there are.

You cannot smoothly convert date and time and timezone to universal format. Proof of this thesis is observable in this debate, from born of internet to today.

Yes, there are norms, like ISO — but how this is not universal… So NO.

  • If you want the TIMESPAMP you should explain why.
  • If you want the date — hard to do naturally (timezone??)
  • If you want the time — very similar thing
  • If you want record exact moment in local history — sure, definitely relativistic thing

Why I'm against norm:

If you introduce the standart then all relevant points mentioned above will be in very hard and outstanding position.

Be careful — history and opinion here:

I'm from Czech. We have a total gov. We make a Velvet revolution, but we experiencing return of normalization with very bad expectations.

Norms are good as far as they help with participating. If they are too fixed, they devaluates work, because they are too tight — no carrying of losing facta. We cannot allow loosing facta in so modern transport layer like GraphQL is!

@excitement-engineer So sorry, but GraphQL is only (and at all) about data exchange, semantics is on the App.

— Peace and sleep, forgive me a typos please

@leebyron
Copy link
Contributor

leebyron commented Dec 2, 2017

I think the conclusion from this PR is that there is not one DateTime type to rule them all. Tradeoffs involved will lead to reasonable different decisions for different APIs.

Having these utility scalar types available in a package on NPM would be highly valuable, but since there isn't consensus on a single one, this package is probably the wrong place to host it. It's been incredibly valuable to work through this discussion in this forum!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.