use std::time::{SystemTime, UNIX_EPOCH};
use crate::protobuf::ygw::Timestamp;
const TIMESECS: [i64; 37] = [
62294400, 62380801, 62467202, 62553603, 62640004, 62726405, 62812806, 62899207, 62985608,
63072009, 78796810, 94694411, 126230412, 157766413, 189302414, 220924815, 252460816, 283996817,
315532818, 362793619, 394329620, 425865621, 489024022, 567993623, 631152024, 662688025,
709948826, 741484827, 773020828, 820454429, 867715230, 915148831, 1136073632, 1230768033,
1341100834, 1435708835, 1483228836,
];
const TIMES365: [i32; 4] = [0, 365, 730, 1095];
const TIMES36524: [i32; 4] = [0, 36524, 73048, 109572];
const MONTAB: [i32; 12] = [0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337];
const PICOS_PER_MILLIS: i64 = 1000000000;
const DIFF_TAI_UTC: i32 = 37;
#[derive(Debug)]
pub struct DateTimeComponents {
pub year: i32,
pub month: i32,
pub day: i32,
pub hour: i32,
pub minute: i32,
pub second: i32,
pub millis: i32,
}
#[derive(Default)]
pub struct Instant {
millis: i64,
picos: u32,
}
impl From<Timestamp> for Instant {
fn from(time: Timestamp) -> Self {
Instant {
millis: time.millis,
picos: time.picos,
}
}
}
impl From<Instant> for Timestamp {
fn from(time: Instant) -> Self {
Timestamp {
millis: time.millis,
picos: time.picos,
}
}
}
pub fn get_instant(millis: i64, picos: i64) -> Instant {
let picos1;
let mut mil = millis;
if !(0..PICOS_PER_MILLIS).contains(&picos) {
mil += picos / PICOS_PER_MILLIS;
picos1 = picos % PICOS_PER_MILLIS;
} else {
picos1 = picos;
}
Instant {
millis: mil,
picos: picos1 as u32,
}
}
fn caldate_from_mjd(input_day: i32) -> (i32, i32, i32) {
let mut year = input_day / 146097;
let mut day = input_day % 146097;
day += 678881;
while day >= 146097 {
day -= 146097;
year += 1;
}
year *= 4;
if day == 146096 {
year += 3;
day = 36524;
} else {
year += day / 36524;
day %= 36524;
}
year *= 25;
year += day / 1461;
day %= 1461;
year *= 4;
if day == 1460 {
year += 3;
day = 365;
} else {
year += day / 365;
day %= 365;
}
day *= 10;
let mut month = (day + 5) / 306;
day = (day + 5) % 306;
day /= 10;
if month >= 10 {
year += 1;
month -= 10;
} else {
month += 2;
}
(year, month + 1, day + 1)
}
fn caldate_to_mjd(dtc: &DateTimeComponents) -> i32 {
let mut y: i32 = dtc.year;
let mut m: i32 = dtc.month - 1;
let mut d: i32 = dtc.day - 678882;
d += 146097 * (y / 400);
y %= 400;
if m >= 2 {
m -= 2;
} else {
m += 10;
y -= 1;
}
y += m / 12;
m %= 12;
if m < 0 {
m += 12;
y -= 1;
}
d += MONTAB[m as usize];
d += 146097 * (y / 400);
y %= 400;
if y < 0 {
y += 400;
d -= 146097;
}
d += TIMES365[(y & 3) as usize];
y >>= 2;
d += 1461 * (y % 25);
y /= 25;
d + TIMES36524[(y & 3) as usize]
}
pub fn instant_to_utc(t: Instant) -> DateTimeComponents {
let mut u = t.millis / 1000;
let mut milli = t.millis % 1000;
let mut leap = 0;
if milli < 0 {
milli += 1000;
u -= 1;
}
let mut ls = DIFF_TAI_UTC;
for i in (0..(TIMESECS.len())).rev() {
if u > TIMESECS[i] {
break;
}
if u == TIMESECS[i] {
leap = 1;
break;
}
ls -= 1;
}
u -= ls as i64;
let mut s = u % 86400;
if s < 0 {
s += 86400;
u -= 86400;
}
u /= 86400;
let mjd = 40587 + u;
let (y, m, d) = caldate_from_mjd(mjd.try_into().unwrap());
DateTimeComponents {
year: y,
month: m,
day: d,
hour: (s / 3600).try_into().unwrap(),
minute: ((s / 60) % 60).try_into().unwrap(),
second: ((s % 60) + leap).try_into().unwrap(),
millis: (milli).try_into().unwrap(),
}
}
pub fn instant_to_unix(t: Instant) -> i64 {
let u = t.millis / 1000;
let mut ls = DIFF_TAI_UTC;
for i in (0..(TIMESECS.len() - 1)).rev() {
if u >= TIMESECS[i] {
break;
}
ls -= 1;
}
t.millis - (ls as i64) * 1000
}
pub fn utc_to_instant(dtc: DateTimeComponents) -> Instant {
let day = caldate_to_mjd(&dtc);
let mut s = (dtc.hour * 60 + dtc.minute) as i64;
s = s * 60 + dtc.second as i64 + (day as i64 - 40587) * 86400;
let mut ls = DIFF_TAI_UTC as i64;
for i in (0..TIMESECS.len()).rev() {
let u = TIMESECS[i] - ls + 1;
if s > u {
break;
}
if (s < u) || (dtc.second == 60) {
ls -= 1;
}
}
s += ls;
Instant {
millis: dtc.millis as i64 + 1000 * s,
picos: 0,
}
}
pub fn unix_to_instant(t: i64) -> Instant {
let u = t / 1000;
let mut ls = DIFF_TAI_UTC as i64;
for i in (0..(TIMESECS.len())).rev() {
if u > TIMESECS[i] - ls {
break;
}
ls -= 1;
}
Instant {
millis: t + ls * 1000,
picos: 0,
}
}
pub fn nano_unix_to_instant(seconds: i64, subsec_nanos: u32) -> Instant {
let u = seconds;
let mut ls = DIFF_TAI_UTC as i64;
for i in (0..(TIMESECS.len())).rev() {
if u > TIMESECS[i] - ls {
break;
}
ls -= 1;
}
let picos = (subsec_nanos % 1_000_000) * 1000;
let millis = (seconds + ls) * 1000 + subsec_nanos as i64 / 1_000_000;
Instant { millis, picos }
}
pub fn wallclock() -> Instant {
let now = SystemTime::now();
let now_unix = now.duration_since(UNIX_EPOCH).expect("Time went backwards");
nano_unix_to_instant(now_unix.as_secs() as i64, now_unix.subsec_nanos())
}
pub fn to_string(instant: Instant) -> String {
let dtc: DateTimeComponents = instant_to_utc(instant);
let mut sb: String = { dtc.year.to_string() };
sb.push('-');
sb.push_str(&format!("{:0>2}", dtc.month));
sb.push('-');
sb.push_str(&format!("{:0>2}", dtc.day));
sb.push('T');
sb.push_str(&format!("{:0>2}", dtc.hour));
sb.push(':');
sb.push_str(&format!("{:0>2}", dtc.minute));
sb.push(':');
sb.push_str(&format!("{:0>2}", dtc.second));
sb.push('.');
sb.push_str(&format!("{:0>3}", dtc.millis));
sb.push('Z');
sb
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test0() {
let dtc: DateTimeComponents = instant_to_utc(Instant {
millis: 0,
picos: 0,
});
assert_eq!(1970, dtc.year);
assert_eq!(1, dtc.month);
assert_eq!(1, dtc.day);
assert_eq!(0, dtc.hour);
assert_eq!(0, dtc.minute);
assert_eq!(0, dtc.second);
}
#[test]
fn test2008() {
let dtc: DateTimeComponents = instant_to_utc(Instant {
millis: 1230768032000,
picos: 0,
});
assert_eq!(59, dtc.second);
}
#[test]
fn test_nano_unix_to_instant() {
let start = SystemTime::now();
let since_the_epoch = start
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
let hres = wallclock();
assert_eq!(
hres.millis,
unix_to_instant(since_the_epoch.as_millis() as i64).millis
);
}
#[test]
fn test_negative() {
let t = utc_to_instant(DateTimeComponents {
year: 1017,
month: 1,
day: 1,
hour: 11,
minute: 59,
second: 58,
millis: 999,
});
assert_eq!("1017-01-01T11:59:58.999Z", to_string(t));
}
}