1use crate::{ATTOS_PER_SEC_I128, Dt, LiteStr, Scale, Weekday};
2
3mod to_str;
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[cfg_attr(feature = "js", derive(tsify::Tsify))]
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36pub struct YmdHms {
37 pub(crate) yr: i64,
38 pub(crate) mo: u8,
39 pub(crate) day: u8,
40 pub(crate) hr: u8,
41 pub(crate) min: u8,
42 pub(crate) sec: u8, pub(crate) attos: u64, pub(crate) unix_attosec: i128,
45 pub(crate) scale: Scale,
46}
47
48impl YmdHms {
49 #[inline]
51 pub fn to_dt(&self) -> Dt {
52 Dt::from_ymd(
53 self.yr, self.mo, self.day, self.hr, self.min, self.sec, self.attos, self.scale,
54 )
55 }
56
57 #[inline(always)]
58 fn reconstruct(
59 yr: i64,
60 mo: u8,
61 day: u8,
62 hr: u8,
63 min: u8,
64 sec: u8,
65 attos: u64,
66 scale: Scale,
67 ) -> Self {
68 Dt::from_ymd(yr, mo, day, hr, min, sec, attos, scale).to_ymd()
69 }
70
71 pub fn add_yr(&self, years: i64) -> Self {
74 if years == 0 {
75 return *self;
76 }
77 let new_yr = self.yr.saturating_add(years);
78 let max_day = Dt::days_in_month(new_yr, self.mo);
79 let new_day = Dt::clamp_u8(self.day, 1, max_day);
80 Self::reconstruct(
81 new_yr, self.mo, new_day, self.hr, self.min, self.sec, self.attos, self.scale,
82 )
83 }
84
85 pub fn add_mo(&self, months: i64) -> Self {
88 if months == 0 {
89 return *self;
90 }
91 let yr = self.yr as i128;
92 let mo = self.mo as i128;
93 let delta = months as i128;
94
95 let total_months = yr * 12 + (mo - 1) + delta;
96
97 let new_yr = Dt::i128_to_i64(total_months.div_euclid(12));
98 let new_mo = Dt::clamp_u8((total_months.rem_euclid(12) + 1) as u8, 1, 12);
99
100 let max_day = Dt::days_in_month(new_yr, new_mo);
101 let new_day = Dt::clamp_u8(self.day, 1, max_day);
102
103 Self::reconstruct(
104 new_yr, new_mo, new_day, self.hr, self.min, self.sec, self.attos, self.scale,
105 )
106 }
107
108 pub fn add_days(&self, days: i64) -> Self {
111 if days == 0 {
112 return *self;
113 }
114 let jd = Dt::ymd_to_jd(self.yr, self.mo, self.day);
115 let new_jd = jd.saturating_add(days);
116 let (new_yr, new_mo, new_day) = Dt::jd_to_ymd(new_jd);
117 Self::reconstruct(
118 new_yr, new_mo, new_day, self.hr, self.min, self.sec, self.attos, self.scale,
119 )
120 }
121
122 #[inline]
123 pub fn add_wk(&self, weeks: i64) -> Self {
124 self.add_days(weeks.saturating_mul(7))
125 }
126
127 #[inline(never)]
128 fn _add_attos(&self, attos_delta: i128) -> Self {
129 let tai = Dt::from_ymd(
130 self.yr, self.mo, self.day, self.hr, self.min, self.sec, self.attos, self.scale,
131 );
132 let new_tai = tai.add(Dt::span(attos_delta));
133 new_tai.to_ymd()
134 }
135
136 #[inline]
137 pub fn add_attos(&self, attos: i128) -> Self {
138 self._add_attos(attos)
139 }
140
141 #[inline]
142 pub fn add_sec(&self, sec: i64) -> Self {
143 self._add_attos(sec as i128 * ATTOS_PER_SEC_I128)
144 }
145
146 #[inline]
147 pub fn add_min(&self, min: i64) -> Self {
148 self._add_attos(min as i128 * 60 * ATTOS_PER_SEC_I128)
149 }
150
151 #[inline]
152 pub fn add_hr(&self, hr: i64) -> Self {
153 self._add_attos(hr as i128 * 3600 * ATTOS_PER_SEC_I128)
154 }
155
156 #[inline]
157 pub fn yr(&self) -> i64 {
158 self.yr
159 }
160
161 #[inline]
162 pub fn mo(&self) -> u8 {
163 self.mo
164 }
165
166 #[inline]
167 pub fn day(&self) -> u8 {
168 self.day
169 }
170
171 #[inline]
172 pub fn hr(&self) -> u8 {
173 self.hr
174 }
175
176 #[inline]
177 pub fn min(&self) -> u8 {
178 self.min
179 }
180
181 #[inline]
182 pub fn sec(&self) -> u8 {
183 self.sec
184 }
185
186 #[inline]
187 pub fn attos(&self) -> u64 {
188 self.attos
189 }
190
191 #[inline]
194 pub fn unix_attosec(&self) -> i128 {
195 self.unix_attosec
196 }
197
198 #[inline]
200 pub fn scale(&self) -> Scale {
201 self.scale
202 }
203
204 pub(crate) fn to_ymd_rich(
205 &self,
206 iso_yr: i64,
207 iso_wk: u8,
208 iso_wkday: Weekday,
209 day_of_yr: u16,
210 wkday: u8,
211 wk_of_yr_sun: u8,
212 wk_of_yr_mon: u8,
213 ) -> YmdHmsRich {
214 YmdHmsRich::new(
215 self.unix_attosec,
216 self.yr,
217 self.mo,
218 self.day,
219 self.hr,
220 self.min,
221 self.sec,
222 self.attos,
223 iso_yr,
224 iso_wk,
225 iso_wkday,
226 day_of_yr,
227 wkday,
228 wk_of_yr_sun,
229 wk_of_yr_mon,
230 self.scale,
231 )
232 }
233}
234
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237#[cfg_attr(feature = "js", derive(tsify::Tsify))]
238#[derive(Clone, Copy, Debug, PartialEq, Eq)]
239pub struct YmdHmsRich {
240 pub(crate) unix_attosec: i128,
242 pub(crate) yr: i64,
244 pub(crate) mo: u8,
246 pub(crate) day: u8,
248 pub(crate) hr: u8,
250 pub(crate) min: u8,
252 pub(crate) sec: u8,
254 pub(crate) attos: u64,
256 pub(crate) iso_yr: i64,
258 pub(crate) iso_wk: u8,
260 pub(crate) iso_wkday: Weekday,
262 pub(crate) day_of_yr: u16,
264 pub(crate) wkday: u8,
266 pub(crate) wk_of_yr_sun: u8,
268 pub(crate) wk_of_yr_mon: u8,
270 pub(crate) offset_sec: Option<i32>,
273 pub(crate) tz: Option<LiteStr<49>>,
275 pub(crate) tz_abbrev: Option<LiteStr<49>>,
277 pub(crate) scale: Scale,
279}
280
281impl YmdHmsRich {
282 #[inline]
284 pub(crate) const fn new(
285 unix_attosec: i128,
286 yr: i64,
287 mo: u8,
288 day: u8,
289 hr: u8,
290 min: u8,
291 sec: u8,
292 attos: u64,
293 iso_yr: i64,
294 iso_wk: u8,
295 iso_wkday: Weekday,
296 day_of_yr: u16,
297 wkday: u8,
298 wk_of_yr_sun: u8,
299 wk_of_yr_mon: u8,
300 scale: Scale,
301 ) -> Self {
302 Self {
303 unix_attosec,
304 yr,
305 mo,
306 day,
307 hr,
308 min,
309 sec,
310 attos,
311 iso_yr,
312 iso_wk,
313 iso_wkday,
314 day_of_yr,
315 wkday,
316 wk_of_yr_sun,
317 wk_of_yr_mon,
318 offset_sec: None,
319 tz: None,
320 tz_abbrev: None,
321 scale,
322 }
323 }
324
325 #[inline]
327 pub fn to_dt(&self) -> Dt {
328 Dt::from_ymd(
329 self.yr, self.mo, self.day, self.hr, self.min, self.sec, self.attos, self.scale,
330 )
331 }
332
333 #[inline]
336 pub const fn unix_attosec(&self) -> i128 {
337 self.unix_attosec
338 }
339
340 #[inline]
342 pub const fn scale(&self) -> Scale {
343 self.scale
344 }
345
346 #[inline]
353 pub const fn unix_timestamp(&self) -> (i64, u64) {
354 const ATTOS_PER_SEC_I128: i128 = 1_000_000_000_000_000_000;
355 let total = self.unix_attosec;
356 let secs = (total / ATTOS_PER_SEC_I128) as i64;
357 let frac = (total % ATTOS_PER_SEC_I128).unsigned_abs() as u64;
358 (secs, frac)
359 }
360
361 #[inline]
363 pub const fn yr(&self) -> i64 {
364 self.yr
365 }
366
367 #[inline]
369 pub const fn mo(&self) -> u8 {
370 self.mo
371 }
372
373 #[inline]
375 pub const fn day(&self) -> u8 {
376 self.day
377 }
378
379 #[inline]
381 pub const fn hr(&self) -> u8 {
382 self.hr
383 }
384
385 #[inline]
387 pub const fn min(&self) -> u8 {
388 self.min
389 }
390
391 #[inline]
393 pub const fn sec(&self) -> u8 {
394 self.sec
395 }
396
397 #[inline]
399 pub const fn attos(&self) -> u64 {
400 self.attos
401 }
402
403 #[inline]
405 pub const fn iso_yr(&self) -> i64 {
406 self.iso_yr
407 }
408
409 #[inline]
411 pub const fn iso_wk(&self) -> u8 {
412 self.iso_wk
413 }
414
415 #[inline]
417 pub const fn iso_wkday(&self) -> Weekday {
418 self.iso_wkday
419 }
420
421 #[inline]
423 pub const fn day_of_yr(&self) -> u16 {
424 self.day_of_yr
425 }
426
427 #[inline]
429 pub const fn wkday_sun(&self) -> u8 {
430 self.wkday
431 }
432
433 #[inline]
435 pub const fn wkday_mon(&self) -> u8 {
436 self.iso_wkday.wk_mon()
437 }
438
439 #[inline]
441 pub const fn wk_of_yr_sun(&self) -> u8 {
442 self.wk_of_yr_sun
443 }
444
445 #[inline]
447 pub const fn wk_of_yr_mon(&self) -> u8 {
448 self.wk_of_yr_mon
449 }
450
451 #[inline]
452 pub(crate) const fn offset_sec(&self) -> Option<i32> {
453 self.offset_sec
454 }
455
456 #[inline]
457 pub(crate) const fn tz(&self) -> Option<&LiteStr<49>> {
458 self.tz.as_ref()
459 }
460
461 #[inline]
462 pub(crate) const fn tz_abbrev(&self) -> Option<&LiteStr<49>> {
463 self.tz_abbrev.as_ref()
464 }
465
466 #[inline]
467 pub(crate) fn set_offset(&mut self, offset_sec: Option<i32>) -> &mut Self {
468 self.offset_sec = offset_sec;
469 self
470 }
471
472 #[inline]
473 pub(crate) fn set_tz(&mut self, tz: Option<&str>) -> &mut Self {
474 self.tz = tz.map(LiteStr::new);
475 self
476 }
477
478 #[inline]
479 pub(crate) fn set_tz_abbrev(&mut self, tz_abbrev: Option<&str>) -> &mut Self {
480 self.tz_abbrev = tz_abbrev.map(LiteStr::new);
481 self
482 }
483}