@gracefullight/saju 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +665 -0
- package/README.md +665 -0
- package/dist/__tests__/date-fns-adapter.test.d.ts +2 -0
- package/dist/__tests__/date-fns-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/date-fns-adapter.test.js +155 -0
- package/dist/__tests__/four-pillars.test.d.ts +2 -0
- package/dist/__tests__/four-pillars.test.d.ts.map +1 -0
- package/dist/__tests__/four-pillars.test.js +289 -0
- package/dist/__tests__/luxon-adapter.test.d.ts +2 -0
- package/dist/__tests__/luxon-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/luxon-adapter.test.js +166 -0
- package/dist/adapters/date-adapter.d.ts +75 -0
- package/dist/adapters/date-adapter.d.ts.map +1 -0
- package/dist/adapters/date-adapter.js +1 -0
- package/dist/adapters/date-fns.d.ts +8 -0
- package/dist/adapters/date-fns.d.ts.map +1 -0
- package/dist/adapters/date-fns.js +73 -0
- package/dist/adapters/luxon.d.ts +3 -0
- package/dist/adapters/luxon.d.ts.map +1 -0
- package/dist/adapters/luxon.js +39 -0
- package/dist/core/four-pillars.d.ts +88 -0
- package/dist/core/four-pillars.d.ts.map +1 -0
- package/dist/core/four-pillars.js +221 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
# @gracefullight/saju
|
|
2
|
+
|
|
3
|
+
> 유연한 날짜 어댑터를 지원하는 사주(四柱命理) 계산 TypeScript 라이브러리
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@gracefullight/saju)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
**한국어** | [English](./README.en.md)
|
|
9
|
+
|
|
10
|
+
## 주요 기능
|
|
11
|
+
|
|
12
|
+
- **정확한 사주 계산** - 천문학적 정밀도로 전통 중국 역법 알고리즘 구현
|
|
13
|
+
- **유연한 날짜 어댑터 패턴** - Luxon, date-fns 또는 원하는 날짜 라이브러리 사용 가능
|
|
14
|
+
- **타임존 & 위치 지원** - 타임존 및 지리적 좌표 적절히 처리
|
|
15
|
+
- **태양시 보정** - 경도 기반 평균 태양시 조정 옵션
|
|
16
|
+
- **트리쉐이킹 지원** - 필요한 것만 import
|
|
17
|
+
- **완전한 타입 지원** - TypeScript 정의 완비
|
|
18
|
+
- **충분한 테스트** - 85개 이상 테스트, 91% 이상 커버리지
|
|
19
|
+
|
|
20
|
+
## 사주(四柱)란?
|
|
21
|
+
|
|
22
|
+
사주(四柱), 또는 사주명리는 출생 연월일시를 기반으로 한 전통 한국/중국 운명 분석 시스템입니다. 각 기둥은 다음으로 구성됩니다:
|
|
23
|
+
- **천간(天干)**: 10개 원소 (甲乙丙丁戊己庚辛壬癸)
|
|
24
|
+
- **지지(地支)**: 12지지 (子丑寅卯辰巳午未申酉戌亥)
|
|
25
|
+
|
|
26
|
+
이 라이브러리는 다음을 사용하여 기둥을 계산합니다:
|
|
27
|
+
- **입춘(立春)** 을 이용한 연주 전환
|
|
28
|
+
- **태양 황경** 을 이용한 월주 결정
|
|
29
|
+
- **율리우스 적일** 을 이용한 일주 계산
|
|
30
|
+
- **전통 중국 시진(時辰) 체계** 를 이용한 시주
|
|
31
|
+
|
|
32
|
+
## 설치
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# pnpm 사용
|
|
36
|
+
pnpm add @gracefullight/saju
|
|
37
|
+
|
|
38
|
+
# npm 사용
|
|
39
|
+
npm install @gracefullight/saju
|
|
40
|
+
|
|
41
|
+
# yarn 사용
|
|
42
|
+
yarn add @gracefullight/saju
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 날짜 라이브러리 어댑터
|
|
46
|
+
|
|
47
|
+
선호도에 따라 선택:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 옵션 1: Luxon (현대적인 앱에 권장)
|
|
51
|
+
pnpm add luxon @types/luxon
|
|
52
|
+
|
|
53
|
+
# 옵션 2: date-fns (가벼운 대안)
|
|
54
|
+
pnpm add date-fns date-fns-tz
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 빠른 시작
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { DateTime } from "luxon";
|
|
61
|
+
import { createLuxonAdapter } from "@gracefullight/saju/adapters/luxon";
|
|
62
|
+
import { getFourPillars, STANDARD_PRESET } from "@gracefullight/saju";
|
|
63
|
+
|
|
64
|
+
const adapter = await createLuxonAdapter();
|
|
65
|
+
|
|
66
|
+
const birthDateTime = DateTime.fromObject(
|
|
67
|
+
{ year: 2000, month: 1, day: 1, hour: 18, minute: 0 },
|
|
68
|
+
{ zone: "Asia/Seoul" }
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const result = getFourPillars(adapter, birthDateTime, {
|
|
72
|
+
longitudeDeg: 126.9778, // 서울 경도
|
|
73
|
+
preset: STANDARD_PRESET,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
console.log(result);
|
|
77
|
+
// {
|
|
78
|
+
// year: "己卯", // 연주 (천간 + 지지)
|
|
79
|
+
// month: "丙子", // 월주
|
|
80
|
+
// day: "庚辰", // 일주
|
|
81
|
+
// hour: "辛酉", // 시주
|
|
82
|
+
// meta: {
|
|
83
|
+
// solarYear: 1999,
|
|
84
|
+
// sunLonDeg: 280.9,
|
|
85
|
+
// effectiveDayDate: { year: 2000, month: 1, day: 1 },
|
|
86
|
+
// adjustedHour: 18
|
|
87
|
+
// }
|
|
88
|
+
// }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 사용법
|
|
92
|
+
|
|
93
|
+
### Luxon 사용
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { DateTime } from "luxon";
|
|
97
|
+
import { createLuxonAdapter } from "@gracefullight/saju/adapters/luxon";
|
|
98
|
+
import { getFourPillars, STANDARD_PRESET, TRADITIONAL_PRESET } from "@gracefullight/saju";
|
|
99
|
+
|
|
100
|
+
const adapter = await createLuxonAdapter();
|
|
101
|
+
|
|
102
|
+
const dt = DateTime.fromObject(
|
|
103
|
+
{ year: 2000, month: 1, day: 1, hour: 18, minute: 0 },
|
|
104
|
+
{ zone: "Asia/Seoul" }
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// 표준 프리셋: 자정(00:00) 날짜 경계, 태양시 보정 없음
|
|
108
|
+
const resultStandard = getFourPillars(adapter, dt, {
|
|
109
|
+
longitudeDeg: 126.9778,
|
|
110
|
+
preset: STANDARD_PRESET,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 전통 프리셋: 자시(23:00) 날짜 경계, 태양시 보정 사용
|
|
114
|
+
const resultTraditional = getFourPillars(adapter, dt, {
|
|
115
|
+
longitudeDeg: 126.9778,
|
|
116
|
+
preset: TRADITIONAL_PRESET,
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### date-fns 사용
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { createDateFnsAdapter } from "@gracefullight/saju/adapters/date-fns";
|
|
124
|
+
import { getFourPillars, STANDARD_PRESET } from "@gracefullight/saju";
|
|
125
|
+
|
|
126
|
+
const adapter = await createDateFnsAdapter();
|
|
127
|
+
|
|
128
|
+
const dt = {
|
|
129
|
+
date: new Date(1992, 9, 12, 19, 16), // 주의: 월은 0부터 시작
|
|
130
|
+
timeZone: "Asia/Seoul",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const result = getFourPillars(adapter, dt, {
|
|
134
|
+
longitudeDeg: 126.9778,
|
|
135
|
+
preset: STANDARD_PRESET,
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 커스텀 날짜 어댑터
|
|
140
|
+
|
|
141
|
+
`DateAdapter` 인터페이스를 구현하여 원하는 날짜 라이브러리 사용:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import type { DateAdapter } from "@gracefullight/saju";
|
|
145
|
+
|
|
146
|
+
const myAdapter: DateAdapter<MyDateType> = {
|
|
147
|
+
// 날짜 컴포넌트 getter
|
|
148
|
+
getYear: (date) => date.year,
|
|
149
|
+
getMonth: (date) => date.month,
|
|
150
|
+
getDay: (date) => date.day,
|
|
151
|
+
getHour: (date) => date.hour,
|
|
152
|
+
getMinute: (date) => date.minute,
|
|
153
|
+
getSecond: (date) => date.second,
|
|
154
|
+
getZoneName: (date) => date.zoneName,
|
|
155
|
+
|
|
156
|
+
// 날짜 연산
|
|
157
|
+
plusMinutes: (date, minutes) => date.add({ minutes }),
|
|
158
|
+
plusDays: (date, days) => date.add({ days }),
|
|
159
|
+
minusDays: (date, days) => date.subtract({ days }),
|
|
160
|
+
|
|
161
|
+
// 타임존 연산
|
|
162
|
+
toUTC: (date) => date.toUTC(),
|
|
163
|
+
setZone: (date, zoneName) => date.setZone(zoneName),
|
|
164
|
+
|
|
165
|
+
// 변환
|
|
166
|
+
toISO: (date) => date.toISO(),
|
|
167
|
+
toMillis: (date) => date.valueOf(),
|
|
168
|
+
fromMillis: (millis, zone) => MyDate.fromMillis(millis, zone),
|
|
169
|
+
|
|
170
|
+
// 유틸리티
|
|
171
|
+
createUTC: (year, month, day, hour, minute, second) =>
|
|
172
|
+
MyDate.utc(year, month, day, hour, minute, second),
|
|
173
|
+
isGreaterThanOrEqual: (date1, date2) => date1 >= date2,
|
|
174
|
+
};
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## API 레퍼런스
|
|
178
|
+
|
|
179
|
+
### 설정 프리셋
|
|
180
|
+
|
|
181
|
+
#### `STANDARD_PRESET`
|
|
182
|
+
자정 날짜 경계와 태양시 보정 없는 현대적 해석
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
{
|
|
186
|
+
dayBoundary: "midnight", // 날짜는 00:00에 시작
|
|
187
|
+
useMeanSolarTimeForHour: false, // 시주는 현지 시간 사용
|
|
188
|
+
useMeanSolarTimeForBoundary: false // 날짜 경계는 현지 시간 사용
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### `TRADITIONAL_PRESET`
|
|
193
|
+
자시(23:00) 날짜 경계와 태양시 보정을 사용하는 전통적 해석
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
{
|
|
197
|
+
dayBoundary: "zi23", // 날짜는 23:00(子時)에 시작
|
|
198
|
+
useMeanSolarTimeForHour: true, // 시주는 태양시 사용
|
|
199
|
+
useMeanSolarTimeForBoundary: true // 날짜 경계는 태양시 사용
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### 사용 중단된 별칭
|
|
204
|
+
- `presetA` → `STANDARD_PRESET` 사용 권장
|
|
205
|
+
- `presetB` → `TRADITIONAL_PRESET` 사용 권장
|
|
206
|
+
|
|
207
|
+
### 핵심 함수
|
|
208
|
+
|
|
209
|
+
#### `getFourPillars(adapter, datetime, options)`
|
|
210
|
+
|
|
211
|
+
네 기둥(연주, 월주, 일주, 시주) 모두 계산
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
function getFourPillars<T>(
|
|
215
|
+
adapter: DateAdapter<T>,
|
|
216
|
+
datetime: T,
|
|
217
|
+
options: {
|
|
218
|
+
longitudeDeg: number;
|
|
219
|
+
preset?: {
|
|
220
|
+
dayBoundary: "midnight" | "zi23";
|
|
221
|
+
useMeanSolarTimeForHour: boolean;
|
|
222
|
+
useMeanSolarTimeForBoundary: boolean;
|
|
223
|
+
};
|
|
224
|
+
tzOffsetHours?: number;
|
|
225
|
+
}
|
|
226
|
+
): {
|
|
227
|
+
year: string;
|
|
228
|
+
month: string;
|
|
229
|
+
day: string;
|
|
230
|
+
hour: string;
|
|
231
|
+
meta: {
|
|
232
|
+
solarYear: number;
|
|
233
|
+
sunLonDeg: number;
|
|
234
|
+
effectiveDayDate: { year: number; month: number; day: number };
|
|
235
|
+
adjustedHour: number;
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**매개변수:**
|
|
241
|
+
- `adapter`: DateAdapter 인스턴스
|
|
242
|
+
- `datetime`: 어댑터 형식의 날짜/시간 객체
|
|
243
|
+
- `options`:
|
|
244
|
+
- `longitudeDeg`: 지리적 경도(도 단위) (예: 서울 126.9778)
|
|
245
|
+
- `preset`: 설정 프리셋 (`STANDARD_PRESET` 또는 `TRADITIONAL_PRESET` 사용)
|
|
246
|
+
- `tzOffsetHours`: 타임존 오프셋(시간 단위), 선택사항 (기본값: 9, KST)
|
|
247
|
+
|
|
248
|
+
**반환값:** 연월일시 기둥과 메타데이터를 포함한 객체
|
|
249
|
+
|
|
250
|
+
#### `yearPillar(adapter, datetime)`
|
|
251
|
+
|
|
252
|
+
입춘(立春, 봄의 시작) 기준으로 연주만 계산
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
function yearPillar<T>(
|
|
256
|
+
adapter: DateAdapter<T>,
|
|
257
|
+
datetime: T
|
|
258
|
+
): {
|
|
259
|
+
idx60: number;
|
|
260
|
+
pillar: string;
|
|
261
|
+
solarYear: number;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### `monthPillar(adapter, datetime)`
|
|
266
|
+
|
|
267
|
+
태양 황경 기준으로 월주만 계산
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
function monthPillar<T>(
|
|
271
|
+
adapter: DateAdapter<T>,
|
|
272
|
+
datetime: T
|
|
273
|
+
): {
|
|
274
|
+
pillar: string;
|
|
275
|
+
sunLonDeg: number;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### `dayPillarFromDate({ year, month, day })`
|
|
280
|
+
|
|
281
|
+
율리우스 적일을 사용하여 일주만 계산
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
function dayPillarFromDate(date: {
|
|
285
|
+
year: number;
|
|
286
|
+
month: number;
|
|
287
|
+
day: number;
|
|
288
|
+
}): {
|
|
289
|
+
idx60: number;
|
|
290
|
+
pillar: string;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### `hourPillar(adapter, datetime, options)`
|
|
295
|
+
|
|
296
|
+
태양시 보정 옵션과 함께 시주만 계산
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
function hourPillar<T>(
|
|
300
|
+
adapter: DateAdapter<T>,
|
|
301
|
+
datetime: T,
|
|
302
|
+
options?: {
|
|
303
|
+
longitudeDeg?: number;
|
|
304
|
+
tzOffsetHours?: number;
|
|
305
|
+
useMeanSolarTimeForHour?: boolean;
|
|
306
|
+
dayBoundary?: "midnight" | "zi23";
|
|
307
|
+
useMeanSolarTimeForBoundary?: boolean;
|
|
308
|
+
}
|
|
309
|
+
): {
|
|
310
|
+
pillar: string;
|
|
311
|
+
adjustedDt: T;
|
|
312
|
+
adjustedHour: number;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 상수
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// 10 천간(天干)
|
|
320
|
+
export const STEMS: string[];
|
|
321
|
+
// ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
|
|
322
|
+
|
|
323
|
+
// 12 지지(地支)
|
|
324
|
+
export const BRANCHES: string[];
|
|
325
|
+
// ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 헬퍼 함수
|
|
329
|
+
|
|
330
|
+
#### `applyMeanSolarTime(adapter, dtLocal, longitudeDeg, tzOffsetHours)`
|
|
331
|
+
|
|
332
|
+
경도 기반 평균 태양시 보정 적용
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
function applyMeanSolarTime<T>(
|
|
336
|
+
adapter: DateAdapter<T>,
|
|
337
|
+
dtLocal: T,
|
|
338
|
+
longitudeDeg: number,
|
|
339
|
+
tzOffsetHours: number
|
|
340
|
+
): T
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
#### `effectiveDayDate(adapter, dtLocal, options)`
|
|
344
|
+
|
|
345
|
+
날짜 경계 규칙을 고려한 유효 날짜 계산
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
function effectiveDayDate<T>(
|
|
349
|
+
adapter: DateAdapter<T>,
|
|
350
|
+
dtLocal: T,
|
|
351
|
+
options: {
|
|
352
|
+
dayBoundary?: "midnight" | "zi23";
|
|
353
|
+
longitudeDeg?: number;
|
|
354
|
+
tzOffsetHours?: number;
|
|
355
|
+
useMeanSolarTimeForBoundary?: boolean;
|
|
356
|
+
}
|
|
357
|
+
): {
|
|
358
|
+
year: number;
|
|
359
|
+
month: number;
|
|
360
|
+
day: number;
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## 고급 사용법
|
|
365
|
+
|
|
366
|
+
### 태양시 보정
|
|
367
|
+
|
|
368
|
+
태양시 보정은 현지 시계 시간과 실제 태양시의 차이를 고려하여 경도에 따라 현지 시간을 조정합니다.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { applyMeanSolarTime, createLuxonAdapter } from "@gracefullight/saju";
|
|
372
|
+
import { DateTime } from "luxon";
|
|
373
|
+
|
|
374
|
+
const adapter = await createLuxonAdapter();
|
|
375
|
+
const localTime = DateTime.local(2024, 1, 1, 12, 0, 0, { zone: "Asia/Seoul" });
|
|
376
|
+
|
|
377
|
+
// 서울은 경도 126.9778°E이지만 UTC+9(표준 자오선 135°E) 사용
|
|
378
|
+
// 이로 인해 약 32분 차이 발생
|
|
379
|
+
const solarTime = applyMeanSolarTime(adapter, localTime, 126.9778, 9);
|
|
380
|
+
console.log(solarTime.hour); // ~11.47 (11:28)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 날짜 경계 모드
|
|
384
|
+
|
|
385
|
+
**자정 모드** (`dayBoundary: "midnight"`):
|
|
386
|
+
- 날짜가 00:00 현지 시간에 변경
|
|
387
|
+
- 더 단순하고 현대 달력과 일치
|
|
388
|
+
- 일반적인 사용에 적합
|
|
389
|
+
|
|
390
|
+
**자시 모드** (`dayBoundary: "zi23"`):
|
|
391
|
+
- 날짜가 23:00 현지 시간에 변경
|
|
392
|
+
- 전통 중국 시간 체계
|
|
393
|
+
- 자시(子時)가 자정을 걸침 (23:00-01:00)
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
const result1 = getFourPillars(adapter, dt, {
|
|
397
|
+
longitudeDeg: 126.9778,
|
|
398
|
+
preset: { ...STANDARD_PRESET, dayBoundary: "midnight" },
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const result2 = getFourPillars(adapter, dt, {
|
|
402
|
+
longitudeDeg: 126.9778,
|
|
403
|
+
preset: { ...STANDARD_PRESET, dayBoundary: "zi23" },
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### 커스텀 설정
|
|
408
|
+
|
|
409
|
+
특정 요구사항에 맞게 설정 조합:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
const customConfig = {
|
|
413
|
+
dayBoundary: "midnight" as const, // 현대적인 자정 경계
|
|
414
|
+
useMeanSolarTimeForHour: true, // 하지만 시주는 태양시 사용
|
|
415
|
+
useMeanSolarTimeForBoundary: false, // 날짜 경계는 현지 시간 사용
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const result = getFourPillars(adapter, dt, {
|
|
419
|
+
longitudeDeg: 126.9778,
|
|
420
|
+
preset: customConfig,
|
|
421
|
+
});
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## 지리적 좌표
|
|
425
|
+
|
|
426
|
+
참고용 주요 도시 경도:
|
|
427
|
+
|
|
428
|
+
| 도시 | 경도 | 예시 |
|
|
429
|
+
|------|------|------|
|
|
430
|
+
| 서울, 대한민국 | 126.9778°E | `longitudeDeg: 126.9778` |
|
|
431
|
+
| 베이징, 중국 | 116.4074°E | `longitudeDeg: 116.4074` |
|
|
432
|
+
| 도쿄, 일본 | 139.6917°E | `longitudeDeg: 139.6917` |
|
|
433
|
+
| 상하이, 중국 | 121.4737°E | `longitudeDeg: 121.4737` |
|
|
434
|
+
| 타이베이, 대만 | 121.5654°E | `longitudeDeg: 121.5654` |
|
|
435
|
+
|
|
436
|
+
## 예제
|
|
437
|
+
|
|
438
|
+
### 다양한 타임존에서 계산
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
import { DateTime } from "luxon";
|
|
442
|
+
import { createLuxonAdapter, getFourPillars, TRADITIONAL_PRESET } from "@gracefullight/saju";
|
|
443
|
+
|
|
444
|
+
const adapter = await createLuxonAdapter();
|
|
445
|
+
|
|
446
|
+
// 뉴욕 출생 시간
|
|
447
|
+
const nyTime = DateTime.fromObject(
|
|
448
|
+
{ year: 1992, month: 10, day: 12, hour: 6, minute: 16 },
|
|
449
|
+
{ zone: "America/New_York" }
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
const result = getFourPillars(adapter, nyTime, {
|
|
453
|
+
longitudeDeg: -74.0060, // 뉴욕 경도
|
|
454
|
+
tzOffsetHours: -5, // EST 오프셋
|
|
455
|
+
preset: TRADITIONAL_PRESET,
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### 개별 기둥 계산
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { yearPillar, monthPillar, dayPillarFromDate, hourPillar } from "@gracefullight/saju";
|
|
463
|
+
|
|
464
|
+
// 연주
|
|
465
|
+
const year = yearPillar(adapter, dt);
|
|
466
|
+
console.log(year.pillar, year.solarYear);
|
|
467
|
+
|
|
468
|
+
// 월주
|
|
469
|
+
const month = monthPillar(adapter, dt);
|
|
470
|
+
console.log(month.pillar, month.sunLonDeg);
|
|
471
|
+
|
|
472
|
+
// 일주 (어댑터 불필요)
|
|
473
|
+
const day = dayPillarFromDate({ year: 1992, month: 10, day: 12 });
|
|
474
|
+
console.log(day.pillar);
|
|
475
|
+
|
|
476
|
+
// 태양시를 사용한 시주
|
|
477
|
+
const hour = hourPillar(adapter, dt, {
|
|
478
|
+
longitudeDeg: 126.9778,
|
|
479
|
+
useMeanSolarTimeForHour: true,
|
|
480
|
+
});
|
|
481
|
+
console.log(hour.pillar, hour.adjustedHour);
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### 일괄 처리
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
const birthDates = [
|
|
488
|
+
{ year: 1990, month: 1, day: 15, hour: 10, minute: 30 },
|
|
489
|
+
{ year: 1995, month: 5, day: 20, hour: 14, minute: 45 },
|
|
490
|
+
{ year: 2000, month: 12, day: 25, hour: 18, minute: 0 },
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
const adapter = await createLuxonAdapter();
|
|
494
|
+
|
|
495
|
+
const results = birthDates.map((birth) => {
|
|
496
|
+
const dt = DateTime.fromObject(birth, { zone: "Asia/Seoul" });
|
|
497
|
+
return {
|
|
498
|
+
birth,
|
|
499
|
+
pillars: getFourPillars(adapter, dt, {
|
|
500
|
+
longitudeDeg: 126.9778,
|
|
501
|
+
preset: STANDARD_PRESET,
|
|
502
|
+
}),
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## 개발
|
|
508
|
+
|
|
509
|
+
### 설정
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
# 저장소 클론
|
|
513
|
+
git clone https://github.com/gracefullight/saju.git
|
|
514
|
+
cd saju
|
|
515
|
+
|
|
516
|
+
# 의존성 설치
|
|
517
|
+
pnpm install
|
|
518
|
+
|
|
519
|
+
# 테스트 실행
|
|
520
|
+
pnpm test
|
|
521
|
+
|
|
522
|
+
# 커버리지와 함께 테스트 실행
|
|
523
|
+
pnpm test:coverage
|
|
524
|
+
|
|
525
|
+
# 빌드
|
|
526
|
+
pnpm build
|
|
527
|
+
|
|
528
|
+
# 린트
|
|
529
|
+
pnpm lint
|
|
530
|
+
|
|
531
|
+
# 포맷
|
|
532
|
+
pnpm lint:fix
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### 프로젝트 구조
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
packages/saju/
|
|
539
|
+
├── src/
|
|
540
|
+
│ ├── adapters/ # 날짜 라이브러리 어댑터
|
|
541
|
+
│ │ ├── date-adapter.ts # 어댑터 인터페이스
|
|
542
|
+
│ │ ├── luxon.ts # Luxon 어댑터
|
|
543
|
+
│ │ └── date-fns.ts # date-fns 어댑터
|
|
544
|
+
│ ├── core/ # 핵심 계산 로직
|
|
545
|
+
│ │ └── four-pillars.ts # 메인 알고리즘
|
|
546
|
+
│ ├── __tests__/ # 테스트 스위트
|
|
547
|
+
│ └── index.ts # 공개 API
|
|
548
|
+
├── dist/ # 컴파일된 출력
|
|
549
|
+
├── coverage/ # 테스트 커버리지 리포트
|
|
550
|
+
└── README.md
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### 테스트 실행
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
# 모든 테스트 실행
|
|
557
|
+
pnpm test
|
|
558
|
+
|
|
559
|
+
# watch 모드로 테스트 실행
|
|
560
|
+
pnpm test:watch
|
|
561
|
+
|
|
562
|
+
# 커버리지 리포트 생성
|
|
563
|
+
pnpm test:coverage
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
커버리지 결과:
|
|
567
|
+
```
|
|
568
|
+
File | % Stmts | % Branch | % Funcs | % Lines
|
|
569
|
+
-------------------|---------|----------|---------|----------
|
|
570
|
+
All files | 91.45 | 80.68 | 96.55 | 91.45
|
|
571
|
+
src/adapters | 94.59 | 90.24 | 100 | 94.59
|
|
572
|
+
src/core | 96.87 | 75.55 | 100 | 96.87
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## 자주 묻는 질문
|
|
576
|
+
|
|
577
|
+
### 단일 날짜 라이브러리 대신 날짜 어댑터를 사용하는 이유는?
|
|
578
|
+
|
|
579
|
+
프로젝트마다 다른 날짜 라이브러리를 사용합니다. 어댑터 패턴을 통해:
|
|
580
|
+
- 추가 의존성 없이 기존 날짜 라이브러리 사용 가능
|
|
581
|
+
- 필요한 것만 포함하여 번들 크기 최소화
|
|
582
|
+
- 프로젝트의 날짜 처리 방식과 일관성 유지
|
|
583
|
+
|
|
584
|
+
### STANDARD_PRESET과 TRADITIONAL_PRESET의 차이는?
|
|
585
|
+
|
|
586
|
+
**STANDARD_PRESET**은 현대적 관례 사용:
|
|
587
|
+
- 날짜가 자정(00:00)에 시작
|
|
588
|
+
- 현지 시계 시간 사용
|
|
589
|
+
- 일반적인 사용에 더 간단
|
|
590
|
+
|
|
591
|
+
**TRADITIONAL_PRESET**은 전통 중국 점성술 따름:
|
|
592
|
+
- 날짜가 자시(23:00)에 시작
|
|
593
|
+
- 경도 기반 태양시 보정 적용
|
|
594
|
+
- 역사적으로 더 정확
|
|
595
|
+
|
|
596
|
+
### 계산은 얼마나 정확한가요?
|
|
597
|
+
|
|
598
|
+
이 라이브러리는 다음을 구현합니다:
|
|
599
|
+
- 일주를 위한 율리우스 적일 알고리즘 (모든 역사적 날짜에 정확)
|
|
600
|
+
- 월주를 위한 천문학적 태양 황경 계산
|
|
601
|
+
- 연주를 위한 입춘(봄의 시작) 계산
|
|
602
|
+
- 시주를 위한 전통 중국 시진(時辰) 체계
|
|
603
|
+
|
|
604
|
+
모든 알고리즘은 알려진 역사적 날짜로 테스트되었으며 전통 중국 달력 참고자료와 일치합니다.
|
|
605
|
+
|
|
606
|
+
### 역사적 날짜에도 사용할 수 있나요?
|
|
607
|
+
|
|
608
|
+
네! 율리우스 적일 알고리즘은 다음에서 올바르게 작동합니다:
|
|
609
|
+
- 그레고리력의 모든 날짜 (1582년 이후)
|
|
610
|
+
- 율리우스력의 대부분 날짜 (적절한 달력 변환 포함)
|
|
611
|
+
- 먼 미래의 날짜
|
|
612
|
+
|
|
613
|
+
다만, 약 1970년 이전 날짜의 타임존 데이터는 덜 정확할 수 있습니다.
|
|
614
|
+
|
|
615
|
+
### 같은 출생 시간이 다른 프리셋에서 다른 결과를 주는 이유는?
|
|
616
|
+
|
|
617
|
+
프리셋은 다음에 영향을 미칩니다:
|
|
618
|
+
1. **날짜 경계**: 날짜가 실제로 변경되는 시점 (자정 vs. 23:00)
|
|
619
|
+
2. **태양시**: 경도 차이에 대한 조정 여부
|
|
620
|
+
|
|
621
|
+
예를 들어, 23:30은 다음과 같을 수 있습니다:
|
|
622
|
+
- 같은 날의 자시 (자정 경계 사용 시)
|
|
623
|
+
- 다음 날의 자시 (자시23 경계 사용 시)
|
|
624
|
+
|
|
625
|
+
이는 의도적이며 사주 해석의 다양한 학파를 반영합니다.
|
|
626
|
+
|
|
627
|
+
## 기여하기
|
|
628
|
+
|
|
629
|
+
기여를 환영합니다! Pull Request를 자유롭게 제출해주세요.
|
|
630
|
+
|
|
631
|
+
1. 저장소 포크
|
|
632
|
+
2. feature 브랜치 생성 (`git checkout -b feature/amazing-feature`)
|
|
633
|
+
3. 변경사항 커밋 (`git commit -m 'Add some amazing feature'`)
|
|
634
|
+
4. 브랜치에 푸시 (`git push origin feature/amazing-feature`)
|
|
635
|
+
5. Pull Request 오픈
|
|
636
|
+
|
|
637
|
+
### 가이드라인
|
|
638
|
+
|
|
639
|
+
- 새 기능에 대한 테스트 작성
|
|
640
|
+
- 코드 커버리지 유지 또는 개선
|
|
641
|
+
- 기존 코드 스타일 따르기 (Biome으로 강제)
|
|
642
|
+
- 필요에 따라 문서 업데이트
|
|
643
|
+
|
|
644
|
+
## 라이선스
|
|
645
|
+
|
|
646
|
+
MIT © [gracefullight](https://github.com/gracefullight)
|
|
647
|
+
|
|
648
|
+
## 크레딧
|
|
649
|
+
|
|
650
|
+
이 라이브러리는 사주명리(四柱命理)에 사용되는 전통 중국 역법 알고리즘과 천문 계산을 기반으로 합니다.
|
|
651
|
+
|
|
652
|
+
## 관련 프로젝트
|
|
653
|
+
|
|
654
|
+
- [Luxon](https://moment.github.io/luxon/) - 현대적인 날짜/시간 라이브러리
|
|
655
|
+
- [date-fns](https://date-fns.org/) - 현대적인 JavaScript 날짜 유틸리티 라이브러리
|
|
656
|
+
|
|
657
|
+
## 지원
|
|
658
|
+
|
|
659
|
+
- [문서](https://github.com/gracefullight/saju#readme)
|
|
660
|
+
- [이슈 트래커](https://github.com/gracefullight/saju/issues)
|
|
661
|
+
- [토론](https://github.com/gracefullight/saju/discussions)
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
Made by [gracefullight](https://github.com/gracefullight)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-fns-adapter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/date-fns-adapter.test.ts"],"names":[],"mappings":""}
|