react-native-lich-am 1.0.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/LICENSE +21 -0
- package/README.md +248 -0
- package/core/index.d.ts +1 -0
- package/core/index.js +1 -0
- package/core/package.json +5 -0
- package/dataset/2024.d.ts +1 -0
- package/dataset/2024.js +1 -0
- package/dataset/2025.d.ts +1 -0
- package/dataset/2025.js +1 -0
- package/dataset/2026.d.ts +1 -0
- package/dataset/2026.js +1 -0
- package/dataset/2027.d.ts +1 -0
- package/dataset/2027.js +1 -0
- package/dataset/2028.d.ts +1 -0
- package/dataset/2028.js +1 -0
- package/dataset/index.d.ts +1 -0
- package/dataset/index.js +1 -0
- package/dataset/package.json +5 -0
- package/lib/core/astro.d.ts +43 -0
- package/lib/core/astro.js +203 -0
- package/lib/core/canchi.d.ts +47 -0
- package/lib/core/canchi.js +78 -0
- package/lib/core/constants.d.ts +54 -0
- package/lib/core/constants.js +141 -0
- package/lib/core/day.d.ts +30 -0
- package/lib/core/day.js +103 -0
- package/lib/core/format.d.ts +26 -0
- package/lib/core/format.js +60 -0
- package/lib/core/huong.d.ts +26 -0
- package/lib/core/huong.js +44 -0
- package/lib/core/index.d.ts +16 -0
- package/lib/core/index.js +32 -0
- package/lib/core/lunar.d.ts +39 -0
- package/lib/core/lunar.js +130 -0
- package/lib/core/tietkhi.d.ts +40 -0
- package/lib/core/tietkhi.js +115 -0
- package/lib/core/truc.d.ts +47 -0
- package/lib/core/truc.js +70 -0
- package/lib/core/types.d.ts +66 -0
- package/lib/core/types.js +3 -0
- package/lib/dataset/2024.d.ts +30 -0
- package/lib/dataset/2024.js +69 -0
- package/lib/dataset/2025.d.ts +30 -0
- package/lib/dataset/2025.js +69 -0
- package/lib/dataset/2026.d.ts +30 -0
- package/lib/dataset/2026.js +69 -0
- package/lib/dataset/2027.d.ts +30 -0
- package/lib/dataset/2027.js +69 -0
- package/lib/dataset/2028.d.ts +30 -0
- package/lib/dataset/2028.js +69 -0
- package/lib/dataset/data/days-2024.json +1 -0
- package/lib/dataset/data/days-2025.json +1 -0
- package/lib/dataset/data/days-2026.json +1 -0
- package/lib/dataset/data/days-2027.json +1 -0
- package/lib/dataset/data/days-2028.json +1 -0
- package/lib/dataset/data/dict.json +1 -0
- package/lib/dataset/data/gio-table.json +1 -0
- package/lib/dataset/decode.d.ts +225 -0
- package/lib/dataset/decode.js +285 -0
- package/lib/dataset/index.d.ts +37 -0
- package/lib/dataset/index.js +87 -0
- package/lib/index.d.ts +21 -0
- package/lib/index.js +37 -0
- package/lib/ui/LunarCalendar.d.ts +58 -0
- package/lib/ui/LunarCalendar.js +311 -0
- package/lib/ui/LunarDayDetail.d.ts +32 -0
- package/lib/ui/LunarDayDetail.js +221 -0
- package/lib/ui/index.d.ts +9 -0
- package/lib/ui/index.js +18 -0
- package/lib/ui/theme.d.ts +46 -0
- package/lib/ui/theme.js +60 -0
- package/package.json +431 -0
- package/ui/index.d.ts +1 -0
- package/ui/index.js +1 -0
- package/ui/package.json +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nguyen Hoa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# react-native-lich-am
|
|
2
|
+
|
|
3
|
+
[](https://github.com/sep21th03/react-native-lich-am/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Thư viện **lịch âm (âm lịch Việt Nam)** cho React Native và JavaScript.
|
|
6
|
+
|
|
7
|
+
- Thuật toán thuần — **không phụ thuộc thư viện nào**, không cần native module
|
|
8
|
+
- **Không cần build**: `lib/` đã dựng sẵn trong package
|
|
9
|
+
- Kèm **dữ liệu chi tiết 1.462 ngày** (01/01/2024 → 01/01/2028): sao tốt/xấu, việc nên làm, điểm từng giờ, hướng tốt, tuổi xung
|
|
10
|
+
- Kèm component `<LunarCalendar />` và `<LunarDayDetail />` sẵn dùng
|
|
11
|
+
- TypeScript đầy đủ — chạy trên Hermes, Expo, Node và trình duyệt
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Cài đặt
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i react-native-lich-am
|
|
19
|
+
# hoặc: yarn add react-native-lich-am | pnpm add react-native-lich-am
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Yêu cầu (chỉ khi dùng phần component): `react >= 17`, `react-native >= 0.64`.
|
|
23
|
+
Hai gói này là **peer dependency tuỳ chọn** — chỉ dùng phần core/dữ liệu thì không cần.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Chọn entry point
|
|
28
|
+
|
|
29
|
+
| Entry point | Nội dung | Kích thước |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `react-native-lich-am` | Core thuật toán — không kèm dữ liệu | ~60 KB |
|
|
32
|
+
| `react-native-lich-am/dataset` | Dữ liệu chi tiết 2024–2028 | ~1 MB |
|
|
33
|
+
| `react-native-lich-am/dataset/2024` | Chỉ một năm | ~245 KB |
|
|
34
|
+
| `react-native-lich-am/ui` | Component React Native | ~52 KB |
|
|
35
|
+
|
|
36
|
+
Chỉ cần đổi ngày dương ↔ âm thì import entry gốc là đủ, bundle không tăng.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Dùng cơ bản
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { getLunarDayInfo, getTodayLunar, solarToLunar, lunarToSolar } from 'react-native-lich-am';
|
|
44
|
+
|
|
45
|
+
const info = getLunarDayInfo(new Date());
|
|
46
|
+
|
|
47
|
+
info.solar.iso; // "2026-09-29"
|
|
48
|
+
info.lunar.text; // "19/8/2026"
|
|
49
|
+
info.lunar.monthNameFull; // "tháng Tám"
|
|
50
|
+
info.canChi.day.name; // "Bính Ngọ"
|
|
51
|
+
info.canChi.year.name; // "Bính Ngọ" (can chi năm)
|
|
52
|
+
info.conGiap; // "Ngựa"
|
|
53
|
+
info.napAm.day.name; // "Thiên Hà Thủy"
|
|
54
|
+
info.tietKhi.name; // "Thu phân"
|
|
55
|
+
info.truc.name; // "Bình"
|
|
56
|
+
info.gioHoangDao.map(g => `${g.chi} ${g.range}`);
|
|
57
|
+
// ["Tý 23:00 – 00:59", "Sửu 01:00 – 02:59", ...]
|
|
58
|
+
info.huongXuatHanh; // { hyThan: "Tây Nam", taiThan: "Tây", hacThan: "Đông" }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Chuyển đổi
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
solarToLunar(10, 2, 2024);
|
|
65
|
+
// { day: 1, month: 1, year: 2024, leap: false, jd: 2460351 } → mùng 1 Tết Giáp Thìn
|
|
66
|
+
|
|
67
|
+
lunarToSolar(1, 1, 2024);
|
|
68
|
+
// { day: 10, month: 2, year: 2024, jd: 2460351 }
|
|
69
|
+
|
|
70
|
+
solarToLunar(25, 7, 2025).leap; // true (tháng 6 nhuận năm 2025)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Ngày âm hôm nay
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
getTodayLunar(); // { day, month, year, leap, monthName, text }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Dữ liệu chi tiết theo ngày
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { getDayData, findGoodDays, topDays, getQualityStats, listActivities } from 'react-native-lich-am/dataset';
|
|
85
|
+
|
|
86
|
+
const d = getDayData('2026-09-29');
|
|
87
|
+
|
|
88
|
+
d.lunar; // { day: 19, month: 8, year: 2026 }
|
|
89
|
+
d.lunarRaw; // "19-8-2026"
|
|
90
|
+
d.saoTot; // ["Thiên ân", "Nguyệt ân", ...]
|
|
91
|
+
d.saoXau; // ["Thiên hỏa", "Nguyệt yếm", ...]
|
|
92
|
+
d.nenLam; // ["Cầu cúng", "Khai trương", ...]
|
|
93
|
+
d.khongNenLam; // ["Động thổ", "An táng", ...]
|
|
94
|
+
d.gio; // đủ 12 khung: { index, chi, diem, sao, top, nguon }
|
|
95
|
+
d.gioRaw; // 12 khung ở dạng chưa chuẩn hoá
|
|
96
|
+
d.gioTotNhat; // 5 giờ tốt nhất (thang điểm riêng, xem ghi chú bên dưới)
|
|
97
|
+
d.bonGioDaiCat; // 4 giờ đại cát (Thiên ất / Thiên không)
|
|
98
|
+
d.gioQuyDangThienMon;
|
|
99
|
+
d.huong.ngay; // [{ son: "Khôn", huong: "Chính Tây Nam", soSao: 4 }, ...]
|
|
100
|
+
d.tuoiXung; // ["Mậu Tý", "Nhâm Tý", "Giáp Ngọ", ...]
|
|
101
|
+
d.diem; // 0–100
|
|
102
|
+
d.chatLuong; // 'dep' | 'tot' | 'bt' | 'xau' | 'ratxau'
|
|
103
|
+
d.ngayRatXau; // boolean
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `d.gio` — luôn đủ 12 khung giờ
|
|
107
|
+
|
|
108
|
+
Mỗi khung có dạng `{ index, chi, diem, sao, top, nguon }`:
|
|
109
|
+
|
|
110
|
+
- `diem` — điểm của giờ, tính bằng `Σ trọng số các sao` chiếu vào giờ đó
|
|
111
|
+
- `sao` — danh sách sao; luôn có **đúng một** thần hoàng đạo/hắc đạo
|
|
112
|
+
- `nguon` — `'nguon' | 'sua' | 'suy-ra'`, mức độ can thiệp so với dữ liệu thô
|
|
113
|
+
- `gioRaw` — bản thô chưa chuẩn hoá, nếu bạn cần đối chiếu
|
|
114
|
+
|
|
115
|
+
`d.gio` luôn có **12 phần tử** với `index` từ 0 (Tý) đến 11 (Hợi), nên render lưới
|
|
116
|
+
giờ không cần kiểm tra thiếu phần tử.
|
|
117
|
+
|
|
118
|
+
### Tìm ngày tốt cho một việc
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
listActivities(); // 31 việc: "Thành hôn", "Khai trương", "Động thổ", ...
|
|
122
|
+
|
|
123
|
+
const days = findGoodDays({
|
|
124
|
+
activity: 'Khai trương',
|
|
125
|
+
from: '2026-10-01',
|
|
126
|
+
to: '2026-12-31',
|
|
127
|
+
minQuality: 'tot',
|
|
128
|
+
excludeVeryBad: true,
|
|
129
|
+
limit: 10,
|
|
130
|
+
});
|
|
131
|
+
// → 10 ngày, xếp theo điểm giảm dần
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Với **một việc cụ thể**, thư viện dùng thẳng dữ liệu: việc đó có nằm trong nhóm
|
|
135
|
+
**"Nên làm"** của ngày hay không — không suy diễn thêm.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
topDays('2026-10-01', '2026-10-31', 5); // 5 ngày đẹp nhất tháng
|
|
139
|
+
getQualityStats('2026-01-01', '2026-12-31'); // { dep, tot, bt, xau, ratxau, total, avgDiem }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Component
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { LunarCalendar, LunarDayDetail } from 'react-native-lich-am/ui';
|
|
148
|
+
import { getDayData } from 'react-native-lich-am/dataset';
|
|
149
|
+
import { getLunarDayInfo } from 'react-native-lich-am';
|
|
150
|
+
import { useState } from 'react';
|
|
151
|
+
|
|
152
|
+
export default function App() {
|
|
153
|
+
const [info, setInfo] = useState(() => getLunarDayInfo(new Date()));
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<>
|
|
157
|
+
<LunarCalendar
|
|
158
|
+
dayDataProvider={getDayData} // có → tô màu chất lượng ngày + hiện chú thích
|
|
159
|
+
onSelectDay={(day) => setInfo(day)}
|
|
160
|
+
weekStartsOn={1} // tuần bắt đầu từ Thứ hai
|
|
161
|
+
colorScheme="light" // 'light' | 'dark'
|
|
162
|
+
/>
|
|
163
|
+
<LunarDayDetail day={info} data={getDayData(info.solar.iso)} />
|
|
164
|
+
</>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`<LunarDayDetail />` có prop `sections` để chọn phần hiển thị:
|
|
170
|
+
`'canchi' | 'gio' | 'sao' | 'viec' | 'huong' | 'tuoixung'`.
|
|
171
|
+
|
|
172
|
+
### Tuỳ biến màu
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<LunarCalendar
|
|
176
|
+
theme={{ primary: '#0EA5E9', radius: 16, quality: { dep: '#22C55E' } }}
|
|
177
|
+
/>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Đã kiểm chứng
|
|
183
|
+
|
|
184
|
+
Đối chiếu trên **1.462 ngày** liên tục:
|
|
185
|
+
|
|
186
|
+
| Trường | Khớp |
|
|
187
|
+
|---|---|
|
|
188
|
+
| Ngày âm (ngày/tháng/năm, kể cả tháng nhuận) | 1462/1462 |
|
|
189
|
+
| Can chi năm / tháng / ngày | 1462/1462 |
|
|
190
|
+
| Ngũ hành nạp âm của ngày | 1462/1462 |
|
|
191
|
+
| Trực | 1462/1462 |
|
|
192
|
+
| Tiết khí (24 tiết) | 1462/1462 |
|
|
193
|
+
| Giờ hoàng đạo | 1462/1462 |
|
|
194
|
+
| Thần cai quản từng giờ | 43.541/43.541 |
|
|
195
|
+
|
|
196
|
+
Bảng giờ hoàng đạo và hướng xuất hành cũng đối chiếu khớp với nguồn độc lập
|
|
197
|
+
(29/09/2026 — ngày Bính Ngọ: Tý Sửu Mão Ngọ Thân Dậu · Hỷ Tây Nam · Tài Tây · Hạc Đông).
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Ghi chú kỹ thuật
|
|
202
|
+
|
|
203
|
+
- **Múi giờ**: mặc định UTC+7 (lịch Việt Nam). Mọi hàm đều nhận tham số `timeZone` nếu cần.
|
|
204
|
+
- **Sóc (new moon)** dùng chuỗi Meeus 49.1 + 49.2 đầy đủ, kể cả số hạng nhiễu hành tinh —
|
|
205
|
+
chính xác cỡ vài chục giây, đủ để không lệch ngày ở những kỳ sóc rơi sát nửa đêm.
|
|
206
|
+
- **Tiết khí** ấn định cho ngày mà **thời điểm giao tiết rơi vào**, nên vị trí mặt trời
|
|
207
|
+
được lấy ở **cuối ngày** địa phương chứ không phải 00:00.
|
|
208
|
+
- **Trực** tính theo **chi tháng tiết khí** (Lập xuân → Dần, …), không phải tháng âm.
|
|
209
|
+
- **Can chi tháng** tính theo **tháng âm** (Ngũ Hổ Độn); can chi giờ theo Ngũ Thử Độn.
|
|
210
|
+
- **Giờ hoàng đạo** sinh từ công thức `(2 × chi ngày + 8) mod 12` — cho ra đúng bảng cổ điển.
|
|
211
|
+
- **Điểm ngày đẹp** (`d.diem`) là chỉ số tổng hợp do thư viện tính:
|
|
212
|
+
`1,0 × sao tốt − 1,2 × sao xấu − 3,0 × (ngày rất xấu) + (tổng điểm giờ tốt)/4 + hệ số Trực`,
|
|
213
|
+
rồi quy về thang 0–100 theo phân vị trên toàn bộ 1.462 ngày.
|
|
214
|
+
- **`d.gioTotNhat` dùng thang điểm riêng**, không so được với `d.gio[].diem`.
|
|
215
|
+
- **`d.tuoiXung`** là dữ liệu tra cứu, không suy ra từ công thức.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Phát triển
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
npm run smoke # 66 assert, chạy trong 1 giây
|
|
223
|
+
npm run build # dựng lại lib/ từ src/ (TypeScript)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`lib/` là bản dựng sẵn đã commit — người dùng cài là chạy, không cần build.
|
|
227
|
+
CI dựng lại `lib/` từ `src/` và so với bản đã commit, nên nhớ chạy `npm run build`
|
|
228
|
+
trước khi push.
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
src/ nguồn TypeScript (nguồn chân lý)
|
|
232
|
+
core/ thuật toán — không phụ thuộc gì
|
|
233
|
+
dataset/ giải mã dữ liệu + tra cứu
|
|
234
|
+
ui/ component React Native
|
|
235
|
+
lib/ bản JS đã dựng (được publish)
|
|
236
|
+
core/ dataset/ ui/ proxy cho bundler không hỗ trợ trường "exports"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Giấy phép
|
|
242
|
+
|
|
243
|
+
**MIT** — xem [LICENSE](./LICENSE).
|
|
244
|
+
|
|
245
|
+
Bộ dữ liệu lịch kèm theo chỉ nên dùng cho mục đích cá nhân / tham khảo.
|
|
246
|
+
|
|
247
|
+
Nội dung can chi, sao tốt xấu, giờ hoàng đạo là **tham khảo văn hoá**, không phải
|
|
248
|
+
lời khuyên về tài chính, y tế hay pháp lý.
|
package/core/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/core/index';
|
package/core/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/core/index.js');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/dataset/2024';
|
package/dataset/2024.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/dataset/2024.js');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/dataset/2025';
|
package/dataset/2025.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/dataset/2025.js');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/dataset/2026';
|
package/dataset/2026.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/dataset/2026.js');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/dataset/2027';
|
package/dataset/2027.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/dataset/2027.js');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/dataset/2028';
|
package/dataset/2028.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/dataset/2028.js');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../lib/dataset/index';
|
package/dataset/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../lib/dataset/index.js');
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phần thiên văn: số ngày Julian, sóc (new moon) và kinh độ mặt trời.
|
|
3
|
+
*
|
|
4
|
+
* Công thức theo bộ tính âm lịch phổ biến của Hồ Ngọc Đức (dựa trên
|
|
5
|
+
* Jean Meeus — "Astronomical Algorithms"), đã được đối chiếu với
|
|
6
|
+
* 1.462 ngày dữ liệu lịch Việt (2024–2028) trong quá trình kiểm thử.
|
|
7
|
+
*/
|
|
8
|
+
/** Làm tròn xuống (khác `Math.floor` với số âm, giống `int()` của C) */
|
|
9
|
+
export declare function int(v: number): number;
|
|
10
|
+
/**
|
|
11
|
+
* Số ngày Julian của ngày dương lịch.
|
|
12
|
+
* Tự động dùng lịch Julius trước 15/10/1582 và lịch Gregorian sau đó.
|
|
13
|
+
*/
|
|
14
|
+
export declare function jdFromDate(dd: number, mm: number, yy: number): number;
|
|
15
|
+
/** Đổi số ngày Julian về [ngày, tháng, năm] dương lịch */
|
|
16
|
+
export declare function jdToDate(jd: number): [number, number, number];
|
|
17
|
+
/**
|
|
18
|
+
* Thời điểm sóc (new moon) thứ k, trả về JD theo giờ UT.
|
|
19
|
+
*
|
|
20
|
+
* Dùng chuỗi Meeus 49.1 + 49.2 đầy đủ (kể cả nhiễu hành tinh), chính xác
|
|
21
|
+
* tới cỡ vài chục giây. Bản rút gọn của Hồ Ngọc Đức sai tới ~15 phút, đủ để
|
|
22
|
+
* lật ngày ở những kỳ sóc rơi sát nửa đêm (ví dụ tháng 7 âm lịch năm 2026).
|
|
23
|
+
*/
|
|
24
|
+
export declare function newMoon(k: number): number;
|
|
25
|
+
/** Kinh độ thật của mặt trời tại thời điểm jdn, đơn vị radian (0 → 2π) */
|
|
26
|
+
export declare function sunLongitude(jdn: number): number;
|
|
27
|
+
/** Ngày (theo múi giờ) chứa thời điểm sóc thứ k */
|
|
28
|
+
export declare function getNewMoonDay(k: number, timeZone?: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* Kinh độ mặt trời tại nửa đêm địa phương, quy về 12 cung
|
|
31
|
+
* (mỗi cung 30°, tương ứng 12 "trung khí").
|
|
32
|
+
*/
|
|
33
|
+
export declare function getSunLongitude(dayNumber: number, timeZone?: number): number;
|
|
34
|
+
/** Ngày bắt đầu tháng 11 âm lịch của năm dương lịch `yy` (dạng JD) */
|
|
35
|
+
export declare function getLunarMonth11(yy: number, timeZone?: number): number;
|
|
36
|
+
/** Độ lệch của tháng nhuận so với tháng 11 âm lịch (0 = không nhuận) */
|
|
37
|
+
export declare function getLeapMonthOffset(a11: number, timeZone?: number): number;
|
|
38
|
+
/** JD của 00:00 ngày hôm nay theo múi giờ `timeZone` (tính từ Date) */
|
|
39
|
+
export declare function jdFromDateObject(date: Date, timeZone?: number): number;
|
|
40
|
+
/** JD (tại 00:00 UTC) → timestamp mili giây */
|
|
41
|
+
export declare function jdToTimestamp(jd: number): number;
|
|
42
|
+
/** Timestamp mili giây → JD */
|
|
43
|
+
export declare function timestampToJd(ms: number): number;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phần thiên văn: số ngày Julian, sóc (new moon) và kinh độ mặt trời.
|
|
4
|
+
*
|
|
5
|
+
* Công thức theo bộ tính âm lịch phổ biến của Hồ Ngọc Đức (dựa trên
|
|
6
|
+
* Jean Meeus — "Astronomical Algorithms"), đã được đối chiếu với
|
|
7
|
+
* 1.462 ngày dữ liệu lịch Việt (2024–2028) trong quá trình kiểm thử.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.timestampToJd = exports.jdToTimestamp = exports.jdFromDateObject = exports.getLeapMonthOffset = exports.getLunarMonth11 = exports.getSunLongitude = exports.getNewMoonDay = exports.sunLongitude = exports.newMoon = exports.jdToDate = exports.jdFromDate = exports.int = void 0;
|
|
11
|
+
const constants_1 = require("./constants");
|
|
12
|
+
const PI = Math.PI;
|
|
13
|
+
const DR = PI / 180;
|
|
14
|
+
/** Làm tròn xuống (khác `Math.floor` với số âm, giống `int()` của C) */
|
|
15
|
+
function int(v) {
|
|
16
|
+
return Math.floor(v);
|
|
17
|
+
}
|
|
18
|
+
exports.int = int;
|
|
19
|
+
/**
|
|
20
|
+
* Số ngày Julian của ngày dương lịch.
|
|
21
|
+
* Tự động dùng lịch Julius trước 15/10/1582 và lịch Gregorian sau đó.
|
|
22
|
+
*/
|
|
23
|
+
function jdFromDate(dd, mm, yy) {
|
|
24
|
+
const a = int((14 - mm) / 12);
|
|
25
|
+
const y = yy + 4800 - a;
|
|
26
|
+
const m = mm + 12 * a - 3;
|
|
27
|
+
let jd = dd + int((153 * m + 2) / 5) + 365 * y + int(y / 4) - int(y / 100) + int(y / 400) - 32045;
|
|
28
|
+
if (jd < 2299161) {
|
|
29
|
+
jd = dd + int((153 * m + 2) / 5) + 365 * y + int(y / 4) - 32083;
|
|
30
|
+
}
|
|
31
|
+
return jd;
|
|
32
|
+
}
|
|
33
|
+
exports.jdFromDate = jdFromDate;
|
|
34
|
+
/** Đổi số ngày Julian về [ngày, tháng, năm] dương lịch */
|
|
35
|
+
function jdToDate(jd) {
|
|
36
|
+
let a;
|
|
37
|
+
let b;
|
|
38
|
+
let c;
|
|
39
|
+
if (jd > 2299160) {
|
|
40
|
+
a = jd + 32044;
|
|
41
|
+
b = int((4 * a + 3) / 146097);
|
|
42
|
+
c = a - int((b * 146097) / 4);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
b = 0;
|
|
46
|
+
c = jd + 32082;
|
|
47
|
+
}
|
|
48
|
+
const d = int((4 * c + 3) / 1461);
|
|
49
|
+
const e = c - int((1461 * d) / 4);
|
|
50
|
+
const m = int((5 * e + 2) / 153);
|
|
51
|
+
const day = e - int((153 * m + 2) / 5) + 1;
|
|
52
|
+
const month = m + 3 - 12 * int(m / 10);
|
|
53
|
+
const year = b * 100 + d - 4800 + int(m / 10);
|
|
54
|
+
return [day, month, year];
|
|
55
|
+
}
|
|
56
|
+
exports.jdToDate = jdToDate;
|
|
57
|
+
/**
|
|
58
|
+
* Hệ số k của Hồ Ngọc Đức lấy mốc 1900-01-01, còn Meeus lấy mốc 2000-01-06.
|
|
59
|
+
* Hai mốc cách nhau đúng 1237 tuần trăng nên chỉ cần dịch k;
|
|
60
|
+
* pha của các số hạng tuần hoàn vẫn giữ nguyên.
|
|
61
|
+
*/
|
|
62
|
+
const MEEUS_K_OFFSET = 1237;
|
|
63
|
+
/** ΔT (TT − UT) tính bằng giây — Espenak & Meeus, dùng cho giai đoạn 2005–2050 */
|
|
64
|
+
function deltaTSeconds(year) {
|
|
65
|
+
if (year < 2005) {
|
|
66
|
+
const t0 = year - 2000;
|
|
67
|
+
return 63.86 + 0.3345 * t0 - 0.060374 * t0 * t0 + 0.0017275 * t0 * t0 * t0;
|
|
68
|
+
}
|
|
69
|
+
const t = year - 2000;
|
|
70
|
+
return 62.92 + 0.32217 * t + 0.005589 * t * t;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Thời điểm sóc (new moon) thứ k, trả về JD theo giờ UT.
|
|
74
|
+
*
|
|
75
|
+
* Dùng chuỗi Meeus 49.1 + 49.2 đầy đủ (kể cả nhiễu hành tinh), chính xác
|
|
76
|
+
* tới cỡ vài chục giây. Bản rút gọn của Hồ Ngọc Đức sai tới ~15 phút, đủ để
|
|
77
|
+
* lật ngày ở những kỳ sóc rơi sát nửa đêm (ví dụ tháng 7 âm lịch năm 2026).
|
|
78
|
+
*/
|
|
79
|
+
function newMoon(k) {
|
|
80
|
+
const km = k - MEEUS_K_OFFSET;
|
|
81
|
+
const T = km / 1236.85;
|
|
82
|
+
const T2 = T * T;
|
|
83
|
+
const T3 = T2 * T;
|
|
84
|
+
const T4 = T3 * T;
|
|
85
|
+
const jde = 2451550.09766 + 29.530588861 * km
|
|
86
|
+
+ 0.00015437 * T2 - 0.000000150 * T3 + 0.00000000073 * T4;
|
|
87
|
+
const E = 1 - 0.002516 * T - 0.0000074 * T2;
|
|
88
|
+
const M = (2.5534 + 29.10535670 * km - 0.0000014 * T2 - 0.00000011 * T3) * DR;
|
|
89
|
+
const Mp = (201.5643 + 385.81693528 * km + 0.0107582 * T2 + 0.00001238 * T3 - 0.000000058 * T4) * DR;
|
|
90
|
+
const F = (160.7108 + 390.67050284 * km - 0.0016118 * T2 - 0.00000227 * T3 + 0.000000011 * T4) * DR;
|
|
91
|
+
const Om = (124.7746 - 1.56375588 * km + 0.0020672 * T2 + 0.00000215 * T3) * DR;
|
|
92
|
+
let c = 0;
|
|
93
|
+
c -= 0.40720 * Math.sin(Mp);
|
|
94
|
+
c += 0.17241 * E * Math.sin(M);
|
|
95
|
+
c += 0.01608 * Math.sin(2 * Mp);
|
|
96
|
+
c += 0.01039 * Math.sin(2 * F);
|
|
97
|
+
c += 0.00739 * E * Math.sin(Mp - M);
|
|
98
|
+
c -= 0.00514 * E * Math.sin(Mp + M);
|
|
99
|
+
c += 0.00208 * E * E * Math.sin(2 * M);
|
|
100
|
+
c -= 0.00111 * Math.sin(Mp - 2 * F);
|
|
101
|
+
c -= 0.00057 * Math.sin(Mp + 2 * F);
|
|
102
|
+
c += 0.00056 * E * Math.sin(2 * Mp + M);
|
|
103
|
+
c -= 0.00042 * Math.sin(3 * Mp);
|
|
104
|
+
c += 0.00042 * E * Math.sin(M + 2 * F);
|
|
105
|
+
c += 0.00038 * E * Math.sin(M - 2 * F);
|
|
106
|
+
c -= 0.00024 * E * Math.sin(2 * Mp - M);
|
|
107
|
+
c -= 0.00017 * Math.sin(Om);
|
|
108
|
+
c -= 0.00007 * Math.sin(Mp + 2 * M);
|
|
109
|
+
c += 0.00004 * Math.sin(2 * Mp - 2 * F);
|
|
110
|
+
c += 0.00004 * Math.sin(3 * M);
|
|
111
|
+
c += 0.00003 * Math.sin(Mp + M - 2 * F);
|
|
112
|
+
c += 0.00003 * Math.sin(2 * Mp + 2 * F);
|
|
113
|
+
c -= 0.00003 * Math.sin(Mp + M + 2 * F);
|
|
114
|
+
c += 0.00003 * Math.sin(Mp - M + 2 * F);
|
|
115
|
+
c -= 0.00002 * Math.sin(Mp - M - 2 * F);
|
|
116
|
+
c -= 0.00002 * Math.sin(3 * Mp + M);
|
|
117
|
+
c += 0.00002 * Math.sin(4 * Mp);
|
|
118
|
+
// Nhiễu do các hành tinh khác (Meeus, bảng 49.A)
|
|
119
|
+
c += 0.000325 * Math.sin((299.77 + 0.107408 * km - 0.009173 * T2) * DR);
|
|
120
|
+
c += 0.000165 * Math.sin((251.88 + 0.016321 * km) * DR);
|
|
121
|
+
c += 0.000164 * Math.sin((251.83 + 26.651886 * km) * DR);
|
|
122
|
+
c += 0.000126 * Math.sin((349.42 + 36.412478 * km) * DR);
|
|
123
|
+
c += 0.000110 * Math.sin((84.66 + 18.206239 * km) * DR);
|
|
124
|
+
c += 0.000062 * Math.sin((141.74 + 53.303771 * km) * DR);
|
|
125
|
+
c += 0.000060 * Math.sin((207.14 + 2.453732 * km) * DR);
|
|
126
|
+
c += 0.000056 * Math.sin((154.84 + 7.306860 * km) * DR);
|
|
127
|
+
c += 0.000047 * Math.sin((34.52 + 27.261239 * km) * DR);
|
|
128
|
+
c += 0.000042 * Math.sin((207.19 + 0.121824 * km) * DR);
|
|
129
|
+
c += 0.000040 * Math.sin((291.34 + 1.844379 * km) * DR);
|
|
130
|
+
c += 0.000037 * Math.sin((161.72 + 24.198154 * km) * DR);
|
|
131
|
+
c += 0.000035 * Math.sin((239.56 + 25.513099 * km) * DR);
|
|
132
|
+
c += 0.000023 * Math.sin((331.55 + 3.592518 * km) * DR);
|
|
133
|
+
return jde + c - deltaTSeconds(1900 + k / 12.3685) / 86400;
|
|
134
|
+
}
|
|
135
|
+
exports.newMoon = newMoon;
|
|
136
|
+
/** Kinh độ thật của mặt trời tại thời điểm jdn, đơn vị radian (0 → 2π) */
|
|
137
|
+
function sunLongitude(jdn) {
|
|
138
|
+
const T = (jdn - 2451545.0) / 36525;
|
|
139
|
+
const T2 = T * T;
|
|
140
|
+
const M = 357.5291 + 35999.0503 * T - 0.0001559 * T2 - 0.00000048 * T * T2;
|
|
141
|
+
const L0 = 280.46645 + 36000.76983 * T + 0.0003032 * T2;
|
|
142
|
+
let dl = (1.9146 - 0.004817 * T - 0.000014 * T2) * Math.sin(DR * M);
|
|
143
|
+
dl += (0.019993 - 0.000101 * T) * Math.sin(DR * 2 * M) + 0.00029 * Math.sin(DR * 3 * M);
|
|
144
|
+
let l = (L0 + dl) * DR;
|
|
145
|
+
l -= PI * 2 * int(l / (PI * 2));
|
|
146
|
+
return l;
|
|
147
|
+
}
|
|
148
|
+
exports.sunLongitude = sunLongitude;
|
|
149
|
+
/** Ngày (theo múi giờ) chứa thời điểm sóc thứ k */
|
|
150
|
+
function getNewMoonDay(k, timeZone = constants_1.TZ_VN) {
|
|
151
|
+
return int(newMoon(k) + 0.5 + timeZone / 24);
|
|
152
|
+
}
|
|
153
|
+
exports.getNewMoonDay = getNewMoonDay;
|
|
154
|
+
/**
|
|
155
|
+
* Kinh độ mặt trời tại nửa đêm địa phương, quy về 12 cung
|
|
156
|
+
* (mỗi cung 30°, tương ứng 12 "trung khí").
|
|
157
|
+
*/
|
|
158
|
+
function getSunLongitude(dayNumber, timeZone = constants_1.TZ_VN) {
|
|
159
|
+
return int((sunLongitude(dayNumber - 0.5 - timeZone / 24) / PI) * 6);
|
|
160
|
+
}
|
|
161
|
+
exports.getSunLongitude = getSunLongitude;
|
|
162
|
+
/** Ngày bắt đầu tháng 11 âm lịch của năm dương lịch `yy` (dạng JD) */
|
|
163
|
+
function getLunarMonth11(yy, timeZone = constants_1.TZ_VN) {
|
|
164
|
+
const off = jdFromDate(31, 12, yy) - 2415021;
|
|
165
|
+
const k = int(off / 29.530588853);
|
|
166
|
+
let nm = getNewMoonDay(k, timeZone);
|
|
167
|
+
const sunLong = getSunLongitude(nm, timeZone);
|
|
168
|
+
if (sunLong >= 9) {
|
|
169
|
+
nm = getNewMoonDay(k - 1, timeZone);
|
|
170
|
+
}
|
|
171
|
+
return nm;
|
|
172
|
+
}
|
|
173
|
+
exports.getLunarMonth11 = getLunarMonth11;
|
|
174
|
+
/** Độ lệch của tháng nhuận so với tháng 11 âm lịch (0 = không nhuận) */
|
|
175
|
+
function getLeapMonthOffset(a11, timeZone = constants_1.TZ_VN) {
|
|
176
|
+
const k = int((a11 - 2415021.076998695) / 29.530588853 + 0.5);
|
|
177
|
+
let last = 0;
|
|
178
|
+
let i = 1;
|
|
179
|
+
let arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);
|
|
180
|
+
do {
|
|
181
|
+
last = arc;
|
|
182
|
+
i += 1;
|
|
183
|
+
arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);
|
|
184
|
+
} while (arc !== last && i < 14);
|
|
185
|
+
return i - 1;
|
|
186
|
+
}
|
|
187
|
+
exports.getLeapMonthOffset = getLeapMonthOffset;
|
|
188
|
+
/** JD của 00:00 ngày hôm nay theo múi giờ `timeZone` (tính từ Date) */
|
|
189
|
+
function jdFromDateObject(date, timeZone = constants_1.TZ_VN) {
|
|
190
|
+
const shifted = new Date(date.getTime() + timeZone * 3600 * 1000);
|
|
191
|
+
return jdFromDate(shifted.getUTCDate(), shifted.getUTCMonth() + 1, shifted.getUTCFullYear());
|
|
192
|
+
}
|
|
193
|
+
exports.jdFromDateObject = jdFromDateObject;
|
|
194
|
+
/** JD (tại 00:00 UTC) → timestamp mili giây */
|
|
195
|
+
function jdToTimestamp(jd) {
|
|
196
|
+
return Math.round((jd - constants_1.JD_UNIX_EPOCH) * 86400000);
|
|
197
|
+
}
|
|
198
|
+
exports.jdToTimestamp = jdToTimestamp;
|
|
199
|
+
/** Timestamp mili giây → JD */
|
|
200
|
+
function timestampToJd(ms) {
|
|
201
|
+
return ms / 86400000 + constants_1.JD_UNIX_EPOCH;
|
|
202
|
+
}
|
|
203
|
+
exports.timestampToJd = timestampToJd;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Can Chi: năm, tháng, ngày, giờ — kèm Nạp Âm (ngũ hành) và con giáp.
|
|
3
|
+
*/
|
|
4
|
+
export interface CanChi {
|
|
5
|
+
/** Chỉ số 0–9 của thiên can */
|
|
6
|
+
canIndex: number;
|
|
7
|
+
/** Chỉ số 0–11 của địa chi */
|
|
8
|
+
chiIndex: number;
|
|
9
|
+
/** Chuỗi ghép, ví dụ "Giáp Tý" */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Can */
|
|
12
|
+
can: string;
|
|
13
|
+
/** Chi */
|
|
14
|
+
chi: string;
|
|
15
|
+
}
|
|
16
|
+
export interface NapAm {
|
|
17
|
+
/** Tên nạp âm, ví dụ "Hải Trung Kim" */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Ngũ hành: Kim | Mộc | Thủy | Hỏa | Thổ */
|
|
20
|
+
element: string;
|
|
21
|
+
}
|
|
22
|
+
/** Chỉ số 0–59 trong vòng 60 hoa giáp, từ cặp (can, chi) */
|
|
23
|
+
export declare function canChiIndex(canIndex: number, chiIndex: number): number;
|
|
24
|
+
/** Can Chi của năm âm lịch */
|
|
25
|
+
export declare function getCanChiYear(lunarYear: number): CanChi;
|
|
26
|
+
/** Can Chi của ngày, suy từ số ngày Julian */
|
|
27
|
+
export declare function getCanChiDay(jd: number): CanChi;
|
|
28
|
+
/**
|
|
29
|
+
* Can Chi của tháng âm lịch (theo Ngũ Hổ Độn).
|
|
30
|
+
*
|
|
31
|
+
* Tháng 1 (Dần) của năm Giáp/Kỷ là Bính Dần, Ất/Canh là Mậu Dần,
|
|
32
|
+
* Bính/Tân là Canh Dần, Đinh/Nhâm là Nhâm Dần, Mậu/Quý là Giáp Dần.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getCanChiMonth(lunarMonth: number, lunarYear: number): CanChi;
|
|
35
|
+
/**
|
|
36
|
+
* Can Chi của giờ (theo Ngũ Thử Độn).
|
|
37
|
+
*
|
|
38
|
+
* @param dayCanIndex chỉ số can của ngày (0 = Giáp)
|
|
39
|
+
* @param hourChiIndex chỉ số chi của giờ (0 = Tý)
|
|
40
|
+
*/
|
|
41
|
+
export declare function getCanChiHour(dayCanIndex: number, hourChiIndex: number): CanChi;
|
|
42
|
+
/** Nạp âm của một cặp can chi */
|
|
43
|
+
export declare function getNapAm(canIndex: number, chiIndex: number): NapAm;
|
|
44
|
+
/** Ngũ hành của nạp âm ngày (dạng số 1..5 đúng quy ước Lịch Việt) */
|
|
45
|
+
export declare function getNguHanhId(element: string): number;
|
|
46
|
+
/** Con giáp của năm âm lịch */
|
|
47
|
+
export declare function getConGiap(lunarYear: number): string;
|