@simplysm/core-common 13.0.69 → 13.0.71
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 +66 -267
- package/dist/common.types.d.ts +14 -14
- package/dist/errors/argument-error.d.ts +10 -10
- package/dist/errors/argument-error.d.ts.map +1 -1
- package/dist/errors/argument-error.js +2 -2
- package/dist/errors/argument-error.js.map +1 -1
- package/dist/errors/not-implemented-error.d.ts +8 -8
- package/dist/errors/not-implemented-error.js +2 -2
- package/dist/errors/not-implemented-error.js.map +1 -1
- package/dist/errors/sd-error.d.ts +10 -10
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/timeout-error.d.ts +10 -10
- package/dist/errors/timeout-error.js +3 -3
- package/dist/errors/timeout-error.js.map +1 -1
- package/dist/extensions/arr-ext.d.ts +2 -2
- package/dist/extensions/arr-ext.helpers.d.ts +8 -8
- package/dist/extensions/arr-ext.helpers.js +1 -1
- package/dist/extensions/arr-ext.helpers.js.map +1 -1
- package/dist/extensions/arr-ext.js +13 -13
- package/dist/extensions/arr-ext.js.map +1 -1
- package/dist/extensions/arr-ext.types.d.ts +57 -57
- package/dist/extensions/arr-ext.types.d.ts.map +1 -1
- package/dist/extensions/map-ext.d.ts +16 -16
- package/dist/extensions/set-ext.d.ts +11 -11
- package/dist/features/debounce-queue.d.ts +17 -15
- package/dist/features/debounce-queue.d.ts.map +1 -1
- package/dist/features/debounce-queue.js +6 -6
- package/dist/features/debounce-queue.js.map +1 -1
- package/dist/features/event-emitter.d.ts +20 -20
- package/dist/features/event-emitter.js +17 -17
- package/dist/features/serial-queue.d.ts +11 -11
- package/dist/features/serial-queue.js +5 -5
- package/dist/features/serial-queue.js.map +1 -1
- package/dist/globals.d.ts +4 -4
- package/dist/types/date-only.d.ts +64 -64
- package/dist/types/date-only.d.ts.map +1 -1
- package/dist/types/date-only.js +63 -63
- package/dist/types/date-time.d.ts +37 -37
- package/dist/types/date-time.d.ts.map +1 -1
- package/dist/types/date-time.js +54 -37
- package/dist/types/date-time.js.map +1 -1
- package/dist/types/lazy-gc-map.d.ts +26 -26
- package/dist/types/lazy-gc-map.d.ts.map +1 -1
- package/dist/types/lazy-gc-map.js +26 -26
- package/dist/types/lazy-gc-map.js.map +1 -1
- package/dist/types/time.d.ts +25 -25
- package/dist/types/time.d.ts.map +1 -1
- package/dist/types/time.js +25 -25
- package/dist/types/time.js.map +1 -1
- package/dist/types/uuid.d.ts +11 -11
- package/dist/types/uuid.d.ts.map +1 -1
- package/dist/types/uuid.js +12 -12
- package/dist/types/uuid.js.map +1 -1
- package/dist/utils/bytes.d.ts +17 -17
- package/dist/utils/bytes.js +4 -4
- package/dist/utils/bytes.js.map +1 -1
- package/dist/utils/date-format.d.ts +45 -45
- package/dist/utils/date-format.js +1 -1
- package/dist/utils/date-format.js.map +1 -1
- package/dist/utils/error.d.ts +4 -4
- package/dist/utils/json.d.ts +17 -17
- package/dist/utils/json.js +3 -3
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/num.d.ts +23 -23
- package/dist/utils/obj.d.ts +111 -111
- package/dist/utils/obj.d.ts.map +1 -1
- package/dist/utils/obj.js +3 -3
- package/dist/utils/obj.js.map +1 -1
- package/dist/utils/path.d.ts +10 -10
- package/dist/utils/primitive.d.ts +5 -5
- package/dist/utils/primitive.js +1 -1
- package/dist/utils/primitive.js.map +1 -1
- package/dist/utils/str.d.ts +46 -46
- package/dist/utils/str.d.ts.map +1 -1
- package/dist/utils/str.js +5 -5
- package/dist/utils/str.js.map +1 -1
- package/dist/utils/template-strings.d.ts +26 -26
- package/dist/utils/transferable.d.ts +18 -18
- package/dist/utils/transferable.js +1 -1
- package/dist/utils/transferable.js.map +1 -1
- package/dist/utils/wait.d.ts +9 -9
- package/dist/utils/xml.d.ts +13 -13
- package/dist/utils/xml.d.ts.map +1 -1
- package/dist/utils/xml.js +1 -0
- package/dist/utils/xml.js.map +1 -1
- package/dist/zip/sd-zip.d.ts +22 -22
- package/dist/zip/sd-zip.js +16 -16
- package/package.json +4 -4
- package/src/common.types.ts +17 -17
- package/src/errors/argument-error.ts +15 -15
- package/src/errors/not-implemented-error.ts +9 -9
- package/src/errors/sd-error.ts +12 -12
- package/src/errors/timeout-error.ts +12 -12
- package/src/extensions/arr-ext.helpers.ts +10 -10
- package/src/extensions/arr-ext.ts +57 -57
- package/src/extensions/arr-ext.types.ts +59 -59
- package/src/extensions/map-ext.ts +16 -16
- package/src/extensions/set-ext.ts +11 -11
- package/src/features/debounce-queue.ts +21 -19
- package/src/features/event-emitter.ts +25 -25
- package/src/features/serial-queue.ts +13 -13
- package/src/globals.ts +4 -4
- package/src/index.ts +1 -1
- package/src/types/date-only.ts +83 -83
- package/src/types/date-time.ts +64 -44
- package/src/types/lazy-gc-map.ts +45 -45
- package/src/types/time.ts +34 -34
- package/src/types/uuid.ts +17 -17
- package/src/utils/bytes.ts +35 -35
- package/src/utils/date-format.ts +65 -65
- package/src/utils/error.ts +4 -4
- package/src/utils/json.ts +39 -39
- package/src/utils/num.ts +23 -23
- package/src/utils/obj.ts +138 -138
- package/src/utils/path.ts +10 -10
- package/src/utils/primitive.ts +6 -6
- package/src/utils/str.ts +260 -261
- package/src/utils/template-strings.ts +29 -29
- package/src/utils/transferable.ts +284 -284
- package/src/utils/wait.ts +10 -10
- package/src/utils/xml.ts +20 -19
- package/src/zip/sd-zip.ts +25 -25
- package/tests/errors/errors.spec.ts +80 -0
- package/tests/extensions/array-extension.spec.ts +796 -0
- package/tests/extensions/map-extension.spec.ts +147 -0
- package/tests/extensions/set-extension.spec.ts +74 -0
- package/tests/types/date-only.spec.ts +638 -0
- package/tests/types/date-time.spec.ts +391 -0
- package/tests/types/lazy-gc-map.spec.ts +692 -0
- package/tests/types/time.spec.ts +559 -0
- package/tests/types/uuid.spec.ts +74 -0
- package/tests/utils/bytes-utils.spec.ts +230 -0
- package/tests/utils/date-format.spec.ts +373 -0
- package/tests/utils/debounce-queue.spec.ts +272 -0
- package/tests/utils/json.spec.ts +486 -0
- package/tests/utils/number.spec.ts +157 -0
- package/tests/utils/object.spec.ts +829 -0
- package/tests/utils/path.spec.ts +78 -0
- package/tests/utils/primitive.spec.ts +43 -0
- package/tests/utils/sd-event-emitter.spec.ts +216 -0
- package/tests/utils/serial-queue.spec.ts +365 -0
- package/tests/utils/string.spec.ts +281 -0
- package/tests/utils/template-strings.spec.ts +57 -0
- package/tests/utils/transferable.spec.ts +703 -0
- package/tests/utils/wait.spec.ts +145 -0
- package/tests/utils/xml.spec.ts +146 -0
- package/tests/zip/sd-zip.spec.ts +238 -0
- package/docs/extensions.md +0 -503
- package/docs/features.md +0 -109
- package/docs/types.md +0 -486
- package/docs/utils.md +0 -780
package/src/types/date-only.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { ArgumentError } from "../errors/argument-error";
|
|
|
2
2
|
import { formatDate, normalizeMonth } from "../utils/date-format";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Date class (without time: yyyy-MM-dd, immutable)
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* An immutable class that stores only the date without time information.
|
|
8
|
+
* Operates based on local timezone.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* const today = new DateOnly();
|
|
@@ -17,13 +17,13 @@ export class DateOnly {
|
|
|
17
17
|
|
|
18
18
|
readonly date: Date;
|
|
19
19
|
|
|
20
|
-
/**
|
|
20
|
+
/** Current time */
|
|
21
21
|
constructor();
|
|
22
|
-
/**
|
|
22
|
+
/** Initialize with year, month, day */
|
|
23
23
|
constructor(year: number, month: number, day: number);
|
|
24
|
-
/** tick (millisecond)
|
|
24
|
+
/** Create from tick (millisecond) */
|
|
25
25
|
constructor(tick: number);
|
|
26
|
-
/** Date
|
|
26
|
+
/** Create from Date type */
|
|
27
27
|
constructor(date: Date);
|
|
28
28
|
constructor(arg1?: number | Date, arg2?: number, arg3?: number) {
|
|
29
29
|
if (arg1 === undefined) {
|
|
@@ -42,26 +42,26 @@ export class DateOnly {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
* @param str
|
|
47
|
-
* @returns DateOnly
|
|
45
|
+
* Parse a string into DateOnly
|
|
46
|
+
* @param str Date string
|
|
47
|
+
* @returns DateOnly instance
|
|
48
48
|
*
|
|
49
|
-
*
|
|
50
|
-
* - `yyyy-MM-dd` (
|
|
51
|
-
* - `yyyyMMdd` (
|
|
52
|
-
* - ISO 8601 (
|
|
49
|
+
* Supported formats:
|
|
50
|
+
* - `yyyy-MM-dd` (e.g., '2024-01-15') - Extracted directly from string, timezone-independent
|
|
51
|
+
* - `yyyyMMdd` (e.g., '20240115') - Extracted directly from string, timezone-independent
|
|
52
|
+
* - ISO 8601 (e.g., '2024-01-15T00:00:00Z') - Interpreted as UTC, then converted to local timezone
|
|
53
53
|
*
|
|
54
|
-
* @note
|
|
55
|
-
* @note DST
|
|
54
|
+
* @note For different server/client timezones, `yyyy-MM-dd` format is recommended
|
|
55
|
+
* @note For DST regions when parsing ISO 8601 format, the offset of the parsing target date is used.
|
|
56
56
|
*/
|
|
57
57
|
static parse(str: string): DateOnly {
|
|
58
|
-
// yyyy-MM-dd
|
|
58
|
+
// yyyy-MM-dd format (timezone-independent)
|
|
59
59
|
const matchYMD = /^(\d{4})-(\d{2})-(\d{2})$/.exec(str);
|
|
60
60
|
if (matchYMD != null) {
|
|
61
61
|
return new DateOnly(Number(matchYMD[1]), Number(matchYMD[2]), Number(matchYMD[3]));
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// yyyyMMdd
|
|
64
|
+
// yyyyMMdd format (timezone-independent)
|
|
65
65
|
const matchCompact = /^(\d{4})(\d{2})(\d{2})$/.exec(str);
|
|
66
66
|
if (matchCompact != null) {
|
|
67
67
|
return new DateOnly(
|
|
@@ -71,11 +71,11 @@ export class DateOnly {
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// ISO 8601
|
|
75
|
-
// Date.parse()
|
|
76
|
-
// getTimezoneOffset()
|
|
77
|
-
//
|
|
78
|
-
//
|
|
74
|
+
// ISO 8601 and other formats (using Date.parse, timezone conversion applied)
|
|
75
|
+
// Date.parse() returns UTC tick for ISO 8601 with 'Z' suffix
|
|
76
|
+
// getTimezoneOffset() returns "minutes to add when converting from local to UTC" (KST is -540 = UTC+9)
|
|
77
|
+
// Here we do "UTC → local" conversion, so apply opposite sign (subtraction)
|
|
78
|
+
// Use the offset of the parsing target date for accurate conversion in DST regions
|
|
79
79
|
const utcTick = Date.parse(str);
|
|
80
80
|
if (!Number.isNaN(utcTick)) {
|
|
81
81
|
const tempDate = new Date(utcTick);
|
|
@@ -85,45 +85,45 @@ export class DateOnly {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
throw new ArgumentError(
|
|
88
|
-
|
|
88
|
+
`Failed to parse date format. Supported formats: 'yyyy-MM-dd', 'yyyyMMdd', ISO 8601 date`,
|
|
89
89
|
{
|
|
90
90
|
input: str,
|
|
91
91
|
},
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
//#region
|
|
95
|
+
//#region Week calculation
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
* @param weekStartDay
|
|
100
|
-
* @param minDaysInFirstWeek
|
|
101
|
-
* @returns
|
|
98
|
+
* Return the base year and month based on week information
|
|
99
|
+
* @param weekStartDay Week start day (0=Sunday, 1=Monday, ..., 6=Saturday). Default: 1(Monday)
|
|
100
|
+
* @param minDaysInFirstWeek Minimum days to be considered the first week (1~7). Default: 4 (ISO 8601 standard)
|
|
101
|
+
* @returns Base year and month of the week containing this date
|
|
102
102
|
*
|
|
103
103
|
* @example
|
|
104
|
-
* // ISO 8601
|
|
104
|
+
* // ISO 8601 standard (Monday start, first week minimum 4 days)
|
|
105
105
|
* new DateOnly(2024, 1, 1).getBaseYearMonthSeqForWeekSeq(1, 4)
|
|
106
|
-
* //
|
|
106
|
+
* // US style (Sunday start, first week minimum 1 day)
|
|
107
107
|
* new DateOnly(2024, 1, 1).getBaseYearMonthSeqForWeekSeq(0, 1)
|
|
108
108
|
*/
|
|
109
109
|
getBaseYearMonthSeqForWeekSeq(weekStartDay: number = 1, minDaysInFirstWeek: number = 4) {
|
|
110
|
-
//
|
|
110
|
+
// Calculate day of week index based on week start day (0 = week start day)
|
|
111
111
|
const dayOfWeek = (this.dayOfWeek + 7 - weekStartDay) % 7;
|
|
112
|
-
//
|
|
112
|
+
// Remaining days in the current week (including current date)
|
|
113
113
|
const daysInWeek = 7 - dayOfWeek;
|
|
114
114
|
|
|
115
|
-
//
|
|
115
|
+
// If remaining days in current week is less than minimum, consider as previous week
|
|
116
116
|
if (daysInWeek < minDaysInFirstWeek) {
|
|
117
117
|
const prevWeek = this.addDays(-7);
|
|
118
118
|
return { year: prevWeek.year, monthSeq: prevWeek.month };
|
|
119
119
|
} else {
|
|
120
|
-
//
|
|
120
|
+
// Calculate actual remaining days of the week considering month boundary
|
|
121
121
|
const nextMonthDate = this.addMonths(1).setDay(1);
|
|
122
122
|
const remainedDays = (nextMonthDate.tick - this.tick) / DateOnly.MS_PER_DAY;
|
|
123
123
|
|
|
124
|
-
//
|
|
124
|
+
// Take the smaller of actual days to month boundary and week remaining days
|
|
125
125
|
const realDaysInWeek = Math.min(daysInWeek, remainedDays);
|
|
126
|
-
//
|
|
126
|
+
// If still less than minimum when considering month boundary, consider as next week
|
|
127
127
|
if (realDaysInWeek < minDaysInFirstWeek) {
|
|
128
128
|
const nextWeek = this.addDays(7);
|
|
129
129
|
return { year: nextWeek.year, monthSeq: nextWeek.month };
|
|
@@ -134,10 +134,10 @@ export class DateOnly {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
-
*
|
|
138
|
-
* @param weekStartDay
|
|
139
|
-
* @param minDaysInFirstWeek
|
|
140
|
-
* @returns
|
|
137
|
+
* Calculate the start date of the week based on week information
|
|
138
|
+
* @param weekStartDay Week start day (0=Sunday, 1=Monday, ..., 6=Saturday). Default: 1(Monday)
|
|
139
|
+
* @param minDaysInFirstWeek Minimum days to be considered the first week (1~7). Default: 4 (ISO 8601 standard)
|
|
140
|
+
* @returns Start date of the week containing this date
|
|
141
141
|
*/
|
|
142
142
|
getWeekSeqStartDate(weekStartDay: number = 1, minDaysInFirstWeek: number = 4) {
|
|
143
143
|
const dayOfWeek = (this.dayOfWeek + 7 - weekStartDay) % 7;
|
|
@@ -151,16 +151,16 @@ export class DateOnly {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
/**
|
|
154
|
-
*
|
|
155
|
-
* @param weekStartDay
|
|
156
|
-
* @param minDaysInFirstWeek
|
|
157
|
-
* @returns
|
|
154
|
+
* Return year and week sequence information
|
|
155
|
+
* @param weekStartDay Week start day (0=Sunday, 1=Monday, ..., 6=Saturday). Default: 1(Monday)
|
|
156
|
+
* @param minDaysInFirstWeek Minimum days to be considered the first week (1~7). Default: 4 (ISO 8601 standard)
|
|
157
|
+
* @returns Year and week number within that year
|
|
158
158
|
*
|
|
159
159
|
* @example
|
|
160
|
-
* // ISO 8601
|
|
160
|
+
* // ISO 8601 standard (Monday start, first week minimum 4 days)
|
|
161
161
|
* new DateOnly(2025, 1, 6).getWeekSeqOfYear(); // { year: 2025, weekSeq: 2 }
|
|
162
162
|
*
|
|
163
|
-
* //
|
|
163
|
+
* // US style (Sunday start, first week minimum 1 day)
|
|
164
164
|
* new DateOnly(2025, 1, 1).getWeekSeqOfYear(0, 1); // { year: 2025, weekSeq: 1 }
|
|
165
165
|
*/
|
|
166
166
|
getWeekSeqOfYear(
|
|
@@ -182,16 +182,16 @@ export class DateOnly {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
|
-
*
|
|
186
|
-
* @param weekStartDay
|
|
187
|
-
* @param minDaysInFirstWeek
|
|
188
|
-
* @returns
|
|
185
|
+
* Return year, month and week sequence information for the given date
|
|
186
|
+
* @param weekStartDay Week start day (0=Sunday, 1=Monday, ..., 6=Saturday). Default: 1(Monday)
|
|
187
|
+
* @param minDaysInFirstWeek Minimum days to be considered the first week (1~7). Default: 4 (ISO 8601 standard)
|
|
188
|
+
* @returns Year, month and week number within that month
|
|
189
189
|
*
|
|
190
190
|
* @example
|
|
191
|
-
* // ISO 8601
|
|
191
|
+
* // ISO 8601 standard (Monday start, first week minimum 4 days)
|
|
192
192
|
* new DateOnly(2025, 1, 15).getWeekSeqOfMonth(); // { year: 2025, monthSeq: 1, weekSeq: 3 }
|
|
193
193
|
*
|
|
194
|
-
* //
|
|
194
|
+
* // US style (Sunday start, first week minimum 1 day)
|
|
195
195
|
* new DateOnly(2025, 1, 15).getWeekSeqOfMonth(0, 1); // { year: 2025, monthSeq: 1, weekSeq: 3 }
|
|
196
196
|
*/
|
|
197
197
|
getWeekSeqOfMonth(
|
|
@@ -214,18 +214,18 @@ export class DateOnly {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
/**
|
|
217
|
-
*
|
|
218
|
-
* @param arg
|
|
219
|
-
* @param weekStartDay
|
|
220
|
-
* @param minDaysInFirstWeek
|
|
221
|
-
* @returns
|
|
217
|
+
* Get the start date of a week based on week information
|
|
218
|
+
* @param arg Year, optional month, and week number
|
|
219
|
+
* @param weekStartDay Week start day (0=Sunday, 1=Monday, ..., 6=Saturday). Default: 1(Monday)
|
|
220
|
+
* @param minDaysInFirstWeek Minimum days to be considered the first week (1~7). Default: 4 (ISO 8601 standard)
|
|
221
|
+
* @returns Start date of the specified week
|
|
222
222
|
*
|
|
223
223
|
* @example
|
|
224
|
-
* //
|
|
225
|
-
* DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 }); // 2025-01-06 (
|
|
224
|
+
* // Start date of week 2 in 2025 (ISO 8601 standard)
|
|
225
|
+
* DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 }); // 2025-01-06 (Monday)
|
|
226
226
|
*
|
|
227
|
-
* //
|
|
228
|
-
* DateOnly.getDateByYearWeekSeq({ year: 2025, month: 1, weekSeq: 3 }); // 2025-01-13 (
|
|
227
|
+
* // Start date of week 3 in January 2025
|
|
228
|
+
* DateOnly.getDateByYearWeekSeq({ year: 2025, month: 1, weekSeq: 3 }); // 2025-01-13 (Monday)
|
|
229
229
|
*/
|
|
230
230
|
static getDateByYearWeekSeq(
|
|
231
231
|
arg: { year: number; month?: number; weekSeq: number },
|
|
@@ -240,9 +240,9 @@ export class DateOnly {
|
|
|
240
240
|
|
|
241
241
|
//#endregion
|
|
242
242
|
|
|
243
|
-
//#region Getters (
|
|
243
|
+
//#region Getters (read-only)
|
|
244
244
|
|
|
245
|
-
/**
|
|
245
|
+
/** Whether the date is set correctly */
|
|
246
246
|
get isValid(): boolean {
|
|
247
247
|
return this.date instanceof Date && !Number.isNaN(this.date.getTime());
|
|
248
248
|
}
|
|
@@ -263,25 +263,25 @@ export class DateOnly {
|
|
|
263
263
|
return this.date.getTime();
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
/**
|
|
266
|
+
/** Day of week (Sunday~Saturday: 0~6) */
|
|
267
267
|
get dayOfWeek(): number {
|
|
268
268
|
return this.date.getDay();
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
//#endregion
|
|
272
272
|
|
|
273
|
-
//#region
|
|
273
|
+
//#region Immutable transformation methods (returns new instance)
|
|
274
274
|
|
|
275
|
-
/**
|
|
275
|
+
/** Return new instance with specified year */
|
|
276
276
|
setYear(year: number): DateOnly {
|
|
277
277
|
return new DateOnly(year, this.month, this.day);
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
/**
|
|
281
|
-
*
|
|
282
|
-
* @param month
|
|
283
|
-
* @note
|
|
284
|
-
* (
|
|
281
|
+
* Return new DateOnly instance with specified month
|
|
282
|
+
* @param month Month to set (1-12, out-of-range values are adjusted in year)
|
|
283
|
+
* @note If current day is greater than target month's day count, it will be adjusted to last day of month
|
|
284
|
+
* (e.g., setMonth(2) on Jan 31 → Feb 28 or 29)
|
|
285
285
|
*/
|
|
286
286
|
setMonth(month: number): DateOnly {
|
|
287
287
|
const normalized = normalizeMonth(this.year, month, this.day);
|
|
@@ -289,10 +289,10 @@ export class DateOnly {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
/**
|
|
292
|
-
*
|
|
293
|
-
* @param day
|
|
294
|
-
* @note
|
|
295
|
-
*
|
|
292
|
+
* Return new DateOnly instance with specified day
|
|
293
|
+
* @param day Day to set
|
|
294
|
+
* @note Days outside valid month range are automatically adjusted to next/previous month per JavaScript Date behavior
|
|
295
|
+
* (e.g., day=32 in January → February 1)
|
|
296
296
|
*/
|
|
297
297
|
setDay(day: number): DateOnly {
|
|
298
298
|
return new DateOnly(this.year, this.month, day);
|
|
@@ -300,31 +300,31 @@ export class DateOnly {
|
|
|
300
300
|
|
|
301
301
|
//#endregion
|
|
302
302
|
|
|
303
|
-
//#region
|
|
303
|
+
//#region Arithmetic methods (returns new instance)
|
|
304
304
|
|
|
305
|
-
/**
|
|
305
|
+
/** Return new instance with specified years added */
|
|
306
306
|
addYears(years: number): DateOnly {
|
|
307
307
|
return this.setYear(this.year + years);
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
/**
|
|
310
|
+
/** Return new instance with specified months added */
|
|
311
311
|
addMonths(months: number): DateOnly {
|
|
312
312
|
return this.setMonth(this.month + months);
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
/**
|
|
315
|
+
/** Return new instance with specified days added */
|
|
316
316
|
addDays(days: number): DateOnly {
|
|
317
317
|
return new DateOnly(this.tick + days * DateOnly.MS_PER_DAY);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
//#endregion
|
|
321
321
|
|
|
322
|
-
//#region
|
|
322
|
+
//#region Formatting
|
|
323
323
|
|
|
324
324
|
/**
|
|
325
|
-
*
|
|
326
|
-
* @param format
|
|
327
|
-
* @see dtFormat
|
|
325
|
+
* Convert to string with specified format
|
|
326
|
+
* @param format Format string
|
|
327
|
+
* @see dtFormat for supported format strings
|
|
328
328
|
*/
|
|
329
329
|
toFormatString(formatStr: string): string {
|
|
330
330
|
return formatDate(formatStr, {
|
package/src/types/date-time.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { ArgumentError } from "../errors/argument-error";
|
|
|
2
2
|
import { convert12To24, formatDate, normalizeMonth } from "../utils/date-format";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* DateTime class (immutable)
|
|
6
6
|
*
|
|
7
|
-
* JavaScript Date
|
|
8
|
-
*
|
|
7
|
+
* Wraps JavaScript Date object to provide immutability and convenient API.
|
|
8
|
+
* Supports millisecond precision and operates based on local timezone.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* const now = new DateTime();
|
|
@@ -15,9 +15,9 @@ import { convert12To24, formatDate, normalizeMonth } from "../utils/date-format"
|
|
|
15
15
|
export class DateTime {
|
|
16
16
|
readonly date: Date;
|
|
17
17
|
|
|
18
|
-
/**
|
|
18
|
+
/** Create with current time */
|
|
19
19
|
constructor();
|
|
20
|
-
/**
|
|
20
|
+
/** Create with year, month, day, hour, minute, second, millisecond */
|
|
21
21
|
constructor(
|
|
22
22
|
year: number,
|
|
23
23
|
month: number,
|
|
@@ -27,9 +27,9 @@ export class DateTime {
|
|
|
27
27
|
second?: number,
|
|
28
28
|
millisecond?: number,
|
|
29
29
|
);
|
|
30
|
-
/** tick (
|
|
30
|
+
/** Create from tick (millisecond) */
|
|
31
31
|
constructor(tick: number);
|
|
32
|
-
/** Date
|
|
32
|
+
/** Create from Date object */
|
|
33
33
|
constructor(date: Date);
|
|
34
34
|
constructor(
|
|
35
35
|
arg1?: number | Date,
|
|
@@ -60,17 +60,17 @@ export class DateTime {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Parse a string to create DateTime instance
|
|
64
64
|
*
|
|
65
|
-
* @param str
|
|
66
|
-
* @returns
|
|
67
|
-
* @throws ArgumentError
|
|
65
|
+
* @param str DateTime string
|
|
66
|
+
* @returns Parsed DateTime instance
|
|
67
|
+
* @throws ArgumentError If unsupported format
|
|
68
68
|
*
|
|
69
69
|
* @example
|
|
70
70
|
* DateTime.parse("2025-01-15 10:30:00") // yyyy-MM-dd HH:mm:ss
|
|
71
71
|
* DateTime.parse("2025-01-15 10:30:00.123") // yyyy-MM-dd HH:mm:ss.fff
|
|
72
72
|
* DateTime.parse("20250115103000") // yyyyMMddHHmmss
|
|
73
|
-
* DateTime.parse("2025-01-15
|
|
73
|
+
* DateTime.parse("2025-01-15 AM 10:30:00") // yyyy-MM-dd AM/PM HH:mm:ss
|
|
74
74
|
* DateTime.parse("2025-01-15T10:30:00Z") // ISO 8601
|
|
75
75
|
*/
|
|
76
76
|
static parse(str: string): DateTime {
|
|
@@ -80,12 +80,12 @@ export class DateTime {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const match1 =
|
|
83
|
-
/^([0-9]{4})-([0-9]{2})-([0-9]{2}) (
|
|
83
|
+
/^([0-9]{4})-([0-9]{2})-([0-9]{2}) (AM|PM) ([0-9]{1,2}):([0-9]{2}):([0-9]{2})(\.([0-9]{1,3}))?$/i.exec(
|
|
84
84
|
str,
|
|
85
85
|
);
|
|
86
86
|
if (match1 != null) {
|
|
87
87
|
const rawHour = Number(match1[5]);
|
|
88
|
-
const isPM = match1[4] === "
|
|
88
|
+
const isPM = match1[4].toUpperCase() === "PM";
|
|
89
89
|
const hour = convert12To24(rawHour, isPM);
|
|
90
90
|
return new DateTime(
|
|
91
91
|
Number(match1[1]),
|
|
@@ -98,6 +98,26 @@ export class DateTime {
|
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// Korean AM/PM format (오전/오후)
|
|
102
|
+
const matchKorean =
|
|
103
|
+
/^([0-9]{4})-([0-9]{2})-([0-9]{2}) (오전|오후) ([0-9]{1,2}):([0-9]{2}):([0-9]{2})(\.([0-9]{1,3}))?$/.exec(
|
|
104
|
+
str,
|
|
105
|
+
);
|
|
106
|
+
if (matchKorean != null) {
|
|
107
|
+
const rawHour = Number(matchKorean[5]);
|
|
108
|
+
const isPM = matchKorean[4] === "오후";
|
|
109
|
+
const hour = convert12To24(rawHour, isPM);
|
|
110
|
+
return new DateTime(
|
|
111
|
+
Number(matchKorean[1]),
|
|
112
|
+
Number(matchKorean[2]),
|
|
113
|
+
Number(matchKorean[3]),
|
|
114
|
+
hour,
|
|
115
|
+
Number(matchKorean[6]),
|
|
116
|
+
Number(matchKorean[7]),
|
|
117
|
+
matchKorean[9] ? Number(matchKorean[9].padEnd(3, "0")) : undefined,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
101
121
|
const match2 = /^[0-9]{14}$/.exec(str);
|
|
102
122
|
if (match2 != null) {
|
|
103
123
|
return new DateTime(
|
|
@@ -127,12 +147,12 @@ export class DateTime {
|
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
throw new ArgumentError(
|
|
130
|
-
|
|
150
|
+
`Failed to parse datetime format. Supported formats: 'yyyy-MM-dd HH:mm:ss', 'yyyyMMddHHmmss', 'yyyy-MM-dd AM/PM HH:mm:ss', ISO 8601`,
|
|
131
151
|
{ input: str },
|
|
132
152
|
);
|
|
133
153
|
}
|
|
134
154
|
|
|
135
|
-
//#region Getters (
|
|
155
|
+
//#region Getters (read-only)
|
|
136
156
|
|
|
137
157
|
get year(): number {
|
|
138
158
|
return this.date.getFullYear();
|
|
@@ -166,7 +186,7 @@ export class DateTime {
|
|
|
166
186
|
return this.date.getTime();
|
|
167
187
|
}
|
|
168
188
|
|
|
169
|
-
/**
|
|
189
|
+
/** Day of week (Sunday~Saturday: 0~6) */
|
|
170
190
|
get dayOfWeek(): number {
|
|
171
191
|
return this.date.getDay();
|
|
172
192
|
}
|
|
@@ -175,16 +195,16 @@ export class DateTime {
|
|
|
175
195
|
return -this.date.getTimezoneOffset();
|
|
176
196
|
}
|
|
177
197
|
|
|
178
|
-
/**
|
|
198
|
+
/** Whether the datetime is set correctly */
|
|
179
199
|
get isValid(): boolean {
|
|
180
200
|
return this.date instanceof Date && !Number.isNaN(this.date.getTime());
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
//#endregion
|
|
184
204
|
|
|
185
|
-
//#region
|
|
205
|
+
//#region Immutable transformation methods (returns new instance)
|
|
186
206
|
|
|
187
|
-
/**
|
|
207
|
+
/** Return new instance with specified year */
|
|
188
208
|
setYear(year: number): DateTime {
|
|
189
209
|
return new DateTime(
|
|
190
210
|
year,
|
|
@@ -198,10 +218,10 @@ export class DateTime {
|
|
|
198
218
|
}
|
|
199
219
|
|
|
200
220
|
/**
|
|
201
|
-
*
|
|
202
|
-
* @param month
|
|
203
|
-
* @note
|
|
204
|
-
* (
|
|
221
|
+
* Return new DateTime instance with specified month
|
|
222
|
+
* @param month Month to set (1-12, out-of-range values are adjusted in year)
|
|
223
|
+
* @note If current day is greater than target month's day count, it will be adjusted to last day of month
|
|
224
|
+
* (e.g., setMonth(2) on Jan 31 → Feb 28 or 29)
|
|
205
225
|
*/
|
|
206
226
|
setMonth(month: number): DateTime {
|
|
207
227
|
const normalized = normalizeMonth(this.year, month, this.day);
|
|
@@ -217,10 +237,10 @@ export class DateTime {
|
|
|
217
237
|
}
|
|
218
238
|
|
|
219
239
|
/**
|
|
220
|
-
*
|
|
221
|
-
* @param day
|
|
222
|
-
* @note
|
|
223
|
-
*
|
|
240
|
+
* Return new DateTime instance with specified day
|
|
241
|
+
* @param day Day to set
|
|
242
|
+
* @note Days outside valid month range are automatically adjusted to next/previous month per JavaScript Date behavior
|
|
243
|
+
* (e.g., day=32 in January → February 1)
|
|
224
244
|
*/
|
|
225
245
|
setDay(day: number): DateTime {
|
|
226
246
|
return new DateTime(
|
|
@@ -234,7 +254,7 @@ export class DateTime {
|
|
|
234
254
|
);
|
|
235
255
|
}
|
|
236
256
|
|
|
237
|
-
/**
|
|
257
|
+
/** Return new instance with specified hour */
|
|
238
258
|
setHour(hour: number): DateTime {
|
|
239
259
|
return new DateTime(
|
|
240
260
|
this.year,
|
|
@@ -247,7 +267,7 @@ export class DateTime {
|
|
|
247
267
|
);
|
|
248
268
|
}
|
|
249
269
|
|
|
250
|
-
/**
|
|
270
|
+
/** Return new instance with specified minute */
|
|
251
271
|
setMinute(minute: number): DateTime {
|
|
252
272
|
return new DateTime(
|
|
253
273
|
this.year,
|
|
@@ -260,7 +280,7 @@ export class DateTime {
|
|
|
260
280
|
);
|
|
261
281
|
}
|
|
262
282
|
|
|
263
|
-
/**
|
|
283
|
+
/** Return new instance with specified second */
|
|
264
284
|
setSecond(second: number): DateTime {
|
|
265
285
|
return new DateTime(
|
|
266
286
|
this.year,
|
|
@@ -273,7 +293,7 @@ export class DateTime {
|
|
|
273
293
|
);
|
|
274
294
|
}
|
|
275
295
|
|
|
276
|
-
/**
|
|
296
|
+
/** Return new instance with specified millisecond */
|
|
277
297
|
setMillisecond(millisecond: number): DateTime {
|
|
278
298
|
return new DateTime(
|
|
279
299
|
this.year,
|
|
@@ -288,51 +308,51 @@ export class DateTime {
|
|
|
288
308
|
|
|
289
309
|
//#endregion
|
|
290
310
|
|
|
291
|
-
//#region
|
|
311
|
+
//#region Arithmetic methods (returns new instance)
|
|
292
312
|
|
|
293
|
-
/**
|
|
313
|
+
/** Return new instance with specified years added */
|
|
294
314
|
addYears(years: number): DateTime {
|
|
295
315
|
return this.setYear(this.year + years);
|
|
296
316
|
}
|
|
297
317
|
|
|
298
|
-
/**
|
|
318
|
+
/** Return new instance with specified months added */
|
|
299
319
|
addMonths(months: number): DateTime {
|
|
300
320
|
return this.setMonth(this.month + months);
|
|
301
321
|
}
|
|
302
322
|
|
|
303
|
-
/**
|
|
323
|
+
/** Return new instance with specified days added */
|
|
304
324
|
addDays(days: number): DateTime {
|
|
305
325
|
return new DateTime(this.tick + days * 24 * 60 * 60 * 1000);
|
|
306
326
|
}
|
|
307
327
|
|
|
308
|
-
/**
|
|
328
|
+
/** Return new instance with specified hours added */
|
|
309
329
|
addHours(hours: number): DateTime {
|
|
310
330
|
return new DateTime(this.tick + hours * 60 * 60 * 1000);
|
|
311
331
|
}
|
|
312
332
|
|
|
313
|
-
/**
|
|
333
|
+
/** Return new instance with specified minutes added */
|
|
314
334
|
addMinutes(minutes: number): DateTime {
|
|
315
335
|
return new DateTime(this.tick + minutes * 60 * 1000);
|
|
316
336
|
}
|
|
317
337
|
|
|
318
|
-
/**
|
|
338
|
+
/** Return new instance with specified seconds added */
|
|
319
339
|
addSeconds(seconds: number): DateTime {
|
|
320
340
|
return new DateTime(this.tick + seconds * 1000);
|
|
321
341
|
}
|
|
322
342
|
|
|
323
|
-
/**
|
|
343
|
+
/** Return new instance with specified milliseconds added */
|
|
324
344
|
addMilliseconds(milliseconds: number): DateTime {
|
|
325
345
|
return new DateTime(this.tick + milliseconds);
|
|
326
346
|
}
|
|
327
347
|
|
|
328
348
|
//#endregion
|
|
329
349
|
|
|
330
|
-
//#region
|
|
350
|
+
//#region Formatting
|
|
331
351
|
|
|
332
352
|
/**
|
|
333
|
-
*
|
|
334
|
-
* @param format
|
|
335
|
-
* @see dtFormat
|
|
353
|
+
* Convert to string with specified format
|
|
354
|
+
* @param format Format string
|
|
355
|
+
* @see dtFormat for supported format strings
|
|
336
356
|
*/
|
|
337
357
|
toFormatString(formatStr: string): string {
|
|
338
358
|
return formatDate(formatStr, {
|