diff --git a/README.md b/README.md
index 59ff70c..b60e744 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,8 @@ So, a relative date phrase is used for up to a month and then the actual date is
| `month` | `month` | `'numeric'\|'2-digit'\|'short'\|'long'\|'narrow'\|undefined` | *** |
| `year` | `year` | `'numeric'\|'2-digit'\|undefined` | **** |
| `timeZoneName` | `time-zone-name` | `'long'\|'short'\|'shortOffset'\|'longOffset'` `\|'shortGeneric'\|'longGeneric'\|undefined` | `undefined` |
-| `timeZone` | `time-zone` | `string\|undefined` | Browser default time zone |
+| `timeZone` | `time-zone` | `string\|undefined` | Browser default time zone |
+| `hourCycle` | `hour-cycle` | `'h11'\|'h12'\|'h23'\|'h24'\|undefined` | 'h12' or 'h23' based on browser |
| `noTitle` | `no-title` | `-` | `-` |
*: If unspecified, `formatStyle` will return `'narrow'` if `format` is `'elapsed'` or `'micro'`, `'short'` if the format is `'relative'` or `'datetime'`, otherwise it will be `'long'`.
diff --git a/examples/index.html b/examples/index.html
index 886af5c..b190940 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -29,6 +29,20 @@
Format DateTime
+
+ h12 cycle:
+
+ Jan 1 1970
+
+
+
+
+ h23 cycle:
+
+ Jan 1 1970
+
+
+
Customised options:
diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts
index 353655b..3ea3fb5 100644
--- a/src/relative-time-element.ts
+++ b/src/relative-time-element.ts
@@ -32,6 +32,18 @@ function getUnitFactor(el: RelativeTimeElement): number {
return 60 * 60 * 1000
}
+// Determine whether the user has a 12 (vs. 24) hour cycle preference. This relies on the hour formatting in
+// a 12 hour preference being formatted like "1 AM" including a space, while with a 24 hour preference, the
+// same is formatted as "01" without a space. In the future `Intl.Locale.prototype.getHourCycles()` could be
+// used but it is not as well-supported as this method.
+function isBrowser12hCycle() {
+ return Boolean(/\s/.exec(new Intl.DateTimeFormat([], {hour: 'numeric'}).format(new Date(0))))
+}
+
+function isHour12(hourCycle: Intl.DateTimeFormatOptions['hourCycle']) {
+ return hourCycle === 'h11' || hourCycle === 'h12'
+}
+
const dateObserver = new (class {
elements: Set = new Set()
time = Infinity
@@ -98,6 +110,14 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
return tz || undefined
}
+ get hourCycle() {
+ // Prefer attribute, then closest, then document
+ const hc =
+ this.closest('[hour-cycle]')?.getAttribute('hour-cycle') ||
+ this.ownerDocument.documentElement.getAttribute('hour-cycle')
+ return (hc || (isBrowser12hCycle() ? 'h12' : 'h23')) as Intl.DateTimeFormatOptions['hourCycle']
+ }
+
#renderRoot: Node = this.shadowRoot ? this.shadowRoot : this.attachShadow ? this.attachShadow({mode: 'open'}) : this
static get observedAttributes() {
@@ -139,6 +159,7 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
minute: '2-digit',
timeZoneName: 'short',
timeZone: this.timeZone,
+ hour12: isHour12(this.hourCycle),
}).format(date)
}
@@ -213,6 +234,7 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
year: this.year,
timeZoneName: this.timeZoneName,
timeZone: this.timeZone,
+ hour12: isHour12(this.hourCycle),
})
return `${this.prefix} ${formatter.format(date)}`.trim()
}
@@ -226,6 +248,7 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
minute: '2-digit',
timeZoneName: 'short',
timeZone: this.timeZone,
+ hour12: isHour12(this.hourCycle),
}).format(date)
}