@simplysm/core-common 14.0.1 → 14.0.4
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 +154 -0
- package/dist/env.d.ts +3 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -1
- package/dist/env.js.map +1 -1
- package/docs/array-extensions.md +430 -0
- package/docs/env.md +52 -0
- package/docs/errors.md +104 -0
- package/docs/features.md +128 -0
- package/docs/type-utilities.md +91 -0
- package/docs/types.md +307 -0
- package/docs/utils.md +641 -0
- package/package.json +6 -2
- package/src/env.ts +3 -2
package/docs/errors.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Errors
|
|
2
|
+
|
|
3
|
+
A hierarchy of error classes built on `SdError`, which supports tree-structured cause chaining via the ES2024 `cause` property.
|
|
4
|
+
|
|
5
|
+
## `SdError`
|
|
6
|
+
|
|
7
|
+
Base error class with cause-chain support. Messages are joined in reverse order with ` => `.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
class SdError extends Error {
|
|
11
|
+
cause?: Error;
|
|
12
|
+
|
|
13
|
+
constructor(cause: Error, ...messages: string[]);
|
|
14
|
+
constructor(...messages: string[]);
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Constructor Overload | Description |
|
|
19
|
+
|---------------------|-------------|
|
|
20
|
+
| `new SdError(cause, ...messages)` | Wraps an existing error. Message becomes `"messages[n] => ... => messages[0] => cause.message"` |
|
|
21
|
+
| `new SdError(...messages)` | Creates without cause. Message becomes `"messages[n] => ... => messages[0]"` |
|
|
22
|
+
|
|
23
|
+
When a cause error is provided, its stack trace is appended under a `---- cause stack ----` separator.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
try {
|
|
27
|
+
await fetch(url);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
throw new SdError(err, "API call failed", "User load failed");
|
|
30
|
+
// message: "User load failed => API call failed => <original message>"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw new SdError("Invalid state", "Cannot process");
|
|
34
|
+
// message: "Cannot process => Invalid state"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## `ArgumentError`
|
|
38
|
+
|
|
39
|
+
Error for invalid arguments. Extends `SdError`. Formats the argument object as YAML in the message for easy debugging.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
class ArgumentError extends SdError {
|
|
43
|
+
constructor(argObj: Record<string, unknown>);
|
|
44
|
+
constructor(message: string, argObj: Record<string, unknown>);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Constructor Overload | Description |
|
|
49
|
+
|---------------------|-------------|
|
|
50
|
+
| `new ArgumentError(argObj)` | Default message + YAML-formatted args |
|
|
51
|
+
| `new ArgumentError(message, argObj)` | Custom message + YAML-formatted args |
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
throw new ArgumentError({ userId: 123, name: null });
|
|
55
|
+
// message: "잘못된 인자입니다.\n\nuserId: 123\nname: null"
|
|
56
|
+
|
|
57
|
+
throw new ArgumentError("Invalid user", { userId: 123 });
|
|
58
|
+
// message: "Invalid user\n\nuserId: 123"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## `NotImplementedError`
|
|
62
|
+
|
|
63
|
+
Error for unimplemented features. Extends `SdError`.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
class NotImplementedError extends SdError {
|
|
67
|
+
constructor(message?: string);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Parameter | Type | Description |
|
|
72
|
+
|-----------|------|-------------|
|
|
73
|
+
| `message` | `string \| undefined` | Optional description of what is not implemented |
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
throw new NotImplementedError();
|
|
77
|
+
// message: "미구현"
|
|
78
|
+
|
|
79
|
+
throw new NotImplementedError("Subclass must override");
|
|
80
|
+
// message: "미구현: Subclass must override"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## `TimeoutError`
|
|
84
|
+
|
|
85
|
+
Error for timeout conditions. Extends `SdError`.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
class TimeoutError extends SdError {
|
|
89
|
+
constructor(count?: number, message?: string);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
| Parameter | Type | Description |
|
|
94
|
+
|-----------|------|-------------|
|
|
95
|
+
| `count` | `number \| undefined` | Number of attempts made |
|
|
96
|
+
| `message` | `string \| undefined` | Additional description |
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
throw new TimeoutError(50);
|
|
100
|
+
// message: "대기 시간 초과(50회 시도)"
|
|
101
|
+
|
|
102
|
+
throw new TimeoutError(undefined, "API response wait exceeded");
|
|
103
|
+
// message: "대기 시간 초과: API response wait exceeded"
|
|
104
|
+
```
|
package/docs/features.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Features
|
|
2
|
+
|
|
3
|
+
Async queue and event emitter utilities. All support `using` syntax via `Symbol.dispose`.
|
|
4
|
+
|
|
5
|
+
## `DebounceQueue`
|
|
6
|
+
|
|
7
|
+
Async debounce queue. When called multiple times in rapid succession, only the last enqueued function executes after the delay. Extends `EventEmitter<{ error: SdError }>`.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
class DebounceQueue extends EventEmitter<{ error: SdError }> {
|
|
11
|
+
constructor(delay?: number);
|
|
12
|
+
|
|
13
|
+
run(fn: () => void | Promise<void>): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
[Symbol.dispose](): void;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Constructor
|
|
20
|
+
|
|
21
|
+
| Parameter | Type | Default | Description |
|
|
22
|
+
|-----------|------|---------|-------------|
|
|
23
|
+
| `delay` | `number \| undefined` | `undefined` (next event loop) | Debounce delay in milliseconds |
|
|
24
|
+
|
|
25
|
+
### Methods
|
|
26
|
+
|
|
27
|
+
| Method | Description |
|
|
28
|
+
|--------|-------------|
|
|
29
|
+
| `run(fn)` | Enqueue a function. Replaces any previously pending function. After the delay, executes the last enqueued function |
|
|
30
|
+
| `dispose()` | Cancels pending timer and clears state |
|
|
31
|
+
|
|
32
|
+
### Behavior
|
|
33
|
+
|
|
34
|
+
- If a new `run()` call arrives while a previous function is executing, the new function runs immediately after the current one completes (no debounce delay).
|
|
35
|
+
- Errors are emitted as `"error"` events if listeners exist; otherwise logged via consola.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
const queue = new DebounceQueue(300);
|
|
39
|
+
queue.on("error", (err) => console.error(err));
|
|
40
|
+
|
|
41
|
+
queue.run(() => console.log("1")); // cancelled
|
|
42
|
+
queue.run(() => console.log("2")); // cancelled
|
|
43
|
+
queue.run(() => console.log("3")); // executes after 300ms
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## `SerialQueue`
|
|
47
|
+
|
|
48
|
+
Async serial queue. Functions are executed one at a time in the order they are enqueued. Errors in one function do not prevent subsequent functions from running. Extends `EventEmitter<{ error: SdError }>`.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
class SerialQueue extends EventEmitter<{ error: SdError }> {
|
|
52
|
+
constructor(gap?: number);
|
|
53
|
+
|
|
54
|
+
run(fn: () => void | Promise<void>): void;
|
|
55
|
+
dispose(): void;
|
|
56
|
+
[Symbol.dispose](): void;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Constructor
|
|
61
|
+
|
|
62
|
+
| Parameter | Type | Default | Description |
|
|
63
|
+
|-----------|------|---------|-------------|
|
|
64
|
+
| `gap` | `number` | `0` | Delay between each task execution (ms) |
|
|
65
|
+
|
|
66
|
+
### Methods
|
|
67
|
+
|
|
68
|
+
| Method | Description |
|
|
69
|
+
|--------|-------------|
|
|
70
|
+
| `run(fn)` | Add a function to the queue. Starts processing if not already running |
|
|
71
|
+
| `dispose()` | Clears the pending queue. The currently running task still completes |
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const queue = new SerialQueue();
|
|
75
|
+
queue.on("error", (err) => console.error(err));
|
|
76
|
+
|
|
77
|
+
queue.run(async () => { await fetch("/api/1"); }); // runs first
|
|
78
|
+
queue.run(async () => { await fetch("/api/2"); }); // runs after 1 completes
|
|
79
|
+
queue.run(async () => { await fetch("/api/3"); }); // runs after 2 completes
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## `EventEmitter<TEvents>`
|
|
83
|
+
|
|
84
|
+
Type-safe event emitter built on the `EventTarget` API. Works in both browser and Node.js.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
class EventEmitter<TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>> {
|
|
88
|
+
on<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void;
|
|
89
|
+
off<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void;
|
|
90
|
+
emit<K extends keyof TEvents & string>(
|
|
91
|
+
type: K,
|
|
92
|
+
...args: TEvents[K] extends void ? [] : [data: TEvents[K]]
|
|
93
|
+
): void;
|
|
94
|
+
listenerCount<K extends keyof TEvents & string>(type: K): number;
|
|
95
|
+
dispose(): void;
|
|
96
|
+
[Symbol.dispose](): void;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Methods
|
|
101
|
+
|
|
102
|
+
| Method | Description |
|
|
103
|
+
|--------|-------------|
|
|
104
|
+
| `on(type, listener)` | Register a listener. Duplicate registration for the same event+listener is ignored |
|
|
105
|
+
| `off(type, listener)` | Remove a listener |
|
|
106
|
+
| `emit(type, data?)` | Dispatch an event. For `void` event types, data argument is omitted |
|
|
107
|
+
| `listenerCount(type)` | Returns the number of registered listeners for an event type |
|
|
108
|
+
| `dispose()` | Removes all listeners from all event types |
|
|
109
|
+
|
|
110
|
+
### Type Parameter
|
|
111
|
+
|
|
112
|
+
`TEvents` is a map where keys are event names and values are event data types. Use `void` for events with no data.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
interface MyEvents {
|
|
116
|
+
data: string;
|
|
117
|
+
error: Error;
|
|
118
|
+
done: void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class MyService extends EventEmitter<MyEvents> {}
|
|
122
|
+
|
|
123
|
+
const svc = new MyService();
|
|
124
|
+
svc.on("data", (data) => {}); // data: string
|
|
125
|
+
svc.on("done", () => {}); // no argument
|
|
126
|
+
svc.emit("data", "hello");
|
|
127
|
+
svc.emit("done"); // no argument needed
|
|
128
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Type Utilities
|
|
2
|
+
|
|
3
|
+
TypeScript type aliases and interfaces exported from `common.types.ts`.
|
|
4
|
+
|
|
5
|
+
## `Bytes`
|
|
6
|
+
|
|
7
|
+
Type alias for `Uint8Array`. Used throughout the framework instead of `Buffer`.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
type Bytes = Uint8Array;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## `PrimitiveTypeMap`
|
|
14
|
+
|
|
15
|
+
Maps primitive type string keys to their corresponding TypeScript types. Shared with `orm-common`.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
type PrimitiveTypeMap = {
|
|
19
|
+
string: string;
|
|
20
|
+
number: number;
|
|
21
|
+
boolean: boolean;
|
|
22
|
+
DateTime: DateTime;
|
|
23
|
+
DateOnly: DateOnly;
|
|
24
|
+
Time: Time;
|
|
25
|
+
Uuid: Uuid;
|
|
26
|
+
Bytes: Bytes;
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## `PrimitiveTypeStr`
|
|
31
|
+
|
|
32
|
+
Union of all keys in `PrimitiveTypeMap`.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
type PrimitiveTypeStr = keyof PrimitiveTypeMap;
|
|
36
|
+
// "string" | "number" | "boolean" | "DateTime" | "DateOnly" | "Time" | "Uuid" | "Bytes"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## `PrimitiveType`
|
|
40
|
+
|
|
41
|
+
Union of all primitive type values (including `undefined`).
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
|
|
45
|
+
// string | number | boolean | DateTime | DateOnly | Time | Uuid | Uint8Array | undefined
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## `DeepPartial<TObject>`
|
|
49
|
+
|
|
50
|
+
Recursively makes all properties optional. Primitive types (`PrimitiveType`) are preserved as-is; only object/array types are recursively made partial.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
type DeepPartial<TObject> = Partial<{
|
|
54
|
+
[K in keyof TObject]: TObject[K] extends PrimitiveType
|
|
55
|
+
? TObject[K]
|
|
56
|
+
: DeepPartial<TObject[K]>;
|
|
57
|
+
}>;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
interface User {
|
|
62
|
+
name: string;
|
|
63
|
+
profile: {
|
|
64
|
+
age: number;
|
|
65
|
+
address: { city: string };
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const partial: DeepPartial<User> = {
|
|
70
|
+
profile: { address: {} },
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## `Type<TInstance>`
|
|
75
|
+
|
|
76
|
+
Constructor type interface. Represents a class constructor that creates instances of `TInstance`. Used for dependency injection, factory patterns, and `instanceof` checks.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
interface Type<TInstance> extends Function {
|
|
80
|
+
new (...args: unknown[]): TInstance;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
function create<T>(ctor: Type<T>): T {
|
|
86
|
+
return new ctor();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class MyClass { name = "test"; }
|
|
90
|
+
const instance = create(MyClass); // MyClass instance
|
|
91
|
+
```
|
package/docs/types.md
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Types
|
|
2
|
+
|
|
3
|
+
Immutable value types for UUID, date/time, and a self-expiring map.
|
|
4
|
+
|
|
5
|
+
## `Uuid`
|
|
6
|
+
|
|
7
|
+
UUID v4 class. Uses `crypto.getRandomValues` for cryptographically secure generation.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
class Uuid {
|
|
11
|
+
static generate(): Uuid;
|
|
12
|
+
static fromBytes(bytes: Bytes): Uuid;
|
|
13
|
+
|
|
14
|
+
constructor(uuid: string);
|
|
15
|
+
|
|
16
|
+
toString(): string;
|
|
17
|
+
toBytes(): Bytes;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Static Methods
|
|
22
|
+
|
|
23
|
+
| Method | Description |
|
|
24
|
+
|--------|-------------|
|
|
25
|
+
| `generate()` | Creates a new random UUID v4 instance |
|
|
26
|
+
| `fromBytes(bytes)` | Creates a UUID from a 16-byte `Uint8Array`. Throws `ArgumentError` if length is not 16 |
|
|
27
|
+
|
|
28
|
+
### Constructor
|
|
29
|
+
|
|
30
|
+
| Parameter | Type | Description |
|
|
31
|
+
|-----------|------|-------------|
|
|
32
|
+
| `uuid` | `string` | UUID string in format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. Throws `ArgumentError` if invalid |
|
|
33
|
+
|
|
34
|
+
### Instance Methods
|
|
35
|
+
|
|
36
|
+
| Method | Returns | Description |
|
|
37
|
+
|--------|---------|-------------|
|
|
38
|
+
| `toString()` | `string` | Returns the UUID string representation |
|
|
39
|
+
| `toBytes()` | `Bytes` | Converts to a 16-byte `Uint8Array` |
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const id = Uuid.generate();
|
|
43
|
+
const fromStr = new Uuid("550e8400-e29b-41d4-a716-446655440000");
|
|
44
|
+
const bytes = id.toBytes(); // Uint8Array(16)
|
|
45
|
+
const restored = Uuid.fromBytes(bytes);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## `LazyGcMap<TKey, TValue>`
|
|
49
|
+
|
|
50
|
+
A Map with automatic expiration. Entries not accessed within `expireTime` are garbage collected. Supports `using` syntax (Symbol.dispose).
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
class LazyGcMap<TKey, TValue> {
|
|
54
|
+
constructor(options: {
|
|
55
|
+
gcInterval?: number;
|
|
56
|
+
expireTime: number;
|
|
57
|
+
onExpire?: (key: TKey, value: TValue) => void | Promise<void>;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
get size(): number;
|
|
61
|
+
has(key: TKey): boolean;
|
|
62
|
+
get(key: TKey): TValue | undefined;
|
|
63
|
+
set(key: TKey, value: TValue): void;
|
|
64
|
+
delete(key: TKey): boolean;
|
|
65
|
+
getOrCreate(key: TKey, factory: () => TValue): TValue;
|
|
66
|
+
clear(): void;
|
|
67
|
+
dispose(): void;
|
|
68
|
+
[Symbol.dispose](): void;
|
|
69
|
+
|
|
70
|
+
values(): IterableIterator<TValue>;
|
|
71
|
+
keys(): IterableIterator<TKey>;
|
|
72
|
+
entries(): IterableIterator<[TKey, TValue]>;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Constructor Options
|
|
77
|
+
|
|
78
|
+
| Option | Type | Default | Description |
|
|
79
|
+
|--------|------|---------|-------------|
|
|
80
|
+
| `gcInterval` | `number` | `expireTime / 10` (min 1000ms) | Interval between GC runs (ms) |
|
|
81
|
+
| `expireTime` | `number` | required | Time since last access before entry is removed (ms) |
|
|
82
|
+
| `onExpire` | `(key, value) => void \| Promise<void>` | `undefined` | Callback when an entry expires |
|
|
83
|
+
|
|
84
|
+
### Methods
|
|
85
|
+
|
|
86
|
+
| Method | Description |
|
|
87
|
+
|--------|-------------|
|
|
88
|
+
| `has(key)` | Check if key exists (does NOT refresh access time) |
|
|
89
|
+
| `get(key)` | Get value and refresh access time (LRU) |
|
|
90
|
+
| `set(key, value)` | Set value and start GC timer if needed |
|
|
91
|
+
| `delete(key)` | Delete entry; stops GC if map becomes empty |
|
|
92
|
+
| `getOrCreate(key, factory)` | Get or create value. Throws if disposed |
|
|
93
|
+
| `clear()` | Remove all entries (instance remains usable) |
|
|
94
|
+
| `dispose()` | Stop GC timer and clear all data. Instance becomes unusable |
|
|
95
|
+
|
|
96
|
+
**Important:** Always call `dispose()` or use `using` to prevent memory leaks from the GC timer.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
using map = new LazyGcMap<string, Connection>({
|
|
100
|
+
expireTime: 60_000,
|
|
101
|
+
onExpire: async (_key, conn) => { await conn.close(); },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
map.set("conn1", createConnection());
|
|
105
|
+
const conn = map.get("conn1"); // refreshes access time
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## `DateTime`
|
|
109
|
+
|
|
110
|
+
Immutable date-time class wrapping JavaScript `Date`. Millisecond precision, local timezone.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
class DateTime {
|
|
114
|
+
readonly date: Date;
|
|
115
|
+
|
|
116
|
+
constructor();
|
|
117
|
+
constructor(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millisecond?: number);
|
|
118
|
+
constructor(tick: number);
|
|
119
|
+
constructor(date: Date);
|
|
120
|
+
|
|
121
|
+
static parse(str: string): DateTime;
|
|
122
|
+
|
|
123
|
+
// Getters
|
|
124
|
+
get year(): number;
|
|
125
|
+
get month(): number; // 1-12
|
|
126
|
+
get day(): number;
|
|
127
|
+
get hour(): number;
|
|
128
|
+
get minute(): number;
|
|
129
|
+
get second(): number;
|
|
130
|
+
get millisecond(): number;
|
|
131
|
+
get tick(): number;
|
|
132
|
+
get dayOfWeek(): number; // 0(Sun)-6(Sat)
|
|
133
|
+
get timezoneOffsetMinutes(): number;
|
|
134
|
+
get isValid(): boolean;
|
|
135
|
+
|
|
136
|
+
// Immutable setters (return new instance)
|
|
137
|
+
setYear(year: number): DateTime;
|
|
138
|
+
setMonth(month: number): DateTime;
|
|
139
|
+
setDay(day: number): DateTime;
|
|
140
|
+
setHour(hour: number): DateTime;
|
|
141
|
+
setMinute(minute: number): DateTime;
|
|
142
|
+
setSecond(second: number): DateTime;
|
|
143
|
+
setMillisecond(millisecond: number): DateTime;
|
|
144
|
+
|
|
145
|
+
// Arithmetic (return new instance)
|
|
146
|
+
addYears(years: number): DateTime;
|
|
147
|
+
addMonths(months: number): DateTime;
|
|
148
|
+
addDays(days: number): DateTime;
|
|
149
|
+
addHours(hours: number): DateTime;
|
|
150
|
+
addMinutes(minutes: number): DateTime;
|
|
151
|
+
addSeconds(seconds: number): DateTime;
|
|
152
|
+
addMilliseconds(milliseconds: number): DateTime;
|
|
153
|
+
|
|
154
|
+
// Formatting
|
|
155
|
+
toFormatString(formatStr: string): string;
|
|
156
|
+
toString(): string; // "yyyy-MM-ddTHH:mm:ss.fffzzz"
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `DateTime.parse` Supported Formats
|
|
161
|
+
|
|
162
|
+
| Format | Example |
|
|
163
|
+
|--------|---------|
|
|
164
|
+
| `yyyy-MM-dd HH:mm:ss` | `"2025-01-15 10:30:00"` |
|
|
165
|
+
| `yyyy-MM-dd HH:mm:ss.fff` | `"2025-01-15 10:30:00.123"` |
|
|
166
|
+
| `yyyyMMddHHmmss` | `"20250115103000"` |
|
|
167
|
+
| `yyyy-MM-dd AM/PM HH:mm:ss` | `"2025-01-15 AM 10:30:00"` |
|
|
168
|
+
| ISO 8601 | `"2025-01-15T10:30:00Z"` |
|
|
169
|
+
| Korean AM/PM | `"2025-01-15 오전 10:30:00"` |
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const now = new DateTime();
|
|
173
|
+
const d = new DateTime(2025, 1, 15, 10, 30, 0);
|
|
174
|
+
const parsed = DateTime.parse("2025-01-15 10:30:00");
|
|
175
|
+
const next = d.addDays(7).setHour(14);
|
|
176
|
+
d.toFormatString("yyyy-MM-dd HH:mm"); // "2025-01-15 10:30"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## `DateOnly`
|
|
180
|
+
|
|
181
|
+
Immutable date class (no time component). Local timezone.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
class DateOnly {
|
|
185
|
+
readonly date: Date;
|
|
186
|
+
|
|
187
|
+
constructor();
|
|
188
|
+
constructor(year: number, month: number, day: number);
|
|
189
|
+
constructor(tick: number);
|
|
190
|
+
constructor(date: Date);
|
|
191
|
+
|
|
192
|
+
static parse(str: string): DateOnly;
|
|
193
|
+
static getDateByYearWeekSeq(
|
|
194
|
+
arg: { year: number; month?: number; weekSeq: number },
|
|
195
|
+
weekStartDay?: number,
|
|
196
|
+
minDaysInFirstWeek?: number,
|
|
197
|
+
): DateOnly;
|
|
198
|
+
|
|
199
|
+
// Getters
|
|
200
|
+
get year(): number;
|
|
201
|
+
get month(): number; // 1-12
|
|
202
|
+
get day(): number;
|
|
203
|
+
get tick(): number;
|
|
204
|
+
get dayOfWeek(): number; // 0(Sun)-6(Sat)
|
|
205
|
+
get isValid(): boolean;
|
|
206
|
+
|
|
207
|
+
// Immutable setters
|
|
208
|
+
setYear(year: number): DateOnly;
|
|
209
|
+
setMonth(month: number): DateOnly;
|
|
210
|
+
setDay(day: number): DateOnly;
|
|
211
|
+
|
|
212
|
+
// Arithmetic
|
|
213
|
+
addYears(years: number): DateOnly;
|
|
214
|
+
addMonths(months: number): DateOnly;
|
|
215
|
+
addDays(days: number): DateOnly;
|
|
216
|
+
|
|
217
|
+
// Week calculations
|
|
218
|
+
getBaseYearMonthSeqForWeekSeq(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; monthSeq: number };
|
|
219
|
+
getWeekSeqStartDate(weekStartDay?: number, minDaysInFirstWeek?: number): DateOnly;
|
|
220
|
+
getWeekSeqOfYear(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; weekSeq: number };
|
|
221
|
+
getWeekSeqOfMonth(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; monthSeq: number; weekSeq: number };
|
|
222
|
+
|
|
223
|
+
// Formatting
|
|
224
|
+
toFormatString(formatStr: string): string;
|
|
225
|
+
toString(): string; // "yyyy-MM-dd"
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `DateOnly.parse` Supported Formats
|
|
230
|
+
|
|
231
|
+
| Format | Example |
|
|
232
|
+
|--------|---------|
|
|
233
|
+
| `yyyy-MM-dd` | `"2025-01-15"` (timezone-safe) |
|
|
234
|
+
| `yyyyMMdd` | `"20250115"` (timezone-safe) |
|
|
235
|
+
| ISO 8601 | `"2025-01-15T00:00:00Z"` (converted to local) |
|
|
236
|
+
|
|
237
|
+
### Week Calculation Parameters
|
|
238
|
+
|
|
239
|
+
| Parameter | Default | Description |
|
|
240
|
+
|-----------|---------|-------------|
|
|
241
|
+
| `weekStartDay` | `1` (Monday) | Week start day: 0=Sun, 1=Mon, ..., 6=Sat |
|
|
242
|
+
| `minDaysInFirstWeek` | `4` (ISO 8601) | Minimum days in the first week |
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const today = new DateOnly();
|
|
246
|
+
const d = new DateOnly(2025, 1, 15);
|
|
247
|
+
const parsed = DateOnly.parse("2025-01-15");
|
|
248
|
+
d.getWeekSeqOfYear(); // { year: 2025, weekSeq: 3 }
|
|
249
|
+
d.getWeekSeqOfMonth(); // { year: 2025, monthSeq: 1, weekSeq: 3 }
|
|
250
|
+
DateOnly.getDateByYearWeekSeq({ year: 2025, weekSeq: 2 }); // 2025-01-06
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## `Time`
|
|
254
|
+
|
|
255
|
+
Immutable time class (no date component). Values automatically wrap within 24 hours.
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
class Time {
|
|
259
|
+
constructor();
|
|
260
|
+
constructor(hour: number, minute: number, second?: number, millisecond?: number);
|
|
261
|
+
constructor(tick: number);
|
|
262
|
+
constructor(date: Date);
|
|
263
|
+
|
|
264
|
+
static parse(str: string): Time;
|
|
265
|
+
|
|
266
|
+
// Getters
|
|
267
|
+
get hour(): number;
|
|
268
|
+
get minute(): number;
|
|
269
|
+
get second(): number;
|
|
270
|
+
get millisecond(): number;
|
|
271
|
+
get tick(): number;
|
|
272
|
+
get isValid(): boolean;
|
|
273
|
+
|
|
274
|
+
// Immutable setters
|
|
275
|
+
setHour(hour: number): Time;
|
|
276
|
+
setMinute(minute: number): Time;
|
|
277
|
+
setSecond(second: number): Time;
|
|
278
|
+
setMillisecond(millisecond: number): Time;
|
|
279
|
+
|
|
280
|
+
// Arithmetic (wraps at 24h)
|
|
281
|
+
addHours(hours: number): Time;
|
|
282
|
+
addMinutes(minutes: number): Time;
|
|
283
|
+
addSeconds(seconds: number): Time;
|
|
284
|
+
addMilliseconds(milliseconds: number): Time;
|
|
285
|
+
|
|
286
|
+
// Formatting
|
|
287
|
+
toFormatString(formatStr: string): string;
|
|
288
|
+
toString(): string; // "HH:mm:ss.fff"
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `Time.parse` Supported Formats
|
|
293
|
+
|
|
294
|
+
| Format | Example |
|
|
295
|
+
|--------|---------|
|
|
296
|
+
| `HH:mm:ss` | `"10:30:00"` |
|
|
297
|
+
| `HH:mm:ss.fff` | `"10:30:00.123"` |
|
|
298
|
+
| `AM/PM HH:mm:ss` | `"AM 10:30:00"` |
|
|
299
|
+
| ISO 8601 (time part) | `"2025-01-15T10:30:00Z"` |
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const now = new Time();
|
|
303
|
+
const t = new Time(14, 30, 0);
|
|
304
|
+
const parsed = Time.parse("10:30:00");
|
|
305
|
+
t.addHours(12); // wraps: 02:30:00
|
|
306
|
+
t.toFormatString("tt h:mm:ss"); // "PM 2:30:00"
|
|
307
|
+
```
|