practicaltimestamp/
unix_timestamp.rs1use super::{
2 result,
3 util,
4};
5
6#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
7pub struct UnixTimestamp(i64);
8
9impl UnixTimestamp {
10 pub const MIN: UnixTimestamp = Self::new(0);
11 pub const MAX: UnixTimestamp = Self::new(253_402_300_800);
12
13 const fn new(value: i64) -> Self {
14 Self(value)
15 }
16
17 #[cfg(feature = "std")]
18 pub fn now() -> Self {
19 super::std_support::system_time_now()
20 }
21
22 pub const fn checked_from_unix_timestamp(timestamp: i64) -> Option<Self> {
23 Self::from_unix_timestamp(timestamp).ok()
24 }
25
26 pub const fn from_unix_timestamp(timestamp: i64) -> result::TimestampResult {
27 if util::is_supported_unix_timestamp(timestamp) {
28 result::TimestampResult::TimestampOk(Self::new(timestamp))
29 } else {
30 result::TimestampResult::OverflowErr(timestamp)
31 }
32 }
33
34 pub const fn unix_timestamp(self) -> i64 {
35 self.0
36 }
37
38 pub const fn midnight(self) -> Self {
39 Self::new(self.unix_timestamp() - self.seconds_since_midnight())
40 }
41
42 pub const fn seconds_since_midnight(self) -> i64 {
43 (self.unix_timestamp() as u64 % util::SECONDS_PER_DAY as u64) as i64
44 }
45
46 pub const fn checked_add(self, seconds: i64) -> Option<Self> {
47 let timestamp = self.unix_timestamp().wrapping_add(seconds);
48 Self::checked_from_unix_timestamp(timestamp)
49 }
50
51 pub const fn checked_sub(self, seconds: i64) -> Option<Self> {
52 let timestamp = self.unix_timestamp().wrapping_sub(seconds);
53 Self::checked_from_unix_timestamp(timestamp)
54 }
55
56 pub const fn saturating_add(self, seconds: i64) -> Self {
57 let timestamp = self.unix_timestamp().saturating_add(seconds); Self::from_unix_timestamp(timestamp).unwrap()
59 }
60
61 pub const fn saturating_sub(self, seconds: i64) -> Self {
62 let timestamp = self.unix_timestamp().saturating_sub(seconds); Self::from_unix_timestamp(timestamp).unwrap()
64 }
65
66 pub const fn checked_from_year_month_day(year: u16, month: u8, day: u8) -> Option<Self> {
67 if util::is_valid_year_month_day(year, month, day) {
68 Self::from_year_month_day(year, month, day).ok()
69 } else {
70 None
71 }
72 }
73
74 pub const fn from_year_month_day(year: u16, month: u8, day: u8) -> result::TimestampResult {
77 let (adj_year, adj_month, day) = if month < 3 {
78 (year as i32 + 399, month as i32 + 12, day as i32)
79 } else {
80 (year as i32 + 400, month as i32, day as i32)
81 };
82 let f = (979 * adj_month - 2_918) >> 5;
84 let julian_day_number = day + f + 365 * adj_year + adj_year / 4 - adj_year / 100 + adj_year / 400 + 1_575_022;
85 Self::from_julian_day_number(julian_day_number)
86 }
87
88 pub const fn to_year_month_day(self) -> (u16, u8, u8) {
91 let julian_day_number = self.julian_day_number() as u32;
92 let z = julian_day_number - 1_575_022;
93 let h = 100 * z - 25;
94 let a = h / 3_652_425;
95 let b = a - a / 4;
96 let adj_year = (100 * b + h) / 36_525;
97 let c = b + z - 365 * adj_year - adj_year / 4;
98 let adj_month = (535 * c + 48_950) >> 14;
100 let f = (979 * adj_month - 2_918) >> 5;
102 let day = c - f;
103 let (year, month) = if adj_month > 12 {
104 (adj_year - 399, adj_month - 12)
105 } else {
106 (adj_year - 400, adj_month)
107 };
108 (year as u16, month as u8, day as u8)
109 }
110
111 pub const fn checked_from_year_ordinal(year: u16, ordinal: u16) -> Option<Self> {
112 if util::is_valid_year_ordinal(year, ordinal) {
113 Self::from_year_ordinal(year, ordinal).ok()
114 } else {
115 None
116 }
117 }
118
119 pub const fn from_year_ordinal(year: u16, ordinal: u16) -> result::TimestampResult {
122 let ordinal = ordinal as u64;
123 let last_day_of_february = 59 + util::is_leap_year(year) as u64;
124 let (adj_ordinal, adj_month, month) = if ordinal > last_day_of_february {
126 let adj_ordinal = ordinal - last_day_of_february;
127 let adj_month = (1_071 * adj_ordinal - 535) >> 15;
128 (adj_ordinal, adj_month, (adj_month + 3) as u8)
129 } else {
130 let adj_ordinal = ordinal + 306;
131 let adj_month = (1_071 * adj_ordinal - 535) >> 15;
132 (adj_ordinal, adj_month, (adj_month - 9) as u8)
133 };
134 let f = (979 * adj_month + 16) >> 5;
136 let day = adj_ordinal - f;
137 Self::from_year_month_day(year, month, day as u8)
138 }
139
140 pub const fn to_year_ordinal(self) -> (u16, u16) {
142 let (year, month, day) = self.to_year_month_day();
143 let (month, day) = (month as u64, day as u64);
144 let ordinal = if month >= 3 {
146 ((979 * (month - 3) + 16) >> 5) + day + 59 + util::is_leap_year(year) as u64
147 } else {
148 ((979 * (month + 9) + 16) >> 5) + day - 306
149 };
150 (year, ordinal as u16)
151 }
152
153 pub const fn checked_from_julian_day_number(julian_day_number: i32) -> Option<Self> {
154 Self::from_julian_day_number(julian_day_number).ok()
155 }
156
157 pub const fn from_julian_day_number(julian_day_number: i32) -> result::TimestampResult {
158 let timestamp = (julian_day_number as i64 - util::UNIX_EPOCH_JULIAN_DAY_NUMBER as i64) * util::SECONDS_PER_DAY;
159 Self::from_unix_timestamp(timestamp)
160 }
161
162 pub const fn julian_day_number(self) -> i32 {
163 (self.unix_timestamp() as u64 / util::SECONDS_PER_DAY as u64) as i32 + util::UNIX_EPOCH_JULIAN_DAY_NUMBER
164 }
165
166 pub const fn weekday(self) -> util::Weekday {
167 let adj_days = self.unix_timestamp() as u64 / util::SECONDS_PER_DAY as u64 + 3;
169 let wd = adj_days - (((adj_days * 613_566_757) >> 32) * 7);
170 util::Weekday::new(wd)
171 }
172}