Template:UI/datetime

From Miniscope
Jump to: navigation, search

Render a UTC SMW datetime in the wiki's local timezone with the TZ abbreviation, wrapped in an HTML5 <time> element so a small Common.js gadget can upgrade the rendered text to the visitor's MediaWiki timezone/date preferences client-side (see "Client-side enhancement" below).

Single source of truth for date/time display across the schema. Anywhere SMW projects ?Has start date#ISO et al, this template owns the rendering. Direct Error: Invalid time. in row/card templates displays the raw UTC value (events entered as "10 AM" in the editor's local timezone get stored as UTC and surface as wrong-time-in-wrong-zone), which is the bug this template exists to fix.

Server-side: wiki's $wgLocaltimezone via #timel

We use Error: Invalid time., which:

  1. Parses the iso with an explicit Z suffix so PHP's
 strtotime anchors it as UTC (no ambiguity about server TZ for input).
  1. Formats in the wiki's configured $wgLocaltimezone rather
 than UTC. So 17:00:00Z renders as 10:00 AM
 on a wiki configured for America/Los_Angeles (PDT), or
 19:00 on a wiki configured for Europe/Paris, etc.
  1. Returns the right TZ abbreviation via Error: Invalid time.
 (e.g. "PDT", "PST", "CEST", "UTC"), DST handled automatically by PHP
 using the OS tzdata.

This replaces an earlier hand-rolled US-DST formula and a manual unix- shift. The earlier comment in Template:Category/Workshop itinerary/local-time warning against Error: Invalid time. still applies — server-TZ-dependent formatting is the bug; here we explicitly opt into the wiki's local timezone via #timel, which is the intent.

Wiki admin requirement: set $wgLocaltimezone in LocalSettings.php to the wiki's canonical timezone (e.g. 'America/Los_Angeles' for the Miniscope public wiki). If left at the MediaWiki default (UTC), the server fallback will render in UTC — visitors with the gadget installed still get their preference, but anonymous/no-JS visitors will see UTC times.

Client-side enhancement (MediaWiki user preferences)

Each rendered datetime is wrapped in <time datetime="<iso>Z" class="wiki-localtime" data-format="<format>"><server fallback></time>. A small Common.js snippet (see #Common.js gadget below) finds these elements at page-load and rewrites textContent based on the viewer's preferences, in this priority order:

  1. Logged-in MediaWiki user with a timezone set in Special:Preferences →
 use that timezone (parsed from mw.user.options.get('timecorrection'),
 which encodes the pref as "ZoneInfo|<offset>|<Zone/Name>" or
 "Offset|<minutes>" or "System|<minutes>").
  1. Otherwise → browser timezone via Intl.DateTimeFormat
 default (no timeZone option set).

Date format honors mw.user.options.get('date') when set (e.g. mdy, dmy, ymd, ISO 8601); otherwise falls back to the browser locale's default.

When JS is disabled or the gadget isn't installed, the server fallback (wiki TZ) stands.

Common.js gadget

Paste into MediaWiki:Common.js on the deployed wiki:

// Upgrade SchemaSync server-rendered timestamps to the visitor's
// preferences. Targets <time class="wiki-localtime"> written by
// Template:UI/datetime. Honors mw.user.options for timezone and date
// format; falls back to the browser locale.
$(function () {
    // Resolve the visitor's preferred IANA TZ name. mw.user.options has
    // 'timecorrection' as one of three encodings:
    //   "ZoneInfo|<minutes>|<Zone/Name>"  → use the zone name
    //   "Offset|<minutes>"                → fixed offset (rare; we can't
    //                                       pass arbitrary offsets to
    //                                       Intl.DateTimeFormat, so fall
    //                                       through to browser default)
    //   "System|<minutes>"                → wiki default; fall through
    function resolveTimeZone() {
        var tc = mw.user.options.get('timecorrection');
        if (!tc) return undefined;
        var parts = tc.split('|');
        if (parts[0] === 'ZoneInfo' && parts[2]) return parts[2];
        return undefined;
    }
    // Resolve date format prefs to Intl options. MediaWiki's 'date'
    // pref is a slug ('mdy', 'dmy', 'ymd', 'ISO 8601', 'default') —
    // map the common ones; let the browser locale handle 'default'.
    function applyDatePref(opts, pref) {
        switch (pref) {
            case 'mdy':
                return $.extend(opts, { month: 'short', day: 'numeric', year: 'numeric' });
            case 'dmy':
                return $.extend(opts, { day: 'numeric', month: 'short', year: 'numeric' });
            case 'ymd':
            case 'ISO 8601':
                return $.extend(opts, { year: 'numeric', month: '2-digit', day: '2-digit' });
            default:
                return opts;
        }
    }
    var tz = resolveTimeZone();
    var datePref = mw.user.options.get('date');
    document.querySelectorAll('time.wiki-localtime').forEach(function (el) {
        var iso = el.getAttribute('datetime');
        if (!iso) return;
        var d = new Date(iso);
        if (isNaN(d.getTime())) return;
        var fmt = el.getAttribute('data-format') || 'long_datetime';
        var opts;
        switch (fmt) {
            case 'time':
                opts = { hour: 'numeric', minute: '2-digit', timeZoneName: 'short' };
                break;
            case 'long_datetime':
            default:
                opts = applyDatePref(
                    { month: 'short', day: 'numeric', year: 'numeric',
                      hour: 'numeric', minute: '2-digit', timeZoneName: 'short' },
                    datePref
                );
        }
        if (tz) opts.timeZone = tz;
        try {
            el.textContent = d.toLocaleString([], opts);
            el.title = d.toLocaleString([], $.extend({ dateStyle: 'full', timeStyle: 'long' }, tz ? { timeZone: tz } : {}));
        } catch (e) { /* leave server fallback */ }
    });
});

Date-only formats deliberately skip TZ conversion

For month_day, month_day_year, and month_year, this template emits plain text with no <time> wrapper. Rationale: an all-day event stored as 2026-07-12T00:00:00 means "July 12 in the event's local sense" — converting midnight UTC into any non-UTC zone would roll the date back to July 11, which is wrong. Workshop and release dates fall into this bucket. The gadget therefore doesn't touch date-only renders, and the server output is correct as-is.

Parameters

  • iso — UTC ISO datetime from SMW (e.g.
 2026-07-12T17:00:00), no Z suffix. Required; empty
 renders nothing.
  • format — one of:
    • time — e.g. "10:00 AM PDT"
    • long_datetime — e.g. "Jul 12, 2026 · 10:00 AM PDT"
  (default)
    • month_day — "Jul 12" (no TZ shift, no wrapper)
    • month_day_year — "Jul 12, 2026" (no TZ shift, no
  wrapper)
    • month_year — "July 2026" (no TZ shift, no wrapper)