Overview
Currently, formatting date properties in HyperTemplates requires external JavaScript or manual string manipulation. This proposal introduces the ht-datetimeformat attribute, enabling high-performance, build-time date formatting using the industry-standard Unicode LDML (Locale Data Markup Language) microsyntax.
Unicode LDML is chosen because it:
- is an internationally recognized standard
- is natively supported in Swift with good support in JavaScript
- supports a wide range of formatting options that other systems lack
Specification
Target Element
The ht-datetimeformat attribute is strictly availble only on the <time> element. This reinforces semantic HTML standards and ensures that date-related features are only used where they are accessibility-compliant.
Behavior
- Source Discovery: The engine looks for a date value via the
datetime attribute on a <time> tag. Note that order of operations requires that ht-attrs="datetime:..." is evaluated FIRST so that the datetime attribute is populated before the ht-datetimeformat attribute is evaluated.
- Standard Compliance: The engine assumes an RFC3339 input string (e.g.,
2026-04-12T14:00:00Z).
- Format Application: The
innerHTML of the <time> tag is replaced at build-time using the LDML pattern provided.
Technical Standard: Unicode LDML Reference
To ensure 100% accuracy and cross-platform consistency with Swift and JavaScript, we will implement the following LDML subset. (See the end of this proposal for a more complete description of LDML date formatting options).
1. Date Components
| Component |
Token |
Example |
Notes |
| Year |
yyyy |
2026 |
4-digit year |
|
yy |
26 |
2-digit truncated year |
| Month |
MMMM |
April |
Full name |
|
MMM |
Apr |
3-letter abbreviation |
|
MM |
04 |
2-digit (zero-padded) |
|
M |
4 |
Numeric (no padding) |
| Day |
dd |
12 |
2-digit (zero-padded) |
|
d |
12 |
Numeric (no padding) |
| Weekday |
EEEE |
Sunday |
Full name |
|
EEE |
Sun |
3-letter abbreviation |
2. Time & Timezone Components
| Component |
Token |
Example |
Notes |
| Hour |
HH |
14 |
24-hour (00-23) |
|
hh |
02 |
12-hour (01-12) |
| Minute |
mm |
30 |
2-digit (zero-padded) |
| Second |
ss |
05 |
2-digit (zero-padded) |
| Period |
a |
PM |
AM/PM marker |
| Timezone |
ZZZZZ |
-07:00 |
ISO 8601 with colon |
|
Z |
-0700 |
ISO 8601 without colon |
Implementation Strategy (Go)
Go’s time package uses a unique "reference time" layout (2006-01-02 15:04:05). The HyperTemplates build engine will map LDML tokens to this reference system.
Full Go Mapping Table
NOTE: The implementation must replace longer tokens first to avoid partial matching (e.g., MMMM before MM).
| LDML Token |
Go Reference |
Translation |
yyyy |
2006 |
Year (4-digit) |
yy |
06 |
Year (2-digit) |
MMMM |
January |
Month (Full) |
MMM |
Jan |
Month (Abbr) |
MM |
01 |
Month (Pad) |
M |
1 |
Month (No pad) |
dd |
02 |
Day (Pad) |
d |
2 |
Day (No pad) |
EEEE |
Monday |
Weekday (Full) |
EEE |
Mon |
Weekday (Abbr) |
HH |
15 |
24-Hour (Pad) |
hh |
03 |
12-Hour (Pad) |
h |
3 |
12-Hour (No pad) |
mm |
04 |
Minute (Pad) |
ss |
05 |
Second (Pad) |
a |
PM |
AM/PM |
ZZZZZ |
Z07:00 |
Timezone (Extended) |
Example Go Translation Function
package main
import (
"fmt"
"strings"
"time"
)
// FormatDateTime takes an RFC3339 date string and an LDML format,
// and returns the human-readable formatted string.
func FormatDateTime(isoDate, ldmlFormat string) (string, error) {
// 1. Parse the input RFC3339 string
t, err := time.Parse(time.RFC3339, isoDate)
if err != nil {
// Fallback for dates without timezone/seconds if strictly needed
t, err = time.Parse("2006-01-02T15:04:05", isoDate)
if err != nil {
return "", err
}
}
// 2. Translate LDML tokens to Go reference layout
// Important: Replace longer tokens first to prevent partial matching.
replacer := strings.NewReplacer(
"ZZZZZ", "Z07:00",
"yyyy", "2006",
"MMMM", "January",
"EEEE", "Monday",
"MMM", "Jan",
"EEE", "Mon",
"SSS", ".000",
"yy", "06",
"MM", "01",
"dd", "02",
"HH", "15",
"hh", "03",
"mm", "04",
"ss", "05",
"y", "2006",
"M", "1",
"d", "2",
"H", "15",
"h", "3",
"a", "PM",
"Z", "-0700",
)
goLayout := replacer.Replace(ldmlFormat)
// 3. Output the formatted string
return t.Format(goLayout), nil
}
func main() {
// Example usage
formatted, _ := FormatDateTime("2026-04-12T14:30:00Z", "dd MMMM yyyy 'at' hh:mm a")
fmt.Println(formatted) // Output: 12 April 2026 at 02:30 PM
}
Unicode LDML (more complete explanation)
LDML is notoriously dense because it tries to cover every calendar system on Earth. For a web-targeting templating system, you only need the Gregorian subset. Even within that, the range of capabilities is large. Here's the full range of LDML date formatting, of which the above recommendation for HyperTemplates is is only a subset of:
1. The Year (y)
| Token |
Meaning |
Example (2024) |
Notes |
y |
Numeric |
2024 |
Minimum digits. |
yy |
2-digit |
24 |
Forced padding/truncation. |
yyyy |
4-digit |
2024 |
Standard ISO/W3C format. |
2. The Month (M or L)
Use M for standard dates. (Use L only if your language requires a different spelling for months when they stand alone).
| Token |
Meaning |
Example |
Notes |
M |
Numeric |
9 |
No leading zero. |
MM |
2-digit |
09 |
Standard W3C/ISO. |
MMM |
Abbreviated |
Sep |
Short name. |
MMMM |
Wide |
September |
Full name. |
MMMMM |
Narrow |
S |
First letter only (may not be unique). |
3. The Day (d)
| Token |
Meaning |
Example |
Notes |
d |
Numeric |
2 |
No leading zero. |
dd |
2-digit |
02 |
Standard W3C/ISO. |
4. The Weekday (E or e)
| Token |
Meaning |
Example |
Notes |
E, EE, EEE |
Abbreviated |
Tue |
Standard short name. |
EEEE |
Wide |
Tuesday |
Full name. |
EEEEE |
Narrow |
T |
Single letter. |
e |
Numeric |
3 |
Local day of week (1-7). |
5. The Hour (H vs h)
Crucial: H is 24-hour (0-23), h is 12-hour (1-12).
| Token |
Meaning |
Example |
Notes |
H |
24-hr |
13 |
No leading zero. |
HH |
24-hr (2-digit) |
13 |
Standard W3C/ISO. |
h |
12-hr |
1 |
Used with AM/PM. |
hh |
12-hr (2-digit) |
01 |
Used with AM/PM. |
6. Minutes and Seconds (m, s, S)
| Token |
Meaning |
Example |
Notes |
m |
Minute |
5 |
No leading zero. |
mm |
Minute (2-digit) |
05 |
Standard W3C/ISO. |
s |
Second |
9 |
No leading zero. |
ss |
Second (2-digit) |
09 |
Standard W3C/ISO. |
SSS |
Fraction |
456 |
Milliseconds (use 3 S). |
7. The Period (a)
| Token |
Meaning |
Example |
Notes |
a |
AM/PM |
PM |
Use with h or hh. |
8. Timezone (z or Z or X)
| Token |
Meaning |
Example |
Notes |
z |
Specific |
PST |
Short name. |
zzzz |
Specific |
Pacific Standard Time |
Full name. |
Z |
ISO 8601 |
-0800 |
Basic format. |
XXXXX |
ISO 8601 |
-08:00 |
Standard W3C/ISO (includes colon). |
x |
ISO 8601 |
-08 |
Hours only if zero minutes. |
Additional Implementation Rules:
- Escaping: If you want to include literal text in your template (like the word "at"), wrap it in single quotes:
yyyy-MM-dd 'at' HH:mm.
- Case Sensitivity:
M is Month, m is Minute. D is "Day of Year" (1-365), whereas d is "Day of Month" (1-31). Always use lowercase d for standard numerical dates.
- The ISO "T": In LDML, the
T separator is a literal. You should represent it as 'T' not a space .
Overview
Currently, formatting date properties in HyperTemplates requires external JavaScript or manual string manipulation. This proposal introduces the
ht-datetimeformatattribute, enabling high-performance, build-time date formatting using the industry-standard Unicode LDML (Locale Data Markup Language) microsyntax.Unicode LDML is chosen because it:
Specification
Target Element
The
ht-datetimeformatattribute is strictly availble only on the<time>element. This reinforces semantic HTML standards and ensures that date-related features are only used where they are accessibility-compliant.Behavior
datetimeattribute on a<time>tag. Note that order of operations requires thatht-attrs="datetime:..."is evaluated FIRST so that thedatetimeattribute is populated before theht-datetimeformatattribute is evaluated.2026-04-12T14:00:00Z).innerHTMLof the<time>tag is replaced at build-time using the LDML pattern provided.Technical Standard: Unicode LDML Reference
To ensure 100% accuracy and cross-platform consistency with Swift and JavaScript, we will implement the following LDML subset. (See the end of this proposal for a more complete description of LDML date formatting options).
1. Date Components
yyyy2026yy26MMMMAprilMMMAprMM04M4dd12d12EEEESundayEEESun2. Time & Timezone Components
HH14hh02mm30ss05aPMZZZZZ-07:00Z-0700Implementation Strategy (Go)
Go’s
timepackage uses a unique "reference time" layout (2006-01-02 15:04:05). The HyperTemplates build engine will map LDML tokens to this reference system.Full Go Mapping Table
NOTE: The implementation must replace longer tokens first to avoid partial matching (e.g.,
MMMMbeforeMM).yyyy2006yy06MMMMJanuaryMMMJanMM01M1dd02d2EEEEMondayEEEMonHH15hh03h3mm04ss05aPMZZZZZZ07:00Example Go Translation Function
Unicode LDML (more complete explanation)
LDML is notoriously dense because it tries to cover every calendar system on Earth. For a web-targeting templating system, you only need the Gregorian subset. Even within that, the range of capabilities is large. Here's the full range of LDML date formatting, of which the above recommendation for HyperTemplates is is only a subset of:
1. The Year (
y)y2024yy24yyyy20242. The Month (
MorL)Use
Mfor standard dates. (UseLonly if your language requires a different spelling for months when they stand alone).M9MM09MMMSepMMMMSeptemberMMMMMS3. The Day (
d)d2dd024. The Weekday (
Eore)E,EE,EEETueEEEETuesdayEEEEETe35. The Hour (
Hvsh)Crucial:
His 24-hour (0-23),his 12-hour (1-12).H13HH13h1hh016. Minutes and Seconds (
m,s,S)m5mm05s9ss09SSS456S).7. The Period (
a)aPMhorhh.8. Timezone (
zorZorX)zPSTzzzzPacific Standard TimeZ-0800XXXXX-08:00x-08Additional Implementation Rules:
yyyy-MM-dd 'at' HH:mm.Mis Month,mis Minute.Dis "Day of Year" (1-365), whereasdis "Day of Month" (1-31). Always use lowercasedfor standard numerical dates.Tseparator is a literal. You should represent it as'T'not a space.