1use crate::KoyomiResult;
7use crate::{Date, Weekday};
8
9pub fn holiday(date: &Date) -> Option<String> {
25 defined_holiday(date)
27 .or(substitute_holiday(date))
29 .or(variable_holiday(1, date, is_second_week))
31 .or(variable_holiday(9, date, is_third_week))
33 .or(variable_holiday(11, date, is_third_week))
35 .or(variable_holiday(12, date, is_second_week))
37 .or(vernal_equinox_day(date))
39 .or(autumnal_equinox_day(date))
41 .or(national_holiday(date))
43 .or(spot_holiday(date))
45}
46
47const AUTUMNAL_EQUINOX_DAYS: [[u32; 4]; 7] = [
50 [23, 24, 24, 24], [23, 23, 24, 24], [23, 23, 23, 24], [23, 23, 23, 23], [22, 23, 23, 23], [22, 22, 23, 23], [22, 22, 22, 23], ];
58
59const HOLIDAYS: [(&str, i32, u32, u32, Option<i32>); 16] = [
62 ("元日", 1948, 1, 1, None),
63 ("成人の日", 1948, 1, 15, Some(1999)),
64 ("建国記念日", 1967, 2, 11, None),
65 ("天皇誕生日", 1948, 4, 29, Some(1988)),
66 ("みどりの日", 1989, 4, 29, Some(2006)),
67 ("昭和の日", 2007, 4, 29, None),
68 ("憲法記念日", 1948, 5, 3, None),
69 ("みどりの日", 2007, 5, 4, None),
70 ("こどもの日", 1948, 5, 5, None),
71 ("海の日", 1996, 7, 20, Some(2002)),
72 ("山の日", 2016, 8, 11, None),
73 ("敬老の日", 1966, 9, 15, Some(2002)),
74 ("体育の日", 1966, 10, 10, Some(1999)),
75 ("文化の日", 1948, 11, 3, None),
76 ("勤労感謝の日", 1948, 11, 23, None),
77 ("天皇誕生日", 1989, 12, 23, Some(2018)),
78];
79
80const SPOT: [(&str, i32, u32, u32); 4] = [
82 ("国民の休日", 2019, 4, 30),
83 ("新天皇即位日", 2019, 5, 1),
84 ("国民の休日", 2019, 5, 2),
85 ("即位礼正殿の儀", 2019, 10, 22),
86];
87
88const VERNAL_EQUINOX_DAYS: [[u32; 4]; 7] = [
91 [21, 21, 21, 22], [21, 21, 21, 21], [20, 21, 21, 21], [20, 20, 21, 21], [20, 20, 20, 21], [20, 20, 20, 20], [19, 20, 20, 20], ];
99
100const HOLIDAY_FROM: i32 = 1948;
102
103const NATION_FROM: i32 = 1986;
105
106const ONE_WEEK: u32 = 7;
108
109const SUBSTITUTE_FROM: i32 = 1973;
111
112fn autumnal_equinox_day(date: &Date) -> Option<String> {
114 if date.year() < HOLIDAY_FROM {
115 return None;
116 }
117
118 if date.month() != 9 {
119 return None;
120 }
121
122 let index = match date.year() {
123 1900..=1919 => Some(0),
124 1920..=1947 => Some(1),
125 1948..=1979 => Some(2),
126 1980..=2011 => Some(3),
127 2012..=2043 => Some(4),
128 2044..=2075 => Some(5),
129 2076..=2099 => Some(6),
130 _ => None,
131 };
132
133 match index {
134 None => None,
135 Some(i) => {
136 let m = date.year() % 4;
137 let d = AUTUMNAL_EQUINOX_DAYS[i as usize][m as usize];
138 if date.day() == d {
139 Some("秋分の日".into())
140 } else {
141 None
142 }
143 }
144 }
145}
146
147fn defined_holiday(date: &Date) -> Option<String> {
149 if date.year() < HOLIDAY_FROM {
150 return None;
151 }
152
153 HOLIDAYS
154 .iter()
155 .filter(|h| match h.4 {
156 Some(until) => h.1 <= date.year() && date.year() <= until,
157 None => h.1 <= date.year(),
158 })
159 .filter(|h| date.month() == h.2 && date.day() == h.3)
160 .nth(0)
161 .map(|h| h.0.into())
162}
163
164fn is_second_week(day: u32) -> bool {
167 (day / ONE_WEEK == 2 && day % ONE_WEEK == 0) || (day / ONE_WEEK == 1 && day % ONE_WEEK > 0)
168}
169
170fn is_third_week(day: u32) -> bool {
173 (day / ONE_WEEK == 3 && day % ONE_WEEK == 0) || (day / ONE_WEEK == 2 && day % ONE_WEEK >= 1)
174}
175
176fn national_holiday(date: &Date) -> Option<String> {
178 if date.year() < NATION_FROM {
179 return None;
180 }
181
182 if date.weekday() == &Weekday::Sunday {
183 return None;
184 }
185
186 if defined_holiday(&date).is_some() {
187 return None;
188 }
189
190 let yesterday = date.yesterday().ok()?;
192 let between = defined_holiday(&yesterday).or(variable_holiday(11, &yesterday, is_third_week));
193 if between.is_none() {
194 return None;
195 }
196
197 let tomorrow = date.tomorrow().ok()?;
199 let between = defined_holiday(&tomorrow).or(autumnal_equinox_day(&tomorrow));
200 if between.is_none() {
201 return None;
202 }
203
204 Some("国民の休日".into())
205}
206
207fn spot_holiday(date: &Date) -> Option<String> {
210 SPOT.iter()
211 .filter(|t| t.1 == date.year() && t.2 == date.month() && t.3 == date.day())
212 .nth(0)
213 .map(|h| h.0.into())
214}
215
216fn substitute(yesterday: KoyomiResult<Date>) -> Option<String> {
221 match yesterday {
222 Err(_) => None,
223 Ok(y) => {
224 let holiday = defined_holiday(&y)
225 .or(vernal_equinox_day(&y))
226 .or(autumnal_equinox_day(&y));
227 match holiday {
228 None => None,
229 Some(_) if y.weekday() == &Weekday::Sunday => Some("振替休日".into()),
230 Some(_) => substitute(y.yesterday()),
231 }
232 }
233 }
234}
235
236fn substitute_holiday(date: &Date) -> Option<String> {
238 if date.year() < SUBSTITUTE_FROM {
239 None
240 } else {
241 substitute(date.yesterday())
242 }
243}
244
245fn variable_holiday(index: usize, date: &Date, week: impl Fn(u32) -> bool) -> Option<String> {
247 if date.month() != HOLIDAYS[index].2 {
248 return None;
249 }
250
251 if date.weekday() != &Weekday::Monday {
252 return None;
253 }
254
255 if date.year() <= HOLIDAYS[index].4.unwrap() {
256 return None;
257 }
258
259 if !week(date.day()) {
260 return None;
261 }
262
263 Some(HOLIDAYS[index].0.into())
264}
265
266fn vernal_equinox_day(date: &Date) -> Option<String> {
268 if date.year() <= HOLIDAY_FROM {
269 return None;
270 }
271
272 if date.month() != 3 {
273 return None;
274 }
275
276 let index = match date.year() {
277 1900..=1923 => Some(0),
278 1924..=1959 => Some(1),
279 1960..=1991 => Some(2),
280 1992..=2023 => Some(3),
281 2024..=2055 => Some(4),
282 2056..=2091 => Some(5),
283 2092..=2099 => Some(6),
284 _ => None,
285 };
286
287 match index {
288 None => None,
289 Some(i) => {
290 let m = date.year() % 4;
291 let d = VERNAL_EQUINOX_DAYS[i as usize][m as usize];
292 if date.day() == d {
293 Some("春分の日".into())
294 } else {
295 None
296 }
297 }
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn new_years_day() {
307 let name = "元日";
308
309 let date = Date::from_ymd(1948, 1, 1).unwrap();
310 assert_eq!(holiday(&date).unwrap(), name);
311
312 let date = Date::from_ymd(1947, 1, 1).unwrap();
313 assert!(holiday(&date).is_none());
314 }
315
316 #[test]
317 fn fixed_coming_of_age() {
318 let name = "成人の日";
319
320 let date = Date::from_ymd(1948, 1, 15).unwrap();
321 assert_eq!(holiday(&date).unwrap(), name);
322
323 let date = Date::from_ymd(1999, 1, 15).unwrap();
324 assert_eq!(holiday(&date).unwrap(), name);
325
326 let date = Date::from_ymd(1947, 1, 15).unwrap();
327 assert!(holiday(&date).is_none());
328
329 let date = Date::from_ymd(2000, 1, 15).unwrap();
330 assert!(holiday(&date).is_none());
331 }
332
333 #[test]
334 fn variable_coming_of_age() {
335 let name = "成人の日";
336
337 let date = Date::from_ymd(2000, 1, 10).unwrap();
338 assert_eq!(holiday(&date).unwrap(), name);
339
340 let date = Date::from_ymd(1999, 1, 11).unwrap();
341 assert!(holiday(&date).is_none());
342
343 let date = Date::from_ymd(2018, 1, 9).unwrap();
344 assert!(holiday(&date).is_none());
345
346 let date = Date::from_ymd(2018, 3, 12).unwrap();
347 assert!(holiday(&date).is_none());
348 }
349
350 #[test]
351 fn national_foundation_day() {
352 let name = "建国記念日";
353
354 let date = Date::from_ymd(1967, 2, 11).unwrap();
355 assert_eq!(holiday(&date).unwrap(), name);
356
357 let date = Date::from_ymd(1966, 2, 11).unwrap();
358 assert!(holiday(&date).is_none());
359 }
360
361 #[test]
362 fn birthday_of_showa_emperor() {
363 let name = "天皇誕生日";
364
365 let date = Date::from_ymd(1947, 4, 29).unwrap();
366 assert!(holiday(&date).is_none());
367
368 let date = Date::from_ymd(1948, 4, 29).unwrap();
369 assert_eq!(holiday(&date).unwrap(), name);
370
371 let date = Date::from_ymd(1988, 4, 29).unwrap();
372 assert_eq!(holiday(&date).unwrap(), name);
373
374 let date = Date::from_ymd(1989, 4, 29).unwrap();
375 assert_ne!(holiday(&date).unwrap(), name);
376 }
377
378 #[test]
379 fn green_day() {
380 let name = "みどりの日";
381
382 let date = Date::from_ymd(1989, 4, 29).unwrap();
383 assert_eq!(holiday(&date).unwrap(), name);
384
385 let date = Date::from_ymd(2006, 4, 29).unwrap();
386 assert_eq!(holiday(&date).unwrap(), name);
387
388 let date = Date::from_ymd(2007, 5, 4).unwrap();
389 assert_eq!(holiday(&date).unwrap(), name);
390
391 let date = Date::from_ymd(2006, 5, 4).unwrap();
392 assert_ne!(holiday(&date).unwrap(), name);
393 }
394
395 #[test]
396 fn showa_day() {
397 let name = "昭和の日";
398
399 let date = Date::from_ymd(2007, 4, 29).unwrap();
400 assert_eq!(holiday(&date).unwrap(), name);
401 }
402
403 #[test]
404 fn constitution_day() {
405 let name = "憲法記念日";
406
407 let date = Date::from_ymd(1948, 5, 3).unwrap();
408 assert_eq!(holiday(&date).unwrap(), name);
409
410 let date = Date::from_ymd(1947, 5, 3).unwrap();
411 assert!(holiday(&date).is_none());
412 }
413
414 #[test]
415 fn childrens_day() {
416 let name = "こどもの日";
417
418 let date = Date::from_ymd(1948, 5, 5).unwrap();
419 assert_eq!(holiday(&date).unwrap(), name);
420
421 let date = Date::from_ymd(1947, 5, 5).unwrap();
422 assert!(holiday(&date).is_none());
423 }
424
425 #[test]
426 fn fixed_marine_day() {
427 let name = "海の日";
428
429 let date = Date::from_ymd(1996, 7, 20).unwrap();
430 assert_eq!(holiday(&date).unwrap(), name);
431
432 let date = Date::from_ymd(2002, 7, 20).unwrap();
433 assert_eq!(holiday(&date).unwrap(), name);
434
435 let date = Date::from_ymd(1995, 7, 20).unwrap();
436 assert!(holiday(&date).is_none());
437
438 let date = Date::from_ymd(2003, 7, 20).unwrap();
439 assert!(holiday(&date).is_none());
440 }
441
442 #[test]
443 fn variable_marine_day() {
444 let name = "海の日";
445
446 let date = Date::from_ymd(2003, 7, 21).unwrap();
447 assert_eq!(holiday(&date).unwrap(), name);
448
449 let date = Date::from_ymd(2002, 7, 15).unwrap();
450 assert!(holiday(&date).is_none());
451
452 let date = Date::from_ymd(2018, 7, 17).unwrap();
453 assert!(holiday(&date).is_none());
454
455 let date = Date::from_ymd(2018, 8, 20).unwrap();
456 assert!(holiday(&date).is_none());
457 }
458
459 #[test]
460 fn mountain_day() {
461 let name = "山の日";
462
463 let date = Date::from_ymd(2016, 8, 11).unwrap();
464 assert_eq!(holiday(&date).unwrap(), name);
465
466 let date = Date::from_ymd(2015, 8, 11).unwrap();
467 assert!(holiday(&date).is_none());
468 }
469
470 #[test]
471 fn fixed_respect_for_the_aged_day() {
472 let name = "敬老の日";
473
474 let date = Date::from_ymd(1966, 9, 15).unwrap();
475 assert_eq!(holiday(&date).unwrap(), name);
476
477 let date = Date::from_ymd(2002, 9, 15).unwrap();
478 assert_eq!(holiday(&date).unwrap(), name);
479
480 let date = Date::from_ymd(1965, 9, 15).unwrap();
481 assert!(holiday(&date).is_none());
482
483 let date = Date::from_ymd(2004, 9, 15).unwrap();
484 assert!(holiday(&date).is_none());
485 }
486
487 #[test]
488 fn variable_respect_for_the_aged_day() {
489 let name = "敬老の日";
490
491 let date = Date::from_ymd(2003, 9, 15).unwrap();
492 assert_eq!(holiday(&date).unwrap(), name);
493
494 let date = Date::from_ymd(2002, 9, 16).unwrap();
495 assert_ne!(holiday(&date).unwrap(), name);
496
497 let date = Date::from_ymd(2018, 9, 18).unwrap();
498 assert!(holiday(&date).is_none());
499
500 let date = Date::from_ymd(2018, 6, 18).unwrap();
501 assert!(holiday(&date).is_none());
502 }
503
504 #[test]
505 fn fixed_sports_day() {
506 let name = "体育の日";
507
508 let date = Date::from_ymd(1966, 10, 10).unwrap();
509 assert_eq!(holiday(&date).unwrap(), name);
510
511 let date = Date::from_ymd(1999, 10, 10).unwrap();
512 assert_eq!(holiday(&date).unwrap(), name);
513
514 let date = Date::from_ymd(1965, 10, 10).unwrap();
515 assert!(holiday(&date).is_none());
516
517 let date = Date::from_ymd(2000, 10, 10).unwrap();
518 assert!(holiday(&date).is_none());
519 }
520
521 #[test]
522 fn variable_sports_day() {
523 let name = "体育の日";
524
525 let date = Date::from_ymd(2000, 10, 9).unwrap();
526 assert_eq!(holiday(&date).unwrap(), name);
527
528 let date = Date::from_ymd(1999, 10, 11).unwrap();
529 assert_ne!(holiday(&date).unwrap(), name);
530
531 let date = Date::from_ymd(2018, 10, 9).unwrap();
532 assert!(holiday(&date).is_none());
533
534 let date = Date::from_ymd(2018, 11, 12).unwrap();
535 assert!(holiday(&date).is_none());
536 }
537
538 #[test]
539 fn culture_day() {
540 let name = "文化の日";
541
542 let date = Date::from_ymd(1948, 11, 3).unwrap();
543 assert_eq!(holiday(&date).unwrap(), name);
544
545 let date = Date::from_ymd(1947, 11, 3).unwrap();
546 assert!(holiday(&date).is_none());
547 }
548
549 #[test]
550 fn labor_thanksgiving_day() {
551 let name = "勤労感謝の日";
552
553 let date = Date::from_ymd(1948, 11, 23).unwrap();
554 assert_eq!(holiday(&date).unwrap(), name);
555
556 let date = Date::from_ymd(1947, 11, 23).unwrap();
557 assert!(holiday(&date).is_none());
558 }
559
560 #[test]
561 fn birthday_of_heisei_emperor() {
562 let name = "天皇誕生日";
563
564 let date = Date::from_ymd(1989, 12, 23).unwrap();
565 assert_eq!(holiday(&date).unwrap(), name);
566
567 let date = Date::from_ymd(1988, 12, 23).unwrap();
568 assert!(holiday(&date).is_none());
569
570 let date = Date::from_ymd(2018, 12, 23).unwrap();
571 assert_eq!(holiday(&date).unwrap(), name);
572
573 let date = Date::from_ymd(2019, 12, 23).unwrap();
574 assert!(holiday(&date).is_none());
575 }
576
577 #[test]
578 fn substitute_holiday() {
579 let name = "振替休日";
580
581 let date = Date::from_ymd(2018, 4, 30).unwrap();
582 assert_eq!(holiday(&date).unwrap(), name);
583
584 let date = Date::from_ymd(2018, 4, 23).unwrap();
585 assert!(holiday(&date).is_none());
586 }
587
588 #[test]
589 fn vernal_equinox_day() {
590 let name = "春分の日";
591
592 let date = Date::from_ymd(2018, 3, 21).unwrap();
593 assert_eq!(holiday(&date).unwrap(), name);
594
595 let date = Date::from_ymd(2018, 2, 21).unwrap();
596 assert!(holiday(&date).is_none());
597
598 let date = Date::from_ymd(1899, 3, 20).unwrap();
599 assert!(holiday(&date).is_none());
600
601 let date = Date::from_ymd(2300, 3, 20).unwrap();
602 assert!(holiday(&date).is_none());
603 }
604
605 #[test]
606 fn autumnal_equinox_day() {
607 let name = "秋分の日";
608
609 let date = Date::from_ymd(2018, 9, 23).unwrap();
610 assert_eq!(holiday(&date).unwrap(), name);
611
612 let date = Date::from_ymd(2018, 8, 23).unwrap();
613 assert!(holiday(&date).is_none());
614
615 let date = Date::from_ymd(1899, 9, 23).unwrap();
616 assert!(holiday(&date).is_none());
617
618 let date = Date::from_ymd(2300, 9, 23).unwrap();
619 assert!(holiday(&date).is_none());
620 }
621
622 #[test]
623 fn national_holiday() {
624 let name = "国民の休日";
625
626 let date = Date::from_ymd(1988, 5, 4).unwrap();
627 assert_eq!(holiday(&date).unwrap(), name);
628
629 let date = Date::from_ymd(1986, 5, 4).unwrap();
630 assert!(holiday(&date).is_none());
631
632 let date = Date::from_ymd(1987, 5, 4).unwrap();
633 assert_ne!(holiday(&date).unwrap(), name);
634 }
635
636 #[test]
637 fn special_holiday() {
638 let name = "新天皇即位日";
639
640 let date = Date::from_ymd(2019, 5, 1).unwrap();
641 assert_eq!(holiday(&date).unwrap(), name);
642
643 let date = Date::from_ymd(2018, 5, 1).unwrap();
644 assert!(holiday(&date).is_none());
645 }
646}