Open mattjohnsonpint opened 8 years ago
Matt, thank you very much for the detailed feedback.
I'll generally just agree with Matt here, but would add the following:
Informally, a DateValue is either an ISO 8601-formatted string or a JSON object containing two properties named kind and value that together define a point in time.
This text assumes that all date values exchanged by a REST API are defined by a point in time. This assumption is incorrect and does not hold water in the real world.
For instance, one might want to express this information in an API:
All Microsoft store locations open at 05:00 in the morning on Black Friday in 2016.
This is a datetime value, that would be expressed by the ISO8601 standard as 2016-11-25T05:00:00.00
, where this is interpreted as local time to the store. It is not any single point on the global timeline.
You may also encounter time-only situations like
All Microsoft store locations open at 11:00 on Saturdays
Again, there is no reference to a point in time, and this should be interpreted as time local to the store.
RFC 3339 would specify this as partial-time
in the format 11:00:00
.
This documentation should explicitly state that absent an offset values shall be interpreted as local time (which may vary based on the context of the problem domain). While ISO8601 and ECMAScript already say this, most people won't go look it up, so if this is best practice documentation, best to put it in. It should also be noted that these formats should only be used if you mean local time. If you mean time that represents a point on the global timeline, ISO8601 with offset should almost universally be used.
On that note, I will echo that StructuredDateLiteral
is not good advice. If these formats had to be handled, I guess it would be a way to do it. That said, this appears to be best practice documentation. The best practice would be to keep formats like that inside your own ecosystem, and convert to ISO8601 for data transfer purposes. Anything else would be in conflict with established internet norms.
Happy to think harder about the local time documentation and make a pull if you would like one.
Yes, I'll agree with Maggie's statements (thank you). The partial-time
format is indeed worth adding.
On the Black Friday example, I suppose that is indeed a valid value - and one might get it by using a .NET DateTime
having DateTimeKind.Unspecified
. However - in the vast majority of use cases I've encountered - the value is usually intended to describing a specific moment in time, rather than a floating value. I'll suggest then that the guidance say something like the following:
A date+time value MUST include a time zone offset in the form
+/-HH:MM
(orZ
for UTC) if it is intended to represent a specific instant in time. A date+time value without an offset specified is assumed to be in some local time, or could be interpreted as a floating time value, not belonging to any specific time zone.
Does that make sense?
@garethj-msft - No problem, glad to help. I'm going to ask some of the other @mspnp guys to take a look at the other parts of the guidelines also. Date/time just happens to be my thing. :smile:
Please review the following constructive criticism with regard to the date and time guidelines in section 11.
Iso8601Literal
is poorly definedZ
or a numeric offset+/-HH:MM
so that they represent a distinct point in time:"2016-07-16T08:23:45Z"
is a specific point in Coordinated Universal Time (UTC)."2016-07-16T01:23:45-07:00"
is the same point in time with a particular time zone offset."2016-07-16T01:23:45"
is not a specific point in time. It could be interpreted differently depending on time zones. While this is type of data may be useful within an application, it has very little meaning in an API and should generally be discouraged as an interchange format."2016-07-16"
is a valid date. Date-only values should always be passed like this."2016-07-16T00:00:00Z"
is not a date. It's a date-time at midnight UTC. The equivalent date value may be quite different when adjusted to the local time zone. This is a commonly encountered anti-pattern, and should be actively discouraged."2016-07-16T00:00:00"
is also not a date. It's a date-time at midnight local time. The main problem here is that not every local date has a midnight (due to daylight saving time spring-forward gaps). For example,"2016-10-16T00:00:00"
does not exist in most of Brazil. Given this value, and this time zone context, some implementations may skip forward to 1:00, some may skip backward to 23:00 the prior day, and some may throw an exception.To address all of these, the guidance should recommend using the
date-time
andfull-date
formats defined in RFC 3339 § 5.6.StructuredDateLiteral
is not good adviceDateKind
value codes appears to be invented here. I know of no JSON library that will create or read value in this split two-state form. You would have to create your own intermediate objects, then either have a long case-statement to handle all the different kinds, or limit yourself to one particular kind. Indeed, the guidance talks about how different clients would need to understand the kind of the server. This requirement defeats the purpose of having an interchange format to begin with. In other words, it's no more helpful to have akind
property that tells you what format to expect in thevalue
property than it would be to describe the custom format in the API's documentation."O"
format is being used for OLE Automation Dates - this format uses an ambiguous local time scale, while most of the others use UTC. Also, some implementations base it from 1900-01-01 or 1899-12-31, instead of 1899-12-30, and there is extra work to account for negative values. This is something that should be only handled directly when interfacing to/from OLE values - such as by callingDateTime.FromOADate
orDateTime.ToOADate
in .NET. The OADate values themselves should never be put into a web API."X"
format honors the Excel 1900 leap year bug - really? This should never leak out of Excel. Putting it in an API would be a horrible thing to do.Misc other items
{ "interval" : "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z" }
is less commonly used than{ "start" : "2007-03-01T13:00:00Z", "end" : "2008-05-11T15:30:00Z" }
. Though either are technically acceptable, the latter is usually prefered because it's easier to work with.Thanks.