Problem/Motivation
Currently, the only function that exists in Drupal Core to format date intervals is DateFormatter::formatInterval(). The input to this function is a number of seconds.
This is fine for small intervals, up to a couple of weeks. However, if the interval goes over 30 days, it is not possible to say how many "months" is really being represented, even though the function will return a formatted string containing "months" and "years" -- so it is not accurate.
The reason is that a "month" is not a fixed number of seconds -- for example, it's 30 days between April 10 and May 10, but 31 between May 10 and June 10, but both of those should be formatted as "1 month". Similarly, a "year" is not a fixed number of seconds, because some years are 365 days and some are 366.
Another problem is that the formatInterval() method currently defines a "month" as 30 days and a year as 365 days, which means that there are not 12 months exactly in a year. This leads to "interesting" sequences, such as #2430529: "Member for" date format has a funny way of describing 11 years, and the strange jumps between 11 months, 12 months, and 1 year that you can see around this page in Tracker: https://www.drupal.org/user/124982/track?page=101 (if you don't see it there, try going to a page or two older until you get to the 1 year mark).
Proposed resolution
The only way to make an accurate formatting of a time interval that is longer than a month or so is to know what the start and end date/times are for the interval, so that you can tell whether, for instance, a given number of seconds is the difference between April 10 and May 11 (1 month 1 day) or May 10 and May 11 (1 month) -- these are the same number of seconds, but the output should be different.
The proposed fix is to make a new function that has inputs for the start and end as date/times, rather than a single input of the number of seconds in the difference between them. Specifically, the proposal is to introduce DateFormatter::formatDiff(), which should be used when the start and end date of the interval are known (as opposed to cases where you just know the difference). The function can use the DateTime and DateInterval class provided by PHP to do the work of formatting.
Most (but not all) uses of the formatInterval() function in Core are really representing a certain date/time as "x time ago" or "x time hence" (comparing now and a given date), so most of them can use this new function. However, there are a few uses in Core that are using formatInterval() that do not have two dates that are being compared. For these cases, thin wrappers for "time ago" and "time hence", using the current Requests REQUEST_TIME as the second date, can be used to make the cases explicit. There are discussions on #445414: format_interval doesn't format monthly intervals correctly about whether that function needs to be improved to fix the "12 months is not a year" problem.
Remaining tasks
- Add the new function.
- Add unit test coverage for the new function.
- Replace all occurrences in core where the interval is the difference between two dates, to use the new function.
- Leave occurrences in core where the interval is not a difference between two dates alone - for instance when choosing how often to run cron or something like that and formatInterval() is used to present a list of options, it cannot be changed to the new formatDiff() function.
- Replace instances with the thin wrappers when the second date is implicitly the current Request's REQUEST_TIME.
- Expand test coverage to cover the thin wrappers
- If needed, add web test coverage for the changes in core. However, existing uses of formatItnterval() already have tests; they do not cover cases where the date intervals are greater than 30 days, but since we will have unit test coverage for the new function, that should be OK.
- Review
User interface changes
Formatted "x time ago" and "x time hence" dates should be more accurate. They are currently inaccurate if they are more than 30 days ago/hence.
API changes
A new function DateFormatter::formatDiff() is introduced, which can be used to calculate and format a datetime interval correctly. No changes to the existing API, because there are several places in Core where only an interval (not a start/end date) is provided (like formatting options for choosing how often to run Cron), and you cannot use the new function for those cases, so we need the old function still. Two think wrappers, DateFormatter::formatTimeSince() and DateFormatter::formatTimeUntil(), are also added when the second date is implicitly the time of the current request.
Beta phase evaluation
Reference: https://www.drupal.org/core/beta-changesIssue category | Bug because date formatting is not accurate as-is for "time ago" formatting more than a month in the past. |
---|
Issue priority | Normal: the current implementation is wrong since it doesn't take a start and end date into account, it sometimes returns unexpected results, and core uses this in several places. Not really major though since it only affects time intervals greater than a month, which is a lot of them (when it's used for comment times etc.) but it's not that crucial to get them exactly right and there aren't many cases where they're really pathological. |
---|
Prioritized changes | Prioritized: The main goal of this issue is fixing a bug in the formatting of date intervals when a start and end date are known and the interval is greater than a month. |
---|
Disruption | None. New API functions are added. Old one remains. New ones are used in Core where appropriate; old one is still needed for a few cases so it is retained. |
---|