1use crate::Error;
4
5#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
7pub struct JulianDate {
8 pub jd: f64,
10}
11
12impl JulianDate {
13 pub fn new(jd: f64) -> Self {
15 Self { jd }
16 }
17
18 pub fn from_calendar(
34 year: i32,
35 month: i32,
36 day: i32,
37 hour: i32,
38 minute: i32,
39 second: f64,
40 ) -> Result<Self, Error> {
41 if !(1..=12).contains(&month) {
42 return Err(Error::InvalidDate(format!("Invalid month: {}", month)));
43 }
44 if !(1..=31).contains(&day) {
45 return Err(Error::InvalidDate(format!("Invalid day: {}", day)));
46 }
47 if !(0..=23).contains(&hour) {
48 return Err(Error::InvalidDate(format!("Invalid hour: {}", hour)));
49 }
50 if !(0..=59).contains(&minute) {
51 return Err(Error::InvalidDate(format!("Invalid minute: {}", minute)));
52 }
53 if !(0.0..60.0).contains(&second) {
54 return Err(Error::InvalidDate(format!("Invalid second: {}", second)));
55 }
56
57 let a = (14 - month) / 12;
58 let y = year + 4800 - a;
59 let m = month + 12 * a - 3;
60
61 let jdn = day as f64 + (153 * m + 2) as f64 / 5.0 + 365.0 * y as f64 + (y / 4) as f64
62 - (y / 100) as f64
63 + (y / 400) as f64
64 - 32045.0;
65
66 let fraction = (hour as f64 + minute as f64 / 60.0 + second / 3600.0) / 24.0;
67
68 Ok(Self {
69 jd: jdn + fraction - 0.5,
70 })
71 }
72
73 pub fn to_calendar(&self) -> CalendarDate {
75 let j = (self.jd + 0.5).floor() as i64;
76 let f = self.jd + 0.5 - j as f64;
77
78 let j0 = j;
79 let j1 = j0 + 68569;
80 let j2 = 4 * j1 / 146097;
81 let j3 = j1 - (146097 * j2 + 3) / 4;
82 let j4 = 4000 * (j3 + 1) / 1461001;
83 let j5 = j3 - 1461 * j4 / 4 + 31;
84 let j6 = 80 * j5 / 2447;
85 let j7 = j5 - 2447 * j6 / 80;
86 let j8 = j6 / 11;
87 let j9 = j6 + 2 - 12 * j8;
88 let j10 = 100 * (j2 - 49) + j4 + j8;
89
90 let year = j10 as i32;
91 let month = j9 as i32;
92 let day = j7 as i32;
93
94 let total_seconds = f * 86400.0;
95 let hour = (total_seconds / 3600.0) as i32;
96 let minute = ((total_seconds % 3600.0) / 60.0) as i32;
97 let second = total_seconds % 60.0;
98
99 CalendarDate {
100 year,
101 month,
102 day,
103 hour,
104 minute,
105 second,
106 }
107 }
108
109 pub fn as_f64(&self) -> f64 {
111 self.jd
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq)]
117pub struct CalendarDate {
118 pub year: i32,
119 pub month: i32,
120 pub day: i32,
121 pub hour: i32,
122 pub minute: i32,
123 pub second: f64,
124}
125
126impl CalendarDate {
127 pub fn new(year: i32, month: i32, day: i32, hour: i32, minute: i32, second: f64) -> Self {
129 Self {
130 year,
131 month,
132 day,
133 hour,
134 minute,
135 second,
136 }
137 }
138
139 pub fn to_julian(&self) -> Result<JulianDate, Error> {
141 JulianDate::from_calendar(
142 self.year,
143 self.month,
144 self.day,
145 self.hour,
146 self.minute,
147 self.second,
148 )
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_julian_date_conversion() {
158 let jd = JulianDate::from_calendar(2024, 1, 15, 12, 0, 0.0).unwrap();
159 let cal = jd.to_calendar();
160 assert_eq!(cal.year, 2024);
161 assert_eq!(cal.month, 1);
162 assert_eq!(cal.day, 15);
163 }
164}