@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 ADDED
@@ -0,0 +1,665 @@
1
+ # @gracefullight/saju
2
+
3
+ > TypeScript library for calculating Four Pillars of Destiny (四柱命理, Saju) with flexible date adapter support.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@gracefullight/saju.svg)](https://www.npmjs.com/package/@gracefullight/saju)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ [한국어](./README.md) | **English**
9
+
10
+ ## Features
11
+
12
+ - **Accurate Four Pillars Calculation** - Implements traditional Chinese calendar algorithms with astronomical precision
13
+ - **Flexible Date Adapter Pattern** - Use Luxon, date-fns, or bring your own date library
14
+ - **Timezone & Location Support** - Proper handling of timezones and geographic coordinates
15
+ - **Solar Time Correction** - Optional mean solar time adjustment based on longitude
16
+ - **Tree-shakeable** - Import only what you need
17
+ - **Fully Typed** - Complete TypeScript definitions
18
+ - **Well Tested** - 85+ tests with 91%+ coverage
19
+
20
+ ## What is Saju (四柱)?
21
+
22
+ Saju, or Four Pillars of Destiny, is a traditional Korean/Chinese fortune-telling system based on one's birth year, month, day, and hour. Each pillar consists of:
23
+ - **Heavenly Stem (天干)**: 10 elements (甲乙丙丁戊己庚辛壬癸)
24
+ - **Earthly Branch (地支)**: 12 zodiac signs (子丑寅卯辰巳午未申酉戌亥)
25
+
26
+ This library calculates these pillars using:
27
+ - **Lichun (立春)** for year pillar transitions
28
+ - **Solar longitude** for month pillar determination
29
+ - **Julian Day Number** for day pillar calculation
30
+ - **Traditional Chinese hour system (時辰)** for hour pillar
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ # Using pnpm
36
+ pnpm add @gracefullight/saju
37
+
38
+ # Using npm
39
+ npm install @gracefullight/saju
40
+
41
+ # Using yarn
42
+ yarn add @gracefullight/saju
43
+ ```
44
+
45
+ ### Date Library Adapters
46
+
47
+ Choose one based on your preference:
48
+
49
+ ```bash
50
+ # Option 1: Luxon (recommended for modern apps)
51
+ pnpm add luxon @types/luxon
52
+
53
+ # Option 2: date-fns (lightweight alternative)
54
+ pnpm add date-fns date-fns-tz
55
+ ```
56
+
57
+ ## Quick Start
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, // Seoul longitude
73
+ preset: STANDARD_PRESET,
74
+ });
75
+
76
+ console.log(result);
77
+ // {
78
+ // year: "己卯", // Year Pillar (Heavenly Stem + Earthly Branch)
79
+ // month: "丙子", // Month Pillar
80
+ // day: "庚辰", // Day Pillar
81
+ // hour: "辛酉", // Hour Pillar
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
+ ## Usage
92
+
93
+ ### With 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
+ // Standard Preset: Midnight (00:00) day boundary, no solar time correction
108
+ const resultStandard = getFourPillars(adapter, dt, {
109
+ longitudeDeg: 126.9778,
110
+ preset: STANDARD_PRESET,
111
+ });
112
+
113
+ // Traditional Preset: Zi hour (23:00) day boundary, with solar time correction
114
+ const resultTraditional = getFourPillars(adapter, dt, {
115
+ longitudeDeg: 126.9778,
116
+ preset: TRADITIONAL_PRESET,
117
+ });
118
+ ```
119
+
120
+ ### With 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), // Note: month is 0-indexed
130
+ timeZone: "Asia/Seoul",
131
+ };
132
+
133
+ const result = getFourPillars(adapter, dt, {
134
+ longitudeDeg: 126.9778,
135
+ preset: STANDARD_PRESET,
136
+ });
137
+ ```
138
+
139
+ ### Custom Date Adapter
140
+
141
+ Implement the `DateAdapter` interface to use any date library:
142
+
143
+ ```typescript
144
+ import type { DateAdapter } from "@gracefullight/saju";
145
+
146
+ const myAdapter: DateAdapter<MyDateType> = {
147
+ // Date component getters
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
+ // Date arithmetic
157
+ plusMinutes: (date, minutes) => date.add({ minutes }),
158
+ plusDays: (date, days) => date.add({ days }),
159
+ minusDays: (date, days) => date.subtract({ days }),
160
+
161
+ // Timezone operations
162
+ toUTC: (date) => date.toUTC(),
163
+ setZone: (date, zoneName) => date.setZone(zoneName),
164
+
165
+ // Conversions
166
+ toISO: (date) => date.toISO(),
167
+ toMillis: (date) => date.valueOf(),
168
+ fromMillis: (millis, zone) => MyDate.fromMillis(millis, zone),
169
+
170
+ // Utilities
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 Reference
178
+
179
+ ### Configuration Presets
180
+
181
+ #### `STANDARD_PRESET`
182
+ Modern interpretation with midnight day boundary and no solar time correction.
183
+
184
+ ```typescript
185
+ {
186
+ dayBoundary: "midnight", // Day starts at 00:00
187
+ useMeanSolarTimeForHour: false, // Use local time for hour
188
+ useMeanSolarTimeForBoundary: false // Use local time for day boundary
189
+ }
190
+ ```
191
+
192
+ #### `TRADITIONAL_PRESET`
193
+ Traditional interpretation with Zi hour (23:00) day boundary and solar time correction.
194
+
195
+ ```typescript
196
+ {
197
+ dayBoundary: "zi23", // Day starts at 23:00 (子時)
198
+ useMeanSolarTimeForHour: true, // Use solar time for hour
199
+ useMeanSolarTimeForBoundary: true // Use solar time for day boundary
200
+ }
201
+ ```
202
+
203
+ #### Deprecated Aliases
204
+ - `presetA` → Use `STANDARD_PRESET`
205
+ - `presetB` → Use `TRADITIONAL_PRESET`
206
+
207
+ ### Core Functions
208
+
209
+ #### `getFourPillars(adapter, datetime, options)`
210
+
211
+ Calculate all four pillars (year, month, day, hour).
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
+ **Parameters:**
241
+ - `adapter`: DateAdapter instance
242
+ - `datetime`: Date/time object in the adapter's format
243
+ - `options`:
244
+ - `longitudeDeg`: Geographic longitude in degrees (e.g., Seoul: 126.9778)
245
+ - `preset`: Configuration preset (use `STANDARD_PRESET` or `TRADITIONAL_PRESET`)
246
+ - `tzOffsetHours`: Optional timezone offset in hours (default: 9 for KST)
247
+
248
+ **Returns:** Object with year, month, day, hour pillars and metadata
249
+
250
+ #### `yearPillar(adapter, datetime)`
251
+
252
+ Calculate only the year pillar based on Lichun (立春, Start of Spring).
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
+ Calculate only the month pillar based on solar longitude.
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
+ Calculate only the day pillar using Julian Day Number.
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
+ Calculate only the hour pillar with optional solar time correction.
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
+ ### Constants
317
+
318
+ ```typescript
319
+ // 10 Heavenly Stems (天干)
320
+ export const STEMS: string[];
321
+ // ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
322
+
323
+ // 12 Earthly Branches (地支)
324
+ export const BRANCHES: string[];
325
+ // ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
326
+ ```
327
+
328
+ ### Helper Functions
329
+
330
+ #### `applyMeanSolarTime(adapter, dtLocal, longitudeDeg, tzOffsetHours)`
331
+
332
+ Apply mean solar time correction based on longitude.
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
+ Calculate the effective date considering day boundary rules.
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
+ ## Advanced Usage
365
+
366
+ ### Solar Time Correction
367
+
368
+ Solar time correction adjusts local time based on longitude to account for the difference between local clock time and actual solar time.
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
+ // Seoul is at 126.9778°E, but uses UTC+9 (135°E standard meridian)
378
+ // This creates a ~32 minute difference
379
+ const solarTime = applyMeanSolarTime(adapter, localTime, 126.9778, 9);
380
+ console.log(solarTime.hour); // ~11.47 (11:28)
381
+ ```
382
+
383
+ ### Day Boundary Modes
384
+
385
+ **Midnight Mode** (`dayBoundary: "midnight"`):
386
+ - Day changes at 00:00 local time
387
+ - Simpler, matches modern calendar
388
+ - Good for general use
389
+
390
+ **Zi Hour Mode** (`dayBoundary: "zi23"`):
391
+ - Day changes at 23:00 local time
392
+ - Traditional Chinese timekeeping
393
+ - Zi hour (子時) straddles midnight (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
+ ### Custom Configuration
408
+
409
+ Mix and match settings for specific needs:
410
+
411
+ ```typescript
412
+ const customConfig = {
413
+ dayBoundary: "midnight" as const, // Modern midnight boundary
414
+ useMeanSolarTimeForHour: true, // But use solar time for hour
415
+ useMeanSolarTimeForBoundary: false, // Local time for day boundary
416
+ };
417
+
418
+ const result = getFourPillars(adapter, dt, {
419
+ longitudeDeg: 126.9778,
420
+ preset: customConfig,
421
+ });
422
+ ```
423
+
424
+ ## Geographic Coordinates
425
+
426
+ Common city longitudes for reference:
427
+
428
+ | City | Longitude | Example |
429
+ |------|-----------|---------|
430
+ | Seoul, South Korea | 126.9778°E | `longitudeDeg: 126.9778` |
431
+ | Beijing, China | 116.4074°E | `longitudeDeg: 116.4074` |
432
+ | Tokyo, Japan | 139.6917°E | `longitudeDeg: 139.6917` |
433
+ | Shanghai, China | 121.4737°E | `longitudeDeg: 121.4737` |
434
+ | Taipei, Taiwan | 121.5654°E | `longitudeDeg: 121.5654` |
435
+
436
+ ## Examples
437
+
438
+ ### Calculate for Different Timezones
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
+ // New York birth time
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, // NYC longitude
454
+ tzOffsetHours: -5, // EST offset
455
+ preset: TRADITIONAL_PRESET,
456
+ });
457
+ ```
458
+
459
+ ### Calculate Individual Pillars
460
+
461
+ ```typescript
462
+ import { yearPillar, monthPillar, dayPillarFromDate, hourPillar } from "@gracefullight/saju";
463
+
464
+ // Year pillar
465
+ const year = yearPillar(adapter, dt);
466
+ console.log(year.pillar, year.solarYear);
467
+
468
+ // Month pillar
469
+ const month = monthPillar(adapter, dt);
470
+ console.log(month.pillar, month.sunLonDeg);
471
+
472
+ // Day pillar (no adapter needed)
473
+ const day = dayPillarFromDate({ year: 1992, month: 10, day: 12 });
474
+ console.log(day.pillar);
475
+
476
+ // Hour pillar with solar time
477
+ const hour = hourPillar(adapter, dt, {
478
+ longitudeDeg: 126.9778,
479
+ useMeanSolarTimeForHour: true,
480
+ });
481
+ console.log(hour.pillar, hour.adjustedHour);
482
+ ```
483
+
484
+ ### Batch Processing
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
+ ## Development
508
+
509
+ ### Setup
510
+
511
+ ```bash
512
+ # Clone repository
513
+ git clone https://github.com/gracefullight/saju.git
514
+ cd saju
515
+
516
+ # Install dependencies
517
+ pnpm install
518
+
519
+ # Run tests
520
+ pnpm test
521
+
522
+ # Run tests with coverage
523
+ pnpm test:coverage
524
+
525
+ # Build
526
+ pnpm build
527
+
528
+ # Lint
529
+ pnpm lint
530
+
531
+ # Format
532
+ pnpm lint:fix
533
+ ```
534
+
535
+ ### Project Structure
536
+
537
+ ```
538
+ packages/saju/
539
+ ├── src/
540
+ │ ├── adapters/ # Date library adapters
541
+ │ │ ├── date-adapter.ts # Adapter interface
542
+ │ │ ├── luxon.ts # Luxon adapter
543
+ │ │ └── date-fns.ts # date-fns adapter
544
+ │ ├── core/ # Core calculation logic
545
+ │ │ └── four-pillars.ts # Main algorithms
546
+ │ ├── __tests__/ # Test suites
547
+ │ └── index.ts # Public API
548
+ ├── dist/ # Compiled output
549
+ ├── coverage/ # Test coverage reports
550
+ └── README.md
551
+ ```
552
+
553
+ ### Running Tests
554
+
555
+ ```bash
556
+ # Run all tests
557
+ pnpm test
558
+
559
+ # Run tests in watch mode
560
+ pnpm test:watch
561
+
562
+ # Generate coverage report
563
+ pnpm test:coverage
564
+ ```
565
+
566
+ Coverage results:
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
+ ## FAQ
576
+
577
+ ### Why use date adapters instead of a single date library?
578
+
579
+ Different projects use different date libraries. The adapter pattern allows you to:
580
+ - Use your existing date library without adding another dependency
581
+ - Keep bundle size minimal by only including what you need
582
+ - Maintain consistency with your project's date handling
583
+
584
+ ### What's the difference between STANDARD_PRESET and TRADITIONAL_PRESET?
585
+
586
+ **STANDARD_PRESET** uses modern conventions:
587
+ - Day starts at midnight (00:00)
588
+ - Uses local clock time
589
+ - Simpler for general use
590
+
591
+ **TRADITIONAL_PRESET** follows traditional Chinese astrology:
592
+ - Day starts at Zi hour (23:00)
593
+ - Applies solar time correction based on longitude
594
+ - More historically accurate
595
+
596
+ ### How accurate are the calculations?
597
+
598
+ The library implements:
599
+ - Julian Day Number algorithm for day pillars (accurate across all historical dates)
600
+ - Astronomical solar longitude calculations for month pillars
601
+ - Lichun (Start of Spring) calculation for year pillars
602
+ - Traditional Chinese hour system (時辰) for hour pillars
603
+
604
+ All algorithms are tested with known historical dates and match traditional Chinese calendar references.
605
+
606
+ ### Can I use this for historical dates?
607
+
608
+ Yes! The Julian Day Number algorithm works correctly for:
609
+ - All dates in the Gregorian calendar (1582 onwards)
610
+ - Most dates in the Julian calendar (with appropriate calendar conversion)
611
+ - Dates far in the future
612
+
613
+ However, note that timezone data may be less accurate for dates before ~1970.
614
+
615
+ ### Why does the same birth time give different results with different presets?
616
+
617
+ The presets affect:
618
+ 1. **Day boundary**: When the day actually changes (midnight vs. 23:00)
619
+ 2. **Solar time**: Whether to adjust for longitude difference
620
+
621
+ For example, 23:30 could be:
622
+ - Same day's Zi hour (with midnight boundary)
623
+ - Next day's Zi hour (with Zi23 boundary)
624
+
625
+ This is intentional and reflects different schools of thought in Saju interpretation.
626
+
627
+ ## Contributing
628
+
629
+ Contributions are welcome! Please feel free to submit a Pull Request.
630
+
631
+ 1. Fork the repository
632
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
633
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
634
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
635
+ 5. Open a Pull Request
636
+
637
+ ### Guidelines
638
+
639
+ - Write tests for new features
640
+ - Maintain or improve code coverage
641
+ - Follow existing code style (enforced by Biome)
642
+ - Update documentation as needed
643
+
644
+ ## License
645
+
646
+ MIT © [gracefullight](https://github.com/gracefullight)
647
+
648
+ ## Credits
649
+
650
+ This library is based on traditional Chinese calendar algorithms and astronomical calculations used in Four Pillars astrology (四柱命理).
651
+
652
+ ## Related Projects
653
+
654
+ - [Luxon](https://moment.github.io/luxon/) - Modern date/time library
655
+ - [date-fns](https://date-fns.org/) - Modern JavaScript date utility library
656
+
657
+ ## Support
658
+
659
+ - [Documentation](https://github.com/gracefullight/saju#readme)
660
+ - [Issue Tracker](https://github.com/gracefullight/saju/issues)
661
+ - [Discussions](https://github.com/gracefullight/saju/discussions)
662
+
663
+ ---
664
+
665
+ Made by [gracefullight](https://github.com/gracefullight)