@voltro/datetime 0.29.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/CHANGELOG.md +4914 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +59 -0
- package/dist/context.d.ts +45 -0
- package/dist/context.js +9 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +37 -0
- package/dist/resolveTimezone-B67Y7K6d.js +22 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Voltro — Proprietary Software License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Voltro UG. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and all associated source code, documentation, and other
|
|
6
|
+
materials (the "Software") are the proprietary property of Voltro UG
|
|
7
|
+
("Voltro"). This is NOT open-source software.
|
|
8
|
+
|
|
9
|
+
Your access to and use of the Software is governed exclusively by the Voltro
|
|
10
|
+
Terms of Service at:
|
|
11
|
+
|
|
12
|
+
https://voltro.cloud/legal/terms
|
|
13
|
+
|
|
14
|
+
By downloading, installing, or using the Software you agree to those Terms.
|
|
15
|
+
Subject to and conditioned on your continued compliance with them, Voltro
|
|
16
|
+
grants you a limited, non-exclusive, non-transferable, non-sublicensable,
|
|
17
|
+
revocable license to use the Software. All rights not expressly granted in the
|
|
18
|
+
Terms are reserved by Voltro. If you do not agree to the Terms, you have no
|
|
19
|
+
license and may not use the Software.
|
|
20
|
+
|
|
21
|
+
The Terms of Service — including the scope of permitted use, plan
|
|
22
|
+
entitlements, fees, and pricing — may change from time to time. The version in
|
|
23
|
+
force is the one published at the URL above. Commercial terms and pricing are
|
|
24
|
+
NOT part of this notice and are not fixed by, or granted in perpetuity by, the
|
|
25
|
+
version of the Software in which this file appears.
|
|
26
|
+
|
|
27
|
+
Permitted use — your own software. Voltro is a development framework. You may
|
|
28
|
+
use the Software to build, run, and commercially distribute your own
|
|
29
|
+
applications and services, and to charge your own customers for them, without
|
|
30
|
+
owing Voltro any share of that revenue or any further permission beyond the
|
|
31
|
+
Terms. Applications you build with the Software are yours.
|
|
32
|
+
|
|
33
|
+
Prohibited use — reselling Voltro. You may NOT redistribute, resell,
|
|
34
|
+
sublicense, publish, or otherwise provide the Software itself — in whole or in
|
|
35
|
+
part, in source or compiled form — to third parties as a library, framework,
|
|
36
|
+
SDK, template, starter, or development tool for them to build upon, whether
|
|
37
|
+
standalone or repackaged. Shipping the Software as a non-separable, bundled
|
|
38
|
+
dependency inside an application you deliver (for example, compiled into your
|
|
39
|
+
deployed app) is permitted; extracting or re-exposing it as a reusable
|
|
40
|
+
component for others is not.
|
|
41
|
+
|
|
42
|
+
Except as expressly permitted above or by the Terms, you may not copy, modify,
|
|
43
|
+
merge, publish, sublicense, sell, distribute, or create derivative works of the
|
|
44
|
+
Software, in whole or in part.
|
|
45
|
+
|
|
46
|
+
Third-party open-source components distributed alongside the Software are the
|
|
47
|
+
property of their respective owners and are licensed under their own terms; see
|
|
48
|
+
THIRD-PARTY-NOTICES.md where provided. Nothing in this notice limits your
|
|
49
|
+
rights, or extends Voltro's, in those components.
|
|
50
|
+
|
|
51
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
52
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
53
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
54
|
+
VOLTRO BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM,
|
|
55
|
+
OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
|
|
56
|
+
|
|
57
|
+
For licensing inquiries, contact Voltro UG.
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @voltro/datetime
|
|
4
|
+
|
|
5
|
+
**The framework's opinion on time: UTC instants in storage, rendering in the viewer's timezone, and zone-aware arithmetic/formatting on the TC39 Temporal standard (via polyfill). Ships the request-scoped timezone-context seam plus pure, browser-safe date helpers.**
|
|
6
|
+
|
|
7
|
+
[📖 Documentation](https://docs.voltro.dev/docs/intro/getting-started) · [voltro.dev](https://voltro.dev) · [Voltro Cloud](https://voltro.cloud)
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @voltro/datetime
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
> ### [→ Read the documentation for @voltro/datetime](https://docs.voltro.dev/docs/intro/getting-started)
|
|
18
|
+
|
|
19
|
+
Installation, guides, the full API reference, and examples all live in the
|
|
20
|
+
**[Voltro documentation](https://docs.voltro.dev/docs/intro/getting-started)** — the single source of truth. This README is
|
|
21
|
+
deliberately short so there is nothing here to drift out of date.
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
Proprietary. © 2026 Voltro UG. Your use is governed by the
|
|
26
|
+
[Terms of Service](https://voltro.cloud/legal/terms). See the bundled `LICENSE`.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
We take the security of Voltro and the applications built on it seriously. This
|
|
4
|
+
document explains how to report a vulnerability and what to expect in return.
|
|
5
|
+
|
|
6
|
+
## Reporting a vulnerability
|
|
7
|
+
|
|
8
|
+
**Please do not open public issues or discuss suspected vulnerabilities in
|
|
9
|
+
public channels.** Report them privately by email:
|
|
10
|
+
|
|
11
|
+
**security contact: [support@voltro.dev](mailto:support@voltro.dev)**
|
|
12
|
+
(subject line: `SECURITY`).
|
|
13
|
+
|
|
14
|
+
Include, where you can:
|
|
15
|
+
|
|
16
|
+
- the affected package(s) and version(s) (e.g. `@voltro/runtime@x.y.z`);
|
|
17
|
+
- a description of the issue and its impact;
|
|
18
|
+
- a minimal reproduction or proof of concept;
|
|
19
|
+
- any suggested remediation.
|
|
20
|
+
|
|
21
|
+
If you'd like to encrypt your report, ask in a first plain email and we'll
|
|
22
|
+
arrange a key.
|
|
23
|
+
|
|
24
|
+
## What to expect
|
|
25
|
+
|
|
26
|
+
- **Acknowledgement** of your report within **3 business days**.
|
|
27
|
+
- An initial assessment (severity, affected surface) and a remediation plan as
|
|
28
|
+
soon as we've reproduced the issue.
|
|
29
|
+
- **Coordinated disclosure:** we'll agree a disclosure timeline with you and
|
|
30
|
+
credit you in the release notes if you wish. Please give us reasonable time to
|
|
31
|
+
ship a fix before any public disclosure.
|
|
32
|
+
|
|
33
|
+
We do not currently run a paid bug-bounty program, but we genuinely appreciate
|
|
34
|
+
responsible disclosure and will credit reporters.
|
|
35
|
+
|
|
36
|
+
## Software Bill of Materials (SBOM)
|
|
37
|
+
|
|
38
|
+
A CycloneDX SBOM of the third-party runtime dependency graph is generated for the
|
|
39
|
+
published packages (`pnpm gen:sbom` → `sbom.cdx.json`). If your procurement or
|
|
40
|
+
security team needs it, request it at
|
|
41
|
+
[support@voltro.dev](mailto:support@voltro.dev).
|
|
42
|
+
|
|
43
|
+
## Supported versions
|
|
44
|
+
|
|
45
|
+
Voltro is pre-1.0 and ships in lockstep: **only the latest published minor of
|
|
46
|
+
each `@voltro/*` package receives security fixes.** Please upgrade to the latest
|
|
47
|
+
release before reporting, and pin exact versions in production.
|
|
48
|
+
|
|
49
|
+
## Scope
|
|
50
|
+
|
|
51
|
+
In scope: the published `@voltro/*` packages and the framework's own code.
|
|
52
|
+
|
|
53
|
+
Out of scope: vulnerabilities in third-party dependencies that are already
|
|
54
|
+
tracked upstream (we monitor these via a production-dependency audit gate in
|
|
55
|
+
CI), and issues that require a non-default, explicitly-unsafe configuration. If
|
|
56
|
+
you're unsure, report it anyway — we'd rather hear about it.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Third-Party Notices — @voltro/datetime
|
|
2
|
+
|
|
3
|
+
This package depends on the open-source software listed below. Each is the
|
|
4
|
+
property of its respective copyright holders and is used under the terms of
|
|
5
|
+
its license. This file is provided for attribution; it grants no rights in
|
|
6
|
+
@voltro/datetime itself, which is proprietary (see LICENSE).
|
|
7
|
+
|
|
8
|
+
Generated from the resolved runtime dependency closure (2 packages).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## temporal-polyfill@0.3.2
|
|
13
|
+
|
|
14
|
+
License: MIT
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
MIT License
|
|
18
|
+
|
|
19
|
+
Copyright (c) 2024 Adam Shaw
|
|
20
|
+
|
|
21
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
22
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
23
|
+
in the Software without restriction, including without limitation the rights
|
|
24
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
25
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
26
|
+
furnished to do so, subject to the following conditions:
|
|
27
|
+
|
|
28
|
+
The above copyright notice and this permission notice shall be included in all
|
|
29
|
+
copies or substantial portions of the Software.
|
|
30
|
+
|
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
33
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
34
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
35
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
36
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
37
|
+
SOFTWARE.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## temporal-spec@0.3.1
|
|
41
|
+
|
|
42
|
+
License: ISC
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Copyright 2017, 2018, 2019, 2020 ECMA International
|
|
46
|
+
|
|
47
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
48
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
49
|
+
copyright notice and this permission notice appear in all copies.
|
|
50
|
+
|
|
51
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
52
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
53
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
54
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
55
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
56
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
57
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
58
|
+
```
|
|
59
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Context } from 'effect';
|
|
2
|
+
import { Effect } from 'effect';
|
|
3
|
+
import { Layer } from 'effect';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The effective timezone in the current context, or `UTC` when none was
|
|
7
|
+
* provided. Never fails — an absent context is the documented UTC default,
|
|
8
|
+
* so call sites don't have to handle a missing-service error.
|
|
9
|
+
*/
|
|
10
|
+
export declare const currentTimezone: Effect.Effect<string>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A layer that RESOLVES the timezone from the request signals (user
|
|
14
|
+
* profile → tenant → browser → UTC) and provides it. This is what the
|
|
15
|
+
* runtime wires per request.
|
|
16
|
+
*/
|
|
17
|
+
export declare const resolvedTimezoneLayer: (args: ResolveTimezoneArgs) => Layer.Layer<TimezoneContext>;
|
|
18
|
+
|
|
19
|
+
declare interface ResolveTimezoneArgs {
|
|
20
|
+
/** The viewing user's stored profile timezone, if any. */
|
|
21
|
+
readonly userTimeZone?: string | null;
|
|
22
|
+
/** The tenant / org default timezone, if any. */
|
|
23
|
+
readonly tenantTimeZone?: string | null;
|
|
24
|
+
/** The browser-reported timezone (from the client), if any. */
|
|
25
|
+
readonly browserTimeZone?: string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The carried value: the effective IANA timezone for the current request. */
|
|
29
|
+
export declare interface Timezone {
|
|
30
|
+
readonly timeZone: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Context tag holding the request's resolved timezone. */
|
|
34
|
+
export declare class TimezoneContext extends TimezoneContext_base {
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare const TimezoneContext_base: Context.TagClass<TimezoneContext, "@voltro/datetime/TimezoneContext", Timezone>;
|
|
38
|
+
|
|
39
|
+
/** A layer providing a fixed timezone. */
|
|
40
|
+
export declare const TimezoneContextLive: (timeZone: string) => Layer.Layer<TimezoneContext>;
|
|
41
|
+
|
|
42
|
+
/** Run `effect` with an explicit timezone in context. */
|
|
43
|
+
export declare const withTimezone: (timeZone: string) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, TimezoneContext>>;
|
|
44
|
+
|
|
45
|
+
export { }
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { n as e } from "./resolveTimezone-B67Y7K6d.js";
|
|
2
|
+
import { Context as t, Effect as n, Layer as r, Option as i } from "effect";
|
|
3
|
+
//#region src/context.ts
|
|
4
|
+
var a = class extends t.Tag("@voltro/datetime/TimezoneContext")() {}, o = n.map(n.serviceOption(a), (e) => i.match(e, {
|
|
5
|
+
onNone: () => "UTC",
|
|
6
|
+
onSome: (e) => e.timeZone
|
|
7
|
+
})), s = (e) => (t) => n.provideService(t, a, { timeZone: e }), c = (e) => r.succeed(a, { timeZone: e }), l = (t) => r.succeed(a, { timeZone: e(t) });
|
|
8
|
+
//#endregion
|
|
9
|
+
export { a as TimezoneContext, c as TimezoneContextLive, o as currentTimezone, l as resolvedTimezoneLayer, s as withTimezone };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Temporal } from 'temporal-polyfill';
|
|
2
|
+
|
|
3
|
+
/** DST-aware: same wall-clock time, `n` calendar days later in `timeZone`. */
|
|
4
|
+
export declare const addDays: (value: InstantInput, n: number, timeZone: string) => Temporal.Instant;
|
|
5
|
+
|
|
6
|
+
/** DST-UNAWARE: exactly `n × 3600` seconds later. No zone — an instant offset. */
|
|
7
|
+
export declare const addHours: (value: InstantInput, n: number) => Temporal.Instant;
|
|
8
|
+
|
|
9
|
+
/** DST-aware: same wall-clock time, `n` calendar months later in `timeZone`
|
|
10
|
+
* (clamps to the last day of a shorter month per Temporal's default). */
|
|
11
|
+
export declare const addMonths: (value: InstantInput, n: number, timeZone: string) => Temporal.Instant;
|
|
12
|
+
|
|
13
|
+
/** Throw unless `timeZone` is a valid IANA name. Storage rule: validate on
|
|
14
|
+
* input, never store an unverified zone string. */
|
|
15
|
+
export declare const assertTimeZone: (timeZone: string) => string;
|
|
16
|
+
|
|
17
|
+
/** The last-resort fallback. UTC is always valid and never surprising. */
|
|
18
|
+
export declare const DEFAULT_TIME_ZONE = "UTC";
|
|
19
|
+
|
|
20
|
+
/** Last instant (23:59:59.999999999) of the local day in `timeZone`. */
|
|
21
|
+
export declare const endOfDay: (value: InstantInput, timeZone: string) => Temporal.Instant;
|
|
22
|
+
|
|
23
|
+
/** Date only, in `timeZone`. Default `dateStyle: 'medium'`. */
|
|
24
|
+
export declare const formatDate: (value: InstantInput, timeZone: string, options?: FormatOptions) => string;
|
|
25
|
+
|
|
26
|
+
/** Date + time, in `timeZone`. Default `dateStyle`/`timeStyle: 'medium'`. */
|
|
27
|
+
export declare const formatDateTime: (value: InstantInput, timeZone: string, options?: FormatOptions) => string;
|
|
28
|
+
|
|
29
|
+
/** Options common to the absolute formatters: an explicit `locale` plus any
|
|
30
|
+
* `Intl.DateTimeFormatOptions` override (a `timeZone` in here wins over the
|
|
31
|
+
* positional `timeZone` argument). */
|
|
32
|
+
export declare type FormatOptions = {
|
|
33
|
+
readonly locale?: string;
|
|
34
|
+
} & Intl.DateTimeFormatOptions;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* "3 hours ago", "in 2 days". Picks the largest unit that fits the signed
|
|
38
|
+
* distance between `value` and `now` (default: the current instant). This
|
|
39
|
+
* is zone-independent: it measures elapsed real time, not wall-clock days.
|
|
40
|
+
*/
|
|
41
|
+
export declare const formatRelativeTime: (value: InstantInput, options?: RelativeTimeOptions) => string;
|
|
42
|
+
|
|
43
|
+
/** Time only, in `timeZone`. Default `timeStyle: 'medium'`. */
|
|
44
|
+
export declare const formatTime: (value: InstantInput, timeZone: string, options?: FormatOptions) => string;
|
|
45
|
+
|
|
46
|
+
/** Bridge a JS `Date` (DB read) to a `Temporal.Instant`. */
|
|
47
|
+
export declare const fromDate: (date: Date) => Temporal.Instant;
|
|
48
|
+
|
|
49
|
+
/** Parse an ISO-8601 instant (offset / `Z` required). Alias: `parseISO`. */
|
|
50
|
+
export declare const instant: (iso: string) => Temporal.Instant;
|
|
51
|
+
|
|
52
|
+
/** Anything that unambiguously denotes a UTC instant: a Temporal.Instant,
|
|
53
|
+
* a JS `Date` (what the DB layer produces), or an ISO-8601 string with an
|
|
54
|
+
* explicit offset / `Z`. */
|
|
55
|
+
export declare type InstantInput = Temporal.Instant | Date | string;
|
|
56
|
+
|
|
57
|
+
/** Is `a` strictly after `b`? Zone-independent (compares absolute instants). */
|
|
58
|
+
export declare const isAfter: (a: InstantInput, b: InstantInput) => boolean;
|
|
59
|
+
|
|
60
|
+
/** Is `a` strictly before `b`? Zone-independent (compares absolute instants). */
|
|
61
|
+
export declare const isBefore: (a: InstantInput, b: InstantInput) => boolean;
|
|
62
|
+
|
|
63
|
+
/** Do `a` and `b` fall on the same calendar day in `timeZone`? */
|
|
64
|
+
export declare const isSameDay: (a: InstantInput, b: InstantInput, timeZone: string) => boolean;
|
|
65
|
+
|
|
66
|
+
/** Is `timeZone` a valid IANA name this runtime knows? Uses `Intl`, which
|
|
67
|
+
* is the authority both Temporal and formatting defer to. */
|
|
68
|
+
export declare const isValidTimeZone: (timeZone: string) => boolean;
|
|
69
|
+
|
|
70
|
+
/** The current UTC instant. */
|
|
71
|
+
export declare const now: () => Temporal.Instant;
|
|
72
|
+
|
|
73
|
+
/** Parse a `YYYY-MM-DD` calendar date. */
|
|
74
|
+
export declare const parseDate: (iso: string) => Temporal.PlainDate;
|
|
75
|
+
|
|
76
|
+
/** Parse an ISO-8601 instant. Same as `instant`, named for parse sites. */
|
|
77
|
+
export declare const parseISO: (iso: string) => Temporal.Instant;
|
|
78
|
+
|
|
79
|
+
/** A calendar date with no time and no zone (birthdays, holidays, due dates). */
|
|
80
|
+
export declare const plainDate: (year: number, month: number, day: number) => Temporal.PlainDate;
|
|
81
|
+
|
|
82
|
+
/** Options for `formatRelativeTime`. */
|
|
83
|
+
export declare interface RelativeTimeOptions {
|
|
84
|
+
readonly locale?: string;
|
|
85
|
+
/** The instant to measure against. Defaults to "now". */
|
|
86
|
+
readonly now?: InstantInput;
|
|
87
|
+
readonly numeric?: Intl.RelativeTimeFormatNumeric;
|
|
88
|
+
readonly style?: Intl.RelativeTimeFormatStyle;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Resolve the effective IANA timezone for a request, in priority order.
|
|
93
|
+
* Returns a validated IANA name, never an unchecked string; falls back to
|
|
94
|
+
* `UTC` when no signal yields a known zone.
|
|
95
|
+
*/
|
|
96
|
+
export declare const resolveTimezone: (args: ResolveTimezoneArgs) => string;
|
|
97
|
+
|
|
98
|
+
export declare interface ResolveTimezoneArgs {
|
|
99
|
+
/** The viewing user's stored profile timezone, if any. */
|
|
100
|
+
readonly userTimeZone?: string | null;
|
|
101
|
+
/** The tenant / org default timezone, if any. */
|
|
102
|
+
readonly tenantTimeZone?: string | null;
|
|
103
|
+
/** The browser-reported timezone (from the client), if any. */
|
|
104
|
+
readonly browserTimeZone?: string | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** First instant of the local day containing `value` in `timeZone`. */
|
|
108
|
+
export declare const startOfDay: (value: InstantInput, timeZone: string) => Temporal.Instant;
|
|
109
|
+
|
|
110
|
+
/** First instant of the local month containing `value` in `timeZone`. */
|
|
111
|
+
export declare const startOfMonth: (value: InstantInput, timeZone: string) => Temporal.Instant;
|
|
112
|
+
|
|
113
|
+
export { Temporal }
|
|
114
|
+
|
|
115
|
+
/** Bridge a `Temporal.Instant` back to a JS `Date` for storage / interop. */
|
|
116
|
+
export declare const toDate: (instant: Temporal.Instant) => Date;
|
|
117
|
+
|
|
118
|
+
/** Today's calendar date in `timeZone` (no clock, no zone on the result). */
|
|
119
|
+
export declare const today: (timeZone: string) => Temporal.PlainDate;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Coerce a permissive instant input to a `Temporal.Instant`. This is the
|
|
123
|
+
* single funnel every helper runs its input through, so callers can pass
|
|
124
|
+
* the `Date` a query returned without a manual conversion.
|
|
125
|
+
*
|
|
126
|
+
* A string MUST carry an explicit offset or `Z` — a naive `2026-01-01T12:00`
|
|
127
|
+
* has no instant and throws, by design (the whole point is no silent
|
|
128
|
+
* zone-guessing).
|
|
129
|
+
*/
|
|
130
|
+
export declare const toInstant: (value: InstantInput) => Temporal.Instant;
|
|
131
|
+
|
|
132
|
+
/** Project a UTC instant into a wall-clock `ZonedDateTime` in `timeZone`. */
|
|
133
|
+
export declare const toTimezone: (value: InstantInput, timeZone: string) => Temporal.ZonedDateTime;
|
|
134
|
+
|
|
135
|
+
/** The canonical wire / storage form: ISO-8601 in UTC (`…Z`). */
|
|
136
|
+
export declare const toUTCString: (value: InstantInput) => string;
|
|
137
|
+
|
|
138
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./resolveTimezone-B67Y7K6d.js";
|
|
2
|
+
//#region src/helpers.ts
|
|
3
|
+
var l = () => r.Now.instant(), u = (e) => r.Now.plainDateISO(e), d = (e) => r.Instant.from(e), f = (e) => r.Instant.from(e), p = (e, t, n) => r.PlainDate.from({
|
|
4
|
+
year: e,
|
|
5
|
+
month: t,
|
|
6
|
+
day: n
|
|
7
|
+
}), m = (e) => r.PlainDate.from(e), h = (e, t) => s(e).toZonedDateTimeISO(t), g = (e, t, n) => s(e).toZonedDateTimeISO(n).add({ days: t }).toInstant(), _ = (e, t, n) => s(e).toZonedDateTimeISO(n).add({ months: t }).toInstant(), v = (e, t) => s(e).add({ hours: t }), y = (e, t) => s(e).toZonedDateTimeISO(t).startOfDay().toInstant(), b = (e, t) => s(e).toZonedDateTimeISO(t).add({ days: 1 }).startOfDay().subtract({ nanoseconds: 1 }).toInstant(), x = (e, t) => s(e).toZonedDateTimeISO(t).with({ day: 1 }).startOfDay().toInstant(), S = (e, t, n) => s(e).toZonedDateTimeISO(n).toPlainDate().equals(s(t).toZonedDateTimeISO(n).toPlainDate()), C = (e, t) => r.Instant.compare(s(e), s(t)) > 0, w = (e, t) => r.Instant.compare(s(e), s(t)) < 0, T = (e, t, n, r) => {
|
|
8
|
+
let { locale: i, ...o } = r ?? {};
|
|
9
|
+
return new Intl.DateTimeFormat(i, {
|
|
10
|
+
timeZone: t,
|
|
11
|
+
...n,
|
|
12
|
+
...o
|
|
13
|
+
}).format(a(s(e)));
|
|
14
|
+
}, E = (e, t, n) => T(e, t, { dateStyle: "medium" }, n), D = (e, t, n) => T(e, t, { timeStyle: "medium" }, n), O = (e, t, n) => T(e, t, {
|
|
15
|
+
dateStyle: "medium",
|
|
16
|
+
timeStyle: "medium"
|
|
17
|
+
}, n), k = [
|
|
18
|
+
["year", 3600 * 24 * 365],
|
|
19
|
+
["month", 3600 * 24 * 30],
|
|
20
|
+
["week", 3600 * 24 * 7],
|
|
21
|
+
["day", 3600 * 24],
|
|
22
|
+
["hour", 3600],
|
|
23
|
+
["minute", 60],
|
|
24
|
+
["second", 1]
|
|
25
|
+
], A = (e, t) => {
|
|
26
|
+
let { locale: n, now: r, numeric: i = "auto", style: a } = t ?? {}, o = s(e), c = r === void 0 ? Date.now() : s(r).epochMilliseconds, l = (o.epochMilliseconds - c) / 1e3, u = new Intl.RelativeTimeFormat(n, {
|
|
27
|
+
numeric: i,
|
|
28
|
+
...a ? { style: a } : {}
|
|
29
|
+
}), d = Math.abs(l);
|
|
30
|
+
for (let [e, t] of k) if (d >= t || e === "second") {
|
|
31
|
+
let n = Math.round(l / t);
|
|
32
|
+
return u.format(n, e);
|
|
33
|
+
}
|
|
34
|
+
return u.format(Math.round(l), "second");
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { c as DEFAULT_TIME_ZONE, r as Temporal, g as addDays, v as addHours, _ as addMonths, o as assertTimeZone, b as endOfDay, E as formatDate, O as formatDateTime, A as formatRelativeTime, D as formatTime, n as fromDate, d as instant, C as isAfter, w as isBefore, S as isSameDay, e as isValidTimeZone, l as now, m as parseDate, f as parseISO, p as plainDate, i as resolveTimezone, y as startOfDay, x as startOfMonth, a as toDate, s as toInstant, h as toTimezone, t as toUTCString, u as today };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Temporal as e } from "temporal-polyfill";
|
|
2
|
+
//#region src/storage.ts
|
|
3
|
+
var t = (t) => t instanceof e.Instant ? t : t instanceof Date ? e.Instant.fromEpochMilliseconds(t.getTime()) : e.Instant.from(t), n = (t) => e.Instant.fromEpochMilliseconds(t.getTime()), r = (e) => new Date(e.epochMilliseconds), i = (e) => t(e).toString(), a = (e) => {
|
|
4
|
+
if (!e) return !1;
|
|
5
|
+
try {
|
|
6
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: e }), !0;
|
|
7
|
+
} catch {
|
|
8
|
+
return !1;
|
|
9
|
+
}
|
|
10
|
+
}, o = (e) => {
|
|
11
|
+
if (!a(e)) throw RangeError(`Invalid IANA time zone: ${JSON.stringify(e)}`);
|
|
12
|
+
return e;
|
|
13
|
+
}, s = "UTC", c = (e) => {
|
|
14
|
+
for (let t of [
|
|
15
|
+
e.userTimeZone,
|
|
16
|
+
e.tenantTimeZone,
|
|
17
|
+
e.browserTimeZone
|
|
18
|
+
]) if (t && a(t)) return t;
|
|
19
|
+
return "UTC";
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { a, i as c, n as i, e as l, c as n, r as o, o as r, t as s, s as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voltro/datetime",
|
|
3
|
+
"version": "0.29.0",
|
|
4
|
+
"description": "The framework's opinion on time: UTC instants in storage, rendering in the viewer's timezone, and zone-aware arithmetic/formatting on the TC39 Temporal standard (via polyfill). Ships the request-scoped timezone-context seam plus pure, browser-safe date helpers.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"voltro",
|
|
7
|
+
"typescript",
|
|
8
|
+
"framework"
|
|
9
|
+
],
|
|
10
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
11
|
+
"homepage": "https://voltro.dev",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"email": "support@voltro.dev"
|
|
14
|
+
},
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Voltro UG",
|
|
17
|
+
"url": "https://voltro.dev"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./context": {
|
|
27
|
+
"types": "./dist/context.d.ts",
|
|
28
|
+
"import": "./dist/context.js",
|
|
29
|
+
"default": "./dist/context.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"module": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"sideEffects": false,
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=24.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"temporal-polyfill": "^0.3.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"effect": "^3.22.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"effect": {
|
|
47
|
+
"optional": true
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
}
|
|
53
|
+
}
|