deep_time/time_parts/
to_deep_time.rs1use crate::tzdb::offset_info_at_local;
2use crate::{
3 Dt, JD_2000_2_451_545, SEC_PER_DAYI64, Scale, TAI_SECS_1970_MIDNIGHT_TO_2000_NOON, an_err,
4 error::{DtErr, DtErrKind},
5 {Meridiem, Offset, TimeParts, Weekday},
6};
7
8impl TimeParts {
9 pub fn to_time_point(&self) -> Result<Dt, DtErr> {
10 if let Some(unix_secs) = self.unix_timestamp_seconds {
14 let sec = (unix_secs as i64) - TAI_SECS_1970_MIDNIGHT_TO_2000_NOON;
15 let subsec = self.attos.unwrap_or(0);
16 return Ok(Dt::from(sec, subsec, Scale::UTC));
17 }
18
19 let hour = match (self.hour, self.meridiem) {
23 (Some(h), Some(m)) => {
24 if !(1..=12).contains(&h) {
25 return Err(an_err!(DtErrKind::OutOfRange, "hour: {}", h));
26 }
27 match (h, m) {
28 (12, Meridiem::AM) => 0,
29 (12, Meridiem::PM) => 12,
30 (h, Meridiem::AM) => h,
31 (h, Meridiem::PM) => h + 12,
32 }
33 }
34 (Some(h), None) => h,
35 (None, _) => 0,
36 };
37
38 if self.year.is_none() && self.iso_week_year.is_none() {
42 return Err(an_err!(DtErrKind::Incomplete, "no year"));
43 }
44
45 let minute = self.minute.unwrap_or(0);
46 let second = self.second.unwrap_or(0);
47 let subsec = self.attos.unwrap_or(0);
48 let mut jdn: Option<i64> = None;
49
50 if let Some(year) = self.year {
51 if let (Some(m), Some(d)) = (self.month, self.day) {
52 if !Dt::is_valid_ymd(year, m, d) {
54 return Err(an_err!(DtErrKind::InvalidInput, "ymd"));
55 }
56 jdn = Some(Dt::ymd_to_jdn(year, m, d));
57 } else if let Some(doy) = self.day_of_year {
58 if doy == 0 || doy > 366 || (doy == 366 && !Dt::is_leap_year(year)) {
60 return Err(an_err!(DtErrKind::OutOfRange, "day of year"));
61 }
62 jdn = Some(Dt::ydoy_to_jdn(year, doy));
63 }
64 }
65
66 if jdn.is_none() {
67 if let (Some(iso_y), Some(iso_w)) = (self.iso_week_year, self.iso_week) {
68 if iso_w == 0 || iso_w > 53 {
70 return Err(an_err!(DtErrKind::OutOfRange, "iso week"));
71 }
72 if iso_w == 53 && !Dt::has_iso_week_53(iso_y) {
73 return Err(an_err!(DtErrKind::InvalidItem, "iso week"));
74 }
75 let wd = self.weekday.unwrap_or(Weekday::Monday);
76 jdn = Some(Dt::ymd_to_jdn_from_iso_week(iso_y, iso_w, wd));
77 } else if let (Some(y), Some(w)) = (self.year, self.week_sun) {
78 if w > 53 {
80 return Err(an_err!(DtErrKind::OutOfRange, "week number"));
81 }
82 let wd = self.weekday.unwrap_or(Weekday::Sunday);
83 jdn = Some(Dt::ymd_to_jdn_from_week_sun(y, w, wd));
84 } else if let (Some(y), Some(w)) = (self.year, self.week_mon) {
85 if w > 53 {
87 return Err(an_err!(DtErrKind::OutOfRange, "week number"));
88 }
89 let wd = self.weekday.unwrap_or(Weekday::Monday);
90 jdn = Some(Dt::ymd_to_jdn_from_week_mon(y, w, wd));
91 }
92 }
93
94 let Some(jdn) = jdn else {
95 return Err(an_err!(DtErrKind::InvalidInput, "could not create julian"));
96 };
97 let days_since_j2000 = jdn - JD_2000_2_451_545;
98 let seconds_from_noon_utc =
99 (hour as i64 - 12) * 3600 + (minute as i64) * 60 + (second as i64);
100 let mut sec_utc = days_since_j2000 * SEC_PER_DAYI64 + seconds_from_noon_utc;
101
102 if let Some(name) = &self.iana_name {
107 let name_str = name.as_str().map_err(|e| {
108 an_err!(
109 DtErrKind::InvalidBytes,
110 "invalid iana ascii: {:?}: {}",
111 name,
112 e
113 )
114 })?;
115
116 if !name_str.is_empty() {
117 let provisional_unix = sec_utc + TAI_SECS_1970_MIDNIGHT_TO_2000_NOON;
118 match offset_info_at_local(name_str, provisional_unix) {
119 Some(info) => {
120 if info.is_gap {
121 sec_utc += info.gap_size as i64; sec_utc -= info.offset as i64; } else {
125 sec_utc -= info.offset as i64;
126 }
127 }
128 None => {
129 return Err(an_err!(
130 DtErrKind::InvalidTimezoneOffset,
131 "invalid iana: {}",
132 name_str
133 ));
134 }
135 }
136 }
137 } else if let Some(Offset::Fixed(offset)) = self.offset {
138 sec_utc -= offset as i64; }
140 Ok(Dt::from(sec_utc, subsec, Scale::UTC))
141 }
142}