1#[derive(Debug, Default)]
27pub struct LunarSolarConverter {
28 lunar_year: i32,
29 lunar_month: u32,
30 lunar_day: u32,
31 is_intercalation: bool,
32 solar_year: u32,
33 solar_month: u32,
34 solar_day: u32,
35 gapja_year_inx: [Option<usize>; 3],
36 gapja_month_inx: [Option<usize>; 3],
37 gapja_day_inx: [Option<usize>; 3],
38}
39
40const KOREAN_LUNAR_MIN_VALUE: u32 = 13910101;
41const KOREAN_LUNAR_MAX_VALUE: u32 = 20501118;
42const KOREAN_SOLAR_MIN_VALUE: u32 = 13910205;
43const KOREAN_SOLAR_MAX_VALUE: u32 = 20501231;
44
45const KOREAN_LUNAR_BASE_YEAR: i32 = 1391;
46const SOLAR_LUNAR_DAY_DIFF: u32 = 35;
47
48const LUNAR_SMALL_MONTH_DAY: u32 = 29;
49const LUNAR_BIG_MONTH_DAY: u32 = 30;
50const SOLAR_SMALL_YEAR_DAY: u32 = 365;
51const SOLAR_BIG_YEAR_DAY: u32 = 366;
52
53const SOLAR_DAYS: [u32; 13] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29];
54const KOREAN_CHEONGAN: [char; 10] = [
55 '\u{ac11}', '\u{c744}', '\u{bcd1}', '\u{c815}', '\u{bb34}', '\u{ae30}', '\u{acbd}', '\u{c2e0}',
56 '\u{c784}', '\u{acc4}',
57];
58const KOREAN_GANJI: [char; 12] = [
59 '\u{c790}', '\u{cd95}', '\u{c778}', '\u{bb18}', '\u{c9c4}', '\u{c0ac}', '\u{c624}', '\u{bbf8}',
60 '\u{c2e0}', '\u{c720}', '\u{c220}', '\u{d574}',
61];
62const KOREAN_GAPJA_UNIT: [char; 3] = ['\u{b144}', '\u{c6d4}', '\u{c77c}'];
63
64const CHINESE_CHEONGAN: [char; 10] = [
65 '\u{7532}', '\u{4e59}', '\u{4e19}', '\u{4e01}', '\u{620a}', '\u{5df1}', '\u{5e9a}', '\u{8f9b}',
66 '\u{58ec}', '\u{7678}',
67];
68const CHINESE_GANJI: [char; 12] = [
69 '\u{5b50}', '\u{4e11}', '\u{5bc5}', '\u{536f}', '\u{8fb0}', '\u{5df3}', '\u{5348}', '\u{672a}',
70 '\u{7533}', '\u{9149}', '\u{620c}', '\u{4ea5}',
71];
72const CHINESE_GAPJA_UNIT: [char; 3] = ['\u{5e74}', '\u{6708}', '\u{65e5}'];
73
74const INTERCALATION_STR: [char; 2] = ['\u{c724}', '\u{958f}'];
75
76const KOREAN_LUNAR_DATA: [u32; 660] = [
77 0x82c40653, 0xc301c6a9, 0x82c405aa, 0x82c60ab5, 0x830092bd, 0xc2c402b6, 0x82c60c37, 0x82fe552e,
78 0x82c40c96, 0xc2c60e4b, 0x82fe3752, 0x82c60daa, 0x8301b5b4, 0xc2c6056d, 0x82c402ae, 0x83007a3d,
79 0x82c40a2d, 0xc2c40d15, 0x83004d95, 0x82c40b52, 0x8300cb69, 0xc2c60ada, 0x82c6055d, 0x8301925b,
80 0x82c4045b, 0xc2c40a2b, 0x83005aab, 0x82c40a95, 0x82c40b52, 0xc3001eaa, 0x82c60ab6, 0x8300c55b,
81 0x82c604b7, 0xc2c40457, 0x83007537, 0x82c4052b, 0x82c40695, 0xc3014695, 0x82c405aa, 0x8300c9b5,
82 0x82c60a6e, 0xc2c404ae, 0x83008a5e, 0x82c40a56, 0x82c40d2a, 0xc3006eaa, 0x82c60d55, 0x82c4056a,
83 0x8301295a, 0xc2c6095e, 0x8300b4af, 0x82c4049b, 0x82c40a4d, 0xc3007d2e, 0x82c40b2a, 0x82c60b55,
84 0x830045d5, 0xc2c402da, 0x82c6095b, 0x83011157, 0x82c4049b, 0xc3009a4f, 0x82c4064b, 0x82c406a9,
85 0x83006aea, 0xc2c606b5, 0x82c402b6, 0x83002aae, 0x82c60937, 0xc2ffb496, 0x82c40c96, 0x82c60e4b,
86 0x82fe76b2, 0xc2c60daa, 0x82c605ad, 0x8300336d, 0x82c4026e, 0xc2c4092e, 0x83002d2d, 0x82c40c95,
87 0x83009d4d, 0xc2c40b4a, 0x82c60b69, 0x8301655a, 0x82c6055b, 0xc2c4025d, 0x83002a5b, 0x82c4092b,
88 0x8300aa97, 0xc2c40695, 0x82c4074a, 0x83008b5a, 0x82c60ab6, 0xc2c6053b, 0x830042b7, 0x82c40257,
89 0x82c4052b, 0xc3001d2b, 0x82c40695, 0x830096ad, 0x82c405aa, 0xc2c60ab5, 0x830054ed, 0x82c404ae,
90 0x82c60a57, 0xc2ff344e, 0x82c40d2a, 0x8301bd94, 0x82c60b55, 0xc2c4056a, 0x8300797a, 0x82c6095d,
91 0x82c404ae, 0xc3004a9b, 0x82c40a4d, 0x82c40d25, 0x83011aaa, 0xc2c60b55, 0x8300956d, 0x82c402da,
92 0x82c6095b, 0xc30054b7, 0x82c40497, 0x82c40a4b, 0x83004b4b, 0xc2c406a9, 0x8300cad5, 0x82c605b5,
93 0x82c402b6, 0xc300895e, 0x82c6092f, 0x82c40497, 0x82fe4696, 0xc2c40d4a, 0x8300cea5, 0x82c60d69,
94 0x82c6056d, 0xc301a2b5, 0x82c4026e, 0x82c4052e, 0x83006cad, 0xc2c40c95, 0x82c40d4a, 0x83002f4a,
95 0x82c60b59, 0xc300c56d, 0x82c6055b, 0x82c4025d, 0x8300793b, 0xc2c4092b, 0x82c40a95, 0x83015b15,
96 0x82c406ca, 0xc2c60ad5, 0x830112b6, 0x82c604bb, 0x8300925f, 0xc2c40257, 0x82c4052b, 0x82fe6aaa,
97 0x82c60e95, 0xc2c406aa, 0x83003baa, 0x82c60ab5, 0x8300b4b7, 0xc2c404ae, 0x82c60a57, 0x82fe752d,
98 0x82c40d26, 0xc2c60d95, 0x830055d5, 0x82c4056a, 0x82c6096d, 0xc300255d, 0x82c404ae, 0x8300aa4f,
99 0x82c40a4d, 0xc2c40d25, 0x83006d69, 0x82c60b55, 0x82c4035a, 0xc3002aba, 0x82c6095b, 0x8301c49b,
100 0x82c40497, 0xc2c40a4b, 0x83008b2b, 0x82c406a5, 0x82c406d4, 0xc3034ab5, 0x82c402b6, 0x82c60937,
101 0x8300252f, 0xc2c40497, 0x82fe964e, 0x82c40d4a, 0x82c60ea5, 0xc30166a9, 0x82c6056d, 0x82c402b6,
102 0x8301385e, 0xc2c4092e, 0x8300bc97, 0x82c40a95, 0x82c40d4a, 0xc3008daa, 0x82c60b4d, 0x82c6056b,
103 0x830042db, 0xc2c4025d, 0x82c4092d, 0x83002d33, 0x82c40a95, 0xc3009b4d, 0x82c406aa, 0x82c60ad5,
104 0x83006575, 0xc2c604bb, 0x82c4025b, 0x83013457, 0x82c4052b, 0xc2ffba94, 0x82c60e95, 0x82c406aa,
105 0x83008ada, 0xc2c609b5, 0x82c404b6, 0x83004aae, 0x82c60a4f, 0xc2c20526, 0x83012d26, 0x82c60d55,
106 0x8301a5a9, 0xc2c4056a, 0x82c6096d, 0x8301649d, 0x82c4049e, 0xc2c40a4d, 0x83004d4d, 0x82c40d25,
107 0x8300bd53, 0xc2c40b54, 0x82c60b5a, 0x8301895a, 0x82c6095b, 0xc2c4049b, 0x83004a97, 0x82c40a4b,
108 0x82c40aa5, 0xc3001ea5, 0x82c406d4, 0x8302badb, 0x82c402b6, 0xc2c60937, 0x830064af, 0x82c40497,
109 0x82c4064b, 0xc2fe374a, 0x82c60da5, 0x8300b6b5, 0x82c6056d, 0xc2c402ba, 0x8300793e, 0x82c4092e,
110 0x82c40c96, 0xc3015d15, 0x82c40d4a, 0x82c60da5, 0x83013555, 0xc2c4056a, 0x83007a7a, 0x82c60a5d,
111 0x82c4092d, 0xc3006aab, 0x82c40a95, 0x82c40b4a, 0x83004baa, 0xc2c60ad5, 0x82c4055a, 0x830128ba,
112 0x82c60a5b, 0xc3007537, 0x82c4052b, 0x82c40693, 0x83015715, 0xc2c406aa, 0x82c60ad9, 0x830035b5,
113 0x82c404b6, 0xc3008a5e, 0x82c40a4e, 0x82c40d26, 0x83006ea6, 0xc2c40d52, 0x82c60daa, 0x8301466a,
114 0x82c6056d, 0xc2c404ae, 0x83003a9d, 0x82c40a4d, 0x83007d2b, 0xc2c40b25, 0x82c40d52, 0x83015d54,
115 0x82c60b5a, 0xc2c6055d, 0x8300355b, 0x82c4049d, 0x83007657, 0x82c40a4b, 0x82c40aa5, 0x83006b65,
116 0x82c406d2, 0xc2c60ada, 0x830045b6, 0x82c60937, 0x82c40497, 0xc3003697, 0x82c40a4d, 0x82fe76aa,
117 0x82c60da5, 0xc2c405aa, 0x83005aec, 0x82c60aae, 0x82c4092e, 0xc3003d2e, 0x82c40c96, 0x83018d45,
118 0x82c40d4a, 0xc2c60d55, 0x83016595, 0x82c4056a, 0x82c60a6d, 0xc300455d, 0x82c4052d, 0x82c40a95,
119 0x83003e95, 0xc2c40b4a, 0x83017b4a, 0x82c609d5, 0x82c4055a, 0xc3015a3a, 0x82c60a5b, 0x82c4052b,
120 0x83014a17, 0xc2c40693, 0x830096ab, 0x82c406aa, 0x82c60ab5, 0xc30064f5, 0x82c404b6, 0x82c60a57,
121 0x82fe452e, 0xc2c40d16, 0x82c60e93, 0x82fe3752, 0x82c60daa, 0xc30175aa, 0x82c6056d, 0x82c404ae,
122 0x83015a1b, 0xc2c40a2d, 0x82c40d15, 0x83004da5, 0x82c40b52, 0xc3009d6a, 0x82c60ada, 0x82c6055d,
123 0x8301629b, 0xc2c4045b, 0x82c40a2b, 0x83005b2b, 0x82c40a95, 0xc2c40b52, 0x83012ab2, 0x82c60ad6,
124 0x83017556, 0xc2c60537, 0x82c40457, 0x83005657, 0x82c4052b, 0xc2c40695, 0x83003795, 0x82c405aa,
125 0x8300aab6, 0xc2c60a6d, 0x82c404ae, 0x8300696e, 0x82c40a56, 0xc2c40d2a, 0x83005eaa, 0x82c60d55,
126 0x82c405aa, 0xc3003b6a, 0x82c60a6d, 0x830074bd, 0x82c404ab, 0xc2c40a8d, 0x83005d55, 0x82c40b2a,
127 0x82c60b55, 0xc30045d5, 0x82c404da, 0x82c6095d, 0x83002557, 0xc2c4049b, 0x83006a97, 0x82c4064b,
128 0x82c406a9, 0x83004baa, 0x82c606b5, 0x82c402ba, 0x83002ab6, 0xc2c60937, 0x82fe652e, 0x82c40d16,
129 0x82c60e4b, 0xc2fe56d2, 0x82c60da9, 0x82c605b5, 0x8300336d, 0xc2c402ae, 0x82c40a2e, 0x83002e2d,
130 0x82c40c95, 0xc3006d55, 0x82c40b52, 0x82c60b69, 0x830045da, 0xc2c6055d, 0x82c4025d, 0x83003a5b,
131 0x82c40a2b, 0xc3017a8b, 0x82c40a95, 0x82c40b4a, 0x83015b2a, 0xc2c60ad5, 0x82c6055b, 0x830042b7,
132 0x82c40257, 0xc300952f, 0x82c4052b, 0x82c40695, 0x830066d5, 0xc2c405aa, 0x82c60ab5, 0x8300456d,
133 0x82c404ae, 0xc2c60a57, 0x82ff3456, 0x82c40d2a, 0x83017e8a, 0xc2c60d55, 0x82c405aa, 0x83005ada,
134 0x82c6095d, 0xc2c404ae, 0x83004aab, 0x82c40a4d, 0x83008d2b, 0xc2c40b29, 0x82c60b55, 0x83007575,
135 0x82c402da, 0xc2c6095d, 0x830054d7, 0x82c4049b, 0x82c40a4b, 0xc3013a4b, 0x82c406a9, 0x83008ad9,
136 0x82c606b5, 0xc2c402b6, 0x83015936, 0x82c60937, 0x82c40497, 0xc2fe4696, 0x82c40e4a, 0x8300aea6,
137 0x82c60da9, 0xc2c605ad, 0x830162ad, 0x82c402ae, 0x82c4092e, 0xc3005cad, 0x82c40c95, 0x82c40d4a,
138 0x83013d4a, 0xc2c60b69, 0x8300757a, 0x82c6055b, 0x82c4025d, 0xc300595b, 0x82c4092b, 0x82c40a95,
139 0x83004d95, 0xc2c40b4a, 0x82c60b55, 0x830026d5, 0x82c6055b, 0xc3006277, 0x82c40257, 0x82c4052b,
140 0x82fe5aaa, 0xc2c60e95, 0x82c406aa, 0x83003baa, 0x82c60ab5, 0x830084bd, 0x82c404ae, 0x82c60a57,
141 0x82fe554d, 0xc2c40d26, 0x82c60d95, 0x83014655, 0x82c4056a, 0xc2c609ad, 0x8300255d, 0x82c404ae,
142 0x83006a5b, 0xc2c40a4d, 0x82c40d25, 0x83005da9, 0x82c60b55, 0xc2c4056a, 0x83002ada, 0x82c6095d,
143 0x830074bb, 0xc2c4049b, 0x82c40a4b, 0x83005b4b, 0x82c406a9, 0xc2c40ad4, 0x83024bb5, 0x82c402b6,
144 0x82c6095b, 0xc3002537, 0x82c40497, 0x82fe6656, 0x82c40e4a, 0xc2c60ea5, 0x830156a9, 0x82c605b5,
145 0x82c402b6, 0xc30138ae, 0x82c4092e, 0x83017c8d, 0x82c40c95, 0xc2c40d4a, 0x83016d8a, 0x82c60b69,
146 0x82c6056d, 0xc301425b, 0x82c4025d, 0x82c4092d, 0x83002d2b, 0xc2c40a95, 0x83007d55, 0x82c40b4a,
147 0x82c60b55, 0xc3015555, 0x82c604db, 0x82c4025b, 0x83013857, 0xc2c4052b, 0x83008a9b, 0x82c40695,
148 0x82c406aa, 0xc3006aea, 0x82c60ab5, 0x82c404b6, 0x83004aae, 0xc2c60a57, 0x82c40527, 0x82fe3726,
149 0x82c60d95, 0xc30076b5, 0x82c4056a, 0x82c609ad, 0x830054dd, 0xc2c404ae, 0x82c40a4e, 0x83004d4d,
150 0x82c40d25, 0xc3008d59, 0x82c40b54, 0x82c60d6a, 0x8301695a, 0xc2c6095b, 0x82c4049b, 0x83004a9b,
151 0x82c40a4b, 0xc300ab27, 0x82c406a5, 0x82c406d4, 0x83026b75, 0xc2c402b6, 0x82c6095b, 0x830054b7,
152 0x82c40497, 0xc2c4064b, 0x82fe374a, 0x82c60ea5, 0x830086d9, 0xc2c605ad, 0x82c402b6, 0x8300596e,
153 0x82c4092e, 0xc2c40c96, 0x83004e95, 0x82c40d4a, 0x82c60da5, 0xc3002755, 0x82c4056c, 0x83027abb,
154 0x82c4025d, 0xc2c4092d, 0x83005cab, 0x82c40a95, 0x82c40b4a, 0xc3013b4a, 0x82c60b55, 0x8300955d,
155 0x82c404ba, 0xc2c60a5b, 0x83005557, 0x82c4052b, 0x82c40a95, 0xc3004b95, 0x82c406aa, 0x82c60ad5,
156 0x830026b5, 0xc2c404b6, 0x83006a6e, 0x82c60a57, 0x82c40527, 0xc2fe56a6, 0x82c60d93, 0x82c405aa,
157 0x83003b6a, 0xc2c6096d, 0x8300b4af, 0x82c404ae, 0x82c40a4d, 0xc3016d0d, 0x82c40d25, 0x82c40d52,
158 0x83005dd4, 0xc2c60b6a, 0x82c6096d, 0x8300255b, 0x82c4049b, 0xc3007a57, 0x82c40a4b, 0x82c40b25,
159 0x83015b25, 0xc2c406d4, 0x82c60ada, 0x830138b6,
160];
161
162#[derive(Debug, PartialEq, Eq, Copy, Clone)]
164pub enum DayOfWeek {
165 Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, }
180
181impl LunarSolarConverter {
182 pub fn new() -> Self {
184 LunarSolarConverter::default()
185 }
186
187 fn get_lunar_data(year: i32) -> u32 {
188 if year < KOREAN_LUNAR_BASE_YEAR {
189 0
190 } else {
191 *KOREAN_LUNAR_DATA
192 .get((year - KOREAN_LUNAR_BASE_YEAR) as usize)
193 .unwrap_or(&0)
194 }
195 }
196
197 fn get_lunar_intercalation_month(lunar_data: u32) -> u32 {
198 (lunar_data >> 12) & 0x000F
199 }
200
201 fn shift_lunar_days(year: i32) -> u32 {
202 let lunar_data = Self::get_lunar_data(year);
203 if lunar_data == 0 {
204 return 0;
205 }
206
207 let mut total_days = 0;
208 let month_bits = lunar_data & 0xFFF;
209
210 for month in 1..=12 {
211 if ((month_bits >> (12 - month)) & 0x01) > 0 {
212 total_days += LUNAR_BIG_MONTH_DAY;
213 } else {
214 total_days += LUNAR_SMALL_MONTH_DAY;
215 }
216 }
217
218 let intercalation_month = Self::get_lunar_intercalation_month(lunar_data);
219 if intercalation_month > 0 {
220 if ((lunar_data >> 16) & 0x01) > 0 {
221 total_days += LUNAR_BIG_MONTH_DAY;
222 } else {
223 total_days += LUNAR_SMALL_MONTH_DAY;
224 }
225 }
226
227 total_days
228 }
229
230 fn get_lunar_days(year: i32, month: u32, is_intercalation: bool) -> u32 {
231 let mut days = 0;
232 if year < KOREAN_LUNAR_BASE_YEAR {
233 return 0;
234 }
235 let lunar_data = Self::get_lunar_data(year);
236 let intercalation_month = Self::get_lunar_intercalation_month(lunar_data);
237
238 if is_intercalation && intercalation_month == month {
239 if ((lunar_data >> 16) & 0x01) > 0 {
240 days = LUNAR_BIG_MONTH_DAY;
241 } else {
242 days = LUNAR_SMALL_MONTH_DAY;
243 }
244 } else if month > 0 && month < 13 {
245 if ((lunar_data >> (12 - month)) & 0x01) > 0 {
246 days = LUNAR_BIG_MONTH_DAY;
247 } else {
248 days = LUNAR_SMALL_MONTH_DAY;
249 }
250 }
251
252 days
253 }
254
255 fn get_lunar_days_before_base_year(year: i32) -> u32 {
256 let mut days = 0;
257
258 for base_year in KOREAN_LUNAR_BASE_YEAR..year {
259 days += Self::shift_lunar_days(base_year);
260 }
261
262 days
263 }
264
265 fn get_lunar_days_before_base_month(year: i32, month: u32, is_intercalation: bool) -> u32 {
266 let mut days = 0;
267 if year < KOREAN_LUNAR_BASE_YEAR || month == 0 {
268 return 0;
269 }
270
271 let lunar_data = Self::get_lunar_data(year);
272 let intercalation_month = Self::get_lunar_intercalation_month(lunar_data);
273
274 for base_month in 1..month {
275 days += Self::get_lunar_days(year, base_month, false);
276
277 if intercalation_month > 0 && intercalation_month == base_month {
278 days += Self::get_lunar_days(year, intercalation_month, true);
279 }
280 }
281
282 if is_intercalation && intercalation_month == month {
283 days += Self::get_lunar_days(year, month, false);
284 }
285
286 days
287 }
288
289 fn get_lunar_abs_days(year: i32, month: u32, day: u32, is_intercalation: bool) -> u32 {
290 if year < KOREAN_LUNAR_BASE_YEAR {
291 0
292 } else {
293 Self::get_lunar_days_before_base_year(year)
294 + Self::get_lunar_days_before_base_month(year, month, is_intercalation)
295 + day
296 }
297 }
298
299 fn is_gregorian_leap(year: i32) -> bool {
300 if year <= 1582 {
301 year % 4 == 0
303 } else {
304 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
306 }
307 }
308
309 fn shift_solar_days(year: i32) -> u32 {
310 let mut days;
311
312 if Self::is_gregorian_leap(year) {
314 days = SOLAR_BIG_YEAR_DAY;
315 } else {
316 days = SOLAR_SMALL_YEAR_DAY;
317 }
318
319 if year == 1582 {
320 days -= 10;
321 }
322
323 days
324 }
325
326 fn get_solar_days(year: i32, month: u32) -> u32 {
327 let mut days = 0;
328
329 if year < KOREAN_LUNAR_BASE_YEAR {
330 return 0;
331 }
332
333 if month == 2 && Self::is_gregorian_leap(year) {
335 days = *SOLAR_DAYS.get(12).unwrap_or(&0); } else if month > 0 && month < 13 {
337 days = *SOLAR_DAYS.get((month - 1) as usize).unwrap_or(&0);
338 }
339
340 if year == 1582 && month == 10 {
341 days -= 10;
342 }
343
344 days
345 }
346
347 fn get_solar_day_before_base_year(year: i32) -> u32 {
348 let mut days = 0;
349
350 for base_year in KOREAN_LUNAR_BASE_YEAR..year {
351 days += Self::shift_solar_days(base_year);
352 }
353
354 days
355 }
356
357 fn get_solar_days_before_base_month(year: i32, month: u32) -> u32 {
358 let mut days = 0;
359
360 if year < KOREAN_LUNAR_BASE_YEAR {
361 return 0;
362 }
363
364 for base_month in 1..month {
365 days += Self::get_solar_days(year, base_month);
366 }
367
368 days
369 }
370
371 fn get_solar_abs_days(year: i32, month: u32, day: u32) -> u32 {
372 if year < KOREAN_LUNAR_BASE_YEAR {
373 0
374 } else {
375 let mut days = Self::get_solar_day_before_base_year(year)
376 + Self::get_solar_days_before_base_month(year, month)
377 + day;
378 days -= SOLAR_LUNAR_DAY_DIFF;
379
380 days
381 }
382 }
383
384 pub fn set_lunar_date(
396 &mut self,
397 lunar_year: i32,
398 lunar_month: u32,
399 lunar_day: u32,
400 is_intercalation: bool,
401 ) -> bool {
402 let mut is_valid = false;
403
404 if Self::check_valid_date(
405 true,
406 is_intercalation,
407 lunar_year as u32,
408 lunar_month,
409 lunar_day,
410 ) {
411 self.is_intercalation = is_intercalation
412 && (Self::get_lunar_intercalation_month(Self::get_lunar_data(lunar_year))
413 == lunar_month);
414 self.set_solar_date_by_lunar_date(
415 lunar_year,
416 lunar_month,
417 lunar_day,
418 self.is_intercalation,
419 );
420
421 is_valid = true;
422 }
423
424 is_valid
425 }
426
427 pub fn set_solar_date(&mut self, solar_year: u32, solar_month: u32, solar_day: u32) -> bool {
439 let mut is_valid = false;
440
441 if Self::check_valid_date(false, false, solar_year, solar_month, solar_day) {
442 self.set_lunar_date_by_solar_date(solar_year, solar_month, solar_day);
443 is_valid = true;
444 }
445
446 is_valid
447 }
448
449 fn get_gapja(&mut self) {
450 let abs_days = Self::get_lunar_abs_days(
451 self.lunar_year,
452 self.lunar_month,
453 self.lunar_day,
454 self.is_intercalation,
455 );
456
457 if abs_days > 0 {
458 self.gapja_year_inx[0] = Some(
459 ((self.lunar_year + 7) - KOREAN_LUNAR_BASE_YEAR) as usize % KOREAN_CHEONGAN.len(),
460 );
461 self.gapja_year_inx[1] = Some(
462 ((self.lunar_year + 7) - KOREAN_LUNAR_BASE_YEAR) as usize % KOREAN_GANJI.len(),
463 );
464
465 let mut month_count = self.lunar_month;
466 month_count += 12 * ((self.lunar_year - KOREAN_LUNAR_BASE_YEAR) as u32);
467 self.gapja_month_inx[0] = Some((month_count + 5) as usize % KOREAN_CHEONGAN.len());
468 self.gapja_month_inx[1] = Some((month_count + 1) as usize % KOREAN_GANJI.len());
469
470 self.gapja_day_inx[0] = Some((abs_days + 4) as usize % KOREAN_CHEONGAN.len());
471 self.gapja_day_inx[1] = Some(abs_days as usize % KOREAN_GANJI.len());
472 } else {
473 self.gapja_year_inx = [None, None, None];
474 self.gapja_month_inx = [None, None, None];
475 self.gapja_day_inx = [None, None, None];
476 }
477 }
478
479 pub fn get_gapja_string(&mut self) -> String {
484 self.get_gapja();
485
486 let mut gapja_string = String::new();
487
488 if let (Some(year_cheongan), Some(year_ganji)) =
489 (self.gapja_year_inx[0], self.gapja_year_inx[1])
490 {
491 gapja_string.push(KOREAN_CHEONGAN[year_cheongan]);
492 gapja_string.push(KOREAN_GANJI[year_ganji]);
493 gapja_string.push(KOREAN_GAPJA_UNIT[0]);
494 } else {
495 return "".to_string();
496 }
497
498 gapja_string.push(' ');
499
500 if let (Some(month_cheongan), Some(month_ganji)) =
501 (self.gapja_month_inx[0], self.gapja_month_inx[1])
502 {
503 gapja_string.push(KOREAN_CHEONGAN[month_cheongan]);
504 gapja_string.push(KOREAN_GANJI[month_ganji]);
505 gapja_string.push(KOREAN_GAPJA_UNIT[1]);
506 } else {
507 return "".to_string();
508 }
509
510 gapja_string.push(' ');
511
512 if let (Some(day_cheongan), Some(day_ganji)) =
513 (self.gapja_day_inx[0], self.gapja_day_inx[1])
514 {
515 gapja_string.push(KOREAN_CHEONGAN[day_cheongan]);
516 gapja_string.push(KOREAN_GANJI[day_ganji]);
517 gapja_string.push(KOREAN_GAPJA_UNIT[2]);
518 } else {
519 return "".to_string();
520 }
521
522 if self.is_intercalation {
523 gapja_string.push_str(" (");
524 gapja_string.push(INTERCALATION_STR[0]);
525 gapja_string.push(KOREAN_GAPJA_UNIT[1]);
526 gapja_string.push(')');
527 }
528
529 gapja_string
530 }
531
532 pub fn get_chinese_gapja_string(&mut self) -> String {
537 self.get_gapja();
538
539 let mut gapja_string = String::new();
540
541 if let (Some(year_cheongan), Some(year_ganji)) =
542 (self.gapja_year_inx[0], self.gapja_year_inx[1])
543 {
544 gapja_string.push(CHINESE_CHEONGAN[year_cheongan]);
545 gapja_string.push(CHINESE_GANJI[year_ganji]);
546 gapja_string.push(CHINESE_GAPJA_UNIT[0]);
547 } else {
548 return "".to_string();
549 }
550
551 gapja_string.push(' ');
552
553 if let (Some(month_cheongan), Some(month_ganji)) =
554 (self.gapja_month_inx[0], self.gapja_month_inx[1])
555 {
556 gapja_string.push(CHINESE_CHEONGAN[month_cheongan]);
557 gapja_string.push(CHINESE_GANJI[month_ganji]);
558 gapja_string.push(CHINESE_GAPJA_UNIT[1]);
559 } else {
560 return "".to_string();
561 }
562
563 gapja_string.push(' ');
564
565 if let (Some(day_cheongan), Some(day_ganji)) =
566 (self.gapja_day_inx[0], self.gapja_day_inx[1])
567 {
568 gapja_string.push(CHINESE_CHEONGAN[day_cheongan]);
569 gapja_string.push(CHINESE_GANJI[day_ganji]);
570 gapja_string.push(CHINESE_GAPJA_UNIT[2]);
571 } else {
572 return "".to_string();
573 }
574
575 if self.is_intercalation {
576 gapja_string.push_str(" (");
577 gapja_string.push(INTERCALATION_STR[1]);
578 gapja_string.push(CHINESE_GAPJA_UNIT[1]);
579 gapja_string.push(')');
580 }
581
582 gapja_string
583 }
584
585 pub fn get_lunar_iso_format(&self) -> String {
588 let mut iso_str = format!(
589 "{:04}-{:02}-{:02}",
590 self.lunar_year, self.lunar_month, self.lunar_day
591 );
592
593 if self.is_intercalation {
594 iso_str.push_str(" Intercalation");
595 }
596
597 iso_str
598 }
599
600 pub fn get_solar_iso_format(&self) -> String {
602 format!(
603 "{:04}-{:02}-{:02}",
604 self.solar_year, self.solar_month, self.solar_day
605 )
606 }
607
608 pub fn get_julian_day_number(year: u32, month: u32, day: u32) -> Option<u32> {
632 if year == 1582 && month == 10 && day > 4 && day < 15 {
634 return None;
635 }
636 if month == 0 || month > 12 || day == 0 || day > 31 {
638 return None;
639 }
640
641 let y = year as i32;
643 let m = month as i32;
644 let d = day as i32;
645
646 let adj_y = if m <= 2 { y - 1 } else { y };
648 let adj_m = if m <= 2 { m + 12 } else { m };
649
650 let julian_base = (1461 * (adj_y + 4716)) / 4 + (153 * (adj_m + 1)) / 5 + d;
652
653 let b = if y > 1582 || (y == 1582 && m > 10) || (y == 1582 && m == 10 && d >= 15) {
655 let term1 = adj_y / 100; 2 - term1 + term1 / 4
658 } else {
659 0
661 };
662
663 let jdn = julian_base + b - 1524;
665
666 Some(jdn as u32)
667 }
668
669 pub fn get_day_of_week(year: u32, month: u32, day: u32) -> Option<DayOfWeek> {
690 Self::get_julian_day_number(year, month, day).map(|jdn| {
691 match jdn % 7 {
693 0 => DayOfWeek::Monday,
694 1 => DayOfWeek::Tuesday,
695 2 => DayOfWeek::Wednesday,
696 3 => DayOfWeek::Thursday,
697 4 => DayOfWeek::Friday,
698 5 => DayOfWeek::Saturday,
699 _ => DayOfWeek::Sunday, }
701 })
702 }
703
704 pub fn is_solar_leap_year(year: u32) -> bool {
725 Self::is_gregorian_leap(year as i32)
727 }
728
729 pub fn get_lunar_intercalary_month(year: i32) -> Option<u32> {
748 if year < KOREAN_LUNAR_BASE_YEAR
749 || year > KOREAN_LUNAR_BASE_YEAR + KOREAN_LUNAR_DATA.len() as i32 - 1
750 {
751 return None; }
753 let lunar_data = Self::get_lunar_data(year);
754 let intercalary_month = Self::get_lunar_intercalation_month(lunar_data);
755 if intercalary_month > 0 {
756 Some(intercalary_month)
757 } else {
758 None
759 }
760 }
761
762 #[allow(dead_code)]
765 pub fn solar_year(&self) -> u32 {
766 self.solar_year
767 }
768 #[allow(dead_code)]
770 pub fn solar_month(&self) -> u32 {
771 self.solar_month
772 }
773 #[allow(dead_code)]
775 pub fn solar_day(&self) -> u32 {
776 self.solar_day
777 }
778 pub fn lunar_year(&self) -> i32 {
780 self.lunar_year
781 }
782 #[allow(dead_code)]
784 pub fn lunar_month(&self) -> u32 {
785 self.lunar_month
786 }
787 #[allow(dead_code)]
789 pub fn lunar_day(&self) -> u32 {
790 self.lunar_day
791 }
792 #[allow(dead_code)]
794 pub fn is_intercalation(&self) -> bool {
795 self.is_intercalation
796 }
797 fn set_solar_date_by_lunar_date(
802 &mut self,
803 lunar_year: i32,
804 lunar_month: u32,
805 lunar_day: u32,
806 is_intercalation: bool,
807 ) {
808 let abs_days =
809 Self::get_lunar_abs_days(lunar_year, lunar_month, lunar_day, is_intercalation);
810
811 if abs_days < Self::get_solar_abs_days(lunar_year + 1, 1, 1) {
812 self.solar_year = lunar_year as u32;
813 } else {
814 self.solar_year = (lunar_year + 1) as u32;
815 }
816
817 for month in (1..=12).rev() {
818 let abs_days_by_month = Self::get_solar_abs_days(self.solar_year as i32, month, 1);
819
820 if abs_days >= abs_days_by_month {
821 self.solar_month = month;
822 self.solar_day = abs_days - abs_days_by_month + 1;
823 break;
824 }
825 }
826
827 if self.solar_year == 1582 && self.solar_month == 10 && self.solar_day > 4 {
828 self.solar_day += 10;
829 }
830 }
831
832 fn set_lunar_date_by_solar_date(&mut self, solar_year: u32, solar_month: u32, solar_day: u32) {
833 let abs_days = Self::get_solar_abs_days(solar_year as i32, solar_month, solar_day);
834
835 self.is_intercalation = false;
836
837 if abs_days >= Self::get_lunar_abs_days(solar_year as i32, 1, 1, false) {
838 self.lunar_year = solar_year as i32;
839 } else {
840 self.lunar_year = solar_year as i32 - 1;
841 }
842
843 for month in (1..=12).rev() {
844 let abs_days_by_month = Self::get_lunar_abs_days(self.lunar_year, month, 1, false);
845
846 if abs_days >= abs_days_by_month {
847 self.lunar_month = month;
848
849 if Self::get_lunar_intercalation_month(Self::get_lunar_data(self.lunar_year))
850 == month
851 {
852 self.is_intercalation =
853 abs_days >= Self::get_lunar_abs_days(self.lunar_year, month, 1, true);
854 }
855
856 self.lunar_day = abs_days
857 - Self::get_lunar_abs_days(
858 self.lunar_year,
859 self.lunar_month,
860 1,
861 self.is_intercalation,
862 )
863 + 1;
864
865 break;
866 }
867 }
868 }
869
870 fn is_valid_min(is_lunar: bool, date_value: u32) -> bool {
871 if is_lunar {
872 KOREAN_LUNAR_MIN_VALUE <= date_value
873 } else {
874 KOREAN_SOLAR_MIN_VALUE <= date_value
875 }
876 }
877
878 fn is_valid_max(is_lunar: bool, date_value: u32) -> bool {
879 if is_lunar {
880 KOREAN_LUNAR_MAX_VALUE >= date_value
881 } else {
882 KOREAN_SOLAR_MAX_VALUE >= date_value
883 }
884 }
885
886 fn check_valid_date(
887 is_lunar: bool,
888 is_intercalation: bool,
889 year: u32,
890 month: u32,
891 day: u32,
892 ) -> bool {
893 let mut is_valid = false;
894 let date_value = year * 10000 + month * 100 + day;
895
896 if Self::is_valid_min(is_lunar, date_value) && Self::is_valid_max(is_lunar, date_value) {
898 let mut day_limit;
899
900 if month > 0 && month < 13 && day > 0 {
901 if is_lunar {
902 if is_intercalation
903 && Self::get_lunar_intercalation_month(Self::get_lunar_data(year as i32))
904 != month
905 {
906 return false;
907 }
908 day_limit = Self::get_lunar_days(year as i32, month, is_intercalation);
909 } else {
910 day_limit = Self::get_solar_days(year as i32, month);
911 }
912
913 if !is_lunar && year == 1582 && month == 10 {
914 if day > 4 && day < 15 {
915 return false;
916 } else {
917 day_limit += 10;
918 }
919 }
920
921 if day <= day_limit {
922 is_valid = true;
923 }
924 }
925 }
926
927 is_valid
928 }
929}
930
931#[cfg(test)]
932mod tests {
933 use super::DayOfWeek;
934 use crate::LunarSolarConverter;
935
936 #[test]
937 fn test_lunar_iso_format() {
938 let mut converter = LunarSolarConverter::new();
939 converter.set_solar_date(2022, 7, 10);
940 let lunar = converter.get_lunar_iso_format();
941 let want = "2022-06-12";
942
943 println!("{}", lunar);
944
945 assert_eq!(lunar, want, "got {:?} want {:?}", lunar, want);
946 }
947
948 #[test]
949 fn test_gapja_string() {
950 let mut converter = LunarSolarConverter::new();
951 converter.set_solar_date(2022, 7, 10);
952 let lunar_gapja = converter.get_gapja_string();
953 let want = "임인년 정미월 갑자일";
954
955 println!("{}", lunar_gapja);
956
957 assert_eq!(lunar_gapja, want, "got {:?} want {:?}", lunar_gapja, want);
958 }
959
960 #[test]
961 fn test_chinese_gapja_string() {
962 let mut converter = LunarSolarConverter::new();
963 converter.set_solar_date(2022, 7, 10);
964 let lunar_chinese_gapja = converter.get_chinese_gapja_string();
965 let want = "壬寅年 丁未月 甲子日";
966
967 println!("{}", lunar_chinese_gapja);
968
969 assert_eq!(
970 lunar_chinese_gapja, want,
971 "got {:?} want {:?}",
972 lunar_chinese_gapja, want
973 );
974 }
975
976 #[test]
977 fn test_solar_iso_format() {
978 let mut converter = LunarSolarConverter::new();
979 converter.set_lunar_date(2022, 6, 12, false);
980 let solar = converter.get_solar_iso_format();
981 let want = "2022-07-10";
982
983 println!("{}", solar);
984
985 assert_eq!(solar, want, "got {:?} want {:?}", solar, want);
986 }
987
988 #[test]
989 fn test_gapja_string_intercalation() {
990 let mut converter = LunarSolarConverter::new();
991 let is_valid = converter.set_lunar_date(2022, 6, 12, true);
992
993 assert!(
994 !is_valid,
995 "Expected set_lunar_date to return false for non-existent intercalary month"
996 );
997 }
998
999 #[test]
1000 fn test_chinese_gapja_string_intercalation() {
1001 let mut converter = LunarSolarConverter::new();
1002 let is_valid = converter.set_lunar_date(2022, 6, 12, true);
1003
1004 assert!(
1005 !is_valid,
1006 "Expected set_lunar_date to return false for non-existent intercalary month"
1007 );
1008 }
1009
1010 #[test]
1011 fn test_set_solar_date() {
1012 let mut converter = LunarSolarConverter::new();
1013 let is_valid = converter.set_solar_date(2022, 7, 10);
1014
1015 assert!(is_valid, "Expected solar date to be valid");
1016 assert_eq!(converter.lunar_year, 2022);
1017 assert_eq!(converter.lunar_month, 6);
1018 assert_eq!(converter.lunar_day, 12);
1019 }
1020
1021 #[test]
1022 fn test_set_lunar_date() {
1023 let mut converter = LunarSolarConverter::new();
1024 let is_valid = converter.set_lunar_date(2022, 6, 12, false);
1025
1026 assert!(is_valid, "Expected lunar date to be valid");
1027 assert_eq!(converter.solar_year, 2022);
1028 assert_eq!(converter.solar_month, 7);
1029 assert_eq!(converter.solar_day, 10);
1030 }
1031
1032 #[test]
1033 fn test_invalid_solar_date() {
1034 let mut converter = LunarSolarConverter::new();
1035 let is_valid = converter.set_solar_date(1582, 10, 10);
1036
1037 assert!(!is_valid, "Expected solar date to be invalid");
1038 }
1039
1040 #[test]
1041 fn test_invalid_lunar_date() {
1042 let mut converter = LunarSolarConverter::new();
1043 let is_valid = converter.set_lunar_date(1390, 12, 31, false);
1044
1045 assert!(!is_valid, "Expected lunar date to be invalid");
1046 }
1047
1048 #[test]
1049 fn test_get_lunar_days() {
1050 let days = LunarSolarConverter::get_lunar_days(2022, 6, false);
1051
1052 assert_eq!(days, 30, "Expected 30 days for June 2022");
1053 }
1054
1055 #[test]
1056 fn test_get_lunar_days_invalid_month() {
1057 let days = LunarSolarConverter::get_lunar_days(2022, 13, false);
1058
1059 assert_eq!(days, 0, "Expected 0 days for invalid month 2022");
1060 }
1061
1062 #[test]
1063 fn test_get_lunar_days_invalid_year() {
1064 let days = LunarSolarConverter::get_lunar_days(1390, 6, false);
1065 assert_eq!(days, 0, "Expected 0 days for invalid year 1390");
1066 }
1067
1068 #[test]
1069 fn test_get_solar_days() {
1070 let days = LunarSolarConverter::get_solar_days(2022, 2);
1071
1072 assert_eq!(days, 28, "Expected 28 days for February 2022");
1073 }
1074
1075 #[test]
1076 fn test_get_solar_days_invalid_month() {
1077 let days = LunarSolarConverter::get_solar_days(2022, 13);
1078
1079 assert_eq!(days, 0, "Expected 0 days for invalid month 2022");
1080 }
1081
1082 #[test]
1083 fn test_get_lunar_abs_days() {
1084 let days = LunarSolarConverter::get_lunar_abs_days(2022, 6, 12, false);
1085
1086 assert_eq!(days, 230616, "Expected 230616 absolute lunar days");
1087 }
1088
1089 #[test]
1090 fn test_get_lunar_abs_days_invalid_year() {
1091 let days = LunarSolarConverter::get_lunar_abs_days(1390, 6, 12, false);
1092
1093 assert_eq!(
1094 days, 0,
1095 "Expected 0 absolute lunar days for invalid year 1390"
1096 );
1097 }
1098
1099 #[test]
1100 fn test_get_solar_abs_days() {
1101 let days = LunarSolarConverter::get_solar_abs_days(2022, 7, 10);
1102
1103 assert_eq!(days, 230616, "Expected 230616 absolute solar days");
1105 }
1106
1107 #[test]
1108 fn test_get_solar_abs_days_invalid_year() {
1109 let days = LunarSolarConverter::get_solar_abs_days(1390, 7, 10);
1110
1111 assert_eq!(
1112 days, 0,
1113 "Expected 0 absolute solar days for invalid year 1390"
1114 );
1115 }
1116
1117 #[test]
1118 fn test_invalid_date_for_get_gapja_string() {
1119 let mut converter = LunarSolarConverter::new();
1120 converter.set_lunar_date(1390, 12, 31, false);
1121 let gapja = converter.get_gapja_string();
1122
1123 assert_eq!(gapja, "", "Expected empty string since the date is invalid");
1124 }
1125
1126 #[test]
1127 fn test_invalid_date_for_get_chinese_gapja_string() {
1128 let mut converter = LunarSolarConverter::new();
1129 converter.set_lunar_date(1390, 12, 31, false);
1130 let gapja = converter.get_chinese_gapja_string();
1131
1132 assert_eq!(gapja, "", "Expected empty string since the date is invalid");
1133 }
1134
1135 #[test]
1136 fn test_get_julian_day_number_gregorian() {
1137 let jdn = LunarSolarConverter::get_julian_day_number(2022, 7, 10);
1138 let want = Some(2459771);
1139 assert_eq!(
1140 jdn, want,
1141 "Expected JDN {:?} for 2022-07-10, got {:?}",
1142 want, jdn
1143 );
1144 }
1145
1146 #[test]
1147 fn test_get_julian_day_number_julian() {
1148 let jdn = LunarSolarConverter::get_julian_day_number(1500, 3, 1);
1149 let want = Some(2268993);
1150 assert_eq!(
1151 jdn, want,
1152 "Expected JDN {:?} for 1500-03-01, got {:?}",
1153 want, jdn
1154 );
1155 }
1156
1157 #[test]
1158 fn test_get_julian_day_number_reform_before() {
1159 let jdn = LunarSolarConverter::get_julian_day_number(1582, 10, 4);
1160 let want = Some(2299160);
1161 assert_eq!(
1162 jdn, want,
1163 "Expected JDN {:?} for 1582-10-04, got {:?}",
1164 want, jdn
1165 );
1166 }
1167
1168 #[test]
1169 fn test_get_julian_day_number_reform_after() {
1170 let jdn = LunarSolarConverter::get_julian_day_number(1582, 10, 15);
1171 let want = Some(2299161);
1172 assert_eq!(
1173 jdn, want,
1174 "Expected JDN {:?} for 1582-10-15, got {:?}",
1175 want, jdn
1176 );
1177 }
1178
1179 #[test]
1180 fn test_get_julian_day_number_min_date() {
1181 let jdn = LunarSolarConverter::get_julian_day_number(1391, 2, 5);
1183 let want = Some(2229156);
1184 assert_eq!(
1185 jdn, want,
1186 "Expected JDN {:?} for 1391-02-05, got {:?}",
1187 want, jdn
1188 );
1189 }
1190
1191 #[test]
1192 fn test_get_julian_day_number_invalid_gap() {
1193 let jdn = LunarSolarConverter::get_julian_day_number(1582, 10, 10);
1194 assert_eq!(
1195 jdn, None,
1196 "Expected None for invalid date 1582-10-10 (Gregorian gap)"
1197 );
1198 }
1199
1200 #[test]
1201 fn test_get_julian_day_number_invalid_range_before() {
1202 let jdn = LunarSolarConverter::get_julian_day_number(1391, 2, 4);
1204 let want = Some(2229155);
1205 assert_eq!(
1206 jdn, want,
1207 "Expected JDN {:?} for 1391-02-04, got {:?}",
1208 want, jdn
1209 );
1210 }
1211
1212 #[test]
1213 fn test_get_julian_day_number_invalid_range_after() {
1214 let jdn = LunarSolarConverter::get_julian_day_number(2051, 1, 1);
1217 let want = Some(2470173);
1218 assert_eq!(
1219 jdn, want,
1220 "Expected JDN {:?} for 2051-01-01, got {:?}",
1221 want, jdn
1222 );
1223 }
1224
1225 #[test]
1226 fn test_get_day_of_week_monday() {
1227 let dow = LunarSolarConverter::get_day_of_week(2022, 7, 11);
1229 let want = Some(DayOfWeek::Monday);
1230 assert_eq!(
1231 dow, want,
1232 "Expected {:?} for 2022-07-11, got {:?}",
1233 want, dow
1234 );
1235 }
1236
1237 #[test]
1238 fn test_get_day_of_week_sunday() {
1239 let dow = LunarSolarConverter::get_day_of_week(2022, 7, 10);
1241 let want = Some(DayOfWeek::Sunday);
1242 assert_eq!(
1243 dow, want,
1244 "Expected {:?} for 2022-07-10, got {:?}",
1245 want, dow
1246 );
1247 }
1248
1249 #[test]
1250 fn test_get_day_of_week_reform_before() {
1251 let result = LunarSolarConverter::get_day_of_week(1582, 10, 4);
1253 assert_eq!(result, Some(DayOfWeek::Thursday)); }
1255
1256 #[test]
1257 fn test_get_day_of_week_reform_after() {
1258 let result = LunarSolarConverter::get_day_of_week(1582, 10, 15);
1260 assert_eq!(result, Some(DayOfWeek::Friday)); }
1262
1263 #[test]
1264 fn test_get_day_of_week_invalid_gap() {
1265 let dow = LunarSolarConverter::get_day_of_week(1582, 10, 10);
1266 assert_eq!(dow, None, "Expected None for invalid date 1582-10-10");
1267 }
1268
1269 #[test]
1270 fn test_is_solar_leap_year() {
1271 assert!(
1272 LunarSolarConverter::is_solar_leap_year(2024),
1273 "2024 should be a leap year"
1274 );
1275 assert!(
1276 !LunarSolarConverter::is_solar_leap_year(2023),
1277 "2023 should not be a leap year"
1278 );
1279 assert!(
1280 !LunarSolarConverter::is_solar_leap_year(1900),
1281 "1900 should not be a leap year"
1282 );
1283 assert!(
1284 LunarSolarConverter::is_solar_leap_year(2000),
1285 "2000 should be a leap year"
1286 );
1287 assert!(
1288 LunarSolarConverter::is_solar_leap_year(1500),
1289 "1500 should be a leap year (Julian)"
1290 );
1291 assert!(
1292 !LunarSolarConverter::is_solar_leap_year(1582),
1293 "1582 should not be a leap year"
1294 );
1295 assert!(
1296 LunarSolarConverter::is_solar_leap_year(1600),
1297 "1600 should be a leap year"
1298 );
1299 }
1300
1301 #[test]
1302 fn test_get_lunar_intercalary_month() {
1303 assert_eq!(
1305 LunarSolarConverter::get_lunar_intercalary_month(2023),
1306 Some(2),
1307 "Year 2023 should have intercalary month 2"
1308 );
1309 assert_eq!(
1311 LunarSolarConverter::get_lunar_intercalary_month(2020),
1312 Some(4),
1313 "Year 2020 should have intercalary month 4"
1314 );
1315 assert_eq!(
1317 LunarSolarConverter::get_lunar_intercalary_month(2022),
1318 None,
1319 "Year 2022 should not have an intercalary month"
1320 );
1321 assert_eq!(
1322 LunarSolarConverter::get_lunar_intercalary_month(1391),
1323 None,
1324 "Year 1391 should not have an intercalary month"
1325 ); }
1327
1328 #[test]
1329 fn test_get_lunar_intercalary_month_out_of_range() {
1330 assert_eq!(
1331 LunarSolarConverter::get_lunar_intercalary_month(1390),
1332 None,
1333 "Year 1390 is out of range"
1334 );
1335 assert_eq!(
1337 LunarSolarConverter::get_lunar_intercalary_month(2051),
1338 None,
1339 "Year 2051 is out of range"
1340 );
1341 }
1342}