Skip to content

Portal Dashboard Showed The Wrong Time

Ed Mozley edited this page Aug 25, 2026 · 1 revision

The portal dashboard showed the wrong time

On the self-service portal, the dashboard and the ticket list disagreed about the same ticket. One of them was wrong β€” sometimes by an hour, sometimes by several, and sometimes by nothing at all, depending on the time of year and where the person was sitting.

Found while building the date and time formats work in issue #105.

Fixed in d44735e8, released as update #1195.

This is a separate fault from the one that was reported. #105 was about how dates were written; this one was about which moment they referred to.


1. What you saw

Open the portal dashboard and note the time against a ticket. Click through to My tickets and look at the same ticket. The two could show different times.

Whether they disagreed, and by how much, depended on two things nobody would think to connect:

Where the person was What the dashboard did
UK in winter (GMT) Correct, by coincidence
UK in summer (BST) One hour early
New York in August Four hours late
Sydney Ten hours early

A bug that is invisible for five months of the year in the country the software was written in, and then quietly appears at the end of March, is a bug that survives a long time.

The tell is that the dashboard and the ticket list format exactly the same expression:

// self-service/index.php β€” dashboard
const date = formatDate(t.updated_datetime || t.created_datetime);

// self-service/tickets.php β€” ticket list
shortDate(t.updated_datetime || t.created_datetime)

Same field, same fallback, two different answers. That rules out the data and points squarely at the two formatters.


2. The mechanism

FreeITSM stores every datetime in UTC. A value arrives at the browser as a plain string with no zone marker on it:

2026-08-05 13:07:00

That string does not say what it is. It is the reader who has to know it means UTC.

The ticket list knew:

// self-service/tickets.php β€” correct
const d = new Date(String(s).replace(' ', 'T') + 'Z');

The dashboard did not:

// self-service/index.php β€” the bug
const d = new Date(dateStr);

Without the Z, a browser treats the string as local time. So 13:07 UTC was read as 13:07 in whatever zone the reader happened to be in, and then displayed as 13:07. The number never changed, which is precisely why it looks fine: nothing is obviously mangled, the time is simply the wrong one.

In London in August that is one hour early. In New York it is four hours late β€” a ticket updated at nine in the morning appears to have been updated at one in the afternoon.

Why it read as correct in testing

Two coincidences protected it.

UTC and UK winter time are the same thing. From late October to late March, 13:07 local and 13:07 UTC really are the same moment, so the bug produces the right answer for nearly half the year.

The number always looks plausible. A wrong-instant bug does not produce Invalid Date or a nonsensical year. It produces a real time, correctly formatted, that happens to be the wrong one. There is nothing for the eye to catch.

A second reason not to write this

new Date('2026-08-05 13:07:00') β€” with a space rather than a T β€” is outside what the JavaScript specification requires browsers to parse. Engines accept it by convention and are free to disagree about it. So the line was relying on unspecified behaviour as well as getting the zone wrong.


3. It was the whole portal, not one line

Fixing the missing Z would have addressed the symptom and left the cause.

The internal application has shared date helpers: assets/js/tz.js, and a Tz::scriptTag() that publishes the analyst's timezone to the browser. Every analyst page uses them, which is why no analyst page had this problem.

The portal loaded neither. It had no timezone plumbing whatsoever. Each portal page had rolled its own date handling inline, and two of the three happened to get it right. That is not a codebase where the third one being wrong is surprising β€” it is a codebase where it was only a matter of which one.

So the fix was not to add a Z. It was to give the portal the same helpers everything else uses.


4. The fix

The portal has a single shared header, so it took one wiring point to cover every portal page:

// self-service/includes/header.php
require_once __DIR__ . '/../../includes/timezone.php';
Tz::init();
    <?php echo Tz::scriptTag(); ?>
    <script src="../assets/js/tz.js?v=4"></script>

All three portal date formatters then became calls to the shared family, which marks the value as UTC on the way in:

// self-service/index.php β€” after
function formatDate(dateStr) {
    if (!dateStr) return '';
    try {
        return fmtDateTime(dateStr);
    } catch (e) {
        return dateStr;
    }
}

One more thing was corrected while it was open. The ticket list decided whether to show a time or a date by asking whether the value fell on today:

// before β€” the BROWSER's idea of today
const sameDay = d.toDateString() === today.toDateString();

// after β€” the same day the time is being SHOWN in
const sameDay = ymdInZone(d) === ymdInZone(today);

Bucketing on one day while displaying a time from another is how a timestamp ends up labelled "yesterday" beside a time from today.


5. πŸ“ The files involved

✏️ Fixed

File What changed
self-service/includes/header.php Loads includes/timezone.php, calls Tz::init(), emits Tz::scriptTag() and tz.js. One place, every portal page.
self-service/index.php formatDate β€” the actual wrong-instant bug.
self-service/tickets.php shortDate / fullDate onto the shared helpers; Today bucketing moved onto the display zone.
self-service/help-centre.php formatDate onto the shared helpers.

πŸ—„οΈ Already correct

File Why it was fine
self-service/tickets.php, help-centre.php Both appended Z, so both showed the right instant. They were wrong only about the format, which is #105.

6. How it was verified

All three portal pages were loaded for real with a portal session and checked to be publishing a timezone and a format β€” the plumbing being present is what makes the fix hold rather than the individual lines being right today.

Portal users are not analysts and have no personal preferences, so they follow the install-wide default. That was proved rather than assumed: the system setting was changed to DD.MM.YYYY with a 12-hour clock, the portal was reloaded and confirmed to have followed it, then it was set back.


7. What this means for you

If you use the self-service portal, times on the dashboard are now correct, and they agree with the ticket list. If you are in the UK you may notice nothing changed over the winter, because over the winter nothing was wrong.

Portal dates also now follow whatever is chosen under System β†’ Date and time formats.


Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally