1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use crate::calendars::Calendar;
use crate::datetimes::traits::{CalendarDatetime, IsLeap};
use crate::timezone::Tz;
use crate::utils::{
    get_timestamp_from_hms, get_timestamp_from_ymd, get_ymd_hms_from_timestamp, is_leap_gregorian,
    is_leap_julian,
};

use super::traits::CalendarDatetimeCreator;
pub struct StandardDatetime {
    pub timestamp: i64,
    pub nanoseconds: u32,
    pub tz: Tz,
    pub calendar: Calendar,
}

impl StandardDatetime {
    pub fn new(timestamp: i64, nanoseconds: u32, tz: Tz) -> Self {
        Self {
            timestamp,
            nanoseconds,
            tz,
            calendar: Calendar::Standard,
        }
    }
}
impl IsLeap for StandardDatetime {
    fn is_leap(year: i64) -> bool {
        if year < 1582 {
            is_leap_julian(year)
        } else {
            is_leap_gregorian(year)
        }
    }
}

impl CalendarDatetime for StandardDatetime {
    fn timestamp(&self) -> i64 {
        self.timestamp
    }
    fn nanoseconds(&self) -> u32 {
        self.nanoseconds
    }
    fn calendar(&self) -> Calendar {
        self.calendar
    }
    fn timezone(&self) -> Tz {
        self.tz
    }
    fn ymd_hms(&self) -> Result<(i64, u8, u8, u8, u8, u8), crate::errors::Error> {
        let gregorian_begin = get_timestamp_from_ymd::<StandardDatetime>(1582, 10, 15)?;
        let mut timestamp = self.timestamp;
        if self.timestamp < gregorian_begin {
            let seconds_in_10_days = 10 * 24 * 60 * 60;
            timestamp -= seconds_in_10_days
        }
        Ok(get_ymd_hms_from_timestamp::<StandardDatetime>(timestamp))
    }
}

impl CalendarDatetimeCreator for StandardDatetime {
    fn from_timestamp(timestamp: i64, _nanoseconds: u32) -> Self {
        Self {
            timestamp,
            nanoseconds: 0,
            tz: Tz::new(0, 0).unwrap(),
            calendar: Calendar::Standard,
        }
    }
    fn from_ymd_hms(
        year: i64,
        month: u8,
        day: u8,
        hour: u8,
        minute: u8,
        second: f32,
    ) -> Result<Self, crate::errors::Error> {
        let (mut timestamp, nanoseconds) = get_timestamp_from_hms(hour, minute, second)?;
        if year == 1582
            && month == 10
            && ((day == 4 && (hour > 0 || minute > 0 || second > 0.0)) || (5..15).contains(&day))
        {
            return Err(crate::errors::Error::InvalidDate(
                "Date between 1582-10-04 and 1582-10-15 are not defined in the standard calendar"
                    .to_string(),
            ));
        }
        if year < 1582 || (year == 1582 && month < 10) || (year == 1582 && month == 10 && day < 15)
        {
            // Add 10 days from julian / gregorian break
            timestamp += 10 * 24 * 60 * 60
        }

        timestamp += get_timestamp_from_ymd::<StandardDatetime>(year, month, day)?;
        Ok(Self {
            timestamp,
            nanoseconds,
            tz: Tz::new(0, 0).unwrap(),
            calendar: Calendar::Standard,
        })
    }
}