datetime-lube 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/CHANGELOG.md +92 -0
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/package.json +64 -0
- package/public.d.ts +611 -0
- package/src/add.js +148 -0
- package/src/calendar.js +88 -0
- package/src/date_to_string.js +106 -0
- package/src/diff.js +143 -0
- package/src/end_of.js +118 -0
- package/src/index.js +8 -0
- package/src/start_of.js +113 -0
- package/src/string_to_date.js +178 -0
- package/src/time_of.js +15 -0
- package/src/time_unit.js +6 -0
- package/src/time_zone_offset.js +22 -0
- package/src/zone.js +235 -0
- package/types/add.d.ts +34 -0
- package/types/add.d.ts.map +1 -0
- package/types/calendar.d.ts +25 -0
- package/types/calendar.d.ts.map +1 -0
- package/types/date_to_string.d.ts +29 -0
- package/types/date_to_string.d.ts.map +1 -0
- package/types/diff.d.ts +22 -0
- package/types/diff.d.ts.map +1 -0
- package/types/end_of.d.ts +20 -0
- package/types/end_of.d.ts.map +1 -0
- package/types/index.d.ts +10 -0
- package/types/index.d.ts.map +1 -0
- package/types/start_of.d.ts +20 -0
- package/types/start_of.d.ts.map +1 -0
- package/types/string_to_date.d.ts +36 -0
- package/types/string_to_date.d.ts.map +1 -0
- package/types/time_of.d.ts +7 -0
- package/types/time_of.d.ts.map +1 -0
- package/types/time_unit.d.ts +8 -0
- package/types/time_unit.d.ts.map +1 -0
- package/types/time_zone_offset.d.ts +15 -0
- package/types/time_zone_offset.d.ts.map +1 -0
- package/types/zone.d.ts +67 -0
- package/types/zone.d.ts.map +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- **The package is an ES module only.** `import` loads `src/index.js`, and there is no CommonJS
|
|
8
|
+
build: `require("datetime-lube")` works on Node.js 20.19+ and 22.12+, which load ES modules with `require`.
|
|
9
|
+
Jest loads it once its ESM mode is on or the package is transformed.
|
|
10
|
+
- **Renamed to `datetime-lube`, and the version restarts at `1.0.0`.** `date-lube` is removed
|
|
11
|
+
from npm. Replace `npm i date-lube` with `npm i datetime-lube`,
|
|
12
|
+
and every `from "date-lube"` with `from "datetime-lube"`.
|
|
13
|
+
- **Every function returns a new `Date` and never changes the given one.** `add` used to change
|
|
14
|
+
the date that was passed in, so `const tomorrow = add(today, "1D")` also moved `today`. Replace
|
|
15
|
+
`add(date, "1D")` used for its side effect with `date = add(date, "1D")`.
|
|
16
|
+
- **`timeZone(date, zone, inversion?)` is removed.** It moved the instant so that the local clock
|
|
17
|
+
showed another zone. Every function takes the zone instead: replace
|
|
18
|
+
`dateToString(timeZone(date, zone), format)` with `dateToString(date, format, zone)`, use
|
|
19
|
+
`Intl.DateTimeFormat` with `timeZone` for text that people read, and replace
|
|
20
|
+
`timeZone(shifted, zone, true)` with `stringToDate(dateToString(shifted), undefined, zone)`.
|
|
21
|
+
- **`add` clamps years and months to the end of the month.** `add(new Date(2024, 0, 31), "1M")` was
|
|
22
|
+
Mar 2 and is now Feb 29, and `"1Y"` from Feb 29 lands on Feb 28. Days are unaffected.
|
|
23
|
+
- **`add` adds hours, minutes, seconds and milliseconds as elapsed time.** Across a daylight saving
|
|
24
|
+
transition `"1H"` is exactly one hour; it used to follow the local clock.
|
|
25
|
+
- **`add` throws a `RangeError` for a sum it cannot read.** `"1d"` and `"1 D"` were ignored and
|
|
26
|
+
`"1.5D"` added 5 days; each throws now, as does an empty sum, while spaces between the parts of
|
|
27
|
+
`"1Y 2D"` still work.
|
|
28
|
+
- **A mistake in the call throws, also for an invalid `Date`.** A duration or unit the function
|
|
29
|
+
does not know or a time zone that `Intl` rejects throws a `RangeError` before an invalid `Date`
|
|
30
|
+
returns its invalid result, and a date that is not a `Date` (for `stringToDate`, a string)
|
|
31
|
+
throws a `TypeError`. The message names the function and quotes the value, such as
|
|
32
|
+
`add: Invalid duration "1d"`, `startOf: Invalid unit "sss"` or
|
|
33
|
+
`dateToString: Invalid time zone "Asia/Soul"`, and keeps the error of `Intl` as its `cause`.
|
|
34
|
+
- **`stringToDate` rejects out-of-range values and trailing text.** `2222-02-30` was Mar 2 and hour
|
|
35
|
+
`24` was the next day; both return an invalid `Date` now, with and without a format. Without a
|
|
36
|
+
format only `24:00` stays, as the end of the day in ECMAScript. With a format, text left after it,
|
|
37
|
+
such as `"2024-07-15 junk"` for `"YYYY-MM-DD"`, gives an invalid `Date`, while digits after `sss`
|
|
38
|
+
are still ignored.
|
|
39
|
+
- **`YYYY` prints years outside 0 to 9999 as ECMAScript expanded years**, `+012345` and `-000005`
|
|
40
|
+
instead of `12345` and `-0005`, so the default format is a valid date string again, and
|
|
41
|
+
`stringToDate` reads them back with and without a format.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- `package.json` declares `engines.node` as `>=18`: older Node.js cannot read time zone offsets
|
|
46
|
+
as `longOffset`.
|
|
47
|
+
- **A time zone argument on every function.** `dateToString`, `stringToDate`, `add`, `startOf`,
|
|
48
|
+
`endOf` and `diff` take a time zone as their last argument, the local one when left out. A wall clock inside a daylight saving
|
|
49
|
+
gap moves forward and one that occurs twice takes the first occurrence, in every zone, as
|
|
50
|
+
`new Date(2024, 2, 10, 2, 30)` does in the local zone. A result outside the `Date` range is an
|
|
51
|
+
invalid `Date`, never a throw.
|
|
52
|
+
- `startOf(date, unit, zone?)` and `endOf(date, unit, zone?)` for `"Y"`, `"M"`, `"W"`, `"D"`,
|
|
53
|
+
`"H"`, `"m"` and `"s"`; a week starts on Monday, as in ISO 8601. A unit ends at the last
|
|
54
|
+
millisecond before the next one starts, so a day that begins with a daylight saving gap still
|
|
55
|
+
ends at 23:59:59.999; `"H"`, `"m"` and `"s"` stay in the given occurrence of a repeated hour, and
|
|
56
|
+
a unit whose first moment falls into a gap starts at the transition.
|
|
57
|
+
- `diff(from, to, unit, zone?)` counts whole units, following the clock for `"Y"`, `"M"`, `"W"`
|
|
58
|
+
and `"D"` and elapsed time for the rest. Days are counted on the calendar, so a skipped date
|
|
59
|
+
such as 2011-12-30 in `Pacific/Apia` still counts as a day, and a month is complete once the day
|
|
60
|
+
and time of the first date are reached, on the last day of a month that lacks that day, so
|
|
61
|
+
`diff` counts back what `add` added: from Feb 29, 2024 to Feb 28, 2025 is 1 year.
|
|
62
|
+
- `add` takes `"W"` for weeks, seven days following the clock.
|
|
63
|
+
- `timeZoneOffset(date, zone)` returns the offset east of UTC in milliseconds at that date.
|
|
64
|
+
- The `Unit` type is exported, next to `TimeZone`: `import type { Unit } from "datetime-lube"`.
|
|
65
|
+
It lists `"Y"`, `"M"`, `"W"`, `"D"`, `"H"`, `"m"`, `"s"` and `"sss"`, the units of `diff` and of
|
|
66
|
+
the sum of `add`; `startOf` and `endOf` take `Exclude<Unit, "sss">`.
|
|
67
|
+
- `TimeZone` accepts any string while still suggesting the IANA names, so a zone read from a
|
|
68
|
+
database or `Intl.DateTimeFormat().resolvedOptions().timeZone` needs no cast; a zone that `Intl`
|
|
69
|
+
rejects throws a `RangeError`. An offset such as `"+09:00"` works on a runtime whose `Intl`
|
|
70
|
+
accepts offset time zones.
|
|
71
|
+
- `diff`, `startOf` and `endOf` throw a `RangeError` for a unit they do not know, as `add` does,
|
|
72
|
+
instead of counting or rounding by another unit.
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
|
|
76
|
+
- `add` with a part of zero, such as `"0D"` or `"0M"`, moved a clock time that occurs twice to its
|
|
77
|
+
first occurrence. A zero part adds nothing now, so the second 01:30 of a night that repeats an hour
|
|
78
|
+
stays where it is.
|
|
79
|
+
- `dateToString` replaced only the first occurrence of each token: `"DD.MM.DD"` gave `04.03.DD`.
|
|
80
|
+
`"sss ss"` gave `03s ss`, and negative years were padded as `00-1`.
|
|
81
|
+
- `stringToDate` returned an invalid `Date` for a format without `MM` or `DD`, such as
|
|
82
|
+
`stringToDate("11:22", "HH:mm")`. The missing parts default to `0000-01-01T00:00:00.000` now. A
|
|
83
|
+
format with `sss` before `ss` was misread.
|
|
84
|
+
- `stringToDate` without a format ignored a time zone in the string, so `"2024-07-15T12:00:00Z"`
|
|
85
|
+
was read as local time. The platform parser reads the string now, while a date without a time,
|
|
86
|
+
including `"2024-07"` or `"2024"`, stays local midnight instead of becoming UTC midnight. With a
|
|
87
|
+
time zone argument the wall clock of the string is read in that zone, and an offset in the
|
|
88
|
+
string, such as `Z`, `+09:00` or `EST`, still applies.
|
|
89
|
+
- `stringToDate` needed the exact number of digits, so `"2024-7-1"` with `"YYYY-MM-DD"` failed.
|
|
90
|
+
- `dateToString` printed `"0NaN-NaN-NaN..."` for an invalid Date instead of `"Invalid Date"`.
|
|
91
|
+
- The `TimeZone` type listed `"TZ identifier"`, `"DST"` and `"Factory"`, which `Intl` rejects, and
|
|
92
|
+
lacked `"America/Coyhaique"`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 artxe
|
|
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/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Datetime Lube
|
|
2
|
+
Date math, formatting and parsing for the platform's own `Date`, in any time zone.
|
|
3
|
+
|
|
4
|
+
There is no wrapper object: every function takes and returns the platform's own `Date`, and never changes the one it is given. A time zone needs no plugin or setup, since the math, formatting and parsing functions take one as their last argument, and use the local zone when it is left out.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
```bash
|
|
8
|
+
npm i datetime-lube
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
The results below write a `Date` as `Date { … }`: without an offset it is the time on the local clock, and with `Z` or an offset such as `-04:00` it is the time at that offset.
|
|
13
|
+
|
|
14
|
+
### Add and subtract time
|
|
15
|
+
```ts
|
|
16
|
+
import { add } from "datetime-lube"
|
|
17
|
+
|
|
18
|
+
const date = new Date("2024-01-31T09:00")
|
|
19
|
+
const noon = new Date("2024-03-09T12:00-05:00")
|
|
20
|
+
|
|
21
|
+
add(date, "1M") //=> Date { 2024-02-29T09:00:00.000 }, clamped to the end of the month
|
|
22
|
+
add(date, "1Y-2W 3H30m") //=> Date { 2025-01-17T12:30:00.000 }
|
|
23
|
+
add(noon, "1D", "America/New_York") //=> Date { 2024-03-10T12:00:00.000-04:00 }, 23 hours later
|
|
24
|
+
```
|
|
25
|
+
- The sum lists numbers, each followed by a unit: `"Y"` years, `"M"` months, `"W"` weeks, `"D"` days, `"H"` hours, `"m"` minutes, `"s"` seconds or `"sss"` milliseconds. A number may have a sign and spaces may separate the parts; anything else, such as `"1d"` or an empty sum, throws a `RangeError`.
|
|
26
|
+
- Years, months, weeks and days follow the clock, so a day over a daylight saving change can be 23 or 25 hours, while the smaller units are elapsed time.
|
|
27
|
+
- Units apply from left to right, so `"1M-1D"` and `"-1D1M"` can differ at the end of a month.
|
|
28
|
+
|
|
29
|
+
### Format a date
|
|
30
|
+
```ts
|
|
31
|
+
import { dateToString } from "datetime-lube"
|
|
32
|
+
|
|
33
|
+
const date = new Date("2222-03-04T11:22:33.444")
|
|
34
|
+
|
|
35
|
+
dateToString(date) //=> "2222-03-04T11:22:33.444"
|
|
36
|
+
dateToString(date, "HHhmmmssSsss _ YYYY/MM/DD") //=> "11h22m33S444 _ 2222/03/04"
|
|
37
|
+
dateToString(new Date("2024-07-15T12:00Z"), "MM/DD HH:mm", "Asia/Seoul") //=> "07/15 21:00"
|
|
38
|
+
```
|
|
39
|
+
- Tokens are `YYYY` year, `MM` month, `DD` day, `HH` hour from 00 to 23, `mm` minute, `ss` second and `sss` millisecond, as in the ECMAScript date time string `YYYY-MM-DDTHH:mm:ss.sss`, which is also the default format. So milliseconds are `sss`, never `SSS`.
|
|
40
|
+
- Everything else is printed as it is, like `h`, `m` and `S` above or the `SSS`, `hh` and `a` of other libraries; there is no escape syntax.
|
|
41
|
+
- A year before 0 or after 9999 is printed as the ECMAScript expanded year, such as `+012345` or `-000005`, which `stringToDate` reads back.
|
|
42
|
+
- An invalid `Date` gives `"Invalid Date"`.
|
|
43
|
+
- For text that people read, such as month names, weekdays or AM and PM, use `Intl.DateTimeFormat`.
|
|
44
|
+
|
|
45
|
+
### Parse a string
|
|
46
|
+
```ts
|
|
47
|
+
import { stringToDate } from "datetime-lube"
|
|
48
|
+
|
|
49
|
+
stringToDate("2024-07-15") //=> Date { 2024-07-15T00:00:00.000 }
|
|
50
|
+
stringToDate("2024-07-15T12:00:00+09:00") //=> Date { 2024-07-15T03:00:00.000Z }
|
|
51
|
+
stringToDate("2024/7/1 9h05", "YYYY-MM-DD HH:mm") //=> Date { 2024-07-01T09:05:00.000 }
|
|
52
|
+
stringToDate("15.07.2024", "DD.MM.YYYY", "Asia/Seoul") //=> Date { 2024-07-15T00:00:00.000+09:00 }
|
|
53
|
+
stringToDate("2024-02-30", "YYYY-MM-DD") //=> Invalid Date
|
|
54
|
+
```
|
|
55
|
+
- Without a format, the platform parser reads the string and applies an offset in it, such as `Z`, `+09:00` or `EST`. A string without an offset is read in the time zone, even a date alone, which `new Date("2024-07-15")` would read as UTC. Check user input with a format.
|
|
56
|
+
- With a format, each token reads up to its own number of digits, and every other character skips one character of the string. Tokens missing from the format default to `0000-01-01T00:00:00.000`.
|
|
57
|
+
- A token with no digits or a value out of range, such as February 30, or text left after the format gives an invalid `Date`.
|
|
58
|
+
- [Parsing details](#parsing-details) covers separators, times without a date and strings read without a format.
|
|
59
|
+
|
|
60
|
+
### Start and end of a unit
|
|
61
|
+
```ts
|
|
62
|
+
import { endOf, startOf } from "datetime-lube"
|
|
63
|
+
|
|
64
|
+
const date = new Date("2024-07-17T09:30")
|
|
65
|
+
|
|
66
|
+
startOf(date, "W") //=> Date { 2024-07-15T00:00:00.000 }
|
|
67
|
+
endOf(date, "M") //=> Date { 2024-07-31T23:59:59.999 }
|
|
68
|
+
endOf(new Date("2024-07-17T09:30Z"), "D", "Asia/Seoul") //=> Date { 2024-07-17T23:59:59.999+09:00 }
|
|
69
|
+
```
|
|
70
|
+
- Units are `"Y"`, `"M"`, `"W"`, `"D"`, `"H"`, `"m"` and `"s"`, every `Unit` but `"sss"`, and a week starts on Monday, as in ISO 8601. Any other unit throws a `RangeError`.
|
|
71
|
+
- The end of a unit is the last millisecond before the next one starts, so a day ends at 23:59:59.999 even when daylight saving time starts at midnight.
|
|
72
|
+
|
|
73
|
+
### Count units between dates
|
|
74
|
+
```ts
|
|
75
|
+
import { diff } from "datetime-lube"
|
|
76
|
+
|
|
77
|
+
const from = new Date("2024-01-31T23:00")
|
|
78
|
+
const to = new Date("2024-02-29T01:00")
|
|
79
|
+
|
|
80
|
+
diff(from, to, "M") //=> 0, Feb 29 23:00 is not reached
|
|
81
|
+
diff(from, to, "D") //=> 29
|
|
82
|
+
diff(to, from, "W") //=> -4
|
|
83
|
+
```
|
|
84
|
+
- `"D"` and `"W"` count calendar days, whatever the time of day. `"Y"` and `"M"` count whole months; a month is complete once the day and time of the first date are reached, on the last day of a month that lacks that day, as `add` lands there: `diff(date, add(date, "1M"), "M")` is 1, also from Jan 31 or Feb 29.
|
|
85
|
+
- `"H"`, `"m"`, `"s"` and `"sss"` are elapsed time. Every count is truncated toward zero and negative when the second date is earlier. Any other unit throws a `RangeError`.
|
|
86
|
+
- To compare two dates, use the platform: `a < b`, `+a === +b` and `+b - +a` for milliseconds.
|
|
87
|
+
|
|
88
|
+
### Time zones
|
|
89
|
+
```ts
|
|
90
|
+
import { dateToString, timeZoneOffset } from "datetime-lube"
|
|
91
|
+
import type { TimeZone } from "datetime-lube"
|
|
92
|
+
|
|
93
|
+
const zone: TimeZone = "America/New_York"
|
|
94
|
+
const date = new Date("2024-07-15T12:00Z")
|
|
95
|
+
|
|
96
|
+
timeZoneOffset(date, zone) //=> -14400000
|
|
97
|
+
dateToString(date, undefined, zone) //=> "2024-07-15T08:00:00.000"
|
|
98
|
+
new Intl.DateTimeFormat("en-US", { hour: "2-digit", hourCycle: "h23", minute: "2-digit", timeZone: zone }).format(date) //=> "08:00"
|
|
99
|
+
```
|
|
100
|
+
- To show a date in a time zone, pass the zone to `dateToString`, with `undefined` for the default format, or to `Intl.DateTimeFormat` for text that people read. The `Date` stays the same instant; there is no function that shifts it so the local clock shows another zone.
|
|
101
|
+
- A clock time inside a daylight saving gap moves forward, and one that occurs twice takes the first occurrence, as `new Date(2024, 2, 10, 2, 30)` does in New York. `stringToDate`, `add`, `startOf` and `endOf` follow this rule; [Daylight saving details](#daylight-saving-details) has the exceptions.
|
|
102
|
+
- `timeZoneOffset` is the offset east of UTC in milliseconds at the given date, so a summer date gives the summer offset.
|
|
103
|
+
- `TimeZone` suggests the IANA names but accepts any string, such as a name read from a database. An offset like `"+09:00"` works only on a runtime whose `Intl` accepts offset time zones, a recent addition; elsewhere it throws like any zone that `Intl` rejects.
|
|
104
|
+
- `datetime-lube` exports the `TimeZone` and `Unit` types next to its functions.
|
|
105
|
+
|
|
106
|
+
### Time units in milliseconds
|
|
107
|
+
```ts
|
|
108
|
+
import { timeUnit } from "datetime-lube"
|
|
109
|
+
|
|
110
|
+
timeUnit //=> { DD: 86400000, HH: 3600000, mm: 60000, ss: 1000 }
|
|
111
|
+
2 * timeUnit.HH + 30 * timeUnit.mm //=> 9000000
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Details
|
|
115
|
+
### Parsing details
|
|
116
|
+
- A format checks digits and length, not separators: a separator skips any one character, so `"YYYY-MM-DD"` also reads `2025/01/01`, `2025-1-01` and `25-01-01` (the year 25). To accept only one shape, test the string with a regular expression such as `/^\d{4}-\d{2}-\d{2}$/` first.
|
|
117
|
+
- `sss` reads its digits as a fraction, so `.5` is 500 ms, and ignores further digits; `YYYY` also reads an expanded year such as `+012345`.
|
|
118
|
+
- A format without a date reads the time on 0000-01-01, where a time zone is at its local mean time (`Asia/Seoul` is +08:27:52), so read a time alone with `"UTC"` and join it to a date before it becomes an instant:
|
|
119
|
+
```ts
|
|
120
|
+
const time = stringToDate(input, "HH:mm", "UTC") // check the input
|
|
121
|
+
const start = stringToDate(`${day} ${dateToString(time, "HH:mm", "UTC")}`, "YYYY-MM-DD HH:mm", "Asia/Seoul")
|
|
122
|
+
```
|
|
123
|
+
- Without a format, a date or time out of range in an ECMAScript date string, such as `2024-02-30` or `23:60`, gives an invalid `Date` instead of rolling over; `24:00` is the end of the day, as in ECMAScript. Other strings are read as the platform reads them, and V8 rolls `"2025/02/30"` and `"Feb 30 2025"` over to March 2.
|
|
124
|
+
|
|
125
|
+
### Daylight saving details
|
|
126
|
+
- A part of zero adds nothing, so `add(date, "0D")` is the same instant even in an hour that occurs twice. Every other part of years, months, weeks or days lands on a clock time and takes its first occurrence, so `"1D-1D"` from the second 01:30 of a night that repeats 01:00 to 02:00 ends on the first 01:30, as Temporal adds one part after the other.
|
|
127
|
+
- In an hour that occurs twice, `startOf` and `endOf` of `"H"`, `"m"` and `"s"` stay in the occurrence of the given date, so an hour is never longer than an hour and its end plus one millisecond starts the next.
|
|
128
|
+
- Without a time zone argument the local time zone comes from the platform's `Date`, mistakes included: V8 can report a stale offset right after a few historical transitions, such as Europe/Tirane in 1943. Pass the zone name when historical offsets matter.
|
|
129
|
+
|
|
130
|
+
### Errors
|
|
131
|
+
- An invalid `Date` gives an invalid `Date`, `NaN` or `"Invalid Date"`, while a mistake in the call throws, even for an invalid `Date`: a unit or duration the function does not know or a time zone that `Intl` rejects throws a `RangeError`, and a date that is not a `Date` (a string for `stringToDate`) throws a `TypeError`.
|
|
132
|
+
- The message starts with the function and quotes the value, such as `add: Invalid duration "1d"` or `startOf: Invalid time zone "Asia/Soul"`.
|
|
133
|
+
|
|
134
|
+
## What it is not
|
|
135
|
+
- **Not a replacement for `Temporal`.** When every runtime you target has it, use it: it is the standard, it separates an instant from a wall clock, and the results here are fuzzed against it, so moving over does not change what your code computes. This package is for the code that holds a `Date`.
|
|
136
|
+
- **Not text for people.** Month names, weekday names, AM and PM, and relative time such as "3 days ago" are `Intl.DateTimeFormat` and `Intl.RelativeTimeFormat`, which every runtime has.
|
|
137
|
+
- **Not a comparison library.** Two `Date` values compare with `<` and `>`, and their distance is `b - a`.
|
|
138
|
+
- **Not a calendar library.** Only the Gregorian calendar of `Date`, with no lunar, fiscal or business-day arithmetic.
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "artxe",
|
|
3
|
+
"bugs": {
|
|
4
|
+
"url": "https://github.com/artxe/lube-series/issues"
|
|
5
|
+
},
|
|
6
|
+
"description": "Simplify Date Manipulation with Time Zones in JavaScript",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@typescript/native": "npm:typescript@^7.0.2"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./types/index.d.ts",
|
|
16
|
+
"default": "./src/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"public.d.ts",
|
|
22
|
+
"src",
|
|
23
|
+
"types"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://github.com/artxe/lube-series/tree/master/packages/datetime-lube#readme",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"date",
|
|
28
|
+
"datetime",
|
|
29
|
+
"datetime-lube",
|
|
30
|
+
"diff",
|
|
31
|
+
"dst",
|
|
32
|
+
"format",
|
|
33
|
+
"lube",
|
|
34
|
+
"parse",
|
|
35
|
+
"start of",
|
|
36
|
+
"syntax sugar",
|
|
37
|
+
"time zone",
|
|
38
|
+
"timezone"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"main": "src/index.js",
|
|
42
|
+
"name": "datetime-lube",
|
|
43
|
+
"repository": {
|
|
44
|
+
"directory": "packages/datetime-lube",
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/artxe/lube-series"
|
|
47
|
+
},
|
|
48
|
+
"sideEffects": false,
|
|
49
|
+
"type": "module",
|
|
50
|
+
"types": "types/index.d.ts",
|
|
51
|
+
"typesVersions": {
|
|
52
|
+
"*": {
|
|
53
|
+
"*": [
|
|
54
|
+
"types/index.d.ts"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"version": "1.0.0",
|
|
59
|
+
"scripts": {
|
|
60
|
+
"dts": "node ../../scripts/tsc_with_clean.js datetime-lube",
|
|
61
|
+
"lint": "pnpm -w lint",
|
|
62
|
+
"test": "pnpm -w exec vitest run --coverage test/datetime-lube"
|
|
63
|
+
}
|
|
64
|
+
}
|