workclock 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/NOTICE +28 -0
- package/README.md +84 -2
- package/bundler.d.ts +2 -0
- package/bundler.js +2 -0
- package/errors.d.ts +36 -0
- package/errors.js +46 -0
- package/node.d.ts +2 -0
- package/node.js +2 -0
- package/package.json +86 -4
- package/pkg/bundler/package.json +22 -0
- package/pkg/bundler/workclock.d.ts +209 -0
- package/pkg/bundler/workclock.js +9 -0
- package/pkg/bundler/workclock_bg.js +553 -0
- package/pkg/bundler/workclock_bg.wasm +0 -0
- package/pkg/bundler/workclock_bg.wasm.d.ts +22 -0
- package/pkg/node/package.json +16 -0
- package/pkg/node/workclock.d.ts +209 -0
- package/pkg/node/workclock.js +560 -0
- package/pkg/node/workclock_bg.wasm +0 -0
- package/pkg/node/workclock_bg.wasm.d.ts +22 -0
- package/pkg/web/package.json +20 -0
- package/pkg/web/workclock.d.ts +256 -0
- package/pkg/web/workclock.js +658 -0
- package/pkg/web/workclock_bg.wasm +0 -0
- package/pkg/web/workclock_bg.wasm.d.ts +22 -0
- package/web.d.ts +3 -0
- package/web.js +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Karthik P
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
workclock
|
|
2
|
+
Copyright (c) 2026 Karthik P
|
|
3
|
+
|
|
4
|
+
Parts of the test suite are ported from businesstimedelta
|
|
5
|
+
(https://github.com/seppemans/businesstimedelta), which is distributed
|
|
6
|
+
under the following license:
|
|
7
|
+
|
|
8
|
+
The MIT License (MIT)
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2015 Sebastiaan Boer
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,85 @@
|
|
|
1
|
-
#
|
|
1
|
+
# workclock
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Business-time arithmetic for JavaScript and TypeScript: working hours, breaks,
|
|
4
|
+
holidays and IANA time zones, compiled from Rust to WebAssembly.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install workclock
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
It runs on Node.js 22 or later, in bundlers and in browsers, and ships its
|
|
13
|
+
TypeScript types.
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
A London support desk works 09:00–17:00 with a lunch break, and is closed on
|
|
18
|
+
Christmas Day and the substitute Boxing Day holiday. A ticket opened at 16:30
|
|
19
|
+
on Christmas Eve with an 8-hour SLA:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { Schedule } from "workclock";
|
|
23
|
+
|
|
24
|
+
const support = new Schedule({
|
|
25
|
+
timeZone: "Europe/London",
|
|
26
|
+
rules: [
|
|
27
|
+
{ type: "workday", start: "09:00", end: "17:00" },
|
|
28
|
+
{ type: "break", start: "12:00", end: "13:00" },
|
|
29
|
+
{ type: "holidays", dates: ["2026-12-25", "2026-12-28"] },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
support.add("2026-12-24T16:30:00Z", 8 * 3_600_000); // Date 2026-12-30T09:30:00.000Z
|
|
34
|
+
support.between("2026-12-24T16:30:00Z", new Date("2026-12-29T11:00:00Z")); // 9000000 ms
|
|
35
|
+
support.nextPeriod("2026-12-29T11:00:00Z").end; // Date 2026-12-29T12:00:00.000Z
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`sub` counts back, and `previousPeriod` finds the period of working time at or
|
|
39
|
+
before an instant. Rules are `"workday"`, `"break"` or `"holidays"`; times are
|
|
40
|
+
`HH:MM[:SS[.fff]]`, `days` defaults to Monday to Friday, and each rule can set
|
|
41
|
+
its own `timeZone`.
|
|
42
|
+
|
|
43
|
+
## Instants and durations
|
|
44
|
+
|
|
45
|
+
- Instants are `Date`s or ISO 8601 strings with a UTC offset, such as
|
|
46
|
+
`"2026-01-05T09:00:00Z"` or `"2026-01-05T10:00:00+01:00"`. A string without
|
|
47
|
+
an offset is rejected.
|
|
48
|
+
- Durations are whole numbers of milliseconds, negative to go back in time.
|
|
49
|
+
Any other type throws `TypeError`; it is not converted.
|
|
50
|
+
- Results are `Date`s and numbers, exact to the millisecond.
|
|
51
|
+
- The supported range is -009999-01-02T01:59:59Z to 9999-12-30T22:00:00Z.
|
|
52
|
+
|
|
53
|
+
`betweenMany`, `addMany` and `subMany` take arrays of instants, or a
|
|
54
|
+
`Float64Array` of epoch milliseconds for the fastest path, and process them in
|
|
55
|
+
one call.
|
|
56
|
+
|
|
57
|
+
## Errors
|
|
58
|
+
|
|
59
|
+
Schedule errors are instances of `WorkclockError`, with a `kind`:
|
|
60
|
+
|
|
61
|
+
| Class | `kind` | Thrown when |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `NoWorkingTimeError` | `"no_working_time"` | The rules have no working time, or a search finds none within 366 days, plus seven days per holiday date. |
|
|
64
|
+
| `OutOfRangeError` | `"out_of_range"` | An instant or a result is outside the supported range. |
|
|
65
|
+
| `UnknownZoneError` | `"unknown_zone"` | A time zone is not in the IANA database. |
|
|
66
|
+
|
|
67
|
+
`WorkclockError` itself has the kind `"unknown"`. Malformed input, such as a
|
|
68
|
+
rule without `start` or a string without an offset, throws a built-in
|
|
69
|
+
`TypeError` or `RangeError` instead.
|
|
70
|
+
|
|
71
|
+
## Browsers without a bundler
|
|
72
|
+
|
|
73
|
+
Import from `workclock/web`, and call its default export once before using
|
|
74
|
+
anything else: `await init()` fetches `workclock_bg.wasm` from the package's
|
|
75
|
+
`pkg/web/` directory, so serve that directory with your page. With a bundler,
|
|
76
|
+
or in Node.js, import from `workclock` as above.
|
|
77
|
+
|
|
78
|
+
## Links
|
|
79
|
+
|
|
80
|
+
- [Repository](https://github.com/Karthik-beta/workclock)
|
|
81
|
+
- [Specification and examples](https://github.com/Karthik-beta/workclock/blob/main/DESIGN.md)
|
|
82
|
+
- [JavaScript examples](https://github.com/Karthik-beta/workclock/tree/main/js/examples)
|
|
83
|
+
- [Changelog](https://github.com/Karthik-beta/workclock/blob/main/CHANGELOG.md)
|
|
84
|
+
|
|
85
|
+
MIT licensed.
|
package/bundler.d.ts
ADDED
package/bundler.js
ADDED
package/errors.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class of the errors a schedule throws; check with `instanceof`.
|
|
3
|
+
*
|
|
4
|
+
* Malformed input, such as a rule without `start` or an instant string
|
|
5
|
+
* without a UTC offset, throws a built-in `TypeError` or `RangeError`
|
|
6
|
+
* instead, which is not a `WorkclockError`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WorkclockError extends Error {
|
|
9
|
+
/**
|
|
10
|
+
* `"no_working_time"`, `"out_of_range"` or `"unknown_zone"` for the
|
|
11
|
+
* subclasses below, or `"unknown"` for an error this version does not
|
|
12
|
+
* classify further.
|
|
13
|
+
*/
|
|
14
|
+
readonly kind: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The rules have no working time, or a search found none within 366 days,
|
|
19
|
+
* plus seven days per holiday date.
|
|
20
|
+
*/
|
|
21
|
+
export declare class NoWorkingTimeError extends WorkclockError {
|
|
22
|
+
readonly kind: "no_working_time";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* An instant falls outside the supported range, -009999-01-02T01:59:59Z to
|
|
27
|
+
* 9999-12-30T22:00:00Z.
|
|
28
|
+
*/
|
|
29
|
+
export declare class OutOfRangeError extends WorkclockError {
|
|
30
|
+
readonly kind: "out_of_range";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A time zone name is not in the IANA database. */
|
|
34
|
+
export declare class UnknownZoneError extends WorkclockError {
|
|
35
|
+
readonly kind: "unknown_zone";
|
|
36
|
+
}
|
package/errors.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The WebAssembly module throws plain `Error` objects carrying a `kind`, so
|
|
2
|
+
// besides the usual prototype check these classes recognise errors by `kind`.
|
|
3
|
+
// That also keeps `instanceof` working across the node, bundler and web builds.
|
|
4
|
+
|
|
5
|
+
function isInstance(Class, value, kinds) {
|
|
6
|
+
return (
|
|
7
|
+
Function.prototype[Symbol.hasInstance].call(Class, value) ||
|
|
8
|
+
(value instanceof Error && kinds.includes(value.kind))
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class WorkclockError extends Error {
|
|
13
|
+
name = "WorkclockError";
|
|
14
|
+
kind = "unknown";
|
|
15
|
+
|
|
16
|
+
static [Symbol.hasInstance](value) {
|
|
17
|
+
return isInstance(this, value, ["no_working_time", "out_of_range", "unknown_zone", "unknown"]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class NoWorkingTimeError extends WorkclockError {
|
|
22
|
+
name = "NoWorkingTimeError";
|
|
23
|
+
kind = "no_working_time";
|
|
24
|
+
|
|
25
|
+
static [Symbol.hasInstance](value) {
|
|
26
|
+
return isInstance(this, value, ["no_working_time"]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class OutOfRangeError extends WorkclockError {
|
|
31
|
+
name = "OutOfRangeError";
|
|
32
|
+
kind = "out_of_range";
|
|
33
|
+
|
|
34
|
+
static [Symbol.hasInstance](value) {
|
|
35
|
+
return isInstance(this, value, ["out_of_range"]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class UnknownZoneError extends WorkclockError {
|
|
40
|
+
name = "UnknownZoneError";
|
|
41
|
+
kind = "unknown_zone";
|
|
42
|
+
|
|
43
|
+
static [Symbol.hasInstance](value) {
|
|
44
|
+
return isInstance(this, value, ["unknown_zone"]);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/node.d.ts
ADDED
package/node.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,88 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workclock",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fast, correct business-time arithmetic: working hours, SLAs and shifts",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"business-hours",
|
|
7
|
+
"working-hours",
|
|
8
|
+
"sla",
|
|
9
|
+
"timezone",
|
|
10
|
+
"shift",
|
|
11
|
+
"business-time"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Karthik P",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Karthik-beta/workclock.git",
|
|
18
|
+
"directory": "js"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/Karthik-beta/workclock#readme",
|
|
21
|
+
"bugs": "https://github.com/Karthik-beta/workclock/issues",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"node": {
|
|
26
|
+
"types": "./node.d.ts",
|
|
27
|
+
"default": "./node.js"
|
|
28
|
+
},
|
|
29
|
+
"browser": {
|
|
30
|
+
"types": "./bundler.d.ts",
|
|
31
|
+
"default": "./bundler.js"
|
|
32
|
+
},
|
|
33
|
+
"default": {
|
|
34
|
+
"types": "./bundler.d.ts",
|
|
35
|
+
"default": "./bundler.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"./web": {
|
|
39
|
+
"types": "./web.d.ts",
|
|
40
|
+
"default": "./web.js"
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"types": "./node.d.ts",
|
|
45
|
+
"files": [
|
|
46
|
+
"node.js",
|
|
47
|
+
"node.d.ts",
|
|
48
|
+
"bundler.js",
|
|
49
|
+
"bundler.d.ts",
|
|
50
|
+
"web.js",
|
|
51
|
+
"web.d.ts",
|
|
52
|
+
"errors.js",
|
|
53
|
+
"errors.d.ts",
|
|
54
|
+
"pkg/*/package.json",
|
|
55
|
+
"pkg/*/workclock.js",
|
|
56
|
+
"pkg/*/workclock.d.ts",
|
|
57
|
+
"pkg/*/workclock_bg.js",
|
|
58
|
+
"pkg/*/workclock_bg.wasm",
|
|
59
|
+
"pkg/*/workclock_bg.wasm.d.ts",
|
|
60
|
+
"README.md",
|
|
61
|
+
"LICENSE",
|
|
62
|
+
"NOTICE"
|
|
63
|
+
],
|
|
64
|
+
"sideEffects": [
|
|
65
|
+
"./pkg/bundler/workclock.js"
|
|
66
|
+
],
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=22"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "npm run build:node && npm run build:bundler && npm run build:web && npm run build:unignore",
|
|
72
|
+
"build:node": "wasm-pack build ../crates/workclock-wasm --release --target nodejs --out-dir ../../js/pkg/node --out-name workclock",
|
|
73
|
+
"build:bundler": "wasm-pack build ../crates/workclock-wasm --release --target bundler --out-dir ../../js/pkg/bundler --out-name workclock",
|
|
74
|
+
"build:web": "wasm-pack build ../crates/workclock-wasm --release --target web --out-dir ../../js/pkg/web --out-name workclock",
|
|
75
|
+
"build:unignore": "node --eval \"for (const t of ['node', 'bundler', 'web']) require('node:fs').rmSync('pkg/' + t + '/.gitignore', { force: true })\"",
|
|
76
|
+
"prepack": "node --eval \"for (const f of ['LICENSE', 'NOTICE']) require('node:fs').copyFileSync('../' + f, f)\"",
|
|
77
|
+
"typecheck": "tsc --noEmit",
|
|
78
|
+
"test": "vitest run",
|
|
79
|
+
"bench": "node bench/bench.js"
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@types/node": "^22.20.5",
|
|
83
|
+
"fast-check": "^4.10.2",
|
|
84
|
+
"tinybench": "^6.2.2",
|
|
85
|
+
"typescript": "^7.0.2",
|
|
86
|
+
"vitest": "^5.0.5"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "workclock-wasm",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Karthik-beta/workclock"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"workclock_bg.wasm",
|
|
12
|
+
"workclock.js",
|
|
13
|
+
"workclock_bg.js",
|
|
14
|
+
"workclock.d.ts"
|
|
15
|
+
],
|
|
16
|
+
"main": "workclock.js",
|
|
17
|
+
"types": "workclock.d.ts",
|
|
18
|
+
"sideEffects": [
|
|
19
|
+
"./workclock.js",
|
|
20
|
+
"./snippets/*"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/** A day of the week. */
|
|
5
|
+
export type Weekday = "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Working hours (`"workday"`) or time off within them (`"break"`) on some days
|
|
9
|
+
* of the week. Times are wall-clock times in the rule's time zone, written
|
|
10
|
+
* `HH:MM`, `HH:MM:SS` or `HH:MM:SS.fff` on a 24-hour clock, such as `"09:00"`
|
|
11
|
+
* or `"17:30:00"`.
|
|
12
|
+
*/
|
|
13
|
+
export interface HoursRule {
|
|
14
|
+
type: "workday" | "break";
|
|
15
|
+
/** Wall-clock start, such as `"09:00"`. */
|
|
16
|
+
start: string;
|
|
17
|
+
/** Wall-clock end; at or before `start` means the hours end the next day. */
|
|
18
|
+
end: string;
|
|
19
|
+
/**
|
|
20
|
+
* Days on which the hours start. Defaults to Monday to Friday, for breaks
|
|
21
|
+
* too, so a break on a weekend workday needs its own `days`.
|
|
22
|
+
*/
|
|
23
|
+
days?: readonly Weekday[];
|
|
24
|
+
/** IANA time zone; defaults to the schedule's. */
|
|
25
|
+
timeZone?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Dates that are not worked, each from 00:00 to the next 00:00. */
|
|
29
|
+
export interface HolidaysRule {
|
|
30
|
+
type: "holidays";
|
|
31
|
+
/** Dates such as `"2026-12-25"`. */
|
|
32
|
+
dates: readonly string[];
|
|
33
|
+
/** IANA time zone; defaults to the schedule's. */
|
|
34
|
+
timeZone?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* One rule of a schedule. Working time is the time inside workdays and
|
|
39
|
+
* outside breaks and holidays.
|
|
40
|
+
*/
|
|
41
|
+
export type Rule = HoursRule | HolidaysRule;
|
|
42
|
+
|
|
43
|
+
/** What `new Schedule` takes. */
|
|
44
|
+
export interface ScheduleOptions {
|
|
45
|
+
/** IANA time zone for rules without their own; defaults to `"UTC"`. */
|
|
46
|
+
timeZone?: string;
|
|
47
|
+
/** The rules, in any order. */
|
|
48
|
+
rules: readonly Rule[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A `Date`, or an ISO 8601 string with a UTC offset. */
|
|
52
|
+
export type Instant = Date | string;
|
|
53
|
+
|
|
54
|
+
/** A period: a maximal stretch of working time. */
|
|
55
|
+
export interface Period {
|
|
56
|
+
start: Date;
|
|
57
|
+
end: Date;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Batch methods. Instants come as an array of `Instant`s, returning numbers or
|
|
62
|
+
* `Date`s, or as a `Float64Array` of milliseconds since the epoch, returning a
|
|
63
|
+
* `Float64Array`. Durations come as one number for every item, or one per item.
|
|
64
|
+
* A failing item throws, naming its index.
|
|
65
|
+
*/
|
|
66
|
+
export interface Schedule {
|
|
67
|
+
/**
|
|
68
|
+
* `between` for each pair.
|
|
69
|
+
* @throws {RangeError} If `a` and `b` differ in length.
|
|
70
|
+
*/
|
|
71
|
+
betweenMany(a: Float64Array, b: Float64Array): Float64Array;
|
|
72
|
+
betweenMany(a: readonly Instant[], b: readonly Instant[]): number[];
|
|
73
|
+
betweenMany(a: Float64Array | readonly Instant[], b: Float64Array | readonly Instant[]): Float64Array | number[];
|
|
74
|
+
/** `add` for each instant. */
|
|
75
|
+
addMany(t: Float64Array, ms: number | readonly number[] | Float64Array): Float64Array;
|
|
76
|
+
addMany(t: readonly Instant[], ms: number | readonly number[] | Float64Array): Date[];
|
|
77
|
+
addMany(t: Float64Array | readonly Instant[], ms: number | readonly number[] | Float64Array): Float64Array | Date[];
|
|
78
|
+
/** `sub` for each instant. */
|
|
79
|
+
subMany(t: Float64Array, ms: number | readonly number[] | Float64Array): Float64Array;
|
|
80
|
+
subMany(t: readonly Instant[], ms: number | readonly number[] | Float64Array): Date[];
|
|
81
|
+
subMany(t: Float64Array | readonly Instant[], ms: number | readonly number[] | Float64Array): Float64Array | Date[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Working time defined by rules, evaluated in IANA time zones.
|
|
88
|
+
*
|
|
89
|
+
* Instants are `Date`s or ISO 8601 strings with a UTC offset, and durations
|
|
90
|
+
* are numbers of milliseconds. Results are exact to the millisecond.
|
|
91
|
+
*/
|
|
92
|
+
export class Schedule {
|
|
93
|
+
free(): void;
|
|
94
|
+
[Symbol.dispose](): void;
|
|
95
|
+
/**
|
|
96
|
+
* Finds the instant a given amount of working time after another.
|
|
97
|
+
*
|
|
98
|
+
* @param t The instant to count from.
|
|
99
|
+
* @param ms Working time in milliseconds; a negative amount counts back.
|
|
100
|
+
* @returns The earliest instant with `ms` of working time after `t`, or
|
|
101
|
+
* `t` itself when `ms` is zero.
|
|
102
|
+
* @throws {NoWorkingTimeError} If no working time is found within 366
|
|
103
|
+
* days, plus seven days per holiday date.
|
|
104
|
+
* @throws {OutOfRangeError} If `t` or the result is outside the supported
|
|
105
|
+
* range.
|
|
106
|
+
* @throws {TypeError} If `t` is neither a `Date` nor a string, or `ms` is
|
|
107
|
+
* not a number.
|
|
108
|
+
* @throws {RangeError} If `t` is malformed, or `ms` is not a whole number
|
|
109
|
+
* within ±`Number.MAX_SAFE_INTEGER`.
|
|
110
|
+
* @example
|
|
111
|
+
* ```js
|
|
112
|
+
* const due = support.add("2026-12-24T16:30:00Z", 8 * 3_600_000);
|
|
113
|
+
* due.toISOString(); // "2026-12-30T09:30:00.000Z"
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
add(t: Instant, ms: number): Date;
|
|
117
|
+
/**
|
|
118
|
+
* Measures the working time between two instants.
|
|
119
|
+
*
|
|
120
|
+
* @param a The start.
|
|
121
|
+
* @param b The end.
|
|
122
|
+
* @returns The working time in milliseconds; negative when `b` is before
|
|
123
|
+
* `a`.
|
|
124
|
+
* @throws {OutOfRangeError} If an instant is outside the supported range.
|
|
125
|
+
* @throws {TypeError} If an instant is neither a `Date` nor a string.
|
|
126
|
+
* @throws {RangeError} If an instant is an invalid `Date` or a string
|
|
127
|
+
* without a UTC offset.
|
|
128
|
+
* @example
|
|
129
|
+
* ```js
|
|
130
|
+
* support.between("2026-01-05T09:00:00Z", "2026-01-06T09:00:00Z"); // 25200000, 7 hours
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
between(a: Instant, b: Instant): number;
|
|
134
|
+
/**
|
|
135
|
+
* Builds a schedule from its rules.
|
|
136
|
+
*
|
|
137
|
+
* @param options The default time zone and the rules.
|
|
138
|
+
* @throws {UnknownZoneError} If a time zone is not in the IANA database.
|
|
139
|
+
* @throws {NoWorkingTimeError} If the working hours minus the breaks are
|
|
140
|
+
* empty.
|
|
141
|
+
* @throws {TypeError} If `options` or a rule has the wrong shape; the
|
|
142
|
+
* message names the rule, such as `rules[1]`.
|
|
143
|
+
* @throws {RangeError} If a time, day or date cannot be read.
|
|
144
|
+
* @example
|
|
145
|
+
* ```js
|
|
146
|
+
* const support = new Schedule({
|
|
147
|
+
* timeZone: "Europe/London",
|
|
148
|
+
* rules: [
|
|
149
|
+
* { type: "workday", start: "09:00", end: "17:00" },
|
|
150
|
+
* { type: "break", start: "12:00", end: "13:00" },
|
|
151
|
+
* { type: "holidays", dates: ["2026-12-25", "2026-12-28"] },
|
|
152
|
+
* ],
|
|
153
|
+
* });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
constructor(options: ScheduleOptions);
|
|
157
|
+
/**
|
|
158
|
+
* Finds the period of working time at or after an instant.
|
|
159
|
+
*
|
|
160
|
+
* @param t The instant to search from.
|
|
161
|
+
* @returns The period that contains `t`, starting at `t`, or else the
|
|
162
|
+
* first one after it.
|
|
163
|
+
* @throws {NoWorkingTimeError} If no working time is found within 366
|
|
164
|
+
* days, plus seven days per holiday date.
|
|
165
|
+
* @throws {OutOfRangeError} If `t` is outside the supported range, or the
|
|
166
|
+
* search reaches its end.
|
|
167
|
+
* @throws {TypeError} If `t` is neither a `Date` nor a string.
|
|
168
|
+
* @throws {RangeError} If `t` is malformed.
|
|
169
|
+
*/
|
|
170
|
+
nextPeriod(t: Instant): Period;
|
|
171
|
+
/**
|
|
172
|
+
* Finds the period of working time at or before an instant.
|
|
173
|
+
*
|
|
174
|
+
* @param t The instant to search back from.
|
|
175
|
+
* @returns The period that contains `t`, ending at `t`, or else the last
|
|
176
|
+
* one before it.
|
|
177
|
+
* @throws {NoWorkingTimeError} If no working time is found within 366
|
|
178
|
+
* days, plus seven days per holiday date.
|
|
179
|
+
* @throws {OutOfRangeError} If `t` is outside the supported range, or the
|
|
180
|
+
* search reaches its start.
|
|
181
|
+
* @throws {TypeError} If `t` is neither a `Date` nor a string.
|
|
182
|
+
* @throws {RangeError} If `t` is malformed.
|
|
183
|
+
*/
|
|
184
|
+
previousPeriod(t: Instant): Period;
|
|
185
|
+
/**
|
|
186
|
+
* Finds the instant a given amount of working time before another.
|
|
187
|
+
*
|
|
188
|
+
* @param t The instant to count back from.
|
|
189
|
+
* @param ms Working time in milliseconds; a negative amount counts
|
|
190
|
+
* forward.
|
|
191
|
+
* @returns The latest instant with `ms` of working time before `t`, or
|
|
192
|
+
* `t` itself when `ms` is zero.
|
|
193
|
+
* @throws {NoWorkingTimeError} If no working time is found within 366
|
|
194
|
+
* days, plus seven days per holiday date.
|
|
195
|
+
* @throws {OutOfRangeError} If `t` or the result is outside the supported
|
|
196
|
+
* range.
|
|
197
|
+
* @throws {TypeError} If `t` is neither a `Date` nor a string, or `ms` is
|
|
198
|
+
* not a number.
|
|
199
|
+
* @throws {RangeError} If `t` is malformed, or `ms` is not a whole number
|
|
200
|
+
* within ±`Number.MAX_SAFE_INTEGER`.
|
|
201
|
+
*/
|
|
202
|
+
sub(t: Instant, ms: number): Date;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Returns the version of workclock this module was built from, such as
|
|
207
|
+
* `"0.1.0"`.
|
|
208
|
+
*/
|
|
209
|
+
export function version(): string;
|