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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#![deny(rustdoc::all)]
#![allow(rustdoc::missing_doc_code_examples)]
#![deny(clippy::unwrap_used, clippy::integer_arithmetic)]
#![deny(missing_docs)]
use num_traits::cast::ToPrimitive;
pub const ERA_NUM_PERIODS: usize = (u8::MAX as usize) + 1;
pub const PERIOD_SECONDS: u32 = 86_400 * 3;
pub const SECONDS_PER_ERA: u64 = (ERA_NUM_PERIODS as u64) * (PERIOD_SECONDS as u64);
pub const COMMON_ERA_UNIX_TS: u64 = 1640995200;
pub fn calculate_era_start_ts(era: u16) -> Option<u64> {
COMMON_ERA_UNIX_TS.checked_add(SECONDS_PER_ERA.checked_mul(era.into())?)
}
pub fn calculate_period_start_ts(era: u16, period: u8) -> Option<u64> {
calculate_era_start_ts(era)?
.checked_add(period.to_u64()?.checked_mul(PERIOD_SECONDS.to_u64()?)?)
}
pub fn has_period_elapsed(era: u16, period: u8, now: i64) -> Option<bool> {
let start = calculate_period_start_ts(era, period)?;
let now = now.to_u64()?;
Some(now > start)
}
pub fn calculate_era_and_period_of_ts(now: u64) -> Option<(u16, u8)> {
let current_era: u16 = now
.checked_sub(COMMON_ERA_UNIX_TS)?
.checked_div(SECONDS_PER_ERA)?
.to_u16()?;
let current_era_start_ts = calculate_era_start_ts(current_era)?;
let current_period: u8 = now
.checked_sub(current_era_start_ts)?
.checked_div(PERIOD_SECONDS.into())?
.to_u8()?;
Some((current_era, current_period))
}
pub fn calculate_next_era_and_period(era: u16, period: u8) -> Option<(u16, u8)> {
Some(if period == u8::MAX {
(era.checked_add(1)?, 0_u8)
} else {
(era, period.checked_add(1)?)
})
}
pub fn calculate_next_era_and_period_of_ts(now: u64) -> Option<(u16, u8)> {
let (current_era, current_period) = calculate_era_and_period_of_ts(now)?;
calculate_next_era_and_period(current_era, current_period)
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::integer_arithmetic)]
mod tests {
use super::*;
#[test]
fn test_has_period_elapsed() {
let current_time = (COMMON_ERA_UNIX_TS + (PERIOD_SECONDS as u64) * 2)
.to_i64()
.unwrap();
assert!(has_period_elapsed(0, 0, current_time).unwrap());
assert!(has_period_elapsed(0, 1, current_time).unwrap());
assert!(!has_period_elapsed(0, 2, current_time).unwrap());
assert!(!has_period_elapsed(1, 0, current_time).unwrap());
}
#[test]
fn test_has_period_elapsed_boundary() {
let current_time = (COMMON_ERA_UNIX_TS + (PERIOD_SECONDS as u64) * 2 + 1)
.to_i64()
.unwrap();
assert!(has_period_elapsed(0, 0, current_time).unwrap());
assert!(has_period_elapsed(0, 1, current_time).unwrap());
assert!(has_period_elapsed(0, 2, current_time).unwrap());
assert!(!has_period_elapsed(0, 3, current_time).unwrap());
assert!(!has_period_elapsed(1, 0, current_time).unwrap());
}
#[test]
fn test_calculate_next_era_and_period_normal() {
let era = 2_u16;
let period = 4_u8;
let start = calculate_period_start_ts(era, period).unwrap() + 40;
let (result_era, result_period) = calculate_era_and_period_of_ts(start).unwrap();
assert_eq!(result_era, era);
assert_eq!(result_period, period);
let (result_next_era, result_next_period) =
calculate_next_era_and_period_of_ts(start).unwrap();
assert_eq!(result_next_era, era);
assert_eq!(result_next_period, period + 1);
}
#[test]
fn test_calculate_next_era_and_period_boundary() {
let era = 2_u16;
let period = 255_u8;
let start = calculate_period_start_ts(era, period).unwrap() + 40;
let (result_era, result_period) = calculate_era_and_period_of_ts(start).unwrap();
assert_eq!(result_era, era);
assert_eq!(result_period, period);
let (result_next_era, result_next_period) =
calculate_next_era_and_period_of_ts(start).unwrap();
assert_eq!(result_next_era, era + 1);
assert_eq!(result_next_period, 0_u8);
}
}