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

ClassificationCountDescription
direct-fit20Directly expressible by a future primitive
fit-with-semantic-validation25Probably expressible, needs edge-case verification
not-a-fit-timestamp8Requires exact timestamps or sub-day precision
not-a-fit-formatting2Current-time used only for formatted output
no-current-time-access7No current-time access found
unclear2Fetch failed or insufficient data
Total64

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:

  1. 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")
  1. 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

ModuleClassificationUsage
hi:दिन गणनाfit-with-semantic-validationFestival countdown in days with IST timezone
he:תבנית מידע/אישיותfit-with-semantic-validationPerson infobox age calculation via helper
ru:Надстрочное предупреждениеfit-with-semantic-validationAge-in-days threshold for categories
ar:الرئيسيةnot-a-fit-timestampMain page: RNG seed + daily rotation + date lookup
ja:Schedulednot-a-fit-timestampDaily content rotation (Julian Day)
vi:RandomContentImprovementItemsnot-a-fit-timestampRNG seed only
th:Historical_populationsnot-a-fit-formattingCurrent year for census link formatting
zh:電視收視率曲線圖not-a-fit-formattingCurrent month/year in citation-needed tag

10. Modules with No Current-Time Access

ModuleNotes
ar:قيمة ويكي بياناتWikidata value formatter
ar:Cite_QCitation from Wikidata
fa:PortalPortal image lookup
vi:PortalPortal image lookup
vi:Portal_barPortal 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 ModuleUsed ByAnalyzed?
en:Module:DateAge ports, Time_ago ports, ru:Надстрочное предупреждениеExtract at extracts/en-wikipedia/Date.md (if created)
en:Module:Citation/CS1/Date_validationAll CS1 forksExtract at extracts/en-wikipedia/Citation_CS1_Date_validation.md
Module:Iranian calendar/libraryfa:Iranian_calendarNot 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:

    1. 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
    1. 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}
    1. 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
    1. 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 OriginalWikis Using PortTranslation Drift
en:Module:Agehi, th, vi, zhzh slightly trimmed; others faithful
en:Module:Time agofa, ko, vi (newer); th, zh (older)th/zh lack Module:Date path; zh has HTML comment bug
en:Module:Citation/CS1ar, fa×2, he, hi, ja×3, ko, ru, th, vi, zhAll delegate to Date_validation
en:Module:Webarchivear, zh (older); fa, he, hi, ko, th, vi (newer)Older variant lacks full-date validation
nl:Module:Wdfa, ja, ko, th, vi, zhAll faithful forks
en:Module:Protection bannerar, fa, hi, jahi variant has T416616 cache-aware structure

Key Recommendations

  1. 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.
  1. 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.
  1. 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.
  1. 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.
  1. 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.
  1. 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.