Skip to content

Resolve relative report dates in the site's timezone - #25256

Draft
mneudert wants to merge 3 commits into
6.x-devfrom
relative-date-site-timezone
Draft

Resolve relative report dates in the site's timezone#25256
mneudert wants to merge 3 commits into
6.x-devfrom
relative-date-site-timezone

Conversation

@mneudert

@mneudert mneudert commented Sep 9, 2026

Copy link
Copy Markdown
Member

Relative dates such as yesterday, previous7 and 2026-09-01,today resolved partly in UTC rather than in the site's timezone, so a report could be labelled with a day it did not cover, and a relative range could be counted over one. Three separate things caused it, and a site whose local day differs from UTC's is exposed to all three.

  • The date label ignored the site timezone. ProcessedReport::getProcessedReport() built the period behind prettyDate with Period\Factory::build($period, $date) and no timezone, while the rows that label describes come from the archive, which resolves the date in the site's timezone. On a UTC+12 site, date=yesterday counted the site's yesterday and named UTC's for half of every day.
  • A relative range was anchored on UTC's day. Period\Factory::makePeriodFromQueryParams() passed Date::factory('today', $timezone) as the range's default end date, and Date::factory() shifts the timestamp but hands back a UTC-labelled Date, so previous7 and last7 measured back from UTC's today. It now uses Date::factoryInTimezone(), the way the non-range branch of the same method already did.
  • A relative range endpoint was recognised by shape rather than by keyword. Range::generate() and Range::getRelativeToEndDate() treated an endpoint as relative only when it contained no hyphen, so last-week read as an absolute date and resolved a day away from what last week resolved to. Both now match the same keyword pattern their callers use, and a relative range start resolves in the timezone too.

Range::getRelativeToEndDate() takes its endpoint straight from the request, so it normalises before matching. Date::factoryInTimezone() now trims, url-decodes and lowercases its argument, which lets it accept every spelling the case-insensitive patterns that dispatch to it accept. Previously TODAY, last%20week and a padded today missed the check and fell through to Date::factory(), resolving silently in UTC and a day off.

prettyDate is now read from the period metadata of the table the report actually returned, and built from the request parameters only when the table carries none. idSite does not decide the archive's scope for every module: MultiSites.getAll selects over every site the user can view, and the archive applies a site timezone only when the request resolves to a single site, so the label follows the period that was resolved instead of the one that was requested. A DataTable\Map spanning several periods is labelled with a range from the earliest start to the latest end.

Where this shows up

  • The whole reporting UI, for anyone whose default report date is a relative range. "Report to load by default" in personal settings offers Last 7 days, Last 30 days, Previous 7 days and Previous 30 days, and UsersManager\Controller::getDefaultDates() maps all four onto period=range. Such a user loads every page as period=range&date=last30, which is exactly the request the anchoring fix changes.
  • Emailed and downloaded reports. ScheduledReports\API::generateReport() takes prettyDate straight from API.getProcessedReport and uses it for the "Date range" line on the front page of the HTML and PDF renderers and for the download filename. The scheduled cron run passes an absolute date and is unaffected; the magic keywords generateReport documents (today, yesterday, lastWeek, ...) are the affected input.
  • The All Websites report. ScheduledReports requests MultiSites.getAll for it, which is the module that selects its own site set rather than the requested one, so it is the practical case for reading the label off the resolved period.
  • Evolution graphs, sparklines, and the graphs embedded in emailed reports. JqplotGraph\Evolution, Sparklines\Config, ImageGraph and Controller::getGraphParamsModified() all resolve their window through Range::getRelativeToEndDate(), so they are what the keyword-versus-hyphen fix reaches. last-week, last-month and last-year are the spellings that were wrong; today and yesterday already worked, since they carry no hyphen for the old check to trip over.

Notes

  • The range anchoring fix changes report data and not only labels. ArchiveQueryFactory::getPeriodInfoFromQueryParam() resolves the periods it queries the archive for through the same makePeriodFromQueryParams(), and Period::isMultiplePeriod() returns false whenever the period is range, so period=range&date=previous7 takes the branch that changed. On a site that is not on UTC's day it now covers a different window and reads a different archive. The new integration test asserts the visit count as well as the label for that request, so a window anchored a day early fails on the count.
  • No signatures change. Date::factoryInTimezone() widens what it accepts rather than narrowing it, and the keywords it now takes are the ones its callers already matched before dispatching.
  • getProcessedReport() keys its result on the period label, so in a multi-site request every site's table collides on one key and all but the last are dropped. That is pre-existing and out of scope here; the new test asserts only that the surviving table holds data, with a note on what to assert once the keying is fixed.

Checklist

  • [✔] I have understood, reviewed, and tested all AI outputs before use
  • [✔] All AI instructions respect security, IP, and privacy rules

Review

@mneudert mneudert added this to the 6.0.0 milestone Sep 9, 2026
@mneudert mneudert self-assigned this Sep 9, 2026
@mneudert mneudert added the Bug For errors / faults / flaws / inconsistencies etc. label Sep 9, 2026
@mneudert
mneudert force-pushed the relative-date-site-timezone branch from 99747f2 to f675f0a Compare September 9, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug For errors / faults / flaws / inconsistencies etc.

Development

Successfully merging this pull request may close these issues.

1 participant