Parse DateTime
from Unix timestamp
This commit is contained in:
parent
c905e20b36
commit
3dcb5b6991
2 changed files with 118 additions and 2 deletions
116
src/time/epoch.rs
Normal file
116
src/time/epoch.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use super::{DateTime, TimeZone};
|
||||||
|
|
||||||
|
pub const SECS_PER_DAY: i64 = 86400;
|
||||||
|
|
||||||
|
fn days_since_epoch(ts: i64) -> i64 {
|
||||||
|
ts / SECS_PER_DAY
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_leap(year: i64) -> bool {
|
||||||
|
year % 4 == 0 && (year % 100 != 0 || year % 16 == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn year_from_ts_with_remainder(ts: i64) -> (i64, i64) {
|
||||||
|
let mut year = 1970;
|
||||||
|
let mut days = days_since_epoch(ts);
|
||||||
|
loop {
|
||||||
|
let days_in_year = if is_leap(year) {
|
||||||
|
366
|
||||||
|
} else {
|
||||||
|
365
|
||||||
|
};
|
||||||
|
if days > days_in_year {
|
||||||
|
days -= days_in_year;
|
||||||
|
year += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(year, days)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn days_in_month(month: u8, year: i64) -> i64 {
|
||||||
|
assert!(month < 13);
|
||||||
|
match month {
|
||||||
|
1 | 3 | 5 | 7 | 10 | 12 => 31,
|
||||||
|
2 => {
|
||||||
|
if is_leap(year) {
|
||||||
|
29
|
||||||
|
} else {
|
||||||
|
28
|
||||||
|
}
|
||||||
|
},
|
||||||
|
4 | 6 | 9 | 11 => 30,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn month_and_days_from_ordinal(days: i64, year: i64) -> (u8, i64) {
|
||||||
|
assert!(
|
||||||
|
if is_leap(year) {
|
||||||
|
days < 366
|
||||||
|
} else {
|
||||||
|
days < 365
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let mut days = days;
|
||||||
|
let mut month = 1;
|
||||||
|
while days_in_month(month, year) <= days {
|
||||||
|
days -= days_in_month(month, year);
|
||||||
|
month += 1;
|
||||||
|
}
|
||||||
|
(month, days)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn date_time_from_ts(ts: i64) -> DateTime {
|
||||||
|
let (year, days) = year_from_ts_with_remainder(ts);
|
||||||
|
let (month, days) = month_and_days_from_ordinal(days, year);
|
||||||
|
let seconds = ts % SECS_PER_DAY;
|
||||||
|
println!("Seconds: {seconds}");
|
||||||
|
let minutes = seconds / 60;
|
||||||
|
println!("Minutes: {minutes}");
|
||||||
|
let second = seconds % 60;
|
||||||
|
let hour = minutes / 60;
|
||||||
|
let minute = minutes % 60;
|
||||||
|
DateTime {
|
||||||
|
year: year as u32,
|
||||||
|
month: month.try_into().unwrap(),
|
||||||
|
day: (days + 1).try_into().unwrap(),
|
||||||
|
hour: Some((hour + 1).try_into().unwrap()),
|
||||||
|
minute: Some((minute).try_into().unwrap()),
|
||||||
|
second: Some(second.try_into().unwrap()),
|
||||||
|
tz: Some(TimeZone::UTC),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_year() {
|
||||||
|
let ts = 1686459092;
|
||||||
|
let (y, r) = year_from_ts_with_remainder(ts);
|
||||||
|
assert_eq!(y, 2023);
|
||||||
|
assert_eq!(r, 161);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_month_and_days() {
|
||||||
|
let ts = 1686459092;
|
||||||
|
let (year, days) = year_from_ts_with_remainder(ts);
|
||||||
|
let (month, days) = month_and_days_from_ordinal(days, year);
|
||||||
|
assert_eq!(month, 6);
|
||||||
|
assert_eq!(days, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_datetime() {
|
||||||
|
let ts = 1686462068;
|
||||||
|
let dt = date_time_from_ts(ts);
|
||||||
|
assert_eq!(dt.day, 11);
|
||||||
|
assert_eq!(dt.hour, Some(6));
|
||||||
|
assert_eq!(dt.minute, Some(41));
|
||||||
|
assert_eq!(dt.second, Some(8));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
//! A container representing a moment in ISO-8601 format Time
|
//! A container representing a moment in ISO-8601 format Time
|
||||||
use std::{cmp::Ordering, fmt, str::FromStr};
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
pub mod epoch;
|
||||||
mod error;
|
mod error;
|
||||||
mod parser;
|
mod parser;
|
||||||
pub use {error::Error, parser::Parser};
|
pub use {error::Error, parser::Parser};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use x509_parser::der_parser::ber::ber_read_element_header;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||||
|
|
Loading…
Add table
Reference in a new issue