use std::f64::consts::PI;
pub use dow::DayOfWeek;
use std::convert::TryInto;
use std::fmt::{Display, Error, Formatter};
pub mod dow;
pub const TZ: f64 = 7.0;
pub trait Calendar {
fn to_julian_days(&self) -> i32;
fn from_julian_days(_: i32) -> Self;
fn to_gregorian(&self) -> GregorianDay {
GregorianDay::from_julian_days(self.to_julian_days())
}
fn to_lunar(&self) -> LunarDay {
LunarDay::from_julian_days(self.to_julian_days())
}
fn day_of_week(&self) -> DayOfWeek {
(((self.to_julian_days() + 1) % 7) as u8)
.try_into()
.unwrap()
}
}
#[derive(Debug, Clone)]
pub struct Day {
pub day: i32,
pub month: i32,
pub year: i32,
}
#[derive(Debug, Clone)]
pub struct GregorianDay {
pub inner: Day,
}
#[derive(Debug)]
pub struct LunarDay {
pub inner: Day,
pub leap: bool,
}
impl Display for LunarDay {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str(
format!(
"{:02}/{:02}/{:04} AL",
self.inner.day, self.inner.month, self.inner.year
)
.as_str(),
)
}
}
#[derive(Debug)]
pub struct GregorianMonth {
year: i32,
month: i32,
}
#[derive(Debug)]
pub struct GregorianDayRange {
begin: GregorianDay,
end: GregorianDay,
}
impl GregorianDayRange {
pub fn iter(&self) -> GregorianIter {
GregorianIter {
inner: self.begin.clone(),
left: self.end.to_julian_days() - self.begin.to_julian_days() + 1,
}
}
pub fn to_tuple(&self) -> (GregorianDay, GregorianDay) {
(self.begin.clone(), self.end.clone())
}
}
pub struct GregorianIter {
inner: GregorianDay,
left: i32,
}
impl<'a> Iterator for GregorianIter {
type Item = GregorianDay;
fn next(&mut self) -> Option<Self::Item> {
if self.left <= 0 {
return None;
}
let ret = Some(self.inner.clone());
self.left -= 1;
self.inner = GregorianDay::from_julian_days(self.inner.to_julian_days() + 1);
ret
}
}
impl GregorianMonth {
pub fn new(year: i32, month: i32) -> GregorianMonth {
Self { month, year }
}
pub fn get_bound(&self) -> GregorianDayRange {
let mut last_day_of_month = 31;
while GregorianDay::from_julian_days(
GregorianDay::new(last_day_of_month, self.month, self.year).to_julian_days(),
)
.inner
.day != last_day_of_month
{
last_day_of_month -= 1;
}
GregorianDayRange {
begin: GregorianDay {
inner: Day {
month: self.month,
year: self.year,
day: 1,
},
},
end: GregorianDay {
inner: Day {
month: self.month,
year: self.year,
day: last_day_of_month,
},
},
}
}
pub fn previous(&self) -> GregorianMonth {
let mut year = self.year;
let mut month = self.month - 1;
if month <= 0 {
month += 12;
year -= 1;
}
GregorianMonth { month, year }
}
pub fn next(&self) -> GregorianMonth {
let mut year = self.year;
let mut month = self.month + 1;
if month > 12 {
month -= 12;
year += 1;
}
GregorianMonth { month, year }
}
pub fn to_title(&self) -> String {
format!("Am lich {:02}/{:04}", self.month, self.year)
}
}
impl GregorianDay {
pub fn new(day: i32, month: i32, year: i32) -> Self {
Self {
inner: Day { day, month, year },
}
}
pub fn to_month(&self) -> GregorianMonth {
GregorianMonth {
year: self.inner.year,
month: self.inner.month,
}
}
}
impl Calendar for GregorianDay {
fn to_julian_days(&self) -> i32 {
let a = ((14 - self.inner.month) / 12) as f64;
let y = (self.inner.year as f64 + 4800.0) - a;
let m = self.inner.month as f64 + 12.0 * a - 3.0;
let mut jd = self.inner.day as f64
+ ((153.0 * m + 2.0) / 5.0).floor()
+ 365.0 * y
+ (y / 4.0).floor()
- (y / 100.0).floor()
+ (y / 400.0).floor()
- 32045.0;
if jd < 2299161.0 {
jd = self.inner.day as f64
+ ((153.0 * m + 2.0) / 5.0).floor()
+ 365.0 * y
+ (y / 4.0).floor()
- 32083.0;
}
jd as i32
}
fn from_julian_days(jd: i32) -> Self {
let jd = jd as f64;
let (b, c) = if jd > 2299160.0 {
let a = jd + 32044.0;
let b = ((4.0 * a + 3.0) / 146097.0).floor();
let c = a - (b * 146097.0 / 4.0).floor();
(b, c)
} else {
(0.0, jd + 32082.0)
};
let d = ((4.0 * c + 3.0) / 1461.0).floor();
let e = c - ((1461.0 * d) / 4.0).floor();
let m = ((5.0 * e + 2.0) / 153.0).floor();
let day = e - ((153.0 * m + 2.0) / 5.0).floor() + 1.0;
let month = m + 3.0 - 12.0 * (m / 10.0).floor();
let year = b * 100.0 + d - 4800.0 + (m / 10.0).floor();
Self {
inner: Day {
day: day as i32,
month: month as i32,
year: year as i32,
},
}
}
}
impl LunarDay {
fn get_new_moon_day(k: f64, tz: f64) -> f64 {
let t = k / 1236.85; let tt = t * t;
let ttt = tt * t;
let dr = PI / 180.0;
let mut jd1 = 2415020.75933 + 29.53058868 * k + 0.0001178 * tt - 0.000000155 * ttt;
jd1 = jd1 + 0.00033 * ((166.56 + 132.87 * t - 0.009173 * tt) * dr).sin(); let m = 359.2242 + 29.10535608 * k - 0.0000333 * tt - 0.00000347 * ttt; let mpr = 306.0253 + 385.81691806 * k + 0.0107306 * tt + 0.00001236 * ttt; let f = 21.2964 + 390.67050646 * k - 0.0016528 * tt - 0.00000239 * ttt; let mut c1 = (0.1734 - 0.000393 * t) * (m * dr).sin() + 0.0021 * (2.0 * dr * m).sin();
c1 = c1 - 0.4068 * (mpr * dr).sin() + 0.0161 * (dr * 2.0 * mpr).sin();
c1 = c1 - 0.0004 * (dr * 3.0 * mpr).sin();
c1 = c1 + 0.0104 * (dr * 2.0 * f).sin() - 0.0051 * (dr * (m + mpr)).sin();
c1 = c1 - 0.0074 * (dr * (m - mpr)).sin() + 0.0004 * (dr * (2.0 * f + m)).sin();
c1 = c1 - 0.0004 * (dr * (2.0 * f - m)).sin() - 0.0006 * (dr * (2.0 * f + mpr)).sin();
c1 = c1 + 0.0010 * (dr * (2.0 * f - mpr)).sin() + 0.0005 * (dr * (2.0 * mpr + m)).sin();
let delta_t = if t < -11.0 {
0.001 + 0.000839 * t + 0.0002261 * tt - 0.00000845 * ttt - 0.000000081 * t * ttt
} else {
-0.000278 + 0.000265 * t + 0.000262 * tt
};
let jd_new = jd1 + c1 - delta_t;
(jd_new + 0.5 + tz / 24.0).floor()
}
fn get_sun_longitude(jd: f64, tz: f64) -> f64 {
let t = (jd - 2451545.5 - tz / 24.0) / 36525.0; let tt = t * t;
let dr = PI / 180.0; let m = 357.52910 + 35999.05030 * t - 0.0001559 * tt - 0.00000048 * t * tt; let l0 = 280.46645 + 36000.76983 * t + 0.0003032 * tt; let mut dl = (1.914600 - 0.004817 * t - 0.000014 * tt) * (dr * m).sin();
dl =
dl + (0.019993 - 0.000101 * t) * (dr * 2.0 * m).sin() + 0.000290 * (dr * 3.0 * m).sin();
let mut l = l0 + dl; l = l * dr;
l = l - PI * 2.0 * ((l / (PI * 2.0)).floor()); (l / PI * 6.0).floor()
}
fn get_lunar_month_11(y: f64, tz: f64) -> f64 {
let off = GregorianDay::new(32, 12, y as i32).to_julian_days() as f64 - 2415021.0;
let k = (off / 29.530588853).floor();
let mut nm = Self::get_new_moon_day(k, tz);
let sun_long = Self::get_sun_longitude(nm, tz); if sun_long >= 9.0 {
nm = Self::get_new_moon_day(k - 1.0, tz);
}
nm
}
fn get_leap_month_offset(a11: f64, tz: f64) -> f64 {
let k = ((a11 - 2415021.076998695) / 29.530588853 + 0.5).floor();
let mut last;
let mut i = 1.0; let mut arc = Self::get_sun_longitude(Self::get_new_moon_day(k + i, tz), tz);
loop {
last = arc;
i += 1.0;
arc = Self::get_sun_longitude(Self::get_new_moon_day(k + i, tz), tz);
if arc == last || i >= 14.0 {
break;
}
}
i - 1.0
}
}
impl Calendar for LunarDay {
fn to_julian_days(&self) -> i32 {
let a11;
let b11;
if self.inner.month < 11 {
a11 = Self::get_lunar_month_11(self.inner.year as f64 - 1.0, TZ);
b11 = Self::get_lunar_month_11(self.inner.year as f64, TZ);
} else {
a11 = Self::get_lunar_month_11(self.inner.year as f64, TZ);
b11 = Self::get_lunar_month_11(self.inner.year as f64 + 1.0, TZ);
}
let mut off = self.inner.month as f64 - 11.0;
if off < 0.0 {
off += 12.0;
}
let mut leap_month;
let leap_off;
if b11 - a11 > 365.0 {
leap_off = Self::get_leap_month_offset(a11, TZ);
leap_month = leap_off - 2.0;
if leap_month < 0.0 {
leap_month += 12.0;
}
if self.leap && self.inner.month as f64 != leap_month {
panic!("WRONG!");
} else if self.leap || off >= leap_off {
off += 1.0;
}
}
let k = (0.5 + (a11 - 2415021.076998695) / 29.530588853).trunc();
let month_start = Self::get_new_moon_day(k + off, TZ);
(month_start + self.inner.day as f64 - 1.0) as i32
}
fn from_julian_days(jd: i32) -> Self {
let greg = GregorianDay::from_julian_days(jd);
let jd = jd as f64;
let k = ((jd - 2415021.076998695) / 29.530588853).trunc();
let mut month_start = Self::get_new_moon_day(k + 1.0, TZ);
if month_start > jd {
month_start = Self::get_new_moon_day(k, TZ);
}
let mut a11 = Self::get_new_moon_day(greg.inner.year as f64, TZ);
let mut b11 = a11;
let mut lunar_year;
if a11 >= month_start {
lunar_year = greg.inner.year;
a11 = Self::get_lunar_month_11(greg.inner.year as f64 - 1.0, TZ);
} else {
lunar_year = greg.inner.year + 1;
b11 = Self::get_lunar_month_11(greg.inner.year as f64 + 1.0, TZ);
}
let lunar_day = jd - month_start + 1.0;
let diff = ((month_start - a11) / 29.0).trunc();
let mut lunar_leap = 0;
let mut lunar_month = diff + 11.0;
let leap_month_diff;
if b11 - a11 > 365.0 {
leap_month_diff = Self::get_leap_month_offset(a11, TZ);
if diff >= leap_month_diff {
lunar_month = diff + 10.0;
if diff == leap_month_diff {
lunar_leap = 1;
}
}
}
if lunar_month > 12.0 {
lunar_month = lunar_month - 12.0;
}
if lunar_month >= 11.0 && diff < 4.0 {
lunar_year -= 1;
}
Self {
inner: Day {
day: lunar_day as i32,
month: lunar_month as i32,
year: lunar_year,
},
leap: lunar_leap == 1,
}
}
}