#![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);
}
}