@ethisyscore/core-utils 1.21.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.
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Shared date/time format tokens (date-fns syntax).
3
+ *
4
+ * These are the canonical formats used across the EthisysCore monolith and
5
+ * plugins. Change a value here for a global locale requirement rather than
6
+ * hardcoding a format string at a call site.
7
+ */
8
+ /** ISO wire format `yyyy-MM-dd` — the standard date-only format throughout the app. */
9
+ declare const DATE_FORMAT = "yyyy-MM-dd";
10
+ /** Audit-log timestamp, e.g. `5 March 2026, 14:30`. */
11
+ declare const AUDIT_DATE_FORMAT = "d MMMM yyyy, HH:mm";
12
+ /** User-facing date display, e.g. `5 Mar 2026`. */
13
+ declare const DISPLAY_DATE_FORMAT = "d MMM yyyy";
14
+ /** Weekday + day + month, e.g. `Monday, 5 March`. */
15
+ declare const DAY_DATE_FORMAT = "EEEE, d MMMM";
16
+ /** Abbreviated day + month, e.g. `Mar 5`. */
17
+ declare const DAY_MONTH_FORMAT = "MMM d";
18
+ /** Abbreviated day + month + year, e.g. `Mar 5, 2026`. */
19
+ declare const DAY_MONTH_YEAR_FORMAT = "MMM d, yyyy";
20
+ /** 12-hour time, e.g. `2:30 PM`. */
21
+ declare const TIME_FORMAT = "h:mm a";
22
+ /** Short datetime, e.g. `Mar 5, 2:30 PM`. */
23
+ declare const SHORT_DATETIME_FORMAT = "MMM d, h:mm a";
24
+
25
+ /** Type guard: is `value` a bare `yyyy-MM-dd` date-only string? */
26
+ declare const isDateOnlyString: (value: unknown) => value is string;
27
+ /**
28
+ * Formats a `Date`, defaulting to the standard application ISO date format
29
+ * (`yyyy-MM-dd`). Pass `dateFormat` to override.
30
+ */
31
+ declare const formatDate: (date: Date, dateFormat?: string) => string;
32
+ /** Today's date as a `yyyy-MM-dd` ISO string (local calendar day). */
33
+ declare const getTodayIsoDate: () => string;
34
+ /** First day of the given date's month, as a `yyyy-MM-dd` ISO string. */
35
+ declare const getMonthStartIso: (date: Date) => string;
36
+ /** Last day of the given date's month, as a `yyyy-MM-dd` ISO string. */
37
+ declare const getMonthEndIso: (date: Date) => string;
38
+ /**
39
+ * Parses a date-only `yyyy-MM-dd` string as a LOCAL date. Returns `null` when
40
+ * the value is not a date-only string or is not a valid calendar date. Avoids
41
+ * the `new Date("yyyy-MM-dd")` UTC parse, which shifts the day in non-UTC zones.
42
+ */
43
+ declare const parseIsoDateLocal: (value: string | null | undefined) => Date | null;
44
+ /**
45
+ * Adds `days` to an ISO `yyyy-MM-dd` date and returns the result as a `yyyy-MM-dd`
46
+ * string. Parses and formats in local time (via `parseIsoDateLocal` / `formatDate`)
47
+ * so the arithmetic can't drift across a day boundary in non-UTC timezones/DST.
48
+ * Returns the input unchanged when it is not a valid date-only string.
49
+ */
50
+ declare const addDaysToIsoDate: (isoDate: string, days: number) => string;
51
+ /**
52
+ * Extracts a date-only string (`yyyy-MM-dd`) from a date string.
53
+ * - Date-only strings (`2024-05-15`) are returned as-is.
54
+ * - ISO datetime strings (`2024-05-15T00:00:00+05:30`) have the date portion
55
+ * extracted directly from the string to preserve the original calendar date
56
+ * without timezone conversion.
57
+ * - Returns empty string for null, undefined, or unparseable values.
58
+ */
59
+ declare const toDateOnlyString: (value: string | null | undefined) => string;
60
+
61
+ /**
62
+ * Formats a date string, defaulting to the standard application format
63
+ * (`yyyy-MM-dd`). Uses `parseISO` to safely handle ISO strings. Pass
64
+ * `dateFormat` to override. Returns `"-"` for empty, missing, or unparseable
65
+ * input rather than throwing.
66
+ */
67
+ declare const formatDateString: (date?: string | null, dateFormat?: string) => string;
68
+ /**
69
+ * Safely formats a date string or `Date`. Handles null, undefined, and invalid
70
+ * dates by returning a fallback string.
71
+ * @param date - The date to format (string, Date, null, or undefined)
72
+ * @param formatStr - The desired output format (defaults to `DATE_FORMAT`)
73
+ * @param fallback - Returned when the date is invalid or missing (defaults to `"—"`)
74
+ */
75
+ declare const formatDateSafe: (date: string | Date | null | undefined, formatStr?: string, fallback?: string) => string;
76
+ /**
77
+ * Formats a date string or `Date` to a format like `31st Aug 2025`.
78
+ * Handles date-only strings (`yyyy-MM-dd`) as local dates to avoid timezone shifts.
79
+ * @param date - Date string (`yyyy-MM-dd`) or `Date`
80
+ * @returns Formatted date string with ordinal suffix
81
+ */
82
+ declare const formatDateWithOrdinal: (date: string | Date | null | undefined) => string;
83
+ /**
84
+ * Formats a date as a relative time string (e.g. `2h ago`, `Just now`).
85
+ * Optimised for short labels in dropdowns; anything older than a week falls
86
+ * back to the standard date format.
87
+ * @param date - ISO string or `Date`
88
+ */
89
+ declare const formatTimeAgo: (date: string | Date | null | undefined) => string;
90
+
91
+ /**
92
+ * Pure duration / timespan helpers — no external dependencies.
93
+ *
94
+ * Cover the two duration shapes that flow across the EthisysCore wire: a
95
+ * `"HH:mm:ss"` timespan string (.NET `TimeSpan`) and a millisecond count.
96
+ */
97
+ /** Converts a `"HH:mm:ss"` timespan string to milliseconds. */
98
+ declare const timespanToMilliseconds: (timespan: string) => number;
99
+ /** Converts milliseconds to hours (as a float). */
100
+ declare const millisecondsToHours: (ms: number) => number;
101
+ /** Converts hours to milliseconds. */
102
+ declare const hoursToMilliseconds: (hours: number) => number;
103
+ /** Formats a milliseconds count as `4h 15min`. */
104
+ declare const formatMillisecondsAsTimeSpent: (ms: number) => string;
105
+ /** Formats a `"HH:mm:ss"` duration string as human readable (e.g. `2h 30m 0s`). */
106
+ declare function formatDuration(duration?: string): string;
107
+ /**
108
+ * Formats a fractional-hours number as a compact human-readable duration
109
+ * (`45m`, `2h 30m`, `3d 4h`). Defensively caps unreasonable values.
110
+ */
111
+ declare const formatDurationNumber: (durationHours?: number | null) => string;
112
+ /**
113
+ * Trims an API-supplied `"HH:mm:ss"` down to the `"HH:mm"` string used by
114
+ * MUI `TimePicker`-backed forms. Returns the supplied fallback when the value
115
+ * is missing or shorter than five characters.
116
+ */
117
+ declare function toHHmm(value: string | null | undefined, fallback: string): string;
118
+ /**
119
+ * Serialises a non-empty `"HH:mm"` / `"HH:mm:ss"` value back to the canonical
120
+ * `"HH:mm:ss"` API format. Returns `null` for empty input so callers can block
121
+ * the save rather than silently persisting midnight — an empty TimePicker
122
+ * (cleared via keyboard) is an unsaved edit, not a legitimate `00:00:00` value.
123
+ */
124
+ declare function toHHmmss(value: string): string | null;
125
+
126
+ /** Converts an ISO date string to date-input format (`yyyy-MM-dd`). */
127
+ declare const isoToDateInput: (isoString: string | undefined) => string;
128
+ /** Converts date-input format (`yyyy-MM-dd`) to a UTC ISO string at midnight UTC. */
129
+ declare const dateInputToIso: (dateString: string) => string;
130
+ /**
131
+ * Normalises a date-only value from `<input type="date">` (`yyyy-MM-dd`) into a
132
+ * canonical UTC ISO-8601 timestamp at midnight UTC, e.g.
133
+ * `2026-07-14` → `2026-07-14T00:00:00.000Z`. Use at the API-payload boundary
134
+ * for fields the BE types as `DateTimeOffset` — a bare date-only string risks
135
+ * ambiguous/failed deserialisation. A value that is already a full ISO
136
+ * timestamp is passed through unchanged. Empty/invalid → `undefined` so callers
137
+ * omit the field (BE clears it).
138
+ */
139
+ declare function dateOnlyToIsoUtc(value: string | null | undefined): string | undefined;
140
+ /**
141
+ * Returns the current local wall-clock as a `yyyy-MM-ddTHH:mm` string — the
142
+ * format an `<input type="datetime-local">` element expects. Minute precision;
143
+ * seconds and timezone deliberately omitted.
144
+ */
145
+ declare function nowLocalDateTimeInputValue(): string;
146
+ /**
147
+ * Converts a naive local-time string from `<input type="datetime-local">`
148
+ * (shape `yyyy-MM-ddTHH:mm` or `yyyy-MM-ddTHH:mm:ss`) into a full ISO-8601 UTC
149
+ * string with `Z` suffix that .NET `DateTimeOffset` cannot ambiguously
150
+ * interpret. Empty / undefined inputs pass through as `undefined`.
151
+ */
152
+ declare function localDateTimeInputToIsoUtc(value: string | null | undefined): string | undefined;
153
+ /**
154
+ * Ensures an ISO-8601 string is treated as UTC by appending a `Z` suffix when
155
+ * no explicit offset or UTC indicator is present. The backend may emit
156
+ * timestamps like `2026-05-26T06:21:00` (no Z), which JS would otherwise parse
157
+ * as local time.
158
+ */
159
+ declare function ensureUtcIso(value: string): string;
160
+ /**
161
+ * Converts an ISO-8601 string (with or without explicit offset) into the naive
162
+ * local-time shape `yyyy-MM-ddTHH:mm` expected by `<input type="datetime-local">`.
163
+ * Returns the supplied fallback (default empty string) when the input is
164
+ * missing or unparseable. When `value` carries no offset or `Z` suffix it is
165
+ * treated as UTC before converting to local time, so the edit form displays the
166
+ * correct time regardless of the user's timezone.
167
+ */
168
+ declare function isoUtcToLocalDateTimeInput(value: string | null | undefined, fallback?: string): string;
169
+
170
+ interface DateRange {
171
+ startDate: string;
172
+ endDate: string;
173
+ }
174
+ /**
175
+ * Returns a `{ startDate, endDate }` date range for a named relative period,
176
+ * formatted with `dateFormat` (defaults to the ISO date-only format). Unknown
177
+ * periods fall back to the last week.
178
+ * @param timePeriod - one of `week` | `month` | `quarter` | `year`
179
+ * @param dateFormat - output format for both bounds (defaults to `DATE_FORMAT`)
180
+ */
181
+ declare const getDateRange: (timePeriod: string, dateFormat?: string) => DateRange;
182
+ /** Computes an ISO timestamp range from a number of days back to now. */
183
+ declare function computeDateRange(days: number): {
184
+ from: string;
185
+ to: string;
186
+ };
187
+ /**
188
+ * Returns the current calendar month as a half-open UTC range `[fromUtc, beforeUtc)`,
189
+ * suitable for "this month" count filters. `fromUtc` is the first instant of the
190
+ * month; `beforeUtc` is the first instant of the next month (exclusive).
191
+ */
192
+ declare function currentMonthRangeUtc(today?: Date): {
193
+ fromUtc: string;
194
+ beforeUtc: string;
195
+ };
196
+ /**
197
+ * Computes the current UTC-offset minutes for a given IANA zone name using the
198
+ * runtime's `Intl` implementation. Returns `null` when the zone is unknown.
199
+ *
200
+ * Used by timezone auto-populate fallbacks so a browser reporting an IANA zone
201
+ * that isn't in the backend seed (e.g. `Europe/London` during BST) can still
202
+ * match against a seeded zone sharing the same current offset.
203
+ */
204
+ declare function getCurrentOffsetMinutes(iana: string): number | null;
205
+
206
+ export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, type DateRange, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hoursToMilliseconds, isDateOnlyString, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss };
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Shared date/time format tokens (date-fns syntax).
3
+ *
4
+ * These are the canonical formats used across the EthisysCore monolith and
5
+ * plugins. Change a value here for a global locale requirement rather than
6
+ * hardcoding a format string at a call site.
7
+ */
8
+ /** ISO wire format `yyyy-MM-dd` — the standard date-only format throughout the app. */
9
+ declare const DATE_FORMAT = "yyyy-MM-dd";
10
+ /** Audit-log timestamp, e.g. `5 March 2026, 14:30`. */
11
+ declare const AUDIT_DATE_FORMAT = "d MMMM yyyy, HH:mm";
12
+ /** User-facing date display, e.g. `5 Mar 2026`. */
13
+ declare const DISPLAY_DATE_FORMAT = "d MMM yyyy";
14
+ /** Weekday + day + month, e.g. `Monday, 5 March`. */
15
+ declare const DAY_DATE_FORMAT = "EEEE, d MMMM";
16
+ /** Abbreviated day + month, e.g. `Mar 5`. */
17
+ declare const DAY_MONTH_FORMAT = "MMM d";
18
+ /** Abbreviated day + month + year, e.g. `Mar 5, 2026`. */
19
+ declare const DAY_MONTH_YEAR_FORMAT = "MMM d, yyyy";
20
+ /** 12-hour time, e.g. `2:30 PM`. */
21
+ declare const TIME_FORMAT = "h:mm a";
22
+ /** Short datetime, e.g. `Mar 5, 2:30 PM`. */
23
+ declare const SHORT_DATETIME_FORMAT = "MMM d, h:mm a";
24
+
25
+ /** Type guard: is `value` a bare `yyyy-MM-dd` date-only string? */
26
+ declare const isDateOnlyString: (value: unknown) => value is string;
27
+ /**
28
+ * Formats a `Date`, defaulting to the standard application ISO date format
29
+ * (`yyyy-MM-dd`). Pass `dateFormat` to override.
30
+ */
31
+ declare const formatDate: (date: Date, dateFormat?: string) => string;
32
+ /** Today's date as a `yyyy-MM-dd` ISO string (local calendar day). */
33
+ declare const getTodayIsoDate: () => string;
34
+ /** First day of the given date's month, as a `yyyy-MM-dd` ISO string. */
35
+ declare const getMonthStartIso: (date: Date) => string;
36
+ /** Last day of the given date's month, as a `yyyy-MM-dd` ISO string. */
37
+ declare const getMonthEndIso: (date: Date) => string;
38
+ /**
39
+ * Parses a date-only `yyyy-MM-dd` string as a LOCAL date. Returns `null` when
40
+ * the value is not a date-only string or is not a valid calendar date. Avoids
41
+ * the `new Date("yyyy-MM-dd")` UTC parse, which shifts the day in non-UTC zones.
42
+ */
43
+ declare const parseIsoDateLocal: (value: string | null | undefined) => Date | null;
44
+ /**
45
+ * Adds `days` to an ISO `yyyy-MM-dd` date and returns the result as a `yyyy-MM-dd`
46
+ * string. Parses and formats in local time (via `parseIsoDateLocal` / `formatDate`)
47
+ * so the arithmetic can't drift across a day boundary in non-UTC timezones/DST.
48
+ * Returns the input unchanged when it is not a valid date-only string.
49
+ */
50
+ declare const addDaysToIsoDate: (isoDate: string, days: number) => string;
51
+ /**
52
+ * Extracts a date-only string (`yyyy-MM-dd`) from a date string.
53
+ * - Date-only strings (`2024-05-15`) are returned as-is.
54
+ * - ISO datetime strings (`2024-05-15T00:00:00+05:30`) have the date portion
55
+ * extracted directly from the string to preserve the original calendar date
56
+ * without timezone conversion.
57
+ * - Returns empty string for null, undefined, or unparseable values.
58
+ */
59
+ declare const toDateOnlyString: (value: string | null | undefined) => string;
60
+
61
+ /**
62
+ * Formats a date string, defaulting to the standard application format
63
+ * (`yyyy-MM-dd`). Uses `parseISO` to safely handle ISO strings. Pass
64
+ * `dateFormat` to override. Returns `"-"` for empty, missing, or unparseable
65
+ * input rather than throwing.
66
+ */
67
+ declare const formatDateString: (date?: string | null, dateFormat?: string) => string;
68
+ /**
69
+ * Safely formats a date string or `Date`. Handles null, undefined, and invalid
70
+ * dates by returning a fallback string.
71
+ * @param date - The date to format (string, Date, null, or undefined)
72
+ * @param formatStr - The desired output format (defaults to `DATE_FORMAT`)
73
+ * @param fallback - Returned when the date is invalid or missing (defaults to `"—"`)
74
+ */
75
+ declare const formatDateSafe: (date: string | Date | null | undefined, formatStr?: string, fallback?: string) => string;
76
+ /**
77
+ * Formats a date string or `Date` to a format like `31st Aug 2025`.
78
+ * Handles date-only strings (`yyyy-MM-dd`) as local dates to avoid timezone shifts.
79
+ * @param date - Date string (`yyyy-MM-dd`) or `Date`
80
+ * @returns Formatted date string with ordinal suffix
81
+ */
82
+ declare const formatDateWithOrdinal: (date: string | Date | null | undefined) => string;
83
+ /**
84
+ * Formats a date as a relative time string (e.g. `2h ago`, `Just now`).
85
+ * Optimised for short labels in dropdowns; anything older than a week falls
86
+ * back to the standard date format.
87
+ * @param date - ISO string or `Date`
88
+ */
89
+ declare const formatTimeAgo: (date: string | Date | null | undefined) => string;
90
+
91
+ /**
92
+ * Pure duration / timespan helpers — no external dependencies.
93
+ *
94
+ * Cover the two duration shapes that flow across the EthisysCore wire: a
95
+ * `"HH:mm:ss"` timespan string (.NET `TimeSpan`) and a millisecond count.
96
+ */
97
+ /** Converts a `"HH:mm:ss"` timespan string to milliseconds. */
98
+ declare const timespanToMilliseconds: (timespan: string) => number;
99
+ /** Converts milliseconds to hours (as a float). */
100
+ declare const millisecondsToHours: (ms: number) => number;
101
+ /** Converts hours to milliseconds. */
102
+ declare const hoursToMilliseconds: (hours: number) => number;
103
+ /** Formats a milliseconds count as `4h 15min`. */
104
+ declare const formatMillisecondsAsTimeSpent: (ms: number) => string;
105
+ /** Formats a `"HH:mm:ss"` duration string as human readable (e.g. `2h 30m 0s`). */
106
+ declare function formatDuration(duration?: string): string;
107
+ /**
108
+ * Formats a fractional-hours number as a compact human-readable duration
109
+ * (`45m`, `2h 30m`, `3d 4h`). Defensively caps unreasonable values.
110
+ */
111
+ declare const formatDurationNumber: (durationHours?: number | null) => string;
112
+ /**
113
+ * Trims an API-supplied `"HH:mm:ss"` down to the `"HH:mm"` string used by
114
+ * MUI `TimePicker`-backed forms. Returns the supplied fallback when the value
115
+ * is missing or shorter than five characters.
116
+ */
117
+ declare function toHHmm(value: string | null | undefined, fallback: string): string;
118
+ /**
119
+ * Serialises a non-empty `"HH:mm"` / `"HH:mm:ss"` value back to the canonical
120
+ * `"HH:mm:ss"` API format. Returns `null` for empty input so callers can block
121
+ * the save rather than silently persisting midnight — an empty TimePicker
122
+ * (cleared via keyboard) is an unsaved edit, not a legitimate `00:00:00` value.
123
+ */
124
+ declare function toHHmmss(value: string): string | null;
125
+
126
+ /** Converts an ISO date string to date-input format (`yyyy-MM-dd`). */
127
+ declare const isoToDateInput: (isoString: string | undefined) => string;
128
+ /** Converts date-input format (`yyyy-MM-dd`) to a UTC ISO string at midnight UTC. */
129
+ declare const dateInputToIso: (dateString: string) => string;
130
+ /**
131
+ * Normalises a date-only value from `<input type="date">` (`yyyy-MM-dd`) into a
132
+ * canonical UTC ISO-8601 timestamp at midnight UTC, e.g.
133
+ * `2026-07-14` → `2026-07-14T00:00:00.000Z`. Use at the API-payload boundary
134
+ * for fields the BE types as `DateTimeOffset` — a bare date-only string risks
135
+ * ambiguous/failed deserialisation. A value that is already a full ISO
136
+ * timestamp is passed through unchanged. Empty/invalid → `undefined` so callers
137
+ * omit the field (BE clears it).
138
+ */
139
+ declare function dateOnlyToIsoUtc(value: string | null | undefined): string | undefined;
140
+ /**
141
+ * Returns the current local wall-clock as a `yyyy-MM-ddTHH:mm` string — the
142
+ * format an `<input type="datetime-local">` element expects. Minute precision;
143
+ * seconds and timezone deliberately omitted.
144
+ */
145
+ declare function nowLocalDateTimeInputValue(): string;
146
+ /**
147
+ * Converts a naive local-time string from `<input type="datetime-local">`
148
+ * (shape `yyyy-MM-ddTHH:mm` or `yyyy-MM-ddTHH:mm:ss`) into a full ISO-8601 UTC
149
+ * string with `Z` suffix that .NET `DateTimeOffset` cannot ambiguously
150
+ * interpret. Empty / undefined inputs pass through as `undefined`.
151
+ */
152
+ declare function localDateTimeInputToIsoUtc(value: string | null | undefined): string | undefined;
153
+ /**
154
+ * Ensures an ISO-8601 string is treated as UTC by appending a `Z` suffix when
155
+ * no explicit offset or UTC indicator is present. The backend may emit
156
+ * timestamps like `2026-05-26T06:21:00` (no Z), which JS would otherwise parse
157
+ * as local time.
158
+ */
159
+ declare function ensureUtcIso(value: string): string;
160
+ /**
161
+ * Converts an ISO-8601 string (with or without explicit offset) into the naive
162
+ * local-time shape `yyyy-MM-ddTHH:mm` expected by `<input type="datetime-local">`.
163
+ * Returns the supplied fallback (default empty string) when the input is
164
+ * missing or unparseable. When `value` carries no offset or `Z` suffix it is
165
+ * treated as UTC before converting to local time, so the edit form displays the
166
+ * correct time regardless of the user's timezone.
167
+ */
168
+ declare function isoUtcToLocalDateTimeInput(value: string | null | undefined, fallback?: string): string;
169
+
170
+ interface DateRange {
171
+ startDate: string;
172
+ endDate: string;
173
+ }
174
+ /**
175
+ * Returns a `{ startDate, endDate }` date range for a named relative period,
176
+ * formatted with `dateFormat` (defaults to the ISO date-only format). Unknown
177
+ * periods fall back to the last week.
178
+ * @param timePeriod - one of `week` | `month` | `quarter` | `year`
179
+ * @param dateFormat - output format for both bounds (defaults to `DATE_FORMAT`)
180
+ */
181
+ declare const getDateRange: (timePeriod: string, dateFormat?: string) => DateRange;
182
+ /** Computes an ISO timestamp range from a number of days back to now. */
183
+ declare function computeDateRange(days: number): {
184
+ from: string;
185
+ to: string;
186
+ };
187
+ /**
188
+ * Returns the current calendar month as a half-open UTC range `[fromUtc, beforeUtc)`,
189
+ * suitable for "this month" count filters. `fromUtc` is the first instant of the
190
+ * month; `beforeUtc` is the first instant of the next month (exclusive).
191
+ */
192
+ declare function currentMonthRangeUtc(today?: Date): {
193
+ fromUtc: string;
194
+ beforeUtc: string;
195
+ };
196
+ /**
197
+ * Computes the current UTC-offset minutes for a given IANA zone name using the
198
+ * runtime's `Intl` implementation. Returns `null` when the zone is unknown.
199
+ *
200
+ * Used by timezone auto-populate fallbacks so a browser reporting an IANA zone
201
+ * that isn't in the backend seed (e.g. `Europe/London` during BST) can still
202
+ * match against a seeded zone sharing the same current offset.
203
+ */
204
+ declare function getCurrentOffsetMinutes(iana: string): number | null;
205
+
206
+ export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, type DateRange, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hoursToMilliseconds, isDateOnlyString, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss };
@@ -0,0 +1,241 @@
1
+ import { format, isValid, parseISO, subDays, subYears, subMonths } from 'date-fns';
2
+
3
+ // src/date/constants.ts
4
+ var DATE_FORMAT = "yyyy-MM-dd";
5
+ var AUDIT_DATE_FORMAT = "d MMMM yyyy, HH:mm";
6
+ var DISPLAY_DATE_FORMAT = "d MMM yyyy";
7
+ var DAY_DATE_FORMAT = "EEEE, d MMMM";
8
+ var DAY_MONTH_FORMAT = "MMM d";
9
+ var DAY_MONTH_YEAR_FORMAT = "MMM d, yyyy";
10
+ var TIME_FORMAT = "h:mm a";
11
+ var SHORT_DATETIME_FORMAT = "MMM d, h:mm a";
12
+ var isDateOnlyString = (value) => {
13
+ return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
14
+ };
15
+ var formatDate = (date, dateFormat = DATE_FORMAT) => {
16
+ return format(date, dateFormat);
17
+ };
18
+ var getTodayIsoDate = () => {
19
+ return formatDate(/* @__PURE__ */ new Date());
20
+ };
21
+ var getMonthStartIso = (date) => formatDate(new Date(date.getFullYear(), date.getMonth(), 1));
22
+ var getMonthEndIso = (date) => formatDate(new Date(date.getFullYear(), date.getMonth() + 1, 0));
23
+ var parseIsoDateLocal = (value) => {
24
+ if (!value || !isDateOnlyString(value)) return null;
25
+ const [year, month, day] = value.split("-").map(Number);
26
+ const d = new Date(year, month - 1, day);
27
+ return isValid(d) ? d : null;
28
+ };
29
+ var addDaysToIsoDate = (isoDate, days) => {
30
+ const base = parseIsoDateLocal(isoDate);
31
+ if (!base) return isoDate;
32
+ base.setDate(base.getDate() + days);
33
+ return formatDate(base);
34
+ };
35
+ var toDateOnlyString = (value) => {
36
+ if (!value) return "";
37
+ const datepart = value.split("T")[0];
38
+ return isDateOnlyString(datepart) ? datepart : "";
39
+ };
40
+ var formatDateString = (date, dateFormat = DATE_FORMAT) => {
41
+ if (!date) return "-";
42
+ const parsed = parseISO(date);
43
+ if (!isValid(parsed)) return "-";
44
+ return format(parsed, dateFormat);
45
+ };
46
+ var formatDateSafe = (date, formatStr = DATE_FORMAT, fallback = "\u2014") => {
47
+ if (!date) return fallback;
48
+ try {
49
+ const dateObj = typeof date === "string" ? parseISO(date) : date;
50
+ if (!isValid(dateObj)) return fallback;
51
+ return format(dateObj, formatStr);
52
+ } catch {
53
+ return fallback;
54
+ }
55
+ };
56
+ var getOrdinalSuffix = (day) => {
57
+ if (day > 3 && day < 21) return "th";
58
+ switch (day % 10) {
59
+ case 1:
60
+ return "st";
61
+ case 2:
62
+ return "nd";
63
+ case 3:
64
+ return "rd";
65
+ default:
66
+ return "th";
67
+ }
68
+ };
69
+ var formatDateWithOrdinal = (date) => {
70
+ if (!date) return "Not specified";
71
+ let dateObj;
72
+ if (typeof date === "string") {
73
+ if (isDateOnlyString(date)) {
74
+ const [year2, month2, day2] = date.split("-").map(Number);
75
+ dateObj = new Date(year2, month2 - 1, day2);
76
+ } else {
77
+ dateObj = parseISO(date);
78
+ }
79
+ } else {
80
+ dateObj = date;
81
+ }
82
+ if (!isValid(dateObj)) return "Invalid date";
83
+ const day = dateObj.getDate();
84
+ const month = format(dateObj, "MMM");
85
+ const year = dateObj.getFullYear();
86
+ return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;
87
+ };
88
+ var formatTimeAgo = (date) => {
89
+ if (!date) return "";
90
+ const dateObj = typeof date === "string" ? parseISO(date) : date;
91
+ if (!isValid(dateObj)) return "";
92
+ const now = /* @__PURE__ */ new Date();
93
+ const seconds = Math.floor((now.getTime() - dateObj.getTime()) / 1e3);
94
+ if (seconds < 60) return "Just now";
95
+ const minutes = Math.floor(seconds / 60);
96
+ if (minutes < 60) return `${minutes}m ago`;
97
+ const hours = Math.floor(minutes / 60);
98
+ if (hours < 24) return `${hours}h ago`;
99
+ const days = Math.floor(hours / 24);
100
+ if (days < 7) return `${days}d ago`;
101
+ return formatDate(dateObj);
102
+ };
103
+
104
+ // src/date/duration.ts
105
+ var timespanToMilliseconds = (timespan) => {
106
+ if (!timespan) return 0;
107
+ const [h = "0", m = "0", s = "0"] = timespan.split(":");
108
+ const hours = parseInt(h || "0", 10);
109
+ const minutes = parseInt(m || "0", 10);
110
+ const seconds = parseInt(s || "0", 10);
111
+ return (isNaN(hours) ? 0 : hours) * 60 * 60 * 1e3 + (isNaN(minutes) ? 0 : minutes) * 60 * 1e3 + (isNaN(seconds) ? 0 : seconds) * 1e3;
112
+ };
113
+ var millisecondsToHours = (ms) => ms / (1e3 * 60 * 60);
114
+ var hoursToMilliseconds = (hours) => hours * (1e3 * 60 * 60);
115
+ var formatMillisecondsAsTimeSpent = (ms) => {
116
+ if (!ms || ms < 0) return "0h 0min";
117
+ const totalMinutes = Math.floor(ms / (1e3 * 60));
118
+ const hours = Math.floor(totalMinutes / 60);
119
+ const minutes = totalMinutes % 60;
120
+ let result = "";
121
+ if (hours > 0) result += `${hours}h`;
122
+ if (minutes > 0 || hours === 0) result += (hours > 0 ? " " : "") + `${minutes}min`;
123
+ return result;
124
+ };
125
+ function formatDuration(duration) {
126
+ if (!duration) return "-";
127
+ const [hours, minutes, seconds] = duration.split(":").map(Number);
128
+ let result = "";
129
+ if (hours) result += `${hours}h`;
130
+ if (minutes) result += (result ? " " : "") + `${minutes}m`;
131
+ if (seconds) result += (result ? " " : "") + `${seconds}s`;
132
+ return result || "0m";
133
+ }
134
+ var formatDurationNumber = (durationHours) => {
135
+ if (durationHours == null || durationHours === 0) return "\u2014";
136
+ if (durationHours > 8760) return "Unknown";
137
+ if (durationHours < 0) return "Error";
138
+ const hours = Math.floor(durationHours);
139
+ const minutes = Math.floor(durationHours % 1 * 60);
140
+ if (durationHours < 1) return `${minutes}m`;
141
+ if (hours < 24) {
142
+ if (minutes === 0) return `${hours}h`;
143
+ return `${hours}h ${minutes}m`;
144
+ }
145
+ const days = Math.floor(hours / 24);
146
+ const remainingHours = hours % 24;
147
+ if (remainingHours === 0) return `${days}d`;
148
+ return `${days}d ${remainingHours}h`;
149
+ };
150
+ function toHHmm(value, fallback) {
151
+ if (!value || value.length < 5) return fallback;
152
+ return value.slice(0, 5);
153
+ }
154
+ function toHHmmss(value) {
155
+ const trimmed = value.trim();
156
+ if (!trimmed) return null;
157
+ return trimmed.length === 5 ? `${trimmed}:00` : trimmed;
158
+ }
159
+ var isoToDateInput = (isoString) => {
160
+ if (!isoString) return "";
161
+ try {
162
+ return new Date(isoString).toISOString().split("T")[0];
163
+ } catch {
164
+ return "";
165
+ }
166
+ };
167
+ var dateInputToIso = (dateString) => {
168
+ if (!dateString) return "";
169
+ const parsed = /* @__PURE__ */ new Date(dateString + "T00:00:00Z");
170
+ return Number.isNaN(parsed.getTime()) ? "" : parsed.toISOString();
171
+ };
172
+ function dateOnlyToIsoUtc(value) {
173
+ if (!value) return void 0;
174
+ const parsed = value.includes("T") ? new Date(value) : /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
175
+ return Number.isNaN(parsed.getTime()) ? void 0 : parsed.toISOString();
176
+ }
177
+ function nowLocalDateTimeInputValue() {
178
+ return format(/* @__PURE__ */ new Date(), "yyyy-MM-dd'T'HH:mm");
179
+ }
180
+ function localDateTimeInputToIsoUtc(value) {
181
+ if (!value) return void 0;
182
+ const parsed = new Date(value);
183
+ if (!isValid(parsed)) return void 0;
184
+ return parsed.toISOString();
185
+ }
186
+ function ensureUtcIso(value) {
187
+ return /[Zz]|[+-]\d{2}:?\d{2}$/.test(value) ? value : value + "Z";
188
+ }
189
+ function isoUtcToLocalDateTimeInput(value, fallback = "") {
190
+ if (!value) return fallback;
191
+ const parsed = new Date(ensureUtcIso(value));
192
+ if (!isValid(parsed)) return fallback;
193
+ return format(parsed, "yyyy-MM-dd'T'HH:mm");
194
+ }
195
+ var getDateRange = (timePeriod, dateFormat = DATE_FORMAT) => {
196
+ const today = /* @__PURE__ */ new Date();
197
+ switch (timePeriod) {
198
+ case "month":
199
+ return { startDate: format(subMonths(today, 1), dateFormat), endDate: format(today, dateFormat) };
200
+ case "quarter":
201
+ return { startDate: format(subMonths(today, 3), dateFormat), endDate: format(today, dateFormat) };
202
+ case "year":
203
+ return { startDate: format(subYears(today, 1), dateFormat), endDate: format(today, dateFormat) };
204
+ case "week":
205
+ default:
206
+ return { startDate: format(subDays(today, 7), dateFormat), endDate: format(today, dateFormat) };
207
+ }
208
+ };
209
+ function computeDateRange(days) {
210
+ const to = (/* @__PURE__ */ new Date()).toISOString();
211
+ const from = new Date(Date.now() - days * 864e5).toISOString();
212
+ return { from, to };
213
+ }
214
+ function currentMonthRangeUtc(today = /* @__PURE__ */ new Date()) {
215
+ const year = today.getUTCFullYear();
216
+ const month = today.getUTCMonth();
217
+ return {
218
+ fromUtc: new Date(Date.UTC(year, month, 1)).toISOString(),
219
+ beforeUtc: new Date(Date.UTC(year, month + 1, 1)).toISOString()
220
+ };
221
+ }
222
+ function getCurrentOffsetMinutes(iana) {
223
+ try {
224
+ const parts = new Intl.DateTimeFormat("en", {
225
+ timeZone: iana,
226
+ timeZoneName: "longOffset"
227
+ }).formatToParts(/* @__PURE__ */ new Date());
228
+ const label = parts.find((p) => p.type === "timeZoneName")?.value ?? "";
229
+ if (label === "GMT" || label === "UTC") return 0;
230
+ const m = label.match(/GMT([+-])(\d{2}):(\d{2})/);
231
+ if (!m) return null;
232
+ const sign = m[1] === "+" ? 1 : -1;
233
+ return sign * (parseInt(m[2], 10) * 60 + parseInt(m[3], 10));
234
+ } catch {
235
+ return null;
236
+ }
237
+ }
238
+
239
+ export { AUDIT_DATE_FORMAT, DATE_FORMAT, DAY_DATE_FORMAT, DAY_MONTH_FORMAT, DAY_MONTH_YEAR_FORMAT, DISPLAY_DATE_FORMAT, SHORT_DATETIME_FORMAT, TIME_FORMAT, addDaysToIsoDate, computeDateRange, currentMonthRangeUtc, dateInputToIso, dateOnlyToIsoUtc, ensureUtcIso, formatDate, formatDateSafe, formatDateString, formatDateWithOrdinal, formatDuration, formatDurationNumber, formatMillisecondsAsTimeSpent, formatTimeAgo, getCurrentOffsetMinutes, getDateRange, getMonthEndIso, getMonthStartIso, getTodayIsoDate, hoursToMilliseconds, isDateOnlyString, isoToDateInput, isoUtcToLocalDateTimeInput, localDateTimeInputToIsoUtc, millisecondsToHours, nowLocalDateTimeInputValue, parseIsoDateLocal, timespanToMilliseconds, toDateOnlyString, toHHmm, toHHmmss };
240
+ //# sourceMappingURL=index.js.map
241
+ //# sourceMappingURL=index.js.map