nanoduration 1.0.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/README.md +153 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +267 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Duration
|
|
2
|
+
|
|
3
|
+
An immutable, non-negative, Rust-inspired `Duration` type for JavaScript and TypeScript.
|
|
4
|
+
|
|
5
|
+
This library provides a small, predictable abstraction for representing spans of time with explicit units, saturating arithmetic, and strong invariants—without calendars, time zones, or parsing logic.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
`Duration` is a value-type abstraction for time intervals, modeled after Rust’s `std::time::Duration`.
|
|
12
|
+
|
|
13
|
+
It is designed for:
|
|
14
|
+
|
|
15
|
+
- timeouts
|
|
16
|
+
- delays
|
|
17
|
+
- retry/backoff logic
|
|
18
|
+
- infrastructure and systems-oriented code
|
|
19
|
+
|
|
20
|
+
The library prioritizes correctness, explicitness, and simplicity over feature breadth.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## What this library does
|
|
25
|
+
|
|
26
|
+
- Represents time as an immutable value (internally stored in nanoseconds)
|
|
27
|
+
- Enforces **non-negative durations**
|
|
28
|
+
- Provides **explicit unit constructors** (`seconds`, `milliseconds`, etc.)
|
|
29
|
+
- Supports **saturating arithmetic** (durations never go negative)
|
|
30
|
+
- Avoids all calendar, timezone, and locale concerns
|
|
31
|
+
- Ships with **full TypeScript type definitions**
|
|
32
|
+
|
|
33
|
+
### What it intentionally does not do
|
|
34
|
+
|
|
35
|
+
- Date/time manipulation
|
|
36
|
+
- Parsing human-readable strings
|
|
37
|
+
- Time zones or calendars
|
|
38
|
+
- Implicit unit coercion
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npm install nanoduration
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Creating durations
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Duration } from "nanoduration";
|
|
56
|
+
|
|
57
|
+
const a = Duration.fromSecs(2);
|
|
58
|
+
const b = Duration.fromMillis(500);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Arithmetic
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const total = a.add(b); // 2.5 seconds
|
|
65
|
+
const remaining = total.sub(Duration.fromSecs(5)); // saturates to 0
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Conversions
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
total.asMillis(); // 2500
|
|
72
|
+
total.asSecs(); // 2.5
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Comparisons
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
total.gt(Duration.fromSecs(2)); // true
|
|
79
|
+
total.eq(Duration.fromMillis(2500)); // true
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Utilities
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
Duration.ZERO.isZero(); // true
|
|
86
|
+
|
|
87
|
+
const clamped = total.clamp(Duration.fromSecs(1), Duration.fromSecs(3));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Interop with Node.js APIs
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
// ...
|
|
95
|
+
}, total.asMillis());
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Benefits of using this library
|
|
101
|
+
|
|
102
|
+
### Predictable semantics
|
|
103
|
+
|
|
104
|
+
- Durations never become negative
|
|
105
|
+
- No silent unit conversions
|
|
106
|
+
- No hidden calendar logic
|
|
107
|
+
|
|
108
|
+
### Safer than raw numbers
|
|
109
|
+
|
|
110
|
+
- Units are explicit at construction
|
|
111
|
+
- Arithmetic behavior is well-defined
|
|
112
|
+
- Fewer “milliseconds vs seconds” bugs
|
|
113
|
+
|
|
114
|
+
### Lightweight
|
|
115
|
+
|
|
116
|
+
- No dependencies
|
|
117
|
+
- Small API surface
|
|
118
|
+
- Tree-shakable
|
|
119
|
+
- Minimal runtime overhead
|
|
120
|
+
|
|
121
|
+
### TypeScript-first
|
|
122
|
+
|
|
123
|
+
- Strict typings included
|
|
124
|
+
- Works out of the box with modern ESM setups
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Design notes
|
|
129
|
+
|
|
130
|
+
- Internal unit: **nanoseconds**
|
|
131
|
+
- Arithmetic: **saturating**
|
|
132
|
+
- Mutability: **immutable**
|
|
133
|
+
- Module format: **ESM**
|
|
134
|
+
- Inspired by: Rust’s `std::time::Duration`
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
Contributions are welcome.
|
|
141
|
+
|
|
142
|
+
### Guidelines
|
|
143
|
+
|
|
144
|
+
1. Keep the API minimal and explicit
|
|
145
|
+
2. Maintain non-negative duration invariants
|
|
146
|
+
3. Preserve backward compatibility where possible
|
|
147
|
+
4. Run `npm run build` before submitting changes
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT License.
|
|
152
|
+
|
|
153
|
+
See the `LICENSE` file for details.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents an immutable, non-negative span of time.
|
|
3
|
+
*
|
|
4
|
+
* ## Design characteristics
|
|
5
|
+
* - Internally stored as nanoseconds
|
|
6
|
+
* - Saturating arithmetic (never becomes negative)
|
|
7
|
+
* - No calendar, timezone, or locale semantics
|
|
8
|
+
* - Explicit unit construction and observation
|
|
9
|
+
*
|
|
10
|
+
* This type is intended for:
|
|
11
|
+
* - Timeouts
|
|
12
|
+
* - Intervals
|
|
13
|
+
* - Backoff calculations
|
|
14
|
+
* - System and infrastructure code
|
|
15
|
+
*/
|
|
16
|
+
export declare class Duration {
|
|
17
|
+
/** Nanoseconds per microsecond */
|
|
18
|
+
private static readonly NS_PER_MICRO;
|
|
19
|
+
/** Nanoseconds per millisecond */
|
|
20
|
+
private static readonly NS_PER_MILLI;
|
|
21
|
+
/** Nanoseconds per second */
|
|
22
|
+
private static readonly NS_PER_SECOND;
|
|
23
|
+
/** Nanoseconds per minute */
|
|
24
|
+
private static readonly NS_PER_MINUTE;
|
|
25
|
+
/** Nanoseconds per hour */
|
|
26
|
+
private static readonly NS_PER_HOUR;
|
|
27
|
+
/**
|
|
28
|
+
* A duration of zero length.
|
|
29
|
+
*/
|
|
30
|
+
static readonly ZERO: Duration;
|
|
31
|
+
private readonly nanoseconds;
|
|
32
|
+
/**
|
|
33
|
+
* Internal constructor.
|
|
34
|
+
*
|
|
35
|
+
* All instances are guaranteed to contain a non-negative,
|
|
36
|
+
* finite number of nanoseconds.
|
|
37
|
+
*/
|
|
38
|
+
private constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Creates a duration from nanoseconds.
|
|
41
|
+
*
|
|
42
|
+
* @param nanos - Number of nanoseconds
|
|
43
|
+
*/
|
|
44
|
+
static fromNanos(nanos: number): Duration;
|
|
45
|
+
/**
|
|
46
|
+
* Creates a duration from microseconds.
|
|
47
|
+
*
|
|
48
|
+
* @param micros - Number of microseconds
|
|
49
|
+
*/
|
|
50
|
+
static fromMicros(micros: number): Duration;
|
|
51
|
+
/**
|
|
52
|
+
* Creates a duration from milliseconds.
|
|
53
|
+
*
|
|
54
|
+
* @param millis - Number of milliseconds
|
|
55
|
+
*/
|
|
56
|
+
static fromMillis(millis: number): Duration;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a duration from seconds.
|
|
59
|
+
*
|
|
60
|
+
* @param secs - Number of seconds
|
|
61
|
+
*/
|
|
62
|
+
static fromSecs(secs: number): Duration;
|
|
63
|
+
/**
|
|
64
|
+
* Creates a duration from minutes.
|
|
65
|
+
*
|
|
66
|
+
* @param minutes - Number of minutes
|
|
67
|
+
*/
|
|
68
|
+
static fromMinutes(minutes: number): Duration;
|
|
69
|
+
/**
|
|
70
|
+
* Creates a duration from hours.
|
|
71
|
+
*
|
|
72
|
+
* @param hours - Number of hours
|
|
73
|
+
*/
|
|
74
|
+
static fromHours(hours: number): Duration;
|
|
75
|
+
/**
|
|
76
|
+
* Returns the total duration in nanoseconds.
|
|
77
|
+
*/
|
|
78
|
+
asNanos(): number;
|
|
79
|
+
/**
|
|
80
|
+
* Returns the total duration in microseconds.
|
|
81
|
+
*/
|
|
82
|
+
asMicros(): number;
|
|
83
|
+
/**
|
|
84
|
+
* Returns the total duration in milliseconds.
|
|
85
|
+
*/
|
|
86
|
+
asMillis(): number;
|
|
87
|
+
/**
|
|
88
|
+
* Returns the total duration in seconds.
|
|
89
|
+
*/
|
|
90
|
+
asSecs(): number;
|
|
91
|
+
/**
|
|
92
|
+
* Returns the total duration in minutes.
|
|
93
|
+
*/
|
|
94
|
+
asMinutes(): number;
|
|
95
|
+
/**
|
|
96
|
+
* Returns the total duration in hours.
|
|
97
|
+
*/
|
|
98
|
+
asHours(): number;
|
|
99
|
+
/**
|
|
100
|
+
* Returns the sum of this duration and another.
|
|
101
|
+
*
|
|
102
|
+
* If the result would be negative or overflow, it saturates at zero
|
|
103
|
+
* or throws respectively.
|
|
104
|
+
*/
|
|
105
|
+
add(other: Duration): Duration;
|
|
106
|
+
/**
|
|
107
|
+
* Returns the difference between this duration and another.
|
|
108
|
+
*
|
|
109
|
+
* If the result would be negative, it saturates at zero.
|
|
110
|
+
*/
|
|
111
|
+
sub(other: Duration): Duration;
|
|
112
|
+
/**
|
|
113
|
+
* Multiplies this duration by a factor.
|
|
114
|
+
*
|
|
115
|
+
* Negative or non-finite results saturate at zero or throw.
|
|
116
|
+
*/
|
|
117
|
+
mul(factor: number): Duration;
|
|
118
|
+
/**
|
|
119
|
+
* Divides this duration by a divisor.
|
|
120
|
+
*
|
|
121
|
+
* @throws {RangeError} If divisor is zero
|
|
122
|
+
*/
|
|
123
|
+
div(divisor: number): Duration;
|
|
124
|
+
/**
|
|
125
|
+
* Returns true if both durations are equal.
|
|
126
|
+
*/
|
|
127
|
+
eq(other: Duration): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Returns true if this duration is less than the other.
|
|
130
|
+
*/
|
|
131
|
+
lt(other: Duration): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Returns true if this duration is less than or equal to the other.
|
|
134
|
+
*/
|
|
135
|
+
le(other: Duration): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Returns true if this duration is greater than the other.
|
|
138
|
+
*/
|
|
139
|
+
gt(other: Duration): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Returns true if this duration is greater than or equal to the other.
|
|
142
|
+
*/
|
|
143
|
+
ge(other: Duration): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Returns true if this duration is zero.
|
|
146
|
+
*/
|
|
147
|
+
isZero(): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Clamps this duration between a minimum and maximum.
|
|
150
|
+
*
|
|
151
|
+
* @throws {RangeError} If min is greater than max
|
|
152
|
+
*/
|
|
153
|
+
clamp(min: Duration, max: Duration): Duration;
|
|
154
|
+
/**
|
|
155
|
+
* Returns a human-readable string representation.
|
|
156
|
+
*
|
|
157
|
+
* Chooses the largest unit that divides evenly.
|
|
158
|
+
* Values are rounded to 6 decimal places when needed.
|
|
159
|
+
*/
|
|
160
|
+
toString(): string;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwCA;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ;IACnB,kCAAkC;IAClC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAO;IAE3C,kCAAkC;IAClC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAO;IAE3C,6BAA6B;IAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAO;IAE5C,6BAA6B;IAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAA+B;IAEpE,2BAA2B;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAElE;;OAEG;IACH,gBAAuB,IAAI,WAA+B;IAE1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiB;IAE7C;;;;;OAKG;IACH,OAAO;IAQP;;;;OAIG;WACW,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IAIhD;;;;OAIG;WACW,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAIlD;;;;OAIG;WACW,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAIlD;;;;OAIG;WACW,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ;IAI9C;;;;OAIG;WACW,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAIpD;;;;OAIG;WACW,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IAQhD;;OAEG;IACI,OAAO,IAAI,MAAM;IAIxB;;OAEG;IACI,QAAQ,IAAI,MAAM;IAIzB;;OAEG;IACI,QAAQ,IAAI,MAAM;IAIzB;;OAEG;IACI,MAAM,IAAI,MAAM;IAIvB;;OAEG;IACI,SAAS,IAAI,MAAM;IAI1B;;OAEG;IACI,OAAO,IAAI,MAAM;IAUxB;;;;;OAKG;IACI,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ;IAIrC;;;;OAIG;IACI,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ;IAIrC;;;;OAIG;IACI,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAIpC;;;;OAIG;IACI,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAYrC;;OAEG;IACI,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAInC;;OAEG;IACI,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAInC;;OAEG;IACI,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAInC;;OAEG;IACI,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAInC;;OAEG;IACI,EAAE,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAQnC;;OAEG;IACI,MAAM,IAAI,OAAO;IAIxB;;;;OAIG;IACI,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,GAAG,QAAQ;IAYpD;;;;;OAKG;IACI,QAAQ,IAAI,MAAM;CAqB1B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/* ===========================
|
|
2
|
+
* PositiveNumber (unforgeable)
|
|
3
|
+
* =========================== */
|
|
4
|
+
/**
|
|
5
|
+
* Converts a number into a {@link PositiveNumber} using saturating semantics.
|
|
6
|
+
*
|
|
7
|
+
* - Non-finite values (`NaN`, `Infinity`, `-Infinity`) throw.
|
|
8
|
+
* - Negative values are clamped to `0`.
|
|
9
|
+
*
|
|
10
|
+
* @param num - Input number
|
|
11
|
+
* @returns A non-negative finite number
|
|
12
|
+
* @throws {RangeError} If the value is not finite
|
|
13
|
+
*/
|
|
14
|
+
function toPositive(num) {
|
|
15
|
+
if (!Number.isFinite(num)) {
|
|
16
|
+
throw new RangeError("Value must be finite");
|
|
17
|
+
}
|
|
18
|
+
// Saturating semantics: never negative
|
|
19
|
+
return (num <= 0 ? 0 : num);
|
|
20
|
+
}
|
|
21
|
+
/* ===========================
|
|
22
|
+
* Duration
|
|
23
|
+
* =========================== */
|
|
24
|
+
/**
|
|
25
|
+
* Represents an immutable, non-negative span of time.
|
|
26
|
+
*
|
|
27
|
+
* ## Design characteristics
|
|
28
|
+
* - Internally stored as nanoseconds
|
|
29
|
+
* - Saturating arithmetic (never becomes negative)
|
|
30
|
+
* - No calendar, timezone, or locale semantics
|
|
31
|
+
* - Explicit unit construction and observation
|
|
32
|
+
*
|
|
33
|
+
* This type is intended for:
|
|
34
|
+
* - Timeouts
|
|
35
|
+
* - Intervals
|
|
36
|
+
* - Backoff calculations
|
|
37
|
+
* - System and infrastructure code
|
|
38
|
+
*/
|
|
39
|
+
export class Duration {
|
|
40
|
+
/**
|
|
41
|
+
* Internal constructor.
|
|
42
|
+
*
|
|
43
|
+
* All instances are guaranteed to contain a non-negative,
|
|
44
|
+
* finite number of nanoseconds.
|
|
45
|
+
*/
|
|
46
|
+
constructor(nanoseconds) {
|
|
47
|
+
this.nanoseconds = nanoseconds;
|
|
48
|
+
}
|
|
49
|
+
/* ============
|
|
50
|
+
* Constructors
|
|
51
|
+
* ============ */
|
|
52
|
+
/**
|
|
53
|
+
* Creates a duration from nanoseconds.
|
|
54
|
+
*
|
|
55
|
+
* @param nanos - Number of nanoseconds
|
|
56
|
+
*/
|
|
57
|
+
static fromNanos(nanos) {
|
|
58
|
+
return new Duration(toPositive(nanos));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a duration from microseconds.
|
|
62
|
+
*
|
|
63
|
+
* @param micros - Number of microseconds
|
|
64
|
+
*/
|
|
65
|
+
static fromMicros(micros) {
|
|
66
|
+
return new Duration(toPositive(micros * Duration.NS_PER_MICRO));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates a duration from milliseconds.
|
|
70
|
+
*
|
|
71
|
+
* @param millis - Number of milliseconds
|
|
72
|
+
*/
|
|
73
|
+
static fromMillis(millis) {
|
|
74
|
+
return new Duration(toPositive(millis * Duration.NS_PER_MILLI));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Creates a duration from seconds.
|
|
78
|
+
*
|
|
79
|
+
* @param secs - Number of seconds
|
|
80
|
+
*/
|
|
81
|
+
static fromSecs(secs) {
|
|
82
|
+
return new Duration(toPositive(secs * Duration.NS_PER_SECOND));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a duration from minutes.
|
|
86
|
+
*
|
|
87
|
+
* @param minutes - Number of minutes
|
|
88
|
+
*/
|
|
89
|
+
static fromMinutes(minutes) {
|
|
90
|
+
return new Duration(toPositive(minutes * Duration.NS_PER_MINUTE));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Creates a duration from hours.
|
|
94
|
+
*
|
|
95
|
+
* @param hours - Number of hours
|
|
96
|
+
*/
|
|
97
|
+
static fromHours(hours) {
|
|
98
|
+
return new Duration(toPositive(hours * Duration.NS_PER_HOUR));
|
|
99
|
+
}
|
|
100
|
+
/* =========
|
|
101
|
+
* Observers
|
|
102
|
+
* ========= */
|
|
103
|
+
/**
|
|
104
|
+
* Returns the total duration in nanoseconds.
|
|
105
|
+
*/
|
|
106
|
+
asNanos() {
|
|
107
|
+
return this.nanoseconds;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns the total duration in microseconds.
|
|
111
|
+
*/
|
|
112
|
+
asMicros() {
|
|
113
|
+
return this.nanoseconds / Duration.NS_PER_MICRO;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Returns the total duration in milliseconds.
|
|
117
|
+
*/
|
|
118
|
+
asMillis() {
|
|
119
|
+
return this.nanoseconds / Duration.NS_PER_MILLI;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Returns the total duration in seconds.
|
|
123
|
+
*/
|
|
124
|
+
asSecs() {
|
|
125
|
+
return this.nanoseconds / Duration.NS_PER_SECOND;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns the total duration in minutes.
|
|
129
|
+
*/
|
|
130
|
+
asMinutes() {
|
|
131
|
+
return this.nanoseconds / Duration.NS_PER_MINUTE;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns the total duration in hours.
|
|
135
|
+
*/
|
|
136
|
+
asHours() {
|
|
137
|
+
return this.nanoseconds / Duration.NS_PER_HOUR;
|
|
138
|
+
}
|
|
139
|
+
/* ==========
|
|
140
|
+
* Arithmetic
|
|
141
|
+
* ==========
|
|
142
|
+
* Saturating semantics
|
|
143
|
+
*/
|
|
144
|
+
/**
|
|
145
|
+
* Returns the sum of this duration and another.
|
|
146
|
+
*
|
|
147
|
+
* If the result would be negative or overflow, it saturates at zero
|
|
148
|
+
* or throws respectively.
|
|
149
|
+
*/
|
|
150
|
+
add(other) {
|
|
151
|
+
return new Duration(toPositive(this.nanoseconds + other.nanoseconds));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Returns the difference between this duration and another.
|
|
155
|
+
*
|
|
156
|
+
* If the result would be negative, it saturates at zero.
|
|
157
|
+
*/
|
|
158
|
+
sub(other) {
|
|
159
|
+
return new Duration(toPositive(this.nanoseconds - other.nanoseconds));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Multiplies this duration by a factor.
|
|
163
|
+
*
|
|
164
|
+
* Negative or non-finite results saturate at zero or throw.
|
|
165
|
+
*/
|
|
166
|
+
mul(factor) {
|
|
167
|
+
return new Duration(toPositive(this.nanoseconds * factor));
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Divides this duration by a divisor.
|
|
171
|
+
*
|
|
172
|
+
* @throws {RangeError} If divisor is zero
|
|
173
|
+
*/
|
|
174
|
+
div(divisor) {
|
|
175
|
+
if (divisor === 0) {
|
|
176
|
+
throw new RangeError("Division by zero");
|
|
177
|
+
}
|
|
178
|
+
return new Duration(toPositive(this.nanoseconds / divisor));
|
|
179
|
+
}
|
|
180
|
+
/* ===========
|
|
181
|
+
* Comparisons
|
|
182
|
+
* =========== */
|
|
183
|
+
/**
|
|
184
|
+
* Returns true if both durations are equal.
|
|
185
|
+
*/
|
|
186
|
+
eq(other) {
|
|
187
|
+
return this.nanoseconds === other.nanoseconds;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Returns true if this duration is less than the other.
|
|
191
|
+
*/
|
|
192
|
+
lt(other) {
|
|
193
|
+
return this.nanoseconds < other.nanoseconds;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns true if this duration is less than or equal to the other.
|
|
197
|
+
*/
|
|
198
|
+
le(other) {
|
|
199
|
+
return this.nanoseconds <= other.nanoseconds;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Returns true if this duration is greater than the other.
|
|
203
|
+
*/
|
|
204
|
+
gt(other) {
|
|
205
|
+
return this.nanoseconds > other.nanoseconds;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Returns true if this duration is greater than or equal to the other.
|
|
209
|
+
*/
|
|
210
|
+
ge(other) {
|
|
211
|
+
return this.nanoseconds >= other.nanoseconds;
|
|
212
|
+
}
|
|
213
|
+
/* =========
|
|
214
|
+
* Utilities
|
|
215
|
+
* ========= */
|
|
216
|
+
/**
|
|
217
|
+
* Returns true if this duration is zero.
|
|
218
|
+
*/
|
|
219
|
+
isZero() {
|
|
220
|
+
return this.nanoseconds === 0;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Clamps this duration between a minimum and maximum.
|
|
224
|
+
*
|
|
225
|
+
* @throws {RangeError} If min is greater than max
|
|
226
|
+
*/
|
|
227
|
+
clamp(min, max) {
|
|
228
|
+
if (min.gt(max)) {
|
|
229
|
+
throw new RangeError("min must be <= max");
|
|
230
|
+
}
|
|
231
|
+
return new Duration(toPositive(Math.min(Math.max(this.nanoseconds, min.nanoseconds), max.nanoseconds)));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Returns a human-readable string representation.
|
|
235
|
+
*
|
|
236
|
+
* Chooses the largest unit that divides evenly.
|
|
237
|
+
* Values are rounded to 6 decimal places when needed.
|
|
238
|
+
*/
|
|
239
|
+
toString() {
|
|
240
|
+
const round = (n) => Number.isInteger(n) ? n : Number(n.toFixed(6));
|
|
241
|
+
if (this.nanoseconds % Duration.NS_PER_HOUR === 0)
|
|
242
|
+
return `${round(this.asHours())}h`;
|
|
243
|
+
if (this.nanoseconds % Duration.NS_PER_MINUTE === 0)
|
|
244
|
+
return `${round(this.asMinutes())}m`;
|
|
245
|
+
if (this.nanoseconds % Duration.NS_PER_SECOND === 0)
|
|
246
|
+
return `${round(this.asSecs())}s`;
|
|
247
|
+
if (this.nanoseconds % Duration.NS_PER_MILLI === 0)
|
|
248
|
+
return `${round(this.asMillis())}ms`;
|
|
249
|
+
if (this.nanoseconds % Duration.NS_PER_MICRO === 0)
|
|
250
|
+
return `${round(this.asMicros())}µs`;
|
|
251
|
+
return `${this.nanoseconds}ns`;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/** Nanoseconds per microsecond */
|
|
255
|
+
Duration.NS_PER_MICRO = 1e3;
|
|
256
|
+
/** Nanoseconds per millisecond */
|
|
257
|
+
Duration.NS_PER_MILLI = 1e6;
|
|
258
|
+
/** Nanoseconds per second */
|
|
259
|
+
Duration.NS_PER_SECOND = 1e9;
|
|
260
|
+
/** Nanoseconds per minute */
|
|
261
|
+
Duration.NS_PER_MINUTE = 60 * Duration.NS_PER_SECOND;
|
|
262
|
+
/** Nanoseconds per hour */
|
|
263
|
+
Duration.NS_PER_HOUR = 60 * Duration.NS_PER_MINUTE;
|
|
264
|
+
/**
|
|
265
|
+
* A duration of zero length.
|
|
266
|
+
*/
|
|
267
|
+
Duration.ZERO = new Duration(toPositive(0));
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nanoduration",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "An immutable, non-negative, Rust-inspired `Duration` type for JavaScript and TypeScript",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"duration",
|
|
23
|
+
"time",
|
|
24
|
+
"typescript",
|
|
25
|
+
"immutable",
|
|
26
|
+
"esm",
|
|
27
|
+
"rust",
|
|
28
|
+
"value-object"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|