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.lunar_year = lunar_year;
412 self.lunar_month = lunar_month;
413 self.lunar_day = lunar_day;
414 self.is_intercalation = is_intercalation
415 && (Self::get_lunar_intercalation_month(Self::get_lunar_data(lunar_year))
416 == lunar_month);
417 self.set_solar_date_by_lunar_date(
418 lunar_year,
419 lunar_month,
420 lunar_day,
421 self.is_intercalation,
422 );
423
424 is_valid = true;
425 }
426
427 is_valid
428 }
429
430 pub fn set_solar_date(&mut self, solar_year: u32, solar_month: u32, solar_day: u32) -> bool {
442 let mut is_valid = false;
443
444 if Self::check_valid_date(false, false, solar_year, solar_month, solar_day) {
445 self.solar_year = solar_year;
446 self.solar_month = solar_month;
447 self.solar_day = solar_day;
448 self.set_lunar_date_by_solar_date(solar_year, solar_month, solar_day);
449 is_valid = true;
450 }
451
452 is_valid
453 }
454
455 fn get_gapja(&mut self) {
456 let abs_days = Self::get_lunar_abs_days(
457 self.lunar_year,
458 self.lunar_month,
459 self.lunar_day,
460 self.is_intercalation,
461 );
462
463 if abs_days > 0 {
464 self.gapja_year_inx[0] = Some(
465 ((self.lunar_year + 7) - KOREAN_LUNAR_BASE_YEAR) as usize % KOREAN_CHEONGAN.len(),
466 );
467 self.gapja_year_inx[1] = Some(
468 ((self.lunar_year + 7) - KOREAN_LUNAR_BASE_YEAR) as usize % KOREAN_GANJI.len(),
469 );
470
471 let mut month_count = self.lunar_month;
472 month_count += 12 * ((self.lunar_year - KOREAN_LUNAR_BASE_YEAR) as u32);
473 self.gapja_month_inx[0] = Some((month_count + 5) as usize % KOREAN_CHEONGAN.len());
474 self.gapja_month_inx[1] = Some((month_count + 1) as usize % KOREAN_GANJI.len());
475
476 self.gapja_day_inx[0] = Some((abs_days + 4) as usize % KOREAN_CHEONGAN.len());
477 self.gapja_day_inx[1] = Some(abs_days as usize % KOREAN_GANJI.len());
478 } else {
479 self.gapja_year_inx = [None, None, None];
480 self.gapja_month_inx = [None, None, None];
481 self.gapja_day_inx = [None, None, None];
482 }
483 }
484
485 pub fn get_gapja_string(&mut self) -> String {
490 self.get_gapja();
491
492 let mut gapja_string = String::new();
493
494 if let (Some(year_cheongan), Some(year_ganji)) =
495 (self.gapja_year_inx[0], self.gapja_year_inx[1])
496 {
497 gapja_string.push(KOREAN_CHEONGAN[year_cheongan]);
498 gapja_string.push(KOREAN_GANJI[year_ganji]);
499 gapja_string.push(KOREAN_GAPJA_UNIT[0]);
500 } else {
501 return "".to_string();
502 }
503
504 gapja_string.push(' ');
505
506 if let (Some(month_cheongan), Some(month_ganji)) =
507 (self.gapja_month_inx[0], self.gapja_month_inx[1])
508 {
509 gapja_string.push(KOREAN_CHEONGAN[month_cheongan]);
510 gapja_string.push(KOREAN_GANJI[month_ganji]);
511 gapja_string.push(KOREAN_GAPJA_UNIT[1]);
512 } else {
513 return "".to_string();
514 }
515
516 gapja_string.push(' ');
517
518 if let (Some(day_cheongan), Some(day_ganji)) =
519 (self.gapja_day_inx[0], self.gapja_day_inx[1])
520 {
521 gapja_string.push(KOREAN_CHEONGAN[day_cheongan]);
522 gapja_string.push(KOREAN_GANJI[day_ganji]);
523 gapja_string.push(KOREAN_GAPJA_UNIT[2]);
524 } else {
525 return "".to_string();
526 }
527
528 if self.is_intercalation {
529 gapja_string.push_str(" (");
530 gapja_string.push(INTERCALATION_STR[0]);
531 gapja_string.push(KOREAN_GAPJA_UNIT[1]);
532 gapja_string.push(')');
533 }
534
535 gapja_string
536 }
537
538 pub fn get_chinese_gapja_string(&mut self) -> String {
543 self.get_gapja();
544
545 let mut gapja_string = String::new();
546
547 if let (Some(year_cheongan), Some(year_ganji)) =
548 (self.gapja_year_inx[0], self.gapja_year_inx[1])
549 {
550 gapja_string.push(CHINESE_CHEONGAN[year_cheongan]);
551 gapja_string.push(CHINESE_GANJI[year_ganji]);
552 gapja_string.push(CHINESE_GAPJA_UNIT[0]);
553 } else {
554 return "".to_string();
555 }
556
557 gapja_string.push(' ');
558
559 if let (Some(month_cheongan), Some(month_ganji)) =
560 (self.gapja_month_inx[0], self.gapja_month_inx[1])
561 {
562 gapja_string.push(CHINESE_CHEONGAN[month_cheongan]);
563 gapja_string.push(CHINESE_GANJI[month_ganji]);
564 gapja_string.push(CHINESE_GAPJA_UNIT[1]);
565 } else {
566 return "".to_string();
567 }
568
569 gapja_string.push(' ');
570
571 if let (Some(day_cheongan), Some(day_ganji)) =
572 (self.gapja_day_inx[0], self.gapja_day_inx[1])
573 {
574 gapja_string.push(CHINESE_CHEONGAN[day_cheongan]);
575 gapja_string.push(CHINESE_GANJI[day_ganji]);
576 gapja_string.push(CHINESE_GAPJA_UNIT[2]);
577 } else {
578 return "".to_string();
579 }
580
581 if self.is_intercalation {
582 gapja_string.push_str(" (");
583 gapja_string.push(INTERCALATION_STR[1]);
584 gapja_string.push(CHINESE_GAPJA_UNIT[1]);
585 gapja_string.push(')');
586 }
587
588 gapja_string
589 }
590
591 pub fn get_lunar_iso_format(&self) -> String {
594 let mut iso_str = format!(
595 "{:04}-{:02}-{:02}",
596 self.lunar_year, self.lunar_month, self.lunar_day
597 );
598
599 if self.is_intercalation {
600 iso_str.push_str(" Intercalation");
601 }
602
603 iso_str
604 }
605
606 pub fn get_solar_iso_format(&self) -> String {
608 format!(
609 "{:04}-{:02}-{:02}",
610 self.solar_year, self.solar_month, self.solar_day
611 )
612 }
613
614 pub fn get_julian_day_number(year: u32, month: u32, day: u32) -> Option<u32> {
638 if year == 1582 && month == 10 && day > 4 && day < 15 {
640 return None;
641 }
642 if month == 0 || month > 12 || day == 0 || day > 31 {
644 return None;
645 }
646
647 let y = year as i32;
649 let m = month as i32;
650 let d = day as i32;
651
652 let adj_y = if m <= 2 { y - 1 } else { y };
654 let adj_m = if m <= 2 { m + 12 } else { m };
655
656 let julian_base = (1461 * (adj_y + 4716)) / 4 + (153 * (adj_m + 1)) / 5 + d;
658
659 let b = if y > 1582 || (y == 1582 && m > 10) || (y == 1582 && m == 10 && d >= 15) {
661 let term1 = adj_y / 100; 2 - term1 + term1 / 4
664 } else {
665 0
667 };
668
669 let jdn = julian_base + b - 1524;
671
672 Some(jdn as u32)
673 }
674
675 pub fn get_day_of_week(year: u32, month: u32, day: u32) -> Option<DayOfWeek> {
696 Self::get_julian_day_number(year, month, day).map(|jdn| {
697 match jdn % 7 {
699 0 => DayOfWeek::Monday,
700 1 => DayOfWeek::Tuesday,
701 2 => DayOfWeek::Wednesday,
702 3 => DayOfWeek::Thursday,
703 4 => DayOfWeek::Friday,
704 5 => DayOfWeek::Saturday,
705 _ => DayOfWeek::Sunday, }
707 })
708 }
709
710 pub fn is_solar_leap_year(year: u32) -> bool {
731 Self::is_gregorian_leap(year as i32)
733 }
734
735 pub fn get_lunar_intercalary_month(year: i32) -> Option<u32> {
754 if year < KOREAN_LUNAR_BASE_YEAR
755 || year > KOREAN_LUNAR_BASE_YEAR + KOREAN_LUNAR_DATA.len() as i32 - 1
756 {
757 return None; }
759 let lunar_data = Self::get_lunar_data(year);
760 let intercalary_month = Self::get_lunar_intercalation_month(lunar_data);
761 if intercalary_month > 0 {
762 Some(intercalary_month)
763 } else {
764 None
765 }
766 }
767
768 #[allow(dead_code)]
771 pub fn solar_year(&self) -> u32 {
772 self.solar_year
773 }
774 #[allow(dead_code)]
776 pub fn solar_month(&self) -> u32 {
777 self.solar_month
778 }
779 #[allow(dead_code)]
781 pub fn solar_day(&self) -> u32 {
782 self.solar_day
783 }
784 pub fn lunar_year(&self) -> i32 {
786 self.lunar_year
787 }
788 #[allow(dead_code)]
790 pub fn lunar_month(&self) -> u32 {
791 self.lunar_month
792 }
793 #[allow(dead_code)]
795 pub fn lunar_day(&self) -> u32 {
796 self.lunar_day
797 }
798 #[allow(dead_code)]
800 pub fn is_intercalation(&self) -> bool {
801 self.is_intercalation
802 }
803 fn set_solar_date_by_lunar_date(
808 &mut self,
809 lunar_year: i32,
810 lunar_month: u32,
811 lunar_day: u32,
812 is_intercalation: bool,
813 ) {
814 let abs_days =
815 Self::get_lunar_abs_days(lunar_year, lunar_month, lunar_day, is_intercalation);
816
817 if abs_days < Self::get_solar_abs_days(lunar_year + 1, 1, 1) {
818 self.solar_year = lunar_year as u32;
819 } else {
820 self.solar_year = (lunar_year + 1) as u32;
821 }
822
823 for month in (1..=12).rev() {
824 let abs_days_by_month = Self::get_solar_abs_days(self.solar_year as i32, month, 1);
825
826 if abs_days >= abs_days_by_month {
827 self.solar_month = month;
828 self.solar_day = abs_days - abs_days_by_month + 1;
829 break;
830 }
831 }
832
833 if self.solar_year == 1582 && self.solar_month == 10 && self.solar_day > 4 {
834 self.solar_day += 10;
835 }
836 }
837
838 fn set_lunar_date_by_solar_date(&mut self, solar_year: u32, solar_month: u32, solar_day: u32) {
839 let abs_days = Self::get_solar_abs_days(solar_year as i32, solar_month, solar_day);
840
841 self.is_intercalation = false;
842
843 if abs_days >= Self::get_lunar_abs_days(solar_year as i32, 1, 1, false) {
844 self.lunar_year = solar_year as i32;
845 } else {
846 self.lunar_year = solar_year as i32 - 1;
847 }
848
849 for month in (1..=12).rev() {
850 let abs_days_by_month = Self::get_lunar_abs_days(self.lunar_year, month, 1, false);
851
852 if abs_days >= abs_days_by_month {
853 self.lunar_month = month;
854
855 if Self::get_lunar_intercalation_month(Self::get_lunar_data(self.lunar_year))
856 == month
857 {
858 self.is_intercalation =
859 abs_days >= Self::get_lunar_abs_days(self.lunar_year, month, 1, true);
860 }
861
862 self.lunar_day = abs_days
863 - Self::get_lunar_abs_days(
864 self.lunar_year,
865 self.lunar_month,
866 1,
867 self.is_intercalation,
868 )
869 + 1;
870
871 break;
872 }
873 }
874 }
875
876 fn is_valid_min(is_lunar: bool, date_value: u32) -> bool {
877 if is_lunar {
878 KOREAN_LUNAR_MIN_VALUE <= date_value
879 } else {
880 KOREAN_SOLAR_MIN_VALUE <= date_value
881 }
882 }
883
884 fn is_valid_max(is_lunar: bool, date_value: u32) -> bool {
885 if is_lunar {
886 KOREAN_LUNAR_MAX_VALUE >= date_value
887 } else {
888 KOREAN_SOLAR_MAX_VALUE >= date_value
889 }
890 }
891
892 fn check_valid_date(
893 is_lunar: bool,
894 is_intercalation: bool,
895 year: u32,
896 month: u32,
897 day: u32,
898 ) -> bool {
899 let mut is_valid = false;
900 let date_value = year * 10000 + month * 100 + day;
901
902 if Self::is_valid_min(is_lunar, date_value) && Self::is_valid_max(is_lunar, date_value) {
904 let mut day_limit;
905
906 if month > 0 && month < 13 && day > 0 {
907 if is_lunar {
908 if is_intercalation
909 && Self::get_lunar_intercalation_month(Self::get_lunar_data(year as i32))
910 != month
911 {
912 return false;
913 }
914 day_limit = Self::get_lunar_days(year as i32, month, is_intercalation);
915 } else {
916 day_limit = Self::get_solar_days(year as i32, month);
917 }
918
919 if !is_lunar && year == 1582 && month == 10 {
920 if day > 4 && day < 15 {
921 return false;
922 } else {
923 day_limit += 10;
924 }
925 }
926
927 if day <= day_limit {
928 is_valid = true;
929 }
930 }
931 }
932
933 is_valid
934 }
935}
936
937#[cfg(test)]
938mod tests {
939 use super::DayOfWeek;
940 use crate::LunarSolarConverter;
941
942 #[test]
943 fn test_lunar_iso_format() {
944 let mut converter = LunarSolarConverter::new();
945 converter.set_solar_date(2022, 7, 10);
946 let lunar = converter.get_lunar_iso_format();
947 let want = "2022-06-12";
948
949 println!("{}", lunar);
950
951 assert_eq!(lunar, want, "got {:?} want {:?}", lunar, want);
952 }
953
954 #[test]
955 fn test_gapja_string() {
956 let mut converter = LunarSolarConverter::new();
957 converter.set_solar_date(2022, 7, 10);
958 let lunar_gapja = converter.get_gapja_string();
959 let want = "임인년 정미월 갑자일";
960
961 println!("{}", lunar_gapja);
962
963 assert_eq!(lunar_gapja, want, "got {:?} want {:?}", lunar_gapja, want);
964 }
965
966 #[test]
967 fn test_chinese_gapja_string() {
968 let mut converter = LunarSolarConverter::new();
969 converter.set_solar_date(2022, 7, 10);
970 let lunar_chinese_gapja = converter.get_chinese_gapja_string();
971 let want = "壬寅年 丁未月 甲子日";
972
973 println!("{}", lunar_chinese_gapja);
974
975 assert_eq!(
976 lunar_chinese_gapja, want,
977 "got {:?} want {:?}",
978 lunar_chinese_gapja, want
979 );
980 }
981
982 #[test]
983 fn test_solar_iso_format() {
984 let mut converter = LunarSolarConverter::new();
985 converter.set_lunar_date(2022, 6, 12, false);
986 let solar = converter.get_solar_iso_format();
987 let want = "2022-07-10";
988
989 println!("{}", solar);
990
991 assert_eq!(solar, want, "got {:?} want {:?}", solar, want);
992 }
993
994 #[test]
995 fn test_gapja_string_intercalation() {
996 let mut converter = LunarSolarConverter::new();
997 let is_valid = converter.set_lunar_date(2022, 6, 12, true);
998
999 assert!(
1000 !is_valid,
1001 "Expected set_lunar_date to return false for non-existent intercalary month"
1002 );
1003 }
1004
1005 #[test]
1006 fn test_chinese_gapja_string_intercalation() {
1007 let mut converter = LunarSolarConverter::new();
1008 let is_valid = converter.set_lunar_date(2022, 6, 12, true);
1009
1010 assert!(
1011 !is_valid,
1012 "Expected set_lunar_date to return false for non-existent intercalary month"
1013 );
1014 }
1015
1016 #[test]
1017 fn test_set_solar_date() {
1018 let mut converter = LunarSolarConverter::new();
1019 let is_valid = converter.set_solar_date(2022, 7, 10);
1020
1021 assert!(is_valid, "Expected solar date to be valid");
1022 assert_eq!(converter.lunar_year, 2022);
1023 assert_eq!(converter.lunar_month, 6);
1024 assert_eq!(converter.lunar_day, 12);
1025 }
1026
1027 #[test]
1028 fn test_set_lunar_date() {
1029 let mut converter = LunarSolarConverter::new();
1030 let is_valid = converter.set_lunar_date(2022, 6, 12, false);
1031
1032 assert!(is_valid, "Expected lunar date to be valid");
1033 assert_eq!(converter.solar_year, 2022);
1034 assert_eq!(converter.solar_month, 7);
1035 assert_eq!(converter.solar_day, 10);
1036 }
1037
1038 #[test]
1039 fn test_invalid_solar_date() {
1040 let mut converter = LunarSolarConverter::new();
1041 let is_valid = converter.set_solar_date(1582, 10, 10);
1042
1043 assert!(!is_valid, "Expected solar date to be invalid");
1044 }
1045
1046 #[test]
1047 fn test_invalid_lunar_date() {
1048 let mut converter = LunarSolarConverter::new();
1049 let is_valid = converter.set_lunar_date(1390, 12, 31, false);
1050
1051 assert!(!is_valid, "Expected lunar date to be invalid");
1052 }
1053
1054 #[test]
1055 fn test_get_lunar_days() {
1056 let days = LunarSolarConverter::get_lunar_days(2022, 6, false);
1057
1058 assert_eq!(days, 30, "Expected 30 days for June 2022");
1059 }
1060
1061 #[test]
1062 fn test_get_lunar_days_invalid_month() {
1063 let days = LunarSolarConverter::get_lunar_days(2022, 13, false);
1064
1065 assert_eq!(days, 0, "Expected 0 days for invalid month 2022");
1066 }
1067
1068 #[test]
1069 fn test_get_lunar_days_invalid_year() {
1070 let days = LunarSolarConverter::get_lunar_days(1390, 6, false);
1071 assert_eq!(days, 0, "Expected 0 days for invalid year 1390");
1072 }
1073
1074 #[test]
1075 fn test_get_solar_days() {
1076 let days = LunarSolarConverter::get_solar_days(2022, 2);
1077
1078 assert_eq!(days, 28, "Expected 28 days for February 2022");
1079 }
1080
1081 #[test]
1082 fn test_get_solar_days_invalid_month() {
1083 let days = LunarSolarConverter::get_solar_days(2022, 13);
1084
1085 assert_eq!(days, 0, "Expected 0 days for invalid month 2022");
1086 }
1087
1088 #[test]
1089 fn test_get_lunar_abs_days() {
1090 let days = LunarSolarConverter::get_lunar_abs_days(2022, 6, 12, false);
1091
1092 assert_eq!(days, 230616, "Expected 230616 absolute lunar days");
1093 }
1094
1095 #[test]
1096 fn test_get_lunar_abs_days_invalid_year() {
1097 let days = LunarSolarConverter::get_lunar_abs_days(1390, 6, 12, false);
1098
1099 assert_eq!(
1100 days, 0,
1101 "Expected 0 absolute lunar days for invalid year 1390"
1102 );
1103 }
1104
1105 #[test]
1106 fn test_get_solar_abs_days() {
1107 let days = LunarSolarConverter::get_solar_abs_days(2022, 7, 10);
1108
1109 assert_eq!(days, 230616, "Expected 230616 absolute solar days");
1111 }
1112
1113 #[test]
1114 fn test_get_solar_abs_days_invalid_year() {
1115 let days = LunarSolarConverter::get_solar_abs_days(1390, 7, 10);
1116
1117 assert_eq!(
1118 days, 0,
1119 "Expected 0 absolute solar days for invalid year 1390"
1120 );
1121 }
1122
1123 #[test]
1124 fn test_invalid_date_for_get_gapja_string() {
1125 let mut converter = LunarSolarConverter::new();
1126 converter.set_lunar_date(1390, 12, 31, false);
1127 let gapja = converter.get_gapja_string();
1128
1129 assert_eq!(gapja, "", "Expected empty string since the date is invalid");
1130 }
1131
1132 #[test]
1133 fn test_invalid_date_for_get_chinese_gapja_string() {
1134 let mut converter = LunarSolarConverter::new();
1135 converter.set_lunar_date(1390, 12, 31, false);
1136 let gapja = converter.get_chinese_gapja_string();
1137
1138 assert_eq!(gapja, "", "Expected empty string since the date is invalid");
1139 }
1140
1141 #[test]
1142 fn test_get_julian_day_number_gregorian() {
1143 let jdn = LunarSolarConverter::get_julian_day_number(2022, 7, 10);
1144 let want = Some(2459771);
1145 assert_eq!(
1146 jdn, want,
1147 "Expected JDN {:?} for 2022-07-10, got {:?}",
1148 want, jdn
1149 );
1150 }
1151
1152 #[test]
1153 fn test_get_julian_day_number_julian() {
1154 let jdn = LunarSolarConverter::get_julian_day_number(1500, 3, 1);
1155 let want = Some(2268993);
1156 assert_eq!(
1157 jdn, want,
1158 "Expected JDN {:?} for 1500-03-01, got {:?}",
1159 want, jdn
1160 );
1161 }
1162
1163 #[test]
1164 fn test_get_julian_day_number_reform_before() {
1165 let jdn = LunarSolarConverter::get_julian_day_number(1582, 10, 4);
1166 let want = Some(2299160);
1167 assert_eq!(
1168 jdn, want,
1169 "Expected JDN {:?} for 1582-10-04, got {:?}",
1170 want, jdn
1171 );
1172 }
1173
1174 #[test]
1175 fn test_get_julian_day_number_reform_after() {
1176 let jdn = LunarSolarConverter::get_julian_day_number(1582, 10, 15);
1177 let want = Some(2299161);
1178 assert_eq!(
1179 jdn, want,
1180 "Expected JDN {:?} for 1582-10-15, got {:?}",
1181 want, jdn
1182 );
1183 }
1184
1185 #[test]
1186 fn test_get_julian_day_number_min_date() {
1187 let jdn = LunarSolarConverter::get_julian_day_number(1391, 2, 5);
1189 let want = Some(2229156);
1190 assert_eq!(
1191 jdn, want,
1192 "Expected JDN {:?} for 1391-02-05, got {:?}",
1193 want, jdn
1194 );
1195 }
1196
1197 #[test]
1198 fn test_get_julian_day_number_invalid_gap() {
1199 let jdn = LunarSolarConverter::get_julian_day_number(1582, 10, 10);
1200 assert_eq!(
1201 jdn, None,
1202 "Expected None for invalid date 1582-10-10 (Gregorian gap)"
1203 );
1204 }
1205
1206 #[test]
1207 fn test_get_julian_day_number_invalid_range_before() {
1208 let jdn = LunarSolarConverter::get_julian_day_number(1391, 2, 4);
1210 let want = Some(2229155);
1211 assert_eq!(
1212 jdn, want,
1213 "Expected JDN {:?} for 1391-02-04, got {:?}",
1214 want, jdn
1215 );
1216 }
1217
1218 #[test]
1219 fn test_get_julian_day_number_invalid_range_after() {
1220 let jdn = LunarSolarConverter::get_julian_day_number(2051, 1, 1);
1223 let want = Some(2470173);
1224 assert_eq!(
1225 jdn, want,
1226 "Expected JDN {:?} for 2051-01-01, got {:?}",
1227 want, jdn
1228 );
1229 }
1230
1231 #[test]
1232 fn test_get_day_of_week_monday() {
1233 let dow = LunarSolarConverter::get_day_of_week(2022, 7, 11);
1235 let want = Some(DayOfWeek::Monday);
1236 assert_eq!(
1237 dow, want,
1238 "Expected {:?} for 2022-07-11, got {:?}",
1239 want, dow
1240 );
1241 }
1242
1243 #[test]
1244 fn test_get_day_of_week_sunday() {
1245 let dow = LunarSolarConverter::get_day_of_week(2022, 7, 10);
1247 let want = Some(DayOfWeek::Sunday);
1248 assert_eq!(
1249 dow, want,
1250 "Expected {:?} for 2022-07-10, got {:?}",
1251 want, dow
1252 );
1253 }
1254
1255 #[test]
1256 fn test_get_day_of_week_reform_before() {
1257 let result = LunarSolarConverter::get_day_of_week(1582, 10, 4);
1259 assert_eq!(result, Some(DayOfWeek::Thursday)); }
1261
1262 #[test]
1263 fn test_get_day_of_week_reform_after() {
1264 let result = LunarSolarConverter::get_day_of_week(1582, 10, 15);
1266 assert_eq!(result, Some(DayOfWeek::Friday)); }
1268
1269 #[test]
1270 fn test_get_day_of_week_invalid_gap() {
1271 let dow = LunarSolarConverter::get_day_of_week(1582, 10, 10);
1272 assert_eq!(dow, None, "Expected None for invalid date 1582-10-10");
1273 }
1274
1275 #[test]
1276 fn test_is_solar_leap_year() {
1277 assert!(
1278 LunarSolarConverter::is_solar_leap_year(2024),
1279 "2024 should be a leap year"
1280 );
1281 assert!(
1282 !LunarSolarConverter::is_solar_leap_year(2023),
1283 "2023 should not be a leap year"
1284 );
1285 assert!(
1286 !LunarSolarConverter::is_solar_leap_year(1900),
1287 "1900 should not be a leap year"
1288 );
1289 assert!(
1290 LunarSolarConverter::is_solar_leap_year(2000),
1291 "2000 should be a leap year"
1292 );
1293 assert!(
1294 LunarSolarConverter::is_solar_leap_year(1500),
1295 "1500 should be a leap year (Julian)"
1296 );
1297 assert!(
1298 !LunarSolarConverter::is_solar_leap_year(1582),
1299 "1582 should not be a leap year"
1300 );
1301 assert!(
1302 LunarSolarConverter::is_solar_leap_year(1600),
1303 "1600 should be a leap year"
1304 );
1305 }
1306
1307 #[test]
1308 fn test_get_lunar_intercalary_month() {
1309 assert_eq!(
1311 LunarSolarConverter::get_lunar_intercalary_month(2023),
1312 Some(2),
1313 "Year 2023 should have intercalary month 2"
1314 );
1315 assert_eq!(
1317 LunarSolarConverter::get_lunar_intercalary_month(2020),
1318 Some(4),
1319 "Year 2020 should have intercalary month 4"
1320 );
1321 assert_eq!(
1323 LunarSolarConverter::get_lunar_intercalary_month(2022),
1324 None,
1325 "Year 2022 should not have an intercalary month"
1326 );
1327 assert_eq!(
1328 LunarSolarConverter::get_lunar_intercalary_month(1391),
1329 None,
1330 "Year 1391 should not have an intercalary month"
1331 ); }
1333
1334 #[test]
1335 fn test_get_lunar_intercalary_month_out_of_range() {
1336 assert_eq!(
1337 LunarSolarConverter::get_lunar_intercalary_month(1390),
1338 None,
1339 "Year 1390 is out of range"
1340 );
1341 assert_eq!(
1343 LunarSolarConverter::get_lunar_intercalary_month(2051),
1344 None,
1345 "Year 2051 is out of range"
1346 );
1347 }
1348}