Last time I broke down the default Drupal date field to see what we can learn about how best to handle time. Originally that was going to be the end of the series. However, the writing process dug up even more questions, and circumstances found me putting my new knowledge to the test. So here we are, part three of my two part series on wrestling time in Drupal.
The previous post posed the question: “What’s the best way to compare date values within this framework?” Well one of you must have made a sacrifice to the irony gods, because between the last post and now I unexpectedly had to build a day view calendar which forced me to answer this question with practice and not just theory.
The answer is simply, turn everything into a DrupalDateTime object before calculating. While working on the calendar I quickly learned to always transform my date values into DrupalDateTime before actually using them. That way I had access to all of its useful data-manipulating methods (documented in the previous post). Calculations like iterating on a day or extracting a specific value became an absolute breeze.
BEYOND THE BASIC FIELD
The previous post also asked: How do other date-reliant elements of Drupal use (Drupal’s Datetime) system? The answer is pretty much the same as the last one. Drupal turns most time representation into DrupalDateTime Objects before processing. Drupal has a crucial middle layer of classes found in Drupal/Core/Datetime that all form the backbone of all its date functionality, and they all regularly convert data into DrupalDateTime objects so that further work can be done on them. This includes things like changing dates to different visual formats with the `DateFormatter.php` class, storing important variables like month names in `DateHelper.php`, and even converting unix timestamps into and out of ISO format.
WHY NOT UNIX TIME?
The final question of the previous post was: If unix time is so important for modern computing, why doesn’t Drupal’s core date field use it? The answer is: It's very complicated. This problem has been endlessly debated in the issue queue and is worth understanding. It is important to note that Drupal does indeed have a Unix timestamp field, however it is usually reserved for node creation time and other base fields that the end user is unlikely to interact with (though there is an option to use it as a regular field). Let’s discuss some of the key elements of the debate.
The Drupal community originally decided to use ISO strings for several reasons. First off, they are easily portable between different database types. Also, unlike timestamps, they are directly readable in the codebase without any further processing. Finally, they make it easy to break certain values off programmatically. For example, I can easily extract the month value in `2022-08-30T10:28:00` to know that the month is August.
As any developer who has worked on datetime functionality can attest, ISO can be problematic in its own right and some might prefer to use timestamp. Since they’re integers and not strings, timestamps query much faster in the database. This issue is especially pronounced when you’re using a view that's querying hundreds of dates. As mentioned in the first post, timestamps are also much easier to compare and sort chronologically. Finally, timestamps are by definition stored in UTC, whereas ISO strings can theoretically be in any timezone. If you want to further explore using timestamps for fields, I recommend checking out the Smart Date module.
So both approaches have their pros and cons, but there are a few more elements to consider. Most of Drupal’s other systems are built assuming dates are stored in ISO, which makes using timestamps a bit more arduous. Also, timestamps have a looming problem in the form of the 2038 bug. The problem is that many data structures aren’t capable of storing the amount of integers to represent dates past January 19th 2038. Drupal 10 will address this problem by changing database timestamp fields from into to bigint (source).
The choice of storage type might matter less with the projects like the Date Augmenter API which is a middle layer that helps date data interact regardless of its base storage type.
LESSONS FROM HELL
My journey researching this topic and building an actual calendar has been eye-opening about the challenges of dates in Drupal and what we as developers can do to make them more manageable. I was able to get my calendar working, but it was a challenge because the underlying data structure didn’t fully support the intended functionality, which led to some awkwardness in the codebase. Also, because of the locked-in data structure, some desirable features will be very difficult to add without an accompanying migration. Ultimately the experience has led me to conclude that, events cannot be treated like any other content type, and their time component should be thoroughly examined and prototyped at the beginning of a project to make sure they can accomplish all they need to.
With this in mind, ask yourself the following questions to help define your project’s needs:
- Do we need to compare dates to each other?
- Do we need recurring events?
- Do we need date ranges?
- Do we need all day events?
- How do date ranges/recurring events/all day events interact with each other?
- Do we need a calendar display?
- How will editors interact with dates?
- Are we going to need Views integrations?
- Are we going to need to expand the date functionality in the future?
Once you have a really clear idea of your parameters, do some prototyping and look into the contrib space. Don’t be afraid to push and experiment. A lot of work has gone into giving Drupal editors a much more robust system than you might think. The following modules are all worth considering in addition to those already mentioned:
CONCLUSION (FOR REAL THIS TIME)
Originally this was going to be a single blog post about how I found and fixed a nasty time bug, and here we are three posts later. This topic has an endless supply of complexity worth discussing, and special thanks to Mandclu who graciously took the time to help me understand and encapsulate the issue. While there is doubtlessly more to say, I have to end somewhere. Hopefully through this series you and your team have some useful insight and documentation that will make your next calendar-building experience less painful.