@barefootjs/client 0.21.4 → 0.24.1
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.
- package/dist/format-date.d.ts +37 -0
- package/dist/format-date.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +52 -0
- package/dist/runtime/standalone.js +52 -0
- package/package.json +2 -2
- package/src/format-date.ts +99 -0
- package/src/index.ts +1 -0
- package/src/runtime/index.ts +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `formatDate` — format a `Date` (or ISO-8601 string) with an explicit pattern
|
|
3
|
+
* and an explicit UTC offset. The pure-function date formatter of #2324: every
|
|
4
|
+
* input is explicit, so the output is a deterministic function of its
|
|
5
|
+
* arguments — no host locale, no host timezone, no ICU/CLDR data. That is what
|
|
6
|
+
* lets the SSR adapters lower a `formatDate(...)` call to their native
|
|
7
|
+
* `format_date` runtime helper (spec/template-helpers.md) with byte-identical
|
|
8
|
+
* output on every backend.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <time>{formatDate(createdAt, 'YYYY/M/D', '+09:00')}</time>
|
|
12
|
+
* <time>{formatDate(createdAt, 'MMMM D, YYYY', 'UTC', names)}</time>
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Numeric tokens (longest-match; any other character passes through
|
|
16
|
+
* literally): `YYYY` (4-digit zero-padded year, `-`-prefixed for negative
|
|
17
|
+
* years), `MM` / `M` (zero-padded / bare month `01`–`12` / `1`–`12`), `DD` /
|
|
18
|
+
* `D` (zero-padded / bare day of month).
|
|
19
|
+
*
|
|
20
|
+
* Name tokens (#2334) read the explicit `names` table — the caller owns the
|
|
21
|
+
* values (per-locale name selection is the i18n layer's or the compiler's
|
|
22
|
+
* job, never this function's): `MMMM` / `MMM` (wide / abbreviated month
|
|
23
|
+
* name), `dddd` / `ddd` (wide / abbreviated weekday name, Sunday-first).
|
|
24
|
+
* `names` is a flat array in fixed layout: `[0..11]` wide months, `[12..23]`
|
|
25
|
+
* abbreviated months, `[24..30]` wide weekdays, `[31..37]` abbreviated
|
|
26
|
+
* weekdays. A name token indexing a missing/short table renders `''` — the
|
|
27
|
+
* same normative zero-value discipline as an unparseable date, byte-identical
|
|
28
|
+
* on every backend.
|
|
29
|
+
*
|
|
30
|
+
* `timeZone` is `'UTC'` or a fixed offset `±HH:MM` (`'+09:00'`, `'-05:30'`).
|
|
31
|
+
* The function is total: any other value — including IANA zone names, which
|
|
32
|
+
* would drag host tzdata versions into the output — normalizes to `'UTC'`, so
|
|
33
|
+
* every backend degrades identically instead of diverging. An unset or
|
|
34
|
+
* unparseable `date` renders `''`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatDate(date: Date | string, pattern: string, timeZone?: string, names?: readonly string[]): string;
|
|
37
|
+
//# sourceMappingURL=format-date.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-date.d.ts","sourceRoot":"","sources":["../src/format-date.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAeH,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,GAAG,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,SAAQ,EAChB,KAAK,GAAE,SAAS,MAAM,EAAO,GAC5B,MAAM,CA4CR"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { __slot, type SlotMarker } from './slot.ts';
|
|
|
5
5
|
export { forwardProps } from './forward-props.ts';
|
|
6
6
|
export { unwrap } from './unwrap.ts';
|
|
7
7
|
export { queryHref, type QueryParams, type QueryParamValue } from './query-href.ts';
|
|
8
|
+
export { formatDate } from './format-date.ts';
|
|
8
9
|
export { createContext, type Context } from './context.ts';
|
|
9
10
|
export { useContext, provideContext, createPortal, isSSRPortal, findSiblingSlot, cleanupPortalPlaceholder, type Portal, type PortalChildren, type PortalOptions, type Renderable, } from './shims.ts';
|
|
10
11
|
export { Async, Region, type AsyncProps, type RegionProps, } from './builtins.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,eAAe,EACf,SAAS,EACT,OAAO,EACP,gBAAgB,EAMhB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,eAAe,EACf,SAAS,EACT,OAAO,EACP,gBAAgB,EAMhB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EACL,UAAU,EACV,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,wBAAwB,EACxB,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,YAAY,CAAA;AAKnB,OAAO,EACL,KAAK,EACL,MAAM,EACN,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,eAAe,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -162,6 +162,57 @@ function queryHref(base, params) {
|
|
|
162
162
|
const qs = u.toString();
|
|
163
163
|
return qs ? `${base}?${qs}` : base;
|
|
164
164
|
}
|
|
165
|
+
// src/format-date.ts
|
|
166
|
+
var OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/;
|
|
167
|
+
var TOKEN_RE = /YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D/g;
|
|
168
|
+
var MONTHS_WIDE = 0;
|
|
169
|
+
var MONTHS_ABBR = 12;
|
|
170
|
+
var WEEKDAYS_WIDE = 24;
|
|
171
|
+
var WEEKDAYS_ABBR = 31;
|
|
172
|
+
function pad2(n) {
|
|
173
|
+
return String(n).padStart(2, "0");
|
|
174
|
+
}
|
|
175
|
+
function formatDate(date, pattern, timeZone = "UTC", names = []) {
|
|
176
|
+
if (date === null || date === undefined)
|
|
177
|
+
return "";
|
|
178
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
179
|
+
const t = d.getTime();
|
|
180
|
+
if (Number.isNaN(t))
|
|
181
|
+
return "";
|
|
182
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
183
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
184
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
185
|
+
const year = s.getUTCFullYear();
|
|
186
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
187
|
+
const month = s.getUTCMonth() + 1;
|
|
188
|
+
const day = s.getUTCDate();
|
|
189
|
+
const weekday = s.getUTCDay();
|
|
190
|
+
const nameAt = (index) => names[index] ?? "";
|
|
191
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
192
|
+
switch (token) {
|
|
193
|
+
case "YYYY":
|
|
194
|
+
return yyyy;
|
|
195
|
+
case "MMMM":
|
|
196
|
+
return nameAt(MONTHS_WIDE + month - 1);
|
|
197
|
+
case "MMM":
|
|
198
|
+
return nameAt(MONTHS_ABBR + month - 1);
|
|
199
|
+
case "MM":
|
|
200
|
+
return pad2(month);
|
|
201
|
+
case "M":
|
|
202
|
+
return String(month);
|
|
203
|
+
case "DD":
|
|
204
|
+
return pad2(day);
|
|
205
|
+
case "D":
|
|
206
|
+
return String(day);
|
|
207
|
+
case "dddd":
|
|
208
|
+
return nameAt(WEEKDAYS_WIDE + weekday);
|
|
209
|
+
case "ddd":
|
|
210
|
+
return nameAt(WEEKDAYS_ABBR + weekday);
|
|
211
|
+
default:
|
|
212
|
+
return token;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
165
216
|
// src/context.ts
|
|
166
217
|
function createContext(defaultValue) {
|
|
167
218
|
return {
|
|
@@ -216,6 +267,7 @@ export {
|
|
|
216
267
|
onCleanup,
|
|
217
268
|
isSSRPortal,
|
|
218
269
|
forwardProps,
|
|
270
|
+
formatDate,
|
|
219
271
|
findSiblingSlot,
|
|
220
272
|
endTurn,
|
|
221
273
|
createSignal,
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { __slot, type SlotMarker } from '../slot.ts';
|
|
|
5
5
|
export { forwardProps } from '../forward-props.ts';
|
|
6
6
|
export { unwrap } from '../unwrap.ts';
|
|
7
7
|
export { queryHref, type QueryParams, type QueryParamValue } from '../query-href.ts';
|
|
8
|
+
export { formatDate } from '../format-date.ts';
|
|
8
9
|
export { createContext, useContext, provideContext, setCurrentScope, type Context, } from './context.ts';
|
|
9
10
|
export { createPortal, isSSRPortal, findSiblingSlot, cleanupPortalPlaceholder, type Portal, type PortalOptions, type Renderable, type PortalChildren, } from './portal.ts';
|
|
10
11
|
export { reconcileList, type RenderItemFn } from './list.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,EACP,KAAK,EACL,eAAe,EACf,SAAS,EACT,OAAO,EACP,gBAAgB,EAOhB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/index.js
CHANGED
|
@@ -162,6 +162,57 @@ function queryHref(base, params) {
|
|
|
162
162
|
const qs = u.toString();
|
|
163
163
|
return qs ? `${base}?${qs}` : base;
|
|
164
164
|
}
|
|
165
|
+
// src/format-date.ts
|
|
166
|
+
var OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/;
|
|
167
|
+
var TOKEN_RE = /YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D/g;
|
|
168
|
+
var MONTHS_WIDE = 0;
|
|
169
|
+
var MONTHS_ABBR = 12;
|
|
170
|
+
var WEEKDAYS_WIDE = 24;
|
|
171
|
+
var WEEKDAYS_ABBR = 31;
|
|
172
|
+
function pad2(n) {
|
|
173
|
+
return String(n).padStart(2, "0");
|
|
174
|
+
}
|
|
175
|
+
function formatDate(date, pattern, timeZone = "UTC", names = []) {
|
|
176
|
+
if (date === null || date === undefined)
|
|
177
|
+
return "";
|
|
178
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
179
|
+
const t = d.getTime();
|
|
180
|
+
if (Number.isNaN(t))
|
|
181
|
+
return "";
|
|
182
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
183
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
184
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
185
|
+
const year = s.getUTCFullYear();
|
|
186
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
187
|
+
const month = s.getUTCMonth() + 1;
|
|
188
|
+
const day = s.getUTCDate();
|
|
189
|
+
const weekday = s.getUTCDay();
|
|
190
|
+
const nameAt = (index) => names[index] ?? "";
|
|
191
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
192
|
+
switch (token) {
|
|
193
|
+
case "YYYY":
|
|
194
|
+
return yyyy;
|
|
195
|
+
case "MMMM":
|
|
196
|
+
return nameAt(MONTHS_WIDE + month - 1);
|
|
197
|
+
case "MMM":
|
|
198
|
+
return nameAt(MONTHS_ABBR + month - 1);
|
|
199
|
+
case "MM":
|
|
200
|
+
return pad2(month);
|
|
201
|
+
case "M":
|
|
202
|
+
return String(month);
|
|
203
|
+
case "DD":
|
|
204
|
+
return pad2(day);
|
|
205
|
+
case "D":
|
|
206
|
+
return String(day);
|
|
207
|
+
case "dddd":
|
|
208
|
+
return nameAt(WEEKDAYS_WIDE + weekday);
|
|
209
|
+
case "ddd":
|
|
210
|
+
return nameAt(WEEKDAYS_ABBR + weekday);
|
|
211
|
+
default:
|
|
212
|
+
return token;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
165
216
|
// ../shared/src/markers.ts
|
|
166
217
|
var BF_SCOPE = "bf-s";
|
|
167
218
|
var BF_SLOT = "bf";
|
|
@@ -2885,6 +2936,7 @@ export {
|
|
|
2885
2936
|
getComponentProps,
|
|
2886
2937
|
getComponentInit,
|
|
2887
2938
|
forwardProps,
|
|
2939
|
+
formatDate,
|
|
2888
2940
|
flushHydration,
|
|
2889
2941
|
findSiblingSlot,
|
|
2890
2942
|
findScope,
|
|
@@ -815,6 +815,57 @@ function queryHref(base, params) {
|
|
|
815
815
|
const qs = u.toString();
|
|
816
816
|
return qs ? `${base}?${qs}` : base;
|
|
817
817
|
}
|
|
818
|
+
// src/format-date.ts
|
|
819
|
+
var OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/;
|
|
820
|
+
var TOKEN_RE = /YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D/g;
|
|
821
|
+
var MONTHS_WIDE = 0;
|
|
822
|
+
var MONTHS_ABBR = 12;
|
|
823
|
+
var WEEKDAYS_WIDE = 24;
|
|
824
|
+
var WEEKDAYS_ABBR = 31;
|
|
825
|
+
function pad2(n) {
|
|
826
|
+
return String(n).padStart(2, "0");
|
|
827
|
+
}
|
|
828
|
+
function formatDate(date, pattern, timeZone = "UTC", names = []) {
|
|
829
|
+
if (date === null || date === undefined)
|
|
830
|
+
return "";
|
|
831
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
832
|
+
const t = d.getTime();
|
|
833
|
+
if (Number.isNaN(t))
|
|
834
|
+
return "";
|
|
835
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
836
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
837
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
838
|
+
const year = s.getUTCFullYear();
|
|
839
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
840
|
+
const month = s.getUTCMonth() + 1;
|
|
841
|
+
const day = s.getUTCDate();
|
|
842
|
+
const weekday = s.getUTCDay();
|
|
843
|
+
const nameAt = (index) => names[index] ?? "";
|
|
844
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
845
|
+
switch (token) {
|
|
846
|
+
case "YYYY":
|
|
847
|
+
return yyyy;
|
|
848
|
+
case "MMMM":
|
|
849
|
+
return nameAt(MONTHS_WIDE + month - 1);
|
|
850
|
+
case "MMM":
|
|
851
|
+
return nameAt(MONTHS_ABBR + month - 1);
|
|
852
|
+
case "MM":
|
|
853
|
+
return pad2(month);
|
|
854
|
+
case "M":
|
|
855
|
+
return String(month);
|
|
856
|
+
case "DD":
|
|
857
|
+
return pad2(day);
|
|
858
|
+
case "D":
|
|
859
|
+
return String(day);
|
|
860
|
+
case "dddd":
|
|
861
|
+
return nameAt(WEEKDAYS_WIDE + weekday);
|
|
862
|
+
case "ddd":
|
|
863
|
+
return nameAt(WEEKDAYS_ABBR + weekday);
|
|
864
|
+
default:
|
|
865
|
+
return token;
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
818
869
|
// src/context.ts
|
|
819
870
|
function createContext(defaultValue) {
|
|
820
871
|
return {
|
|
@@ -3285,6 +3336,7 @@ export {
|
|
|
3285
3336
|
getComponentProps,
|
|
3286
3337
|
getComponentInit,
|
|
3287
3338
|
forwardProps,
|
|
3339
|
+
formatDate,
|
|
3288
3340
|
flushHydration,
|
|
3289
3341
|
findSiblingSlot,
|
|
3290
3342
|
findScope,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1",
|
|
4
4
|
"description": "BarefootJS client package: reactive primitives (SSR-safe) plus browser runtime under the `/runtime` subpath (compiler target)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"directory": "packages/client"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@barefootjs/shared": "0.
|
|
58
|
+
"@barefootjs/shared": "0.24.1"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@barefootjs/jsx": ">=0.2.0"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `formatDate` — format a `Date` (or ISO-8601 string) with an explicit pattern
|
|
3
|
+
* and an explicit UTC offset. The pure-function date formatter of #2324: every
|
|
4
|
+
* input is explicit, so the output is a deterministic function of its
|
|
5
|
+
* arguments — no host locale, no host timezone, no ICU/CLDR data. That is what
|
|
6
|
+
* lets the SSR adapters lower a `formatDate(...)` call to their native
|
|
7
|
+
* `format_date` runtime helper (spec/template-helpers.md) with byte-identical
|
|
8
|
+
* output on every backend.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <time>{formatDate(createdAt, 'YYYY/M/D', '+09:00')}</time>
|
|
12
|
+
* <time>{formatDate(createdAt, 'MMMM D, YYYY', 'UTC', names)}</time>
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Numeric tokens (longest-match; any other character passes through
|
|
16
|
+
* literally): `YYYY` (4-digit zero-padded year, `-`-prefixed for negative
|
|
17
|
+
* years), `MM` / `M` (zero-padded / bare month `01`–`12` / `1`–`12`), `DD` /
|
|
18
|
+
* `D` (zero-padded / bare day of month).
|
|
19
|
+
*
|
|
20
|
+
* Name tokens (#2334) read the explicit `names` table — the caller owns the
|
|
21
|
+
* values (per-locale name selection is the i18n layer's or the compiler's
|
|
22
|
+
* job, never this function's): `MMMM` / `MMM` (wide / abbreviated month
|
|
23
|
+
* name), `dddd` / `ddd` (wide / abbreviated weekday name, Sunday-first).
|
|
24
|
+
* `names` is a flat array in fixed layout: `[0..11]` wide months, `[12..23]`
|
|
25
|
+
* abbreviated months, `[24..30]` wide weekdays, `[31..37]` abbreviated
|
|
26
|
+
* weekdays. A name token indexing a missing/short table renders `''` — the
|
|
27
|
+
* same normative zero-value discipline as an unparseable date, byte-identical
|
|
28
|
+
* on every backend.
|
|
29
|
+
*
|
|
30
|
+
* `timeZone` is `'UTC'` or a fixed offset `±HH:MM` (`'+09:00'`, `'-05:30'`).
|
|
31
|
+
* The function is total: any other value — including IANA zone names, which
|
|
32
|
+
* would drag host tzdata versions into the output — normalizes to `'UTC'`, so
|
|
33
|
+
* every backend degrades identically instead of diverging. An unset or
|
|
34
|
+
* unparseable `date` renders `''`.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/
|
|
38
|
+
const TOKEN_RE = /YYYY|MMMM|MMM|MM|DD|dddd|ddd|M|D/g
|
|
39
|
+
|
|
40
|
+
/** `names`-table section offsets (spec/template-helpers.md "format_date"). */
|
|
41
|
+
const MONTHS_WIDE = 0
|
|
42
|
+
const MONTHS_ABBR = 12
|
|
43
|
+
const WEEKDAYS_WIDE = 24
|
|
44
|
+
const WEEKDAYS_ABBR = 31
|
|
45
|
+
|
|
46
|
+
function pad2(n: number): string {
|
|
47
|
+
return String(n).padStart(2, '0')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function formatDate(
|
|
51
|
+
date: Date | string,
|
|
52
|
+
pattern: string,
|
|
53
|
+
timeZone = 'UTC',
|
|
54
|
+
names: readonly string[] = [],
|
|
55
|
+
): string {
|
|
56
|
+
// Explicit nullish guard: `new Date(null)` is epoch 0, not Invalid Date,
|
|
57
|
+
// so without this a nil receiver would format 1970-01-01 instead of the
|
|
58
|
+
// contract's '' (spec/template-helpers.md "format_date", golden vector
|
|
59
|
+
// "nil receiver renders the empty string").
|
|
60
|
+
if (date === null || date === undefined) return ''
|
|
61
|
+
const d = date instanceof Date ? date : new Date(date)
|
|
62
|
+
const t = d.getTime()
|
|
63
|
+
if (Number.isNaN(t)) return ''
|
|
64
|
+
const m = OFFSET_RE.exec(timeZone)
|
|
65
|
+
const offsetMinutes = m ? (m[1] === '-' ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0
|
|
66
|
+
// Shift the instant by the offset, then read UTC fields: the shifted UTC
|
|
67
|
+
// clock face IS the local clock face at that offset.
|
|
68
|
+
const s = new Date(t + offsetMinutes * 60_000)
|
|
69
|
+
const year = s.getUTCFullYear()
|
|
70
|
+
const yyyy = (year < 0 ? '-' : '') + String(Math.abs(year)).padStart(4, '0')
|
|
71
|
+
const month = s.getUTCMonth() + 1
|
|
72
|
+
const day = s.getUTCDate()
|
|
73
|
+
const weekday = s.getUTCDay() // 0 = Sunday, matching the table's Sunday-first layout
|
|
74
|
+
const nameAt = (index: number): string => names[index] ?? ''
|
|
75
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
76
|
+
switch (token) {
|
|
77
|
+
case 'YYYY':
|
|
78
|
+
return yyyy
|
|
79
|
+
case 'MMMM':
|
|
80
|
+
return nameAt(MONTHS_WIDE + month - 1)
|
|
81
|
+
case 'MMM':
|
|
82
|
+
return nameAt(MONTHS_ABBR + month - 1)
|
|
83
|
+
case 'MM':
|
|
84
|
+
return pad2(month)
|
|
85
|
+
case 'M':
|
|
86
|
+
return String(month)
|
|
87
|
+
case 'DD':
|
|
88
|
+
return pad2(day)
|
|
89
|
+
case 'D':
|
|
90
|
+
return String(day)
|
|
91
|
+
case 'dddd':
|
|
92
|
+
return nameAt(WEEKDAYS_WIDE + weekday)
|
|
93
|
+
case 'ddd':
|
|
94
|
+
return nameAt(WEEKDAYS_ABBR + weekday)
|
|
95
|
+
default:
|
|
96
|
+
return token
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export { forwardProps } from './forward-props.ts'
|
|
|
49
49
|
export { unwrap } from './unwrap.ts'
|
|
50
50
|
|
|
51
51
|
export { queryHref, type QueryParams, type QueryParamValue } from './query-href.ts'
|
|
52
|
+
export { formatDate } from './format-date.ts'
|
|
52
53
|
|
|
53
54
|
export { createContext, type Context } from './context.ts'
|
|
54
55
|
|
package/src/runtime/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ export { __slot, type SlotMarker } from '../slot.ts'
|
|
|
53
53
|
export { forwardProps } from '../forward-props.ts'
|
|
54
54
|
export { unwrap } from '../unwrap.ts'
|
|
55
55
|
export { queryHref, type QueryParams, type QueryParamValue } from '../query-href.ts'
|
|
56
|
+
export { formatDate } from '../format-date.ts'
|
|
56
57
|
|
|
57
58
|
// Context API (real DOM-bound implementations; `createContext` is the
|
|
58
59
|
// same pure function re-exported from `../context`).
|