@barefootjs/client 0.21.4 → 0.23.0
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 +30 -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 +38 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +38 -0
- package/dist/runtime/standalone.js +38 -0
- package/package.json +2 -2
- package/src/format-date.ts +71 -0
- package/src/index.ts +1 -0
- package/src/runtime/index.ts +1 -0
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Pattern tokens (longest-match; any other character passes through
|
|
15
|
+
* literally): `YYYY` (4-digit zero-padded year, `-`-prefixed for negative
|
|
16
|
+
* years), `MM` / `M` (zero-padded / bare month `01`–`12` / `1`–`12`), `DD` /
|
|
17
|
+
* `D` (zero-padded / bare day of month). The v1 token set is date-only and
|
|
18
|
+
* numeric — locale-dependent fields (month names, weekday names) are a
|
|
19
|
+
* deliberate later stage of #2324, and per-locale *pattern selection* belongs
|
|
20
|
+
* to the app's i18n layer, not to this function.
|
|
21
|
+
*
|
|
22
|
+
* `timeZone` is `'UTC'` or a fixed offset `±HH:MM` (`'+09:00'`, `'-05:30'`).
|
|
23
|
+
* The function is total: any other value — including IANA zone names, which
|
|
24
|
+
* would drag host tzdata versions into the output — normalizes to `'UTC'`, so
|
|
25
|
+
* every backend degrades identically instead of diverging. An unset or
|
|
26
|
+
* unparseable `date` renders `''` (the same normative zero-value discipline as
|
|
27
|
+
* the `date` helper, spec/template-helpers.md).
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatDate(date: Date | string, pattern: string, timeZone?: string): string;
|
|
30
|
+
//# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,SAAQ,GAAG,MAAM,CAkCzF"}
|
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,43 @@ 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|MM|DD|M|D/g;
|
|
168
|
+
function pad2(n) {
|
|
169
|
+
return String(n).padStart(2, "0");
|
|
170
|
+
}
|
|
171
|
+
function formatDate(date, pattern, timeZone = "UTC") {
|
|
172
|
+
if (date === null || date === undefined)
|
|
173
|
+
return "";
|
|
174
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
175
|
+
const t = d.getTime();
|
|
176
|
+
if (Number.isNaN(t))
|
|
177
|
+
return "";
|
|
178
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
179
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
180
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
181
|
+
const year = s.getUTCFullYear();
|
|
182
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
183
|
+
const month = s.getUTCMonth() + 1;
|
|
184
|
+
const day = s.getUTCDate();
|
|
185
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
186
|
+
switch (token) {
|
|
187
|
+
case "YYYY":
|
|
188
|
+
return yyyy;
|
|
189
|
+
case "MM":
|
|
190
|
+
return pad2(month);
|
|
191
|
+
case "M":
|
|
192
|
+
return String(month);
|
|
193
|
+
case "DD":
|
|
194
|
+
return pad2(day);
|
|
195
|
+
case "D":
|
|
196
|
+
return String(day);
|
|
197
|
+
default:
|
|
198
|
+
return token;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
165
202
|
// src/context.ts
|
|
166
203
|
function createContext(defaultValue) {
|
|
167
204
|
return {
|
|
@@ -216,6 +253,7 @@ export {
|
|
|
216
253
|
onCleanup,
|
|
217
254
|
isSSRPortal,
|
|
218
255
|
forwardProps,
|
|
256
|
+
formatDate,
|
|
219
257
|
findSiblingSlot,
|
|
220
258
|
endTurn,
|
|
221
259
|
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,43 @@ 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|MM|DD|M|D/g;
|
|
168
|
+
function pad2(n) {
|
|
169
|
+
return String(n).padStart(2, "0");
|
|
170
|
+
}
|
|
171
|
+
function formatDate(date, pattern, timeZone = "UTC") {
|
|
172
|
+
if (date === null || date === undefined)
|
|
173
|
+
return "";
|
|
174
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
175
|
+
const t = d.getTime();
|
|
176
|
+
if (Number.isNaN(t))
|
|
177
|
+
return "";
|
|
178
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
179
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
180
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
181
|
+
const year = s.getUTCFullYear();
|
|
182
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
183
|
+
const month = s.getUTCMonth() + 1;
|
|
184
|
+
const day = s.getUTCDate();
|
|
185
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
186
|
+
switch (token) {
|
|
187
|
+
case "YYYY":
|
|
188
|
+
return yyyy;
|
|
189
|
+
case "MM":
|
|
190
|
+
return pad2(month);
|
|
191
|
+
case "M":
|
|
192
|
+
return String(month);
|
|
193
|
+
case "DD":
|
|
194
|
+
return pad2(day);
|
|
195
|
+
case "D":
|
|
196
|
+
return String(day);
|
|
197
|
+
default:
|
|
198
|
+
return token;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
165
202
|
// ../shared/src/markers.ts
|
|
166
203
|
var BF_SCOPE = "bf-s";
|
|
167
204
|
var BF_SLOT = "bf";
|
|
@@ -2885,6 +2922,7 @@ export {
|
|
|
2885
2922
|
getComponentProps,
|
|
2886
2923
|
getComponentInit,
|
|
2887
2924
|
forwardProps,
|
|
2925
|
+
formatDate,
|
|
2888
2926
|
flushHydration,
|
|
2889
2927
|
findSiblingSlot,
|
|
2890
2928
|
findScope,
|
|
@@ -815,6 +815,43 @@ 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|MM|DD|M|D/g;
|
|
821
|
+
function pad2(n) {
|
|
822
|
+
return String(n).padStart(2, "0");
|
|
823
|
+
}
|
|
824
|
+
function formatDate(date, pattern, timeZone = "UTC") {
|
|
825
|
+
if (date === null || date === undefined)
|
|
826
|
+
return "";
|
|
827
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
828
|
+
const t = d.getTime();
|
|
829
|
+
if (Number.isNaN(t))
|
|
830
|
+
return "";
|
|
831
|
+
const m = OFFSET_RE.exec(timeZone);
|
|
832
|
+
const offsetMinutes = m ? (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0;
|
|
833
|
+
const s = new Date(t + offsetMinutes * 60000);
|
|
834
|
+
const year = s.getUTCFullYear();
|
|
835
|
+
const yyyy = (year < 0 ? "-" : "") + String(Math.abs(year)).padStart(4, "0");
|
|
836
|
+
const month = s.getUTCMonth() + 1;
|
|
837
|
+
const day = s.getUTCDate();
|
|
838
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
839
|
+
switch (token) {
|
|
840
|
+
case "YYYY":
|
|
841
|
+
return yyyy;
|
|
842
|
+
case "MM":
|
|
843
|
+
return pad2(month);
|
|
844
|
+
case "M":
|
|
845
|
+
return String(month);
|
|
846
|
+
case "DD":
|
|
847
|
+
return pad2(day);
|
|
848
|
+
case "D":
|
|
849
|
+
return String(day);
|
|
850
|
+
default:
|
|
851
|
+
return token;
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
818
855
|
// src/context.ts
|
|
819
856
|
function createContext(defaultValue) {
|
|
820
857
|
return {
|
|
@@ -3285,6 +3322,7 @@ export {
|
|
|
3285
3322
|
getComponentProps,
|
|
3286
3323
|
getComponentInit,
|
|
3287
3324
|
forwardProps,
|
|
3325
|
+
formatDate,
|
|
3288
3326
|
flushHydration,
|
|
3289
3327
|
findSiblingSlot,
|
|
3290
3328
|
findScope,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
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.23.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@barefootjs/jsx": ">=0.2.0"
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Pattern tokens (longest-match; any other character passes through
|
|
15
|
+
* literally): `YYYY` (4-digit zero-padded year, `-`-prefixed for negative
|
|
16
|
+
* years), `MM` / `M` (zero-padded / bare month `01`–`12` / `1`–`12`), `DD` /
|
|
17
|
+
* `D` (zero-padded / bare day of month). The v1 token set is date-only and
|
|
18
|
+
* numeric — locale-dependent fields (month names, weekday names) are a
|
|
19
|
+
* deliberate later stage of #2324, and per-locale *pattern selection* belongs
|
|
20
|
+
* to the app's i18n layer, not to this function.
|
|
21
|
+
*
|
|
22
|
+
* `timeZone` is `'UTC'` or a fixed offset `±HH:MM` (`'+09:00'`, `'-05:30'`).
|
|
23
|
+
* The function is total: any other value — including IANA zone names, which
|
|
24
|
+
* would drag host tzdata versions into the output — normalizes to `'UTC'`, so
|
|
25
|
+
* every backend degrades identically instead of diverging. An unset or
|
|
26
|
+
* unparseable `date` renders `''` (the same normative zero-value discipline as
|
|
27
|
+
* the `date` helper, spec/template-helpers.md).
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const OFFSET_RE = /^([+-])(\d{2}):(\d{2})$/
|
|
31
|
+
const TOKEN_RE = /YYYY|MM|DD|M|D/g
|
|
32
|
+
|
|
33
|
+
function pad2(n: number): string {
|
|
34
|
+
return String(n).padStart(2, '0')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function formatDate(date: Date | string, pattern: string, timeZone = 'UTC'): string {
|
|
38
|
+
// Explicit nullish guard: `new Date(null)` is epoch 0, not Invalid Date,
|
|
39
|
+
// so without this a nil receiver would format 1970-01-01 instead of the
|
|
40
|
+
// contract's '' (spec/template-helpers.md "format_date", golden vector
|
|
41
|
+
// "nil receiver renders the empty string").
|
|
42
|
+
if (date === null || date === undefined) return ''
|
|
43
|
+
const d = date instanceof Date ? date : new Date(date)
|
|
44
|
+
const t = d.getTime()
|
|
45
|
+
if (Number.isNaN(t)) return ''
|
|
46
|
+
const m = OFFSET_RE.exec(timeZone)
|
|
47
|
+
const offsetMinutes = m ? (m[1] === '-' ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : 0
|
|
48
|
+
// Shift the instant by the offset, then read UTC fields: the shifted UTC
|
|
49
|
+
// clock face IS the local clock face at that offset.
|
|
50
|
+
const s = new Date(t + offsetMinutes * 60_000)
|
|
51
|
+
const year = s.getUTCFullYear()
|
|
52
|
+
const yyyy = (year < 0 ? '-' : '') + String(Math.abs(year)).padStart(4, '0')
|
|
53
|
+
const month = s.getUTCMonth() + 1
|
|
54
|
+
const day = s.getUTCDate()
|
|
55
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
56
|
+
switch (token) {
|
|
57
|
+
case 'YYYY':
|
|
58
|
+
return yyyy
|
|
59
|
+
case 'MM':
|
|
60
|
+
return pad2(month)
|
|
61
|
+
case 'M':
|
|
62
|
+
return String(month)
|
|
63
|
+
case 'DD':
|
|
64
|
+
return pad2(day)
|
|
65
|
+
case 'D':
|
|
66
|
+
return String(day)
|
|
67
|
+
default:
|
|
68
|
+
return token
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
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`).
|