1use crate::angle::{Angle, TWO_PI};
6
7use chrono::{DateTime, Datelike, Timelike, Utc};
8
9#[allow(clippy::excessive_precision)] pub fn earth_rotation_angle(time_julian_ut1: JulianDate) -> Angle {
16 Angle::Radian(
17 TWO_PI * (0.7790572732640 + 1.00273781191135448 * (time_julian_ut1.0 - 2451545.0)),
18 )
19}
20
21#[derive(Debug)]
23pub struct JulianDate(
24 pub f64,
26);
27
28impl<T> From<DateTime<T>> for JulianDate
29where
30 T: chrono::TimeZone,
31 chrono::DateTime<chrono::Utc>: From<chrono::DateTime<T>>,
32{
33 fn from(date: DateTime<T>) -> Self {
34 let date: DateTime<Utc> = date.into();
38 assert!(
39 1801 <= date.year() && date.year() <= 2099,
40 "Datetime must be between year 1801 and 2099" );
42
43 Self(
44 (367 * date.year() - ((7 * (date.year() + (date.month() as i32 + 9) / 12)) / 4)
46 + ((275 * date.month() as i32) / 9)) as f64
47 + date.day() as f64
48 + 1721013.5
49 + (date.hour() as f64
50 + date.minute() as f64 / 60.0
51 + date.second() as f64 / 3600.0)
52 / 24.0
53 - (0.5_f64).copysign(100.0 * date.year() as f64 + date.month() as f64 - 190002.5)
54 + 0.5,
55 )
56 }
57}
58
59pub struct GMST(
61 pub Angle,
63);
64
65impl From<JulianDate> for GMST {
66 fn from(julian_date: JulianDate) -> Self {
67 Self(Angle::Hour(earth_rotation_angle(julian_date).to_hr()))
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use chrono::TimeZone;
75
76 use crate::time::*;
77
78 #[test]
79 fn juliandate() {
80 assert_eq!(
83 JulianDate::from(Utc.with_ymd_and_hms(2000, 1, 1, 12, 0, 0).unwrap()).0,
84 2451545.0
85 );
86
87 assert_float_absolute_eq!(
90 JulianDate::from(Utc.with_ymd_and_hms(2013, 1, 1, 0, 30, 0).unwrap()).0,
91 2_456_293.520_833,
92 1e-6
93 );
94 }
95}