date-vir 9.3.0 → 9.4.1

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.
@@ -0,0 +1,119 @@
1
+ import { type Branded } from '@augment-vir/common';
2
+ import { type RoundOptions } from '@date-vir/duration';
3
+ import { type FullDate } from '../full-date/full-date-shape.js';
4
+ import { type Timezone } from '../timezone/timezones.js';
5
+ import { type UtcIsoString } from './string-format-types.js';
6
+ /**
7
+ * A calendar day with no time of day and no timezone, stored as a {@link UtcIsoString} at midnight
8
+ * UTC of that day. This matches how database `DATE` columns (such as Prisma's `@db.Date`) are read,
9
+ * so the value passes between a database and an API unchanged.
10
+ *
11
+ * @category ISO
12
+ */
13
+ export type DateOnlyUtcIsoString = Branded<UtcIsoString, 'date-only-utc-iso-string'>;
14
+ /**
15
+ * Checks if the input is a valid {@link DateOnlyUtcIsoString}: a {@link UtcIsoString} whose time is
16
+ * exactly midnight UTC.
17
+ *
18
+ * @category ISO
19
+ * @category Assertion
20
+ * @example
21
+ *
22
+ * ```ts
23
+ * import {isValidDateOnlyUtcIsoString} from 'date-vir';
24
+ *
25
+ * isValidDateOnlyUtcIsoString('2024-05-01T00:00:00.000Z'); // `true`
26
+ * isValidDateOnlyUtcIsoString('2024-05-01T20:18:17.123Z'); // `false`
27
+ * ```
28
+ */
29
+ export declare function isValidDateOnlyUtcIsoString(input: unknown): input is DateOnlyUtcIsoString;
30
+ /**
31
+ * A shape definition for {@link DateOnlyUtcIsoString}.
32
+ *
33
+ * @category ISO
34
+ */
35
+ export declare const dateOnlyUtcIsoStringShape: (defaultValue?: DateOnlyUtcIsoString | undefined) => import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<DateOnlyUtcIsoString>>;
36
+ /**
37
+ * Converts a {@link FullDate} into the {@link DateOnlyUtcIsoString} of the calendar day it falls on
38
+ * in its own timezone. To get the day in a different timezone, convert the date first with
39
+ * `toNewTimezone`.
40
+ *
41
+ * @category ISO
42
+ * @category Conversion
43
+ * @example
44
+ *
45
+ * ```ts
46
+ * import {TimezoneName, toDateOnlyUtcIsoString} from 'date-vir';
47
+ *
48
+ * toDateOnlyUtcIsoString({
49
+ * year: 2026,
50
+ * month: 10,
51
+ * day: 2,
52
+ *
53
+ * hour: 23,
54
+ * minute: 30,
55
+ * second: 0,
56
+ * millisecond: 0,
57
+ *
58
+ * timezone: TimezoneName['America/Denver'],
59
+ * }); // outputs `'2026-10-02T00:00:00.000Z'` (even though this is October 3rd in UTC)
60
+ * ```
61
+ */
62
+ export declare function toDateOnlyUtcIsoString(date: Readonly<FullDate>): DateOnlyUtcIsoString;
63
+ /**
64
+ * Converts a {@link DateOnlyUtcIsoString} into a {@link FullDate} at the start of that same calendar
65
+ * day in the given timezone. The calendar day never shifts. The start of a day is usually midnight
66
+ * but can be later in timezones that have had a DST transition at midnight.
67
+ *
68
+ * @category ISO
69
+ * @category Conversion
70
+ * @example
71
+ *
72
+ * ```ts
73
+ * import {
74
+ * createUtcFullDate,
75
+ * dateOnlyUtcIsoStringToFullDate,
76
+ * TimezoneName,
77
+ * toDateOnlyUtcIsoString,
78
+ * } from 'date-vir';
79
+ *
80
+ * const day = toDateOnlyUtcIsoString(createUtcFullDate('2026-10-02T00:00:00.000Z'));
81
+ *
82
+ * dateOnlyUtcIsoStringToFullDate(day, TimezoneName['America/Denver']);
83
+ * // outputs `{year: 2026, month: 10, day: 2, hour: 0, minute: 0, second: 0, millisecond: 0, timezone: 'America/Denver'}`
84
+ * ```
85
+ */
86
+ export declare function dateOnlyUtcIsoStringToFullDate<const SpecificTimezone extends Timezone>(dateOnly: DateOnlyUtcIsoString, timezone: SpecificTimezone): FullDate<SpecificTimezone>;
87
+ /**
88
+ * Gets today's calendar day in the given timezone as a {@link DateOnlyUtcIsoString}.
89
+ *
90
+ * @category Now
91
+ * @example
92
+ *
93
+ * ```ts
94
+ * import {getTodayDateOnlyUtcIsoString, TimezoneName} from 'date-vir';
95
+ *
96
+ * getTodayDateOnlyUtcIsoString(TimezoneName['America/Denver']);
97
+ * ```
98
+ */
99
+ export declare function getTodayDateOnlyUtcIsoString(timezone: Timezone): DateOnlyUtcIsoString;
100
+ /**
101
+ * Counts the whole calendar days from `start` to `end`. Negative when `end` is before `start`. Add
102
+ * `1` for an inclusive count of the days in the range.
103
+ *
104
+ * @category Calculation
105
+ * @example
106
+ *
107
+ * ```ts
108
+ * import {createUtcFullDate, diffDateOnlyDays, toDateOnlyUtcIsoString} from 'date-vir';
109
+ *
110
+ * diffDateOnlyDays({
111
+ * start: toDateOnlyUtcIsoString(createUtcFullDate('2026-10-02T00:00:00.000Z')),
112
+ * end: toDateOnlyUtcIsoString(createUtcFullDate('2026-10-05T00:00:00.000Z')),
113
+ * }); // outputs `3`
114
+ * ```
115
+ */
116
+ export declare function diffDateOnlyDays({ start, end, }: Readonly<{
117
+ start: DateOnlyUtcIsoString;
118
+ end: DateOnlyUtcIsoString;
119
+ }>, options?: Readonly<RoundOptions>): number;
@@ -0,0 +1,141 @@
1
+ import { applyBrand } from '@augment-vir/common';
2
+ import { DateUnit } from '@date-vir/duration';
3
+ import { createCustomShape } from 'object-shape-tester';
4
+ import { getStartDate } from '../date-operations/date-position.js';
5
+ import { diffDates } from '../date-operations/diff-dates.js';
6
+ import { getNowFullDate } from '../extra-utils/now.js';
7
+ import { clearTime } from '../extra-utils/zero.js';
8
+ import { createUtcFullDate } from '../full-date/create-full-date.js';
9
+ import { utcTimezone } from '../timezone/timezones.js';
10
+ import { isValidIsoString } from './string-format-types.js';
11
+ import { toUtcIsoString } from './timestamp.js';
12
+ /**
13
+ * Checks if the input is a valid {@link DateOnlyUtcIsoString}: a {@link UtcIsoString} whose time is
14
+ * exactly midnight UTC.
15
+ *
16
+ * @category ISO
17
+ * @category Assertion
18
+ * @example
19
+ *
20
+ * ```ts
21
+ * import {isValidDateOnlyUtcIsoString} from 'date-vir';
22
+ *
23
+ * isValidDateOnlyUtcIsoString('2024-05-01T00:00:00.000Z'); // `true`
24
+ * isValidDateOnlyUtcIsoString('2024-05-01T20:18:17.123Z'); // `false`
25
+ * ```
26
+ */
27
+ export function isValidDateOnlyUtcIsoString(input) {
28
+ return isValidIsoString(input) && toDateOnlyUtcIsoString(createUtcFullDate(input)) === input;
29
+ }
30
+ /**
31
+ * A shape definition for {@link DateOnlyUtcIsoString}.
32
+ *
33
+ * @category ISO
34
+ */
35
+ export const dateOnlyUtcIsoStringShape = createCustomShape({
36
+ default: applyBrand('2000-01-01T00:00:00.000Z'),
37
+ name: 'DateOnlyUtcIsoString',
38
+ checkValue(value) {
39
+ return isValidDateOnlyUtcIsoString(value);
40
+ },
41
+ });
42
+ /**
43
+ * Converts a {@link FullDate} into the {@link DateOnlyUtcIsoString} of the calendar day it falls on
44
+ * in its own timezone. To get the day in a different timezone, convert the date first with
45
+ * `toNewTimezone`.
46
+ *
47
+ * @category ISO
48
+ * @category Conversion
49
+ * @example
50
+ *
51
+ * ```ts
52
+ * import {TimezoneName, toDateOnlyUtcIsoString} from 'date-vir';
53
+ *
54
+ * toDateOnlyUtcIsoString({
55
+ * year: 2026,
56
+ * month: 10,
57
+ * day: 2,
58
+ *
59
+ * hour: 23,
60
+ * minute: 30,
61
+ * second: 0,
62
+ * millisecond: 0,
63
+ *
64
+ * timezone: TimezoneName['America/Denver'],
65
+ * }); // outputs `'2026-10-02T00:00:00.000Z'` (even though this is October 3rd in UTC)
66
+ * ```
67
+ */
68
+ export function toDateOnlyUtcIsoString(date) {
69
+ return applyBrand(toUtcIsoString({
70
+ ...clearTime(date),
71
+ timezone: utcTimezone,
72
+ }));
73
+ }
74
+ /**
75
+ * Converts a {@link DateOnlyUtcIsoString} into a {@link FullDate} at the start of that same calendar
76
+ * day in the given timezone. The calendar day never shifts. The start of a day is usually midnight
77
+ * but can be later in timezones that have had a DST transition at midnight.
78
+ *
79
+ * @category ISO
80
+ * @category Conversion
81
+ * @example
82
+ *
83
+ * ```ts
84
+ * import {
85
+ * createUtcFullDate,
86
+ * dateOnlyUtcIsoStringToFullDate,
87
+ * TimezoneName,
88
+ * toDateOnlyUtcIsoString,
89
+ * } from 'date-vir';
90
+ *
91
+ * const day = toDateOnlyUtcIsoString(createUtcFullDate('2026-10-02T00:00:00.000Z'));
92
+ *
93
+ * dateOnlyUtcIsoStringToFullDate(day, TimezoneName['America/Denver']);
94
+ * // outputs `{year: 2026, month: 10, day: 2, hour: 0, minute: 0, second: 0, millisecond: 0, timezone: 'America/Denver'}`
95
+ * ```
96
+ */
97
+ export function dateOnlyUtcIsoStringToFullDate(dateOnly, timezone) {
98
+ return getStartDate({
99
+ ...createUtcFullDate(dateOnly),
100
+ timezone,
101
+ }, DateUnit.Day);
102
+ }
103
+ /**
104
+ * Gets today's calendar day in the given timezone as a {@link DateOnlyUtcIsoString}.
105
+ *
106
+ * @category Now
107
+ * @example
108
+ *
109
+ * ```ts
110
+ * import {getTodayDateOnlyUtcIsoString, TimezoneName} from 'date-vir';
111
+ *
112
+ * getTodayDateOnlyUtcIsoString(TimezoneName['America/Denver']);
113
+ * ```
114
+ */
115
+ export function getTodayDateOnlyUtcIsoString(timezone) {
116
+ return toDateOnlyUtcIsoString(getNowFullDate(timezone));
117
+ }
118
+ /**
119
+ * Counts the whole calendar days from `start` to `end`. Negative when `end` is before `start`. Add
120
+ * `1` for an inclusive count of the days in the range.
121
+ *
122
+ * @category Calculation
123
+ * @example
124
+ *
125
+ * ```ts
126
+ * import {createUtcFullDate, diffDateOnlyDays, toDateOnlyUtcIsoString} from 'date-vir';
127
+ *
128
+ * diffDateOnlyDays({
129
+ * start: toDateOnlyUtcIsoString(createUtcFullDate('2026-10-02T00:00:00.000Z')),
130
+ * end: toDateOnlyUtcIsoString(createUtcFullDate('2026-10-05T00:00:00.000Z')),
131
+ * }); // outputs `3`
132
+ * ```
133
+ */
134
+ export function diffDateOnlyDays({ start, end, }, options = {}) {
135
+ return diffDates({
136
+ start: createUtcFullDate(start),
137
+ end: createUtcFullDate(end),
138
+ }, {
139
+ days: true,
140
+ }, options).days;
141
+ }
@@ -47,7 +47,7 @@ export type UtcIsoString = `${JustDateString}T${JustTimeWithSecondsString}.${num
47
47
  *
48
48
  * @category ISO
49
49
  */
50
- export declare const utcIsoStringShape: (defaultValue?: `${number}-${number}-${number}T${number}:${number}:${number}.${number}Z` | undefined) => import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<`${number}-${number}-${number}T${number}:${number}:${number}.${number}Z`>>;
50
+ export declare function utcIsoStringShape(defaultValue?: UtcIsoString): import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<`${number}-${number}-${number}T${number}:${number}:${number}.${number}Z`>>;
51
51
  /**
52
52
  * Checks if the input is a valid UTC ISO string and type guards the input.
53
53
  *
@@ -1,17 +1,26 @@
1
1
  import { DateTime } from 'luxon';
2
2
  import { createCustomShape } from 'object-shape-tester';
3
3
  /**
4
- * A shape definition for {@link UtcIsoString}.
5
- *
6
- * @category ISO
4
+ * Bundlers ignore `@__NO_SIDE_EFFECTS__` on the function `createCustomShape` returns, so
5
+ * {@link utcIsoStringShape} wraps this in a declaration that can carry it. That lets bundlers drop
6
+ * unused shapes that call {@link utcIsoStringShape}.
7
7
  */
8
- export const utcIsoStringShape = createCustomShape({
8
+ const createUtcIsoStringShape = createCustomShape({
9
9
  default: new Date().toISOString(),
10
10
  name: 'UtcIsoString',
11
11
  checkValue(value) {
12
12
  return isValidIsoString(value);
13
13
  },
14
14
  });
15
+ /**
16
+ * A shape definition for {@link UtcIsoString}.
17
+ *
18
+ * @category ISO
19
+ */
20
+ /* @__NO_SIDE_EFFECTS__ */
21
+ export function utcIsoStringShape(defaultValue) {
22
+ return createUtcIsoStringShape(defaultValue);
23
+ }
15
24
  /**
16
25
  * Checks if the input is a valid UTC ISO string and type guards the input.
17
26
  *
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from './extra-utils/js-date-conversion.js';
10
10
  export * from './extra-utils/now.js';
11
11
  export * from './extra-utils/override-date.js';
12
12
  export * from './extra-utils/zero.js';
13
+ export * from './formatting/date-only-iso-string.js';
13
14
  export * from './formatting/formatted-string.js';
14
15
  export * from './formatting/html-input.js';
15
16
  export * from './formatting/relative-string.js';
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ export * from './extra-utils/js-date-conversion.js';
10
10
  export * from './extra-utils/now.js';
11
11
  export * from './extra-utils/override-date.js';
12
12
  export * from './extra-utils/zero.js';
13
+ export * from './formatting/date-only-iso-string.js';
13
14
  export * from './formatting/formatted-string.js';
14
15
  export * from './formatting/html-input.js';
15
16
  export * from './formatting/relative-string.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "date-vir",
3
- "version": "9.3.0",
3
+ "version": "9.4.1",
4
4
  "description": "Easy and explicit dates and times.",
5
5
  "keywords": [
6
6
  "date",
@@ -35,16 +35,16 @@
35
35
  "test:docs": "virmator docs check"
36
36
  },
37
37
  "dependencies": {
38
- "@augment-vir/assert": "^32.5.0",
39
- "@augment-vir/common": "^32.5.0",
40
- "@date-vir/duration": "^9.3.0",
41
- "@types/luxon": "^3.7.5",
38
+ "@augment-vir/assert": "^32.6.0",
39
+ "@augment-vir/common": "^32.6.0",
40
+ "@date-vir/duration": "^9.4.1",
41
+ "@types/luxon": "^3.7.6",
42
42
  "luxon": "^3.7.2",
43
43
  "object-shape-tester": "^6.15.1",
44
44
  "type-fest": "^5.10.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@augment-vir/test": "^32.5.0",
47
+ "@augment-vir/test": "^32.6.0",
48
48
  "@web/dev-server-esbuild": "^2.0.0",
49
49
  "@web/test-runner": "^1.0.0",
50
50
  "@web/test-runner-commands": "^1.0.1",
@@ -52,7 +52,7 @@
52
52
  "@web/test-runner-visual-regression": "^1.0.1",
53
53
  "element-vir": "^27.0.2",
54
54
  "istanbul-smart-text-reporter": "^1.1.5",
55
- "markdown-code-example-inserter": "^3.0.6",
55
+ "markdown-code-example-inserter": "^3.1.1",
56
56
  "typedoc": "^0.28.20",
57
57
  "typescript": "^6.0.3"
58
58
  },