@lichta/core 2.0.1 → 2.2.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.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # @lichta/core
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@lichta/core.svg)](https://www.npmjs.com/package/@lichta/core)
3
+ [![@lichta/core](https://img.shields.io/npm/v/@lichta/core.svg?label=%40lichta%2Fcore)](https://www.npmjs.com/package/@lichta/core)
4
+ [![@lichta/react](https://img.shields.io/npm/v/@lichta/react.svg?label=%40lichta%2Freact)](https://www.npmjs.com/package/@lichta/react)
5
+ [![@lichta/vue](https://img.shields.io/npm/v/@lichta/vue.svg?label=%40lichta%2Fvue)](https://www.npmjs.com/package/@lichta/vue)
6
+ [![@lichta/svelte](https://img.shields.io/npm/v/@lichta/svelte.svg?label=%40lichta%2Fsvelte)](https://www.npmjs.com/package/@lichta/svelte)
7
+ [![@lichta/full-calendar](https://img.shields.io/npm/v/@lichta/full-calendar.svg?label=%40lichta%2Ffull-calendar)](https://www.npmjs.com/package/@lichta/full-calendar)
8
+ [![@lichta/event-calendar](https://img.shields.io/npm/v/@lichta/event-calendar.svg?label=%40lichta%2Fevent-calendar)](https://www.npmjs.com/package/@lichta/event-calendar)
9
+ [![@lichta/react-big-calendar](https://img.shields.io/npm/v/@lichta/react-big-calendar.svg?label=%40lichta%2Freact-big-calendar)](https://www.npmjs.com/package/@lichta/react-big-calendar)
10
+
11
+ 🚀 **[Demo trực tiếp](https://lichta.zeneo.app/)**
4
12
 
5
13
  Thư viện lõi tính toán chuyển đổi **Dương lịch ↔ Âm lịch Việt Nam** cho JavaScript/TypeScript. Thuần TypeScript, **0 dependency**, không phụ thuộc framework nào — dùng được ở Node.js, trình duyệt, hoặc làm nền cho các binding framework khác ([`@lichta/react`](https://www.npmjs.com/package/@lichta/react), [`@lichta/vue`](https://www.npmjs.com/package/@lichta/vue), [`@lichta/svelte`](https://www.npmjs.com/package/@lichta/svelte)).
6
14
 
@@ -34,18 +42,102 @@ const lunarGmt8 = LichTa.toLunar(10, 2, 2024, 8);
34
42
  ## Can Chi & Phong Thủy
35
43
 
36
44
  ```typescript
37
- import { LichTa, getYearDetails, getDayCanChi, getMonthCanChi, getHourCanChi, getAuspiciousHours } from '@lichta/core';
45
+ import { LichTa, getYearDetails, getDayCanChi, getMonthCanChi, getHourCanChi, getAuspiciousHours, getAuspiciousHourIndices, getInauspiciousHours, getInauspiciousHourIndices } from '@lichta/core';
38
46
 
39
47
  getYearDetails(2024);
40
- // → { can: 'Giáp', chi: 'Thìn', menh: 'Thủy', fullString: 'Giáp Thìn - Mệnh Thủy' }
48
+ // → { can: 'Giáp', chi: 'Thìn', menh: 'Hỏa', menhIndex: 3, fullString: 'Giáp Thìn - Mệnh Hỏa' }
49
+ // getYearDetails() nhận mọi số năm nguyên (kể cả 0 hoặc âm) — chu kỳ Can Chi 60 năm lặp vô hạn.
50
+ // menh dùng bảng tra cứu Nạp Âm 60 Hoa Giáp chuẩn (không phải công thức suy diễn) — xem mục Ngũ Hành bên dưới.
41
51
 
42
52
  const lunar = LichTa.toLunar(10, 2, 2024);
43
53
  getDayCanChi(lunar.jd); // 'Giáp Thìn'
44
54
  getMonthCanChi(lunar.month, lunar.year); // 'Bính Dần'
45
55
  getHourCanChi(8, lunar.jd); // giờ Thìn (7h-9h)
46
- getAuspiciousHours(lunar.jd); // 6 giờ Hoàng Đạo trong ngày
56
+ getAuspiciousHours(lunar.jd); // ['Tý', 'Mão', 'Thìn', 'Ngọ', 'Dậu', 'Tuất'] — 6 giờ Hoàng Đạo (giờ tốt)
57
+ getInauspiciousHours(lunar.jd); // ['Sửu', 'Dần', 'Tỵ', 'Mùi', 'Thân', 'Hợi'] — 6 giờ Hắc Đạo (giờ xấu), phần bù
58
+
59
+ // Mỗi hàm trên đều nhận thêm tham số `locale` tùy chọn (mặc định 'vi', không đổi hành vi cũ)
60
+ getDayCanChi(lunar.jd, 'ja'); // Can Chi bằng chữ Nhật
61
+
62
+ // Cần index Địa Chi thay vì tên (ví dụ để tự map sang UI riêng)?
63
+ getAuspiciousHourIndices(lunar.jd); // [0, 3, 4, 6, 9, 10] — index 0-11 (0 = Tý)
64
+ getInauspiciousHourIndices(lunar.jd); // [1, 2, 5, 7, 8, 11] — phần bù, luôn tăng dần
65
+ ```
66
+
67
+ ## Ngũ Hành (Nạp Âm & Tương Sinh Tương Khắc)
68
+
69
+ ```typescript
70
+ import { getElementName, getDayElement, getMonthElement, getHourElement, getElementRelation, getElementRelationIndex } from '@lichta/core';
71
+
72
+ // Mệnh (Ngũ Hành Nạp Âm) cho Ngày/Tháng/Giờ — dùng chung bảng Nạp Âm 60 Hoa Giáp với getYearDetails()
73
+ getDayElement(lunar.jd); // 'Hỏa' (Giáp Thìn/Ất Tỵ = Phú Đăng Hỏa)
74
+ getMonthElement(lunar.month, lunar.year); // 'Hỏa' (Bính Dần/Đinh Mão = Lư Trung Hỏa)
75
+ getHourElement(8, lunar.jd); // 'Mộc' (giờ Mậu Thìn/Kỷ Tỵ = Đại Lâm Mộc)
76
+
77
+ // Mỗi hàm trên có bản `*Index` đi kèm, trả về index 0-4 (vào FIVE_ELEMENTS) thay vì tên:
78
+ // getDayElementIndex(jd), getMonthElementIndex(month, year), getHourElementIndex(hour, dayJd)
79
+ getElementName(3); // 'Hỏa' — tra tên từ index
80
+
81
+ // Tương sinh / Tương khắc giữa 2 hành (nhận index 0-4, không phải tên — dùng getElementName/*ElementIndex để chuyển đổi)
82
+ getElementRelation(1, 3); // 'Tương sinh' — Mộc (1) sinh Hỏa (3)
83
+ getElementRelation(3, 1); // 'Được sinh' — từ góc nhìn của Hỏa: được Mộc sinh
84
+ getElementRelationIndex(1, 3); // 0 — xem bảng index bên dưới
47
85
  ```
48
86
 
87
+ **Bảng Nạp Âm 60 Hoa Giáp:** không có công thức đóng (closed-form) — đây là bảng tra cứu chuẩn theo quy ước truyền thống, `@lichta/core` dùng bảng tra cứu 30 nhóm thay vì suy diễn bằng công thức cộng (bản trước đây từng dùng công thức cộng trọng số Can+Chi, sai lệch 48/60 tổ hợp so với bảng chuẩn — đã fix).
88
+
89
+ **Index quan hệ Ngũ Hành** (`getElementRelationIndex`): `0` = sinh, `1` = được sinh, `2` = khắc, `3` = bị khắc, `4` = hòa (cùng hành).
90
+
91
+ ## Tiết Khí (24 Solar Terms)
92
+
93
+ ```typescript
94
+ import { getSolarTerm, getSolarTermsInYear, getSolarTermName } from '@lichta/core';
95
+
96
+ getSolarTerm(21, 6, 2024);
97
+ // → { index: 6, name: 'Hạ Chí', date: { day: 21, month: 6, year: 2024 }, jd: 2460483 }
98
+
99
+ getSolarTermsInYear(2024);
100
+ // → mảng 24 Tiết Khí trong năm 2024, theo thứ tự thời gian tăng dần, mỗi phần tử { index, name, date, jd }
101
+
102
+ getSolarTermName(18, 'en'); // 'Winter Solstice' — tra tên thuần túy theo index (0-23) + locale
103
+ ```
104
+
105
+ Index 0 = Xuân Phân (kinh độ Mặt Trời 0°), tăng dần mỗi 15°; 12 index lẻ (1, 3, 5, ..., 23) là **Tiết** (Lập Xuân, Kinh Trập, ...), 12 index chẵn là **Trung Khí** (Xuân Phân, Hạ Chí, Đông Chí, ...). Ngày được tính theo quy ước: nếu thời điểm chuyển Tiết Khí rơi vào bất kỳ lúc nào trong ngày, ngày đó được xem là ngày Tiết Khí bắt đầu (khớp cách almanac truyền thống ghi ngày).
106
+
107
+ ## Trực (12 Kiến Trừ)
108
+
109
+ ```typescript
110
+ import { getTruc, getTrucIndex, getTrucQuality, getTrucQualityIndex } from '@lichta/core';
111
+
112
+ getTruc(lunar.jd); // 'Phá'
113
+ getTrucIndex(lunar.jd); // 6
114
+
115
+ getTrucQuality(getTrucIndex(lunar.jd)); // 'Xấu'
116
+ ```
117
+
118
+ Trực được neo theo **Tiết** (12 mốc: Lập Xuân, Kinh Trập, ...) — **không** theo số tháng âm lịch, vì tháng nhuận âm lịch không tương ứng với một Tiết mới (dùng số tháng âm lịch sẽ cho kết quả sai trong các tháng nhuận). Ngày Tiết bắt đầu luôn là "Kiến", Trực tăng dần 1 mỗi ngày và reset về "Kiến" vào Tiết kế tiếp.
119
+
120
+ `getTrucQuality()`/`getTrucQualityIndex()` chỉ phân loại dứt khoát **Phá, Nguy = Xấu** và **Thành, Khai = Tốt** — 4 Trực có sự đồng thuận rất cao giữa các nguồn. 8 Trực còn lại (Kiến, Trừ, Mãn, Bình, Định, Chấp, Thu, Bế) được xếp **"Trung bình"** một cách bảo thủ, vì mức tốt/xấu chi tiết của chúng phụ thuộc vào từng việc cụ thể và có dị bản giữa các nguồn almanac.
121
+
122
+ ## Kỵ — Tuổi Xung Khắc
123
+
124
+ ```typescript
125
+ import { isXung, isHai, isTuHanhXung, getZodiacConflicts, getXungBranchIndex, getHaiBranchIndex, getTuHanhXungGroupMembers } from '@lichta/core';
126
+
127
+ // Tý (0) và Ngọ (6): vừa Lục Xung vừa cùng nhóm Tứ Hành Xung
128
+ getZodiacConflicts(0, 6);
129
+ // → { xung: true, hai: false, tuHanhXung: true }
130
+
131
+ isXung(0, 6); // true — Lục Xung: Tý-Ngọ, Sửu-Mùi, Dần-Thân, Mão-Dậu, Thìn-Tuất, Tỵ-Hợi
132
+ isHai(0, 7); // true — Lục Hại: Tý-Mùi, Sửu-Ngọ, Dần-Tỵ, Mão-Thìn, Thân-Hợi, Dậu-Tuất
133
+ isTuHanhXung(0, 3); // true — Tứ Hành Xung: {Tý,Mão,Ngọ,Dậu} / {Dần,Tỵ,Thân,Hợi} / {Sửu,Thìn,Mùi,Tuất}
134
+
135
+ getXungBranchIndex(0); // 6 — Chi đối xung với Tý
136
+ getTuHanhXungGroupMembers(0); // [0, 3, 6, 9] — cả nhóm chứa Tý
137
+ ```
138
+
139
+ Tất cả nhận vào **index Địa Chi (0-11, 0 = Tý)**, cùng thứ tự với `getZodiacAnimal()`.
140
+
49
141
  ## Format & Hiển thị
50
142
 
51
143
  ```typescript
@@ -57,10 +149,28 @@ formatLunarDate(lunar, 'Ngày d tháng M năm CC'); // 'Ngày 1 tháng 1 năm Gi
57
149
 
58
150
  getMonthName(1); // 'Giêng'
59
151
  getDayName(15); // 'Rằm'
152
+
153
+ // formatLunarDate nhận thêm `locale` tùy chọn (mặc định 'vi', chỉ ảnh hưởng token L)
154
+ formatLunarDate(lunar, 'dd/MM/yyyy', 'en');
60
155
  ```
61
156
 
62
157
  **Format tokens:** `dd`/`d` (ngày), `MM`/`M` (tháng), `yyyy`/`yy` (năm), `CC`/`MC`/`DC` (Can Chi năm/tháng/ngày), `L` ("Nhuận" nếu là tháng nhuận).
63
158
 
159
+ ## Lưới lịch tháng (dựng UI Calendar riêng)
160
+
161
+ Nếu bạn muốn tự dựng UI lịch tháng thay vì dùng component `Calendar` có sẵn (xem [`@lichta/react`](https://www.npmjs.com/package/@lichta/react)/[`@lichta/vue`](https://www.npmjs.com/package/@lichta/vue)/[`@lichta/svelte`](https://www.npmjs.com/package/@lichta/svelte)), `getCalendarGrid()` dựng sẵn lưới 42 ô (6 tuần) cho 1 tháng, gồm cả ngày tràn từ tháng trước/sau và ngày âm lịch tương ứng cho mỗi ô — đây cũng chính là hàm mà `Calendar` và `DatePicker` ở các package binding dùng nội bộ. Hàm này hỗ trợ cả tuần bắt đầu từ Chủ Nhật (0, mặc định) hoặc Thứ Hai (1).
162
+
163
+ ```typescript
164
+ import { getCalendarGrid, getISOWeekNumber } from '@lichta/core';
165
+
166
+ // Lấy lưới lịch với tuần bắt đầu bằng Thứ Hai (1)
167
+ const grid = getCalendarGrid(2, 2024, null, 1); // tháng, năm, ngày đang chọn (hoặc null), FirstDayOfWeek (0 | 1)
168
+ // → CalendarDayCell[42], mỗi ô: { solar: Date, lunar: LunarDate, isToday, isSelected, isCurrentMonth, weekNumber: number }
169
+
170
+ // Lấy số tuần theo chuẩn ISO-8601
171
+ const weekNum = getISOWeekNumber(new Date(2024, 0, 1)); // → 1
172
+ ```
173
+
64
174
  ## i18n
65
175
 
66
176
  Hỗ trợ 4 ngôn ngữ cho tên Can Chi, Ngũ Hành, con giáp, tên tháng, thứ trong tuần: `vi`, `en`, `ja`, `ko`.
@@ -68,31 +178,42 @@ Hỗ trợ 4 ngôn ngữ cho tên Can Chi, Ngũ Hành, con giáp, tên tháng, t
68
178
  ```typescript
69
179
  import { t, getZodiacAnimal } from '@lichta/core';
70
180
 
71
- t('en').fiveElements; // ['Metal', 'Wood', 'Water', 'Fire', 'Earth']
72
- t('ja').weekDays; // ['日', '月', '火', '水', '木', '金', '土']
73
- getZodiacAnimal(0, 'vi'); // 'Chuột'
181
+ t('en').fiveElements; // ['Metal', 'Wood', 'Water', 'Fire', 'Earth']
182
+ t('ja').weekDays; // ['日', '月', '火', '水', '木', '金', '土']
183
+ t('vi').lunarMonthNames; // ['Giêng', 'Hai', ..., 'Chạp'] — tên tháng ÂM lịch
184
+ t('vi').solarMonthNames; // ['Tháng 1', 'Tháng 2', ..., 'Tháng 12'] — tên tháng DƯƠNG lịch
185
+ getZodiacAnimal(0, 'vi'); // 'Chuột'
74
186
  ```
75
187
 
188
+ > `t(locale).monthNames` vẫn còn nhưng đã `@deprecated` — ngữ nghĩa của nó không nhất quán giữa các locale (với `'vi'` là tên tháng âm, với `'en'/'ja'/'ko'` lại là tên tháng dương). Luôn dùng `lunarMonthNames` hoặc `solarMonthNames` tùy ngữ cảnh cho code mới.
189
+
76
190
  ## API đầy đủ
77
191
 
78
192
  | Nhóm | Hàm/Class |
79
193
  |---|---|
80
194
  | Solar ↔ Lunar | `LichTa.toLunar()`, `LichTa.toSolar()` |
81
195
  | Julian Day Number | `jdFromDate()`, `jdToDate()` |
82
- | Can Chi & Phong Thủy | `getYearDetails()`, `getDayCanChi()`, `getMonthCanChi()`, `getHourCanChi()`, `getAuspiciousHours()` |
196
+ | Can Chi & Phong Thủy | `getYearDetails()`, `getDayCanChi()`, `getMonthCanChi()`, `getHourCanChi()`, `getAuspiciousHours()`, `getAuspiciousHourIndices()`, `getInauspiciousHours()`, `getInauspiciousHourIndices()` |
197
+ | Ngũ Hành | `getElementName()`, `getDayElement()`, `getDayElementIndex()`, `getMonthElement()`, `getMonthElementIndex()`, `getHourElement()`, `getHourElementIndex()`, `getElementRelation()`, `getElementRelationIndex()` |
198
+ | Tiết Khí | `getSolarTerm()`, `getSolarTermsInYear()`, `getSolarTermName()` |
199
+ | Trực | `getTruc()`, `getTrucIndex()`, `getTrucName()`, `getTrucQuality()`, `getTrucQualityIndex()` |
200
+ | Kỵ — Tuổi Xung Khắc | `isXung()`, `isHai()`, `isTuHanhXung()`, `getZodiacConflicts()`, `getXungBranchIndex()`, `getHaiBranchIndex()`, `getTuHanhXungGroupIndex()`, `getTuHanhXungGroupMembers()` |
83
201
  | Format & Hiển thị | `formatLunarDate()`, `formatTraditional()`, `getMonthName()`, `getDayName()` |
84
- | i18n | `t()`, `getZodiacAnimal()` |
85
- | Types | `LunarDate`, `SolarDate`, `Locale` |
202
+ | Lưới lịch | `getCalendarGrid()`, `getISOWeekNumber()` |
203
+ | i18n | `t()`, `getZodiacAnimal()`, `getWeekDayLabels()` |
204
+ | Types | `LunarDate`, `SolarDate`, `Locale`, `YearDetails`, `SolarTerm`, `ZodiacConflicts`, `CalendarDayCell`, `FirstDayOfWeek` |
86
205
 
87
206
  Xem chi tiết đầy đủ (tham số, kiểu trả về, ví dụ đã chạy thật) tại trang [API Reference](https://lichta.zeneo.app/api).
88
207
 
89
- ## Styles (dùng chung cho Calendar component)
208
+ ## Styles (dùng chung cho Calendar/DatePicker component)
90
209
 
91
- Package này cũng export sẵn CSS cho component `Calendar` ở các package binding framework — không cần thiết nếu bạn chỉ dùng logic tính toán:
210
+ Package này cũng export sẵn CSS cho component `Calendar`/`DatePicker` ở các package binding framework — không cần thiết nếu bạn chỉ dùng logic tính toán:
92
211
 
93
212
  ```typescript
94
213
  import '@lichta/core/styles/calendar-base.css';
95
- import '@lichta/core/styles/calendar-glass.css'; // theme kính mờ (tùy chọn)
214
+ import '@lichta/core/styles/calendar-glass.css'; // theme kính mờ cho Calendar (tùy chọn)
215
+ import '@lichta/core/styles/datepicker-base.css'; // chỉ cần nếu dùng component DatePicker
216
+ import '@lichta/core/styles/datepicker-glass.css'; // theme kính mờ cho DatePicker (tùy chọn)
96
217
  ```
97
218
 
98
219
  ## Thuật toán