One of the earliest axioms we drill into programmers is "use UTC, use date-time types". Don't store dates as strings, use your database's DateTime type.
If your database has a Date sans-Time type, use that. But many databases don't, and that's where the axiom starts breaking down, because its very natural to say "well, I'll use the next best thing, a DateTime type". But this is wrong! Its far, far better to simply use a YYYY-MM-DD string. I cannot stress how much better this is, but its not obvious why its better to someone who has had that axiom drilled into their heads, and has never ran into this situation before.
Basically when you tried to purchase a ticket, their frontend would take the date-of-flight, and adjust it by the user's timezone. So a ticket for 2015-01-30 00:00 would become 2015-01-29 20:00, or whatever. The dates in the request did not match what was expected for your booking code, and 500 error.
As for how it breaks, you have a booking for 8pm the day after the start of summer time. You stored the date/time in UTC. Government changes the start of summer time. Your database now thinks it's at 7pm because it uses UTC.
Here's some examples:
https://codeofmatt.com/on-the-timing-of-time-zone-changes/
Also the EU is seriously debating getting rid of clock changes, and it comes up in the UK semi-regularly.
Your database will not fix your bad data when that happens.
I work on a registration and reservation system for rowing boats in my rowing club https://github.com/dsroklub/roprotokol
The only time that matters is whatever the clock in the clubhouse shows (Danish time) If you sign out a boat from the UK (or more realistically from a laptop that thinks it is in the UK) at 14:00 it is still 14:00 Danish time. This is surprisingly difficult to achieve with JS Date object, browsers and popular JS components.
So the Temporal Plain time looks promising.
And especially the explicit timezones. Because even without timezone you need to handle DST. For example my system needs to know that if you start a 3 hour trip at midnight a certain day in the spring, you are expected back at 04:00.
I just hope that browsers and components will support is properly.
* Your meeting is scheduled for 2pm tomorrow.
* Oh oh! Your meeting got bumped to the next day.
* Daylight savings switched overnight.
Some things are measured in relative time. Some things are measured in absolute time. Never confuse the two in your database.
Because it’s not bad data, HaHaOnlySerious. I tell developers that if the database attribute accepts a value it must be in the domain of allowable values. Often people refuse check constraints in order to avoid expressing business logic in the database.
If you're storing future datetimes that semantically represent wall clock time, you need to store the locale time plus the full time zone (such as America/New_York) so that your program does the right thing in response to any common regulatory changes that happen after you store the value. Storing the time zone abbreviation (e.g., EST) is inadvisable, as computers sometimes care whether you asked for EST vs EDT. Storing the time offset (e.g., -500) is incorrect, as it has the same pitfalls as storing UTC - you're precomputing the locale's expected time offset at storage time, and your data won't automatically be corrected if time regulations change.
If you're storing historical timestamps, UTC is fine because you can safely convert it to whatever time zone you want to display, knowing that changes to time zone / DST regulations tend not to affect the past.
At this point in the process first normal form flies out the window. Trying to generalize too much can lead you down some weird garden paths. If it looks like you need a function to validate an prospective column value then you probably need to model the value as a relation corresponding to the function parameters. Then you can make it into a foreign key constraint and get on with your project.
I truly appreciate the efforts of those who attempt to expand the utility of datetime value representation to perfect a wider variety of denotational semantics. But with a relational model it may be better to delegate to simpler abstraction sufficient to the specific case.
It's usecase dependent so it seems like the application should deal with this, at which point persisting in UTC is still a reasonable choice.
If you store this in UTC and the timezone changes, you end up converting to the wrong time.
So for future events you should store local time with a location you can map to a timezone.
But if you want to have precise date time far in the future you must not use UTC based milliseconds representations unless you want to check for necessary database migrations every time the time zone definitions change.
My point in mentioning the solstice is that while some events are at “2pm”, others are “when something happens”.
Sure, UTC is not suitable for conducting astronomy. It is suitable for working with human friendly times, since it is the foundation of timezones.
(1) The time in the local timezone (+ the timezone itself) (2) The UTC conversion (3) The version of the IANA database used to compute (2)
Then you can use (2) the UTC time for most computations/queries, but if a timezone changes then you can use (3) to tell which UTC dates need to be updated, and (1) to recompute the new correct version of (2).
No mucking around with IANA databases, everything's easy to program, server usually keeps itself up-to-date.
"Always use UTC" works if you really, really always use UTC.
Never use UTC for dates. Like OP says, a simple YYYYMMDD string is better.
Temporal is very well designed thanks to building on years of prior art. Give it a try and you'll realize that it leads you to ask those questions the right way.
Seems like everyone has to stub their own toe on this to get it.
Except, there is the cost intrinsic to this discussion, where going back to strings makes sense. That doesn't mean that decision comes with no cost. If you store someone's birthday as "1990-02-05", then need to answer the question "who has birthdays in the next month", that's actually essentially impossible to do in practically any database query engine. You have the problem of both ignoring the year field of that string (ok, so, maybe we just store "02-05" because the year doesn't matter for birthdays, moreso just 'dates of birth', we can denormalize)... but even beyond that, the closest most if not all engines could get is generating an array of every upcoming day in application code (02-05, 02-06, 02-07, etc) then matching on each. Alternatively, just sort on the field (sans year) then filter in application code, but that's also not happening in the database, which has negatives, and moreover, this would suddenly start failing across year boundaries, but you can special-case that by running a second query starting at 01-01 if the first one didn't return enough results...
There's no easy solution. Time is just really hard. Its good to have as many tools as possible in your belt, but with that you have to know when to reach for one versus another. There isn't one solution to any of these problems.
You'll have to do a CHECK on the YYYY-MM-DD string anyway to protect your database from bad data, so why not just do it on the native type?
The native type is likely to be more compact, performant, consistent across the database (with strings, some columns may be YYYY-MM-DD, some may be YYYY.MM.DD etc.) and more readily convertible to the client's type system.
Moment.js is deprecated: "Moment.js is a legacy project, now in maintenance mode. In most cases, you should choose a different library."
_"We now generally consider Moment to be a legacy project in maintenance mode. It is not dead, but it is indeed done."_
> We recognize that many existing projects may continue to use Moment, but we would like to discourage Moment from being used in new projects going forward.
What a strange thing to say. Baffling.
It's deprecated because programming standards have changed, the team agrees with those standards and nevertheless feels it would be too much work to incorporate them. Not "there's nothing more to add" but "we choose to prioritize stability over new features"
Nevertheless the handling has to be at least on a similar level.
Well shit, that's one of the biggest things I want from a time library. There are about 15 different forms of human readable string formats that various APIs give you in their JSON responses and if they don't solve that, they haven't solved the biggest pain point.
I guess congratulations for not solving the problem and instead making Javascript even more complicated to use by introducing yet another incomplete date library that skirts around perhaps the most important feature and which millions of hippie JS devs will jump on the bandwagon to use.
Also you mention ISO-8601 but this document does not even describe how to input an ISO-8601 string.
I agree with you on the rest though. And I would love to be able to enforce ISO 8601 for text entry of dates.
I literally just spent hours going around a code base fixing this exact problem.
Our back-end is a legacy, 20+ year old database where it was decided to save dates without a timezone.
console.log(new Date("2011-09-24"));
is:
Fri Sep 23 2011 20:00:00 GMT-0400 (Eastern Daylight Time)
These dates were all "thought" to be EST, not UTC. Good times.
More fun examples here:
Why would you ever do this? Save it as a JSON date. coolDate.toJSON() would give a timezoned human parseable date. Just parse it back to the users locale with a built in Date constructor. You can do milliseconds UTC but someone like my younger stupid self would screw it up like most people.
No "which time zone" confusion necessary.
Safari: "2021-06-28T22:59:18.366Z"
Chrome: "2021-06-28T23:01:09.131Z"
Firefox: "2021-06-28T23:00:14.385Z"
The three that I strive to support all utilize this format. YYYY-MM-DDTHH:mm:ss.sssZ. Looking on MDN this seems to call toISOString() which returns a simplified extended ISO format date.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
You ignored the officially named format and instead settled on the officially named format. Those are ISO8601 timestamps, specified in UTC time. (The Z at the end means there's (Z)ero timezone offset from UTC). If you're going to talk about a standard, its worth knowing its name.
Each of them fall on two separate dates, depending on time zone.
A timestamp is the number of seconds or milliseconds or units like 1624938650 seconds from epoch UTC. What I posted above are human parseable datetimes in ISO 8601. Each of them is a point in time that depending on the locale and tz fall on two separate dates but the most important aspect is that it doesn't matter whether I'm using them in my DB or on a user's machine in a different locale and tz. That's the point.
I've settled with storing Zulu time and in a separate field the timezone offset of the event in seconds.
However, I hate working with dates because many APIs make question about dates a slog.
Consider the following for the poor people on the dark side of the planet (UTC + something). If any application only saves the date and not the time and you have dates like 2021-06-28T00:00:00 or something like that.
Now it gets saved in a DB as 2021-06-27Z23:00:00, because the user lives in UTC+1-land. It is correct but makes my blood boil and propably causes a lot of errors, even in apps that set their time zone correctly. How often have I seen implementations that just /(.*)T.*/ match the date and create a bug.
I have no solution for this, but I wasted too much time on problems like this. Such errors also notoriously evade unit tests...
This seems decent from a quick look. But let us be honest, it has to compete with moment.js, not with Date().