I had an AI agent analyze the current-date / current-time semantics of the Lua modules from T416616#11711690.
I'm including my AGENTS.md file below.
Here's the report:
mw.date Compatibility Audit — Summary Report
Scope
64 unique Lua modules from 10 non-English Wikipedias (ar, fa, he, hi, ja, ko, ru, th, vi, zh), analyzed for current-date/time access patterns and compatibility with future cache-friendly date-comparison primitives.
High-Level Results
| Classification | Count | Description |
|---|---|---|
| direct-fit | 20 | Directly expressible by a future primitive |
| fit-with-semantic-validation | 25 | Probably expressible, needs edge-case verification |
| not-a-fit-timestamp | 8 | Requires exact timestamps or sub-day precision |
| not-a-fit-formatting | 2 | Current-time used only for formatted output |
| no-current-time-access | 7 | No current-time access found |
| unclear | 2 | Fetch failed or insufficient data |
| Total | 64 | |
45 of 64 modules (70%) could benefit from cache-friendly date-comparison primitives, either directly or with semantic validation.
Findings by Module Family
1. Age Modules (en:Module:Age ports)
Modules: hi:Age, th:Age, vi:Age, zh:Age
Classification: direct-fit
Current-time access: Date('currentdate'), Date('currentdatetime') via Module:Date
These are all direct ports of en:Module:Age. They compute:
- Age in full elapsed years from birth date to today
- Age in years, months, days (ymd format)
- Future-death-date validation (death date must not be >1 day in future)
- Time intervals (for timeInterval function, uses currentdatetime)
Candidate primitives:
- diffFromToday(birthDate, "years") — age in full elapsed years
- diffFromToday(birthDate, "ymd") — age as {years, months, days}
- isAfterToday(deathDate) — death date future check
Semantic risks:
- Calendar diff semantics are delegated to Module:Date; must verify PHP DateTime::diff produces identical results
- Leap-day birthday behavior (Feb 29 birth → age change date in non-leap years)
- Partial date handling (year-only, year-month-only inputs)
- MAX_AGE=130 validation
Transition point: Midnight UTC on the next date boundary that changes any age computation.
2. Time Ago Modules (en:Module:Time ago ports)
Modules: fa:Time_ago, ko:Time_ago, vi:Time_ago, th:TimeAgo, zh:TimeAgo
Classification: not-a-fit-timestamp
Current-time access: lang:formatDate('xnU') or lang:formatDate('U') (Unix timestamp)
These compute relative time display ("3 years ago", "5 months ago") using second-level timestamp arithmetic. The newer ports (fa, ko, vi) also use Date('currentdate') / Date('currentdatetime') from Module:Date for calendar-aware diffs at ≥10 day magnitudes.
Not a candidate for date-comparison primitives because:
- Second-level precision required for the magnitude-selection algorithm
- Result changes every second (inherently cache-unfriendly)
- Multiple output units (seconds, minutes, hours, days, weeks, months, years)
Variants:
- Newer (fa, ko, vi): Module:Date integration for month/year magnitudes
- Older (th, zh): Pure timestamp arithmetic, month = 2,678,400s (31 days)
- zh has a bug: future suffix is an HTML comment <!-- time-->
3. Citation/CS1 Modules
Modules: 13 CS1 forks + 3 thin wrappers (ar, fa×2, he, hi, ja×4, ko, ru, th, vi×2, zh)
Classification: fit-with-semantic-validation
Current-time access: Delegated to Module:Citation/CS1/Date_validation
None of the CS1 main modules directly access the current date. All delegate to a shared Date_validation submodule containing exactly two current-time accesses:
- is_valid_accessdate: lang_object.formatDate('U', 'today + 2 days') — Rejects access dates more than 2 days in the future. Day-level UTC check. Candidate: isBeforeToday(accessDate, grace="2 days")
- is_valid_year: os.date('%Y') — Rejects citation years more than 1 year in the future (2 years for PMC embargo). Year-level check. Candidate: isYearNotAfter(year, offset=1)
Additional: ja:Citation/Show_date has its own formatDate('Y') for the same year-range check.
Semantic risks:
- The 48-hour grace window in is_valid_accessdate must be preserved
- Year boundary: result changes Jan 1 each year
4. Webarchive Modules
Modules: ar, fa, he, hi, ko, th, vi, zh
Classification: direct-fit
Current-time access: os.date('%Y'), os.date('*t'), os.time()
All are forks of en:Module:Webarchive. Two variants:
Variant A (older — ar, zh): Year-only check via os.date('%Y') in decodeWebciteDate and decodeWaybackDate. Rejects archive year > current year. Transition: January 1.
Variant B (newer — fa, he, hi, ko, th, vi): Dedicated is_valid_date() with two-level validation: (1) year ≤ current year, (2) full date at midnight ≤ current timestamp. Transition: next UTC midnight.
Candidate primitive: !isAfterToday(archiveDate)
Semantic risks: Minimal. Pure "is this date in the future?" check with no calendar arithmetic.
5. Wikidata/Wd Modules (nl:Module:Wd forks)
Modules: fa:Wd, ja:Wd, ko:Wd, th:Wd, vi:Wd, zh:Wd
Classification: direct-fit
Current-time access: os.date('!%Y-%m-%d') parsed to {year,month,day}
All are forks of nl:Module:Wd. They filter Wikidata claims by temporal validity: a claim is "current" if today falls within its P580 (start time) → P582 (end time) range. Comparison via datePrecedesDate() on {year,month,day} tuples.
Candidate primitives:
- isTodayBefore(date) — for P580 (start time) filtering
- isTodayAfter(date) — for P582 (end time) filtering
Semantic risks: Minimal. UTC date-level only. Overridable via date= parameter. Transition at UTC midnight.
6. Russian Wikidata Module (independent implementation)
Module: ru:Wikidata
Classification: fit-with-semantic-validation
Current-time access: os.time() (second-level, possibly local timezone)
Independent implementation (not a nl:Module:Wd fork). Uses DEFAULT_BOUNDARIES = {os.time()*1000, os.time()*1000} as a point-in-time filter for Wikidata entity label lookup.
Candidate primitive: compareToToday(date) for range-containment
Semantic risks:
- Second-level granularity forces very short cache TTL
- Potential UTC/local timezone mismatch
- Would benefit most from migration to day-level UTC primitives
7. Protection Banner Modules
Modules: ar, fa, hi, ja
Classification: fit-with-semantic-validation (ar, fa, ja); direct-fit (hi)
Current-time access: os.time() or os.date('*t')
All are ports of en:Module:Protection banner. They check whether page protection has expired by comparing the expiry timestamp to the current time.
hi-wikipedia variant is the most interesting: It references Phabricator T416616 and explicitly structures the comparison hierarchically
(year → month → day) to avoid sub-day checks unless the expiry is today. This is the closest existing code to the intended cache-friendly design.
Candidate primitive: isTodayAfter(expiryDate)
Semantic risks:
- Current code uses second precision; day-granularity primitive would delay transition to next midnight
- Informational only (tracking categories), not security-critical
8. Duration / Calendar Modules
ar:وحدة:مدة (Duration)
Classification: fit-with-semantic-validation Port of fr:Module:Durée. Computes elapsed years/months/days via handcrafted calendar subtraction with borrow logic. Uses content-language timezone.
Candidate: diffFromToday(startDate, "ymd", timezone="content")
Risk: Month-end borrow logic may differ from PHP DateTime::diff.
fa:Iranian_calendar
Classification: fit-with-semantic-validation
Computes age in full Jalali (Solar Hijri) calendar years. Delegates to Module:Iranian calendar/library for current Jalali date.
Candidate: diffFromToday(birthDate, "years", calendar="jalali")
Risk: Requires non-Gregorian calendar support in PHP implementation.
he:טווח זמנים (Time Ranges)
Classification: fit-with-semantic-validation
Formats date ranges with optional duration. Current-time access only for
ongoing events. Semantics delegated to Module:תאריך.
Candidate: diffFromToday(startDate, "auto")
9. Other Modules with Current-Time Access
| Module | Classification | Usage |
|---|---|---|
| hi:दिन गणना | fit-with-semantic-validation | Festival countdown in days with IST timezone |
| he:תבנית מידע/אישיות | fit-with-semantic-validation | Person infobox age calculation via helper |
| ru:Надстрочное предупреждение | fit-with-semantic-validation | Age-in-days threshold for categories |
| ar:الرئيسية | not-a-fit-timestamp | Main page: RNG seed + daily rotation + date lookup |
| ja:Scheduled | not-a-fit-timestamp | Daily content rotation (Julian Day) |
| vi:RandomContentImprovementItems | not-a-fit-timestamp | RNG seed only |
| th:Historical_populations | not-a-fit-formatting | Current year for census link formatting |
| zh:電視收視率曲線圖 | not-a-fit-formatting | Current month/year in citation-needed tag |
10. Modules with No Current-Time Access
| Module | Notes |
|---|---|
| ar:قيمة ويكي بيانات | Wikidata value formatter |
| ar:Cite_Q | Citation from Wikidata |
| fa:Portal | Portal image lookup |
| vi:Portal | Portal image lookup |
| vi:Portal_bar | Portal navigation bar |
| ru:Родственные проекты | Sister project links |
| ru:Сезон сериала | TV series season navigation |
Shared Dependencies
Several modules delegate date logic to shared helper modules. These helpers are the true source of date semantics:
| Helper Module | Used By | Analyzed? |
|---|---|---|
| en:Module:Date | Age ports, Time_ago ports, ru:Надстрочное предупреждение | Extract at extracts/en-wikipedia/Date.md (if created) |
| en:Module:Citation/CS1/Date_validation | All CS1 forks | Extract at extracts/en-wikipedia/Citation_CS1_Date_validation.md |
| Module:Iranian calendar/library | fa:Iranian_calendar | Not fetched (Jalali calendar internals) |
| he:Module:תאריך | he:טווח זמנים, he:תבנית מידע/אישיות | Not fetched (Hebrew date module) |
| he:Module:גיל לערכי אישים | he:תבנית מידע/אישיות | Not fetched (Hebrew age calculation) |
Primitive Requirements Summary
Based on the audit, a future mw.date API should support these primitives:
- Core comparison primitives (covers 21 direct-fit modules)
- isTodayBefore(date) — returns true if today < date
- isTodayAfter(date) — returns true if today > date
- isTodayEqualTo(date) — returns true if today == date
- Diff primitives (covers most fit-with-semantic-validation modules)
- diffFromToday(date, "years") — full elapsed years
- diffFromToday(date, "months") — full elapsed months
- diffFromToday(date, "days") — total elapsed days
- diffFromToday(date, "ymd") — structured {years, months, days}
- Required semantics
- Timezone: UTC by default; option for content-language timezone
- Precision: Date-level (no sub-day); day boundaries at midnight
- Partial dates: Support year-only and year-month inputs
- Sign convention: Positive for past dates, negative for future
- Grace windows: CS1 needs a 2-day grace for access-date validation
- Calendar-diff semantics: Must match en:Module:Date behavior for leap-day birthdays and month-end rollover
- Out of scope for comparison primitives
- Second-level timestamp arithmetic (Time_ago modules)
- Formatted date output for display
- RNG seeding / daily rotation indices
- Non-Gregorian calendars (Jalali — would need separate support)
Cross-Wiki Port Analysis
Most modules in this audit are ports of English Wikipedia originals:
| English Original | Wikis Using Port | Translation Drift |
|---|---|---|
| en:Module:Age | hi, th, vi, zh | zh slightly trimmed; others faithful |
| en:Module:Time ago | fa, ko, vi (newer); th, zh (older) | th/zh lack Module:Date path; zh has HTML comment bug |
| en:Module:Citation/CS1 | ar, fa×2, he, hi, ja×3, ko, ru, th, vi, zh | All delegate to Date_validation |
| en:Module:Webarchive | ar, zh (older); fa, he, hi, ko, th, vi (newer) | Older variant lacks full-date validation |
| nl:Module:Wd | fa, ja, ko, th, vi, zh | All faithful forks |
| en:Module:Protection banner | ar, fa, hi, ja | hi variant has T416616 cache-aware structure |
Key Recommendations
- Start with isTodayBefore / isTodayAfter: These two primitives alone would cover 21 modules (Webarchive, Wd, Age future-date checks, Protection banner expiry). They are the simplest to implement and have the clearest semantics.
- Add diffFromToday(date, unit): This covers the Age modules (the most impactful use case) and several unique duration/countdown modules. Calendar diff semantics must be validated against en:Module:Date behavior.
- Validate against en:Module:Date: The Age and Time_ago module families all depend on Module:Date for calendar-diff semantics. A single audit of that module's leap-day, month-end, and partial-date behavior would validate (or invalidate) PHP compatibility for 9+ modules.
- Preserve CS1 grace window: The Citation/CS1 Date_validation module uses a 48-hour grace window for access dates. Any replacement primitive needs to support this.
- Consider timezone parameter: Most modules use UTC, but ar:مدة and hi:दिन गणना use content-language or IST timezone. A timezone parameter (defaulting to UTC) would cover these cases.
- Jalali calendar is a stretch goal: fa:Iranian_calendar needs Solar Hijri support, which is likely out of scope for initial implementation but worth noting for completeness.