Let’s say you’re building your first API. Be it public, private, or some hybrid thereof, don’t be surprised if your first defect is date/time-related. Do not underestimate how much trouble you can get into when it comes to handling date and times. Here are some tips which might keep you out of this potential future.
Caveat: I’m presuming you’re using a Gregorian calendar. In certain international situations, this might be a bad assumption. If you’re operating on a different calendar, this won’t help you.
Law #1: Use ISO-8601 for your dates
There’s really no debate here. From the W3C to the IETF, and even XKCD, the Internet has come to operate on this standard. Don’t try to be smart and do it differently. ISO-8601 provides a host of varieties on how to display dates/times/timezones.
For a quick glimpse from your *nux console, try the following:
ISO 8601 addresses many issues, including:
- Natural sorting – So simple and elegant, it sorts without any extra work.
- Timezone offset – Respecting user location and timezone is critical for an increasingly global and mobile world.
- Locale neutrality – Imagine the nightmare date 2/3/4. Depending on if you’re in the US, EU, or otherwise…that date could be Feb 3, 2004 in the US, or Mar 2, 2004 for most of the rest of the world. In ISO 8601 terms, 2004-02-03 takes away that ambiguity.
- Support in a wide variety of programming languages – While not all languages come with this date format in the default installation (for example, Java), there are typically mature libraries available for parsing ISO 8601 formats. Don’t be a hero and do it yourself.
Law #2: Accept any timezone
Now that you have a tool set with ISO 8601 whereby you can capture relevant timezone offset data, use it! We are in a global age. Especially if your API is open to the public, you will unquestionably be dealing with consumers from around the globe. Do not make any assumptions about what timezone your API users will utilize.
Law #3: Store it in UTC
This one is really more of a systems design law in general. I’ve lost count of how many times I have seen systems built with the timezone of the original company headquarters. Inevitably, unless you are quite disciplined, users will end up seeing their dates in your companies’ timezone. This is pretty annoying, especially if they are located on the other side of the globe.
UTC, or Coordinated Universal Time, is the least common denominator in storing times, as it does not denote any timezone offset. This allows you to purpose dates as needed throughout your system in whatever timezone is appropriate.
Law #4: Return it in UTC
UTC will allow your API consumers the freedom to offset the date to whatever suits their needs. More importantly for you, your API team will not have to fret over calculating all of those offsets for every hit. Documenting and supporting your API, in terms of how you deal with dates, is simple: “you get UTC”.
Law #5: Don’t use time if you don’t need it
ISO 8601 also allows us the flexibility to provide a date without a time. In scenarios in which time is not important, only the date (something like “target completion date”): do not store or return the time. While it seems like no harm done in just storing 11:59pm, or some other random time, this can get messy when it comes to internationalizing that date.
Imagine you stored 2013-03-01T23:59:59 in UTC to represent Mar 1, 2013. Now offset that time based on China Standard Time (UTC+0800), and you are looking at Mar 2, 2013 at 8am. This could be real trouble, as dates get misinterpreted. In this case, we would simply use 2013-03-01 without any additional time/timezone offset info to reduce time interpretation ambiguity. Most modern database systems support this notion of date-only storage.
Summary
While all of that gritty date talk can be a little mind numbing, rest assured that virtually all RESTful platforms in the wild are utilizing these formats. Just be cautious when you start mucking around in the data serialization libraries, that you pay attention to what it does to your dates by the time it leaves your API platform.
Most of us have worked, lived, or communicated in different timezones and/or countries, we can respect how confusing it can be to communicate something as simple as scheduling an appointment. Also if you have had the displeasure of writing lots of legacy date parsing and formatting, you can appreciate how one standard for time representations is such a blessing. Stick to these basic laws of handing dates and times in your API, and you’ll save yourself these potential headaches.
Image credit: hermione13 / 123RF Stock Photo
Law #1.Natural sorting. Isn’t that only true if all times are using the same time zone designator or you convert to UTC? So in other words, and to clarify, natural sorting in ISO-8601 works well if you follow law #4, always return in UTC.
Law #5. That guidance is a bit of a sticky wicket. While I agree that dates with a time zone element in them are inherently difficult to deal with in internationalized UIs, many business uses of dates that I’m aware of have implicit or explicit dependencies on a time zone. Users typically expect dates to be relevant to the timezone in which they are using the app. For example, if you’re building an app that’s primarily going to be used in the US, it might be fine to store a date without a timezone offset. However, such an app ‘might’ display dates strangely to someone logging in from Australia. I think your guidance works if the business use of the date is always expected to represent “local time” for any user of the application, and not associated with coordination, scheduling, deadlines, etc for global users.
Great clarification on #1!
Yeah I definitely saved the most contentious for last. The timezone return issue has probably eaten more time out of my life than all the others combined (mostly in debating). I have definitely had to bend for a variety of reasons to returns localized timezones. That said, as a safe guideline, UTC is a great place to start. For the consistency of your developers’ experience, one standard is best. Let them customize that timezone to their app users’ experience.
Do you have any thought about the profileof ISO8601: RFC 3339?
I appreciate the principles in RFC 3339, but inconsistencies with ISO8601 (such as the “:” in the timezone offset) are sort of confusing. Truth is, many libraries for date parsing/serialization support both, so it’s probably a pretty idiosyncratic difference. Ultimately, RFC 3339 is intended to be a subset of ISO8601, and in practice falls inline with what I’ve prescribed here.
The timezone offset is something that can really bite you when handling daylight savings, as the offset itself carries no information about that, so there are problems in interval calculations over multiple days (because the “local” offset will have changed). The rather verbose Oracle datatype “timestamp with local timezone” seems to be a good implementation but even that struggles with data import with a fixed offset.
Basically I agree with your article. Date / times are a pain!
When storing a specific timezone (typically in a user profile or in a corollary field to the stored date) I’d recommend using the zoneinfo format, http://en.wikipedia.org/wiki/Tz_database.
That’s what Oracle uses according to that wikipedia entry.
Don’t #1 and #3 contradict? You say use ISO8601, but then store and return UTC. The distinction is unclear…
Regarding any potential contradiction of ISO8601 vs UTC, I’ll concede it’s a bit contentious. Allowing users to provide time with an offset is convenient for input, specifically in POST operations. I’ll tell you that I have removed that functionality before and forced UTC-only (namely for GET/PUT parity), which I would lean toward in general. However for those situations that are unavoidable, I figured it would be worthy to address how to properly use offsets (and where to conced, which is POST). Re-reading this, I can see how that might seem ambiguous.
Thanks for the clarification.
Following rule #3 can get you into trouble when storing timestamps for future events. If I want to store a timestamp for a meeting at 12pm local time in New York, but the government changes the DST rules after I save the timestamp, the same timestamp could now be pointing to 11am local time (the same time in UTC, but obviously not the desired result). You would need to store timezone data with the timestamp so the correct time can still be calculated.
Some governments have changed timezone rules fairly last minute, so you shouldn’t depend on having a chance to prepare ahead of time, or that timestamps only a few weeks in the future would be safe.
Very insightful on the future dates when governments change offsets. It made me remember the last time the US did it, getting involved in bulk updates on store dates. Outside of storing the actual zoneinfo with each date I’m honestly not sure how you can really get around that type of edge case.
Store two forms of the date. One in UTC to enable sorting/searching across timezones, and one to store the user’s actual intent. This last form may actually require two fields in most databases; one to store the local (to the user) datetime and one to store the user’s timezone.
This is also the suggestion w3c makes:
http://www.w3.org/TR/timezone/
“When creating an application that can store values in the future, including recurring events, you’ll need to make additional data fields to ensure that you can reconstruct the user’s intentions and adapt future time values to changes in time zones and time zone rules (especially alteration of daylight saving/summer time start and stop).”
Pingback: Dew Drop – March 26, 2013 (#1,514) | Alvin Ashcraft's Morning Dew
Pingback: The 5 laws of API dates and times | nodeJS and REST APIs | Scoop.it
Pingback: The 5 laws of API dates and times | The code is art
Pingback: Un’Intruduzione alla Rappresentazione, Serializzazione e Gestione del Tempo nel Software | Nexse SWAT Team
Pingback: What’s the time? | Energimolnet's Developer Blog
Pingback: How to localize your API | API UX
Pingback: When API time zones make the difference
Pingback: Build a Better API | Mahmoud Zalt (BLOG)
Great article. I am using moment.js and found the following helped solidify how to accept any TZ, store in UTC and return UTC.
/* 1. Gets the local date time in ISO-8601 format. ex: “2015-12-11T18:52:59-05:00” */
var localDateTime = moment().format();
/* 2. POST to the API Body: { date: “2015-12-11T18:52:59-05:00” } */
/* 3. On Server convert the localtime to UTC: “2015-12-11T23:52:59+00:00” */
var UTCTime = moment.utc(req.body.date).format();
/* 4. Store the UTCTime in the DB. */
/* 5. Returned to the user in an JSON */
/* res.json( { date: “2015-12-11T23:52:59+00:00” } ); */
/* 6. Testing in the Browser: */
var localDateTime2 = moment(“2015-12-11T23:52:59+00:00”).format();
/* Assert we got a UTC that converts back into a local time we originally sent. */
localTime == localTime2
This should be to specific moment commands for local and server side dates.
Anyone?
Pingback: Writing OpenAPI (Swagger) Specification Tutorial – Part 4 – Advanced Data Modeling | API Handyman
Pingback: The 5 laws of API dates and times | Topic | Sc...