@rr0/cms 0.3.79 → 0.3.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,7 +60,7 @@ describe("TimeContext", () => {
60
60
  expect(timeContext.getMinutes()).toBe(55);
61
61
  });
62
62
  test("updateFromStr with a time zone containing a P is a date, not a duration", () => {
63
- for (const str of ["1957-02-27 21:45PST", "1991-09-18 00:38:42PDT", "1957-02-27 21:45 (PST)"]) {
63
+ for (const str of ["1957-02-27 21:45PST", "1991-09-18 00:38:42PDT"]) {
64
64
  const context = new TimeContext();
65
65
  expect(context.updateFromStr(str)).toBe(true);
66
66
  expect(context.duration).toBeUndefined();
@@ -0,0 +1,36 @@
1
+ import { Level2Date, Level2Duration } from "@rr0/time";
2
+ /**
3
+ * The `datetime` attribute of a <time> and the EDTF it comes from.
4
+ *
5
+ * HTML knows its own formats only: a year, month, date, local or global date and time ("2004-06-11T09:12:33.123+01:00"),
6
+ * and durations in days, hours, minutes and seconds ("P1DT2H30M"). EDTF is richer: qualified values ("2004-~06", "~P2H"),
7
+ * a time zone written "+01", years and months in durations. So `datetime` holds what HTML can express, and what it cannot
8
+ * is kept as it was written in `data-edtf`, which is only set when it differs from `datetime`.
9
+ */
10
+ export declare class HtmlDatetime {
11
+ /**
12
+ * Sets the attributes of a <time> element for a time.
13
+ *
14
+ * @param el The element to set the attributes of.
15
+ * @param time The time the element is about.
16
+ */
17
+ static apply(el: HTMLElement, time: Level2Date | Level2Duration): void;
18
+ /**
19
+ * @return The `datetime` value that HTML can read for a time, or undefined if HTML has no equivalent.
20
+ */
21
+ static of(time: Level2Date | Level2Duration): string | undefined;
22
+ protected static ofDate(date: Level2Date): string | undefined;
23
+ protected static ofDuration(duration: Level2Duration): string | undefined;
24
+ /**
25
+ * @return The numeric value of a component, if it has one (not a range of values, as for unspecified digits) within bounds.
26
+ */
27
+ protected static value(comp: {
28
+ value: unknown;
29
+ } | undefined, min: number, max?: number): number | undefined;
30
+ protected static pad(n: number): string;
31
+ /**
32
+ * @param minutes The time zone offset, in minutes east of UTC.
33
+ * @return The time zone offset as HTML writes it ("Z", "+01:00").
34
+ */
35
+ protected static offset(minutes: number): string;
36
+ }
@@ -0,0 +1,102 @@
1
+ import { Level2Duration } from "@rr0/time";
2
+ /**
3
+ * The `datetime` attribute of a <time> and the EDTF it comes from.
4
+ *
5
+ * HTML knows its own formats only: a year, month, date, local or global date and time ("2004-06-11T09:12:33.123+01:00"),
6
+ * and durations in days, hours, minutes and seconds ("P1DT2H30M"). EDTF is richer: qualified values ("2004-~06", "~P2H"),
7
+ * a time zone written "+01", years and months in durations. So `datetime` holds what HTML can express, and what it cannot
8
+ * is kept as it was written in `data-edtf`, which is only set when it differs from `datetime`.
9
+ */
10
+ export class HtmlDatetime {
11
+ /**
12
+ * Sets the attributes of a <time> element for a time.
13
+ *
14
+ * @param el The element to set the attributes of.
15
+ * @param time The time the element is about.
16
+ */
17
+ static apply(el, time) {
18
+ const datetime = HtmlDatetime.of(time);
19
+ if (datetime !== undefined) {
20
+ el.dateTime = datetime;
21
+ }
22
+ const edtf = time.toString();
23
+ if (edtf !== datetime) {
24
+ el.dataset.edtf = edtf;
25
+ }
26
+ }
27
+ /**
28
+ * @return The `datetime` value that HTML can read for a time, or undefined if HTML has no equivalent.
29
+ */
30
+ static of(time) {
31
+ return time instanceof Level2Duration ? HtmlDatetime.ofDuration(time) : HtmlDatetime.ofDate(time);
32
+ }
33
+ static ofDate(date) {
34
+ var _a;
35
+ const year = HtmlDatetime.value(date.year, 1);
36
+ if (year === undefined) {
37
+ return undefined;
38
+ }
39
+ let str = String(year).padStart(4, "0");
40
+ const month = HtmlDatetime.value(date.month, 1, 12); // Seasons (21 to 24) are no month
41
+ if (month === undefined) {
42
+ return str;
43
+ }
44
+ str += "-" + HtmlDatetime.pad(month);
45
+ const day = HtmlDatetime.value(date.day, 1, 31);
46
+ if (day === undefined) {
47
+ return str;
48
+ }
49
+ str += "-" + HtmlDatetime.pad(day);
50
+ const hour = HtmlDatetime.value(date.hour, 0, 23);
51
+ const minute = HtmlDatetime.value(date.minute, 0, 59);
52
+ if (hour === undefined || minute === undefined) { // HTML has no time without minutes
53
+ return str;
54
+ }
55
+ str += "T" + HtmlDatetime.pad(hour) + ":" + HtmlDatetime.pad(minute);
56
+ const second = HtmlDatetime.value(date.second, 0, 59);
57
+ if (second !== undefined) {
58
+ str += ":" + HtmlDatetime.pad(second);
59
+ const millisecond = HtmlDatetime.value(date.millisecond, 0, 999); // @rr0/time 1.1.0
60
+ if (millisecond !== undefined) {
61
+ str += "." + String(millisecond).padStart(3, "0");
62
+ }
63
+ }
64
+ const timeshift = (_a = date.timeshift) === null || _a === void 0 ? void 0 : _a.value;
65
+ if (typeof timeshift === "number" && Number.isInteger(timeshift)) {
66
+ str += HtmlDatetime.offset(timeshift);
67
+ }
68
+ return str;
69
+ }
70
+ static ofDuration(duration) {
71
+ const spec = duration.toSpec();
72
+ if (spec.years || spec.months) { // HTML durations have no years nor months, whose length depends on the date
73
+ return undefined;
74
+ }
75
+ const numbers = [spec.days, spec.hours, spec.minutes, spec.seconds].map(comp => HtmlDatetime.value(comp, 0));
76
+ const [days, hours, minutes, seconds] = numbers;
77
+ const time = (hours ? hours + "H" : "") + (minutes ? minutes + "M" : "") + (seconds ? seconds + "S" : "");
78
+ const str = (days ? days + "D" : "") + (time ? "T" + time : "");
79
+ return str ? "P" + str : undefined;
80
+ }
81
+ /**
82
+ * @return The numeric value of a component, if it has one (not a range of values, as for unspecified digits) within bounds.
83
+ */
84
+ static value(comp, min, max = Number.MAX_SAFE_INTEGER) {
85
+ const value = comp === null || comp === void 0 ? void 0 : comp.value;
86
+ return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max ? value : undefined;
87
+ }
88
+ static pad(n) {
89
+ return String(n).padStart(2, "0");
90
+ }
91
+ /**
92
+ * @param minutes The time zone offset, in minutes east of UTC.
93
+ * @return The time zone offset as HTML writes it ("Z", "+01:00").
94
+ */
95
+ static offset(minutes) {
96
+ if (minutes === 0) {
97
+ return "Z";
98
+ }
99
+ const abs = Math.abs(minutes);
100
+ return (minutes < 0 ? "-" : "+") + HtmlDatetime.pad(Math.floor(abs / 60)) + ":" + HtmlDatetime.pad(abs % 60);
101
+ }
102
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,80 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { Level2Date, Level2Duration } from "@rr0/time";
3
+ import { HtmlDatetime } from "./HtmlDatetime.js";
4
+ import { cmsTestUtil } from "../../test/CMSTestUtil.js";
5
+ describe("HtmlDatetime", () => {
6
+ describe("date", () => {
7
+ test.each([
8
+ ["2004", "2004"],
9
+ ["0033", "0033"],
10
+ ["2004-06", "2004-06"],
11
+ ["2004-06-11", "2004-06-11"],
12
+ ["2004-06-11T09:12", "2004-06-11T09:12"],
13
+ ["2004-06-11T09:12:33", "2004-06-11T09:12:33"],
14
+ ["2004-06-11 09:12", "2004-06-11T09:12"],
15
+ ["2004-06-11T09:12:33Z", "2004-06-11T09:12:33Z"],
16
+ ["2004-06-11T09:12:33+01", "2004-06-11T09:12:33+01:00"],
17
+ ["2004-06-11T09:12-05", "2004-06-11T09:12-05:00"],
18
+ ["1947-06-24 21:45PST", "1947-06-24T21:45-08:00"]
19
+ ])("%s is %s", (edtf, expected) => {
20
+ expect(HtmlDatetime.of(Level2Date.fromString(edtf))).toBe(expected);
21
+ });
22
+ test("qualification is not part of the HTML value", () => {
23
+ expect(HtmlDatetime.of(Level2Date.fromString("2004-~06"))).toBe("2004-06");
24
+ expect(HtmlDatetime.of(Level2Date.fromString("2004-06~"))).toBe("2004-06");
25
+ expect(HtmlDatetime.of(Level2Date.fromString("?2004-06-11"))).toBe("2004-06-11");
26
+ });
27
+ test("an hour without minutes has no HTML equivalent, the date remains", () => {
28
+ expect(HtmlDatetime.of(new Level2Date({ year: 2004, month: 6, day: 11, hour: 9 }))).toBe("2004-06-11");
29
+ });
30
+ test("a season is no month", () => {
31
+ expect(HtmlDatetime.of(Level2Date.fromString("2004-21"))).toBe("2004");
32
+ });
33
+ test("a year before 1 has no HTML equivalent", () => {
34
+ expect(HtmlDatetime.of(Level2Date.fromString("-0033"))).toBeUndefined();
35
+ });
36
+ });
37
+ describe("duration", () => {
38
+ test.each([
39
+ ["P2D", "P2D"],
40
+ ["P20H", "PT20H"],
41
+ ["P90S", "PT1M30S"],
42
+ ["P2D10H23M45S", "P2DT10H23M45S"],
43
+ ["PT30M", "PT30M"]
44
+ ])("%s is %s", (edtf, expected) => {
45
+ expect(HtmlDatetime.of(Level2Duration.fromString(edtf))).toBe(expected);
46
+ });
47
+ test("months are written MM in the former notation, and have no HTML equivalent", () => {
48
+ expect(HtmlDatetime.of(Level2Duration.fromString("P2MM"))).toBeUndefined();
49
+ });
50
+ test("years have no HTML equivalent", () => {
51
+ expect(HtmlDatetime.of(Level2Duration.fromString("P1Y"))).toBeUndefined();
52
+ });
53
+ test("qualification is not part of the HTML value", () => {
54
+ expect(HtmlDatetime.of(Level2Duration.fromString("~P20H"))).toBe("PT20H");
55
+ });
56
+ });
57
+ describe("element", () => {
58
+ const newTimeEl = () => cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", "").file.document.createElement("time");
59
+ test("data-edtf is not set when HTML reads the same value", () => {
60
+ const timeEl = newTimeEl();
61
+ HtmlDatetime.apply(timeEl, Level2Date.fromString("2004-06-11"));
62
+ expect(timeEl.getAttribute("datetime")).toBe("2004-06-11");
63
+ expect(timeEl.hasAttribute("data-edtf")).toBe(false);
64
+ });
65
+ test("data-edtf holds the EDTF when HTML reads it differently", () => {
66
+ const timeEl = newTimeEl();
67
+ const date = Level2Date.fromString("2004-06-11T09:12+01");
68
+ HtmlDatetime.apply(timeEl, date);
69
+ expect(timeEl.getAttribute("datetime")).toBe("2004-06-11T09:12+01:00");
70
+ expect(timeEl.getAttribute("data-edtf")).toBe(date.toString());
71
+ });
72
+ test("data-edtf holds the EDTF when HTML has no equivalent, and then datetime is not set", () => {
73
+ const timeEl = newTimeEl();
74
+ const duration = Level2Duration.fromString("P2MM");
75
+ HtmlDatetime.apply(timeEl, duration);
76
+ expect(timeEl.hasAttribute("datetime")).toBe(false);
77
+ expect(timeEl.getAttribute("data-edtf")).toBe(duration.toString());
78
+ });
79
+ });
80
+ });
@@ -77,7 +77,6 @@ export class TimeElementFactory {
77
77
  const duration = context.time.duration;
78
78
  const items = [];
79
79
  const messages = context.messages.context.time.duration;
80
- const datetime = duration.toString();
81
80
  const spec = duration.toSpec();
82
81
  const days = spec.days;
83
82
  if (days) {
@@ -105,7 +104,7 @@ export class TimeElementFactory {
105
104
  if (context.time.approximate) {
106
105
  replacementStr = messages.approximate(replacementStr);
107
106
  }
108
- replacement = TimeReplacer.resolvedTime(context, datetime);
107
+ replacement = TimeReplacer.resolvedTime(context, duration);
109
108
  replacement.classList.add("duration");
110
109
  replacement.textContent = replacementStr;
111
110
  }
@@ -28,7 +28,7 @@ export class TimeRenderer {
28
28
  const currentFileName = file.name;
29
29
  const doc = file.document;
30
30
  let replacement;
31
- const timeEl = TimeReplacer.resolvedTime(context, date.toString());
31
+ const timeEl = TimeReplacer.resolvedTime(context, date);
32
32
  if (title !== text) {
33
33
  timeEl.title = title;
34
34
  }
@@ -1,5 +1,6 @@
1
1
  import { HtmlRR0Context } from "../../RR0Context.js";
2
2
  import { DomReplacement } from "../DomReplacement.js";
3
+ import { Level2Date, Level2Duration } from "@rr0/time";
3
4
  import { TimeElementFactory } from "./TimeElementFactory.js";
4
5
  import { TimeRenderOptions } from "./TimeRenderer.js";
5
6
  import { TimeStringCompleter } from "./TimeStringCompleter.js";
@@ -15,7 +16,11 @@ export declare class TimeReplacer implements DomReplacement<HtmlRR0Context, HTML
15
16
  static readonly inlineTags: string[];
16
17
  protected readonly completer: TimeStringCompleter;
17
18
  constructor(factory: TimeElementFactory);
18
- static resolvedTime(context: HtmlRR0Context, dateTime: string): HTMLTimeElement;
19
+ /**
20
+ * @param context The context to create the element in.
21
+ * @param time The time the element is about, which `datetime` and `data-edtf` are set from.
22
+ */
23
+ static resolvedTime(context: HtmlRR0Context, time: Level2Date | Level2Duration): HTMLTimeElement;
19
24
  /**
20
25
  * @return The text that precedes an element in its sentence.
21
26
  */
@@ -1,4 +1,5 @@
1
1
  import { ObjectUtils } from "@rr0/common";
2
+ import { HtmlDatetime } from "./HtmlDatetime.js";
2
3
  import { TimeStringCompleter } from "./TimeStringCompleter.js";
3
4
  /**
4
5
  * Replaces a <time> tag.
@@ -8,9 +9,13 @@ export class TimeReplacer {
8
9
  this.factory = factory;
9
10
  this.completer = new TimeStringCompleter();
10
11
  }
11
- static resolvedTime(context, dateTime) {
12
+ /**
13
+ * @param context The context to create the element in.
14
+ * @param time The time the element is about, which `datetime` and `data-edtf` are set from.
15
+ */
16
+ static resolvedTime(context, time) {
12
17
  const replacement = context.file.document.createElement("time");
13
- replacement.dateTime = dateTime;
18
+ HtmlDatetime.apply(replacement, time);
14
19
  return replacement;
15
20
  }
16
21
  /**
@@ -34,7 +39,7 @@ export class TimeReplacer {
34
39
  }
35
40
  async replacement(pageContext, origEl) {
36
41
  let replacement;
37
- if (origEl.dateTime || origEl.dataset.format === "none") { // Already done, or not to be interpreted
42
+ if (origEl.dateTime || origEl.dataset.edtf || origEl.dataset.format === "none") { // Already done, or not to be interpreted
38
43
  replacement = origEl;
39
44
  }
40
45
  else {
@@ -153,7 +153,7 @@ describe("TimeReplacer", async () => {
153
153
  original.textContent = "2003-12-24T10:22CDT";
154
154
  const replacement = await replacer.replacement(context, original);
155
155
  expect(replacement.outerHTML)
156
- .toBe(`<span class="time-resolved"><a href="${path.join("/", timeOptions.rootDir, "2/0/0/3/12/24/")}"><time datetime="2003-12-24T10:22-05">mercredi 24 décembre 2003 à 10:22</time></a></span>`); // TODO: Text should have timezone info
156
+ .toBe(`<span class="time-resolved"><a href="${path.join("/", timeOptions.rootDir, "2/0/0/3/12/24/")}"><time datetime="2003-12-24T10:22-05:00" data-edtf="2003-12-24T10:22-05">mercredi 24 décembre 2003 à 10:22</time></a></span>`); // TODO: Text should have timezone info
157
157
  expect(context.time.getYear()).toBe(2003);
158
158
  expect(context.time.getMonth()).toBe(12);
159
159
  expect(context.time.getDayOfMonth()).toBe(24);
@@ -262,7 +262,7 @@ describe("TimeReplacer", async () => {
262
262
  const datetime = "P2D10H23M45S";
263
263
  timeEl.textContent = datetime;
264
264
  const replacement = await replacer.replacement(context, timeEl);
265
- expect(replacement.outerHTML).toBe(`<time datetime="${datetime}" class="duration">2 jours, 10 heures, 23 minutes et 45 secondes</time>`);
265
+ expect(replacement.outerHTML).toBe(`<time datetime="P2DT10H23M45S" data-edtf="${datetime}" class="duration">2 jours, 10 heures, 23 minutes et 45 secondes</time>`);
266
266
  });
267
267
  test("with context", async () => {
268
268
  const context = cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", "");
@@ -276,7 +276,7 @@ describe("TimeReplacer", async () => {
276
276
  const element = context.file.document.createElement("time");
277
277
  element.textContent = "P20M";
278
278
  const replacement = await replacer.replacement(context, element);
279
- expect(replacement.outerHTML).toBe(`<time datetime="P20M" class="duration">20 minutes</time>`);
279
+ expect(replacement.outerHTML).toBe(`<time datetime="PT20M" data-edtf="P20M" class="duration">20 minutes</time>`);
280
280
  }
281
281
  });
282
282
  test("with approximation", async () => {
@@ -284,7 +284,7 @@ describe("TimeReplacer", async () => {
284
284
  const original = context.file.document.createElement("time");
285
285
  original.textContent = "P~2H";
286
286
  const replacement = await replacer.replacement(context, original);
287
- expect(replacement.outerHTML).toBe(`<time datetime="P2H" class="duration">environ 2 heures</time>`);
287
+ expect(replacement.outerHTML).toBe(`<time datetime="PT2H" data-edtf="P~2H" class="duration">environ 2 heures</time>`);
288
288
  });
289
289
  });
290
290
  describe("parses hour", () => {
@@ -374,6 +374,20 @@ describe("TimeReplacer", async () => {
374
374
  const context = cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", "");
375
375
  expect(await render(context, "1947-06-24 21:45PST")).toBe("mardi 24 juin 1947 à 21:45");
376
376
  });
377
+ test("a time zone in parentheses, as pages write it, is read as a time zone", async () => {
378
+ var _a, _b;
379
+ for (const str of ["1947-06-24 21:45 (PST)", "1947-06-24 21:45 PST"]) {
380
+ const context = cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", "");
381
+ expect(await render(context, str)).toBe("mardi 24 juin 1947 à 21:45");
382
+ expect((_b = (_a = context.time.date) === null || _a === void 0 ? void 0 : _a.timeshift) === null || _b === void 0 ? void 0 : _b.value, str).toBe(-480);
383
+ }
384
+ });
385
+ test("a sidereal time is not a time zone, and is kept as text", async () => {
386
+ var _a;
387
+ const context = cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", "");
388
+ expect(await render(context, "1967-05-13 15:40 (LST)")).toBe("samedi 13 mai 1967 à 15:40 (LST)");
389
+ expect((_a = context.time.date) === null || _a === void 0 ? void 0 : _a.timeshift).toBeUndefined();
390
+ });
377
391
  test("time inside a link gets no link of its own", async () => {
378
392
  const context = cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", "");
379
393
  const doc = context.file.document;
@@ -20,6 +20,12 @@ export declare class TimeStringCompleter {
20
20
  * A trailing time zone ("PST", "(CDT)", "UTC+1") is part of the value, not free text.
21
21
  */
22
22
  protected static readonly timeZone: RegExp;
23
+ /**
24
+ * @param str A word following a time.
25
+ * @return The time zone it names, as EDTF writes it right after the time ("EST" for "(EST)"), or undefined if it is not a time zone
26
+ * EDTF knows ("(LST)" is a local sidereal time, whose offset is unknown, so it remains free text).
27
+ */
28
+ protected static zone(str: string): string | undefined;
23
29
  /**
24
30
  * A duration ("P10M", "~P1H"), or a range of durations ("P10M/12M").
25
31
  */
@@ -1,3 +1,4 @@
1
+ import { timeZones } from "@rr0/time";
1
2
  import { TimeContext } from "../TimeContext.mjs";
2
3
  /**
3
4
  * Prepares a <time> string for EDTF parsing.
@@ -8,6 +9,16 @@ import { TimeContext } from "../TimeContext.mjs";
8
9
  * first. Free text written around the value ("le soir", "vers") is set apart so that it can be kept as is.
9
10
  */
10
11
  export class TimeStringCompleter {
12
+ /**
13
+ * @param str A word following a time.
14
+ * @return The time zone it names, as EDTF writes it right after the time ("EST" for "(EST)"), or undefined if it is not a time zone
15
+ * EDTF knows ("(LST)" is a local sidereal time, whose offset is unknown, so it remains free text).
16
+ */
17
+ static zone(str) {
18
+ var _a;
19
+ const name = (_a = TimeStringCompleter.timeZone.exec(str)) === null || _a === void 0 ? void 0 : _a[1];
20
+ return name && (/^UTC[+-]?\d*$/.test(name) || timeZones.some(zone => zone.name === name && zone.timeshift !== "?")) ? name : undefined;
21
+ }
11
22
  split(str) {
12
23
  let prefix = "";
13
24
  let value = str.replace(/[ \t\r\n]+/g, " ").trim(); // "1973-10-11\n 21:00/22:00"
@@ -23,9 +34,15 @@ export class TimeStringCompleter {
23
34
  // A bare number followed by a space may be a quantity ("1 h", "30 mn après", "8 jours plus tard"), so a day of the
24
35
  // month needs a T before its text ("18Tla nuit").
25
36
  const quantity = post && /^\d{1,2}$/.test(post[1]) && !post[2].includes("T");
26
- if (post && !quantity && !TimeStringCompleter.timeZone.test(post[3].trim())) {
27
- value = post[1];
28
- suffix = (post[2].includes("T") ? " " : post[2]) + post[3];
37
+ if (post && !quantity) {
38
+ const zone = TimeStringCompleter.zone(post[3].trim());
39
+ if (zone) {
40
+ value = post[1] + zone; // EDTF reads "21:45EST", not "21:45 EST" nor "21:45 (EST)"
41
+ }
42
+ else {
43
+ value = post[1];
44
+ suffix = (post[2].includes("T") ? " " : post[2]) + post[3];
45
+ }
29
46
  }
30
47
  return { prefix, value, suffix };
31
48
  }
@@ -121,7 +138,7 @@ export class TimeStringCompleter {
121
138
  /**
122
139
  * A trailing time zone ("PST", "(CDT)", "UTC+1") is part of the value, not free text.
123
140
  */
124
- TimeStringCompleter.timeZone = /^\(?(?:[A-Z]{1,5}|UTC[+-]?\d*)\)?$/;
141
+ TimeStringCompleter.timeZone = /^\(?([A-Z]{1,5}|UTC[+-]?\d*)\)?$/;
125
142
  /**
126
143
  * A duration ("P10M", "~P1H"), or a range of durations ("P10M/12M").
127
144
  */
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@rr0/cms",
3
3
  "type": "module",
4
4
  "author": "Jérôme Beau <rr0@rr0.org> (https://rr0.org)",
5
- "version": "0.3.79",
5
+ "version": "0.3.81",
6
6
  "description": "RR0 Content Management System (CMS)",
7
7
  "exports": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
@@ -31,10 +31,10 @@
31
31
  "dependencies": {
32
32
  "@javarome/fileutil": "^0.3.9",
33
33
  "@rr0/common": "^1.1.5",
34
- "@rr0/data": "^0.3.46",
34
+ "@rr0/data": "^0.3.48",
35
35
  "@rr0/lang": "^0.1.13",
36
36
  "@rr0/place": "^0.5.4",
37
- "@rr0/time": "^0.11.3",
37
+ "@rr0/time": "^2.0.0",
38
38
  "canvas": "^3.2.1",
39
39
  "csv-parser": "^3.2.0",
40
40
  "glob": "^11.0.3",