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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use filetime::{self, FileTime};
use std::cmp::{self, PartialEq, PartialOrd};
use std::fs;
use std::io;
use std::path::Path;
pub trait Timestamped {
fn timestamp(&self) -> Timestamp;
}
#[derive(Copy, Clone, PartialEq, Eq, Ord, Debug)]
pub struct Timestamp { pub secs: u64, pub nsecs: u64 }
#[allow(non_snake_case)]
pub fn Timestamp(ms: u64) -> Timestamp {
Timestamp::new(ms / 1_000, (ms % 1_000) * 1_000_000)
}
impl Timestamp {
pub fn new(secs: u64, ns: u64) -> Timestamp {
Timestamp { secs: secs, nsecs: ns }
}
pub fn to_filetime(&self) -> FileTime {
assert!(self.nsecs < ::std::u32::MAX as u64);
FileTime::from_seconds_since_1970(self.secs, self.nsecs as u32)
}
pub fn to_ms(&self) -> u64 {
self.secs * 1000 + self.nsecs / 1_000_000
}
pub fn set_file_times<P: AsRef<Path>>(&self, p: P) -> io::Result<()> {
let t = self.to_filetime();
filetime::set_file_times(p, t, t)
}
pub fn date_fulltime_badly(&self) -> String {
let mut remain = self.to_filetime().seconds_relative_to_1970();
let mut year = None;
for y in 1970.. {
let secs_per_year = secs_per_year(y);
if remain > secs_per_year {
remain -= secs_per_year;
} else {
year = Some(y);
break;
}
}
let year = year.unwrap();
let mut month = None;
for i in 0..12 {
let secs_per_month = if is_leap_year(year) {
SECS_PER_DAY * DAYS_PER_MONTH_IN_LEAP[i]
} else {
SECS_PER_DAY * DAYS_PER_MONTH_IN_COMMON[i]
};
if remain > secs_per_month {
remain -= secs_per_month;
} else {
month = Some(i+1);
break;
}
}
let month = month.unwrap();
let day = remain / SECS_PER_DAY + 1;
let remain = remain % SECS_PER_DAY;
let hour = remain / SECS_PER_HOUR;
let remain = remain % SECS_PER_HOUR;
let min = remain / SECS_PER_MIN;
let remain = remain % SECS_PER_MIN;
let sec = remain;
let nsec = self.nsecs;
format!("{YEAR:04}-{MONTH:02}-{DAY:02} {HOUR:02}:{MIN:02}:{SEC:02}.{NSEC} (GMT)",
YEAR=year, MONTH=month, DAY=day, HOUR=hour, MIN=min, SEC=sec, NSEC=nsec)
}
}
fn is_leap_year(gregorian_year: u64) -> bool {
let year = gregorian_year;
if !(year % 4 == 0) {
false
} else if !(year % 100 == 0) {
true
} else if !(year % 400 == 0) {
false
} else {
true
}
}
fn secs_per_year(gregorian_year: u64) -> u64 {
if is_leap_year(gregorian_year) {
SECS_PER_LEAP_YEAR
} else {
SECS_PER_COMMON_YEAR
}
}
const SECS_PER_LEAP_YEAR: u64 = 366 * SECS_PER_DAY;
const SECS_PER_COMMON_YEAR: u64 = 365 * SECS_PER_DAY;
const DAYS_PER_MONTH_IN_LEAP: [u64; 12] = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DAYS_PER_MONTH_IN_COMMON: [u64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const SECS_PER_DAY: u64 = 24 * SECS_PER_HOUR;
const SECS_PER_HOUR: u64 = 60 * SECS_PER_MIN;
const SECS_PER_MIN: u64 = 60;
impl PartialEq<u64> for Timestamp {
fn eq(&self, other: &u64) -> bool {
self.to_ms().eq(other)
}
}
impl PartialEq<i64> for Timestamp {
fn eq(&self, other: &i64) -> bool {
if *other < 0 {
false
} else {
let other = *other as u64;
self.to_ms().eq(&other)
}
}
}
impl PartialOrd<u64> for Timestamp {
fn partial_cmp(&self, other: &u64) -> Option<cmp::Ordering> {
self.to_ms().partial_cmp(other)
}
}
impl PartialOrd<Timestamp> for Timestamp {
fn partial_cmp(&self, other: &Timestamp) -> Option<cmp::Ordering> {
match self.secs.partial_cmp(&other.secs) {
Some(cmp::Ordering::Equal) => self.nsecs.partial_cmp(&other.nsecs),
otherwise => otherwise,
}
}
}
impl Timestamped for fs::Metadata {
fn timestamp(&self) -> Timestamp {
let ft = FileTime::from_last_modification_time( self );
let s = ft.seconds_relative_to_1970();
let ns = ft.nanoseconds();
Timestamp::new(s as u64, ns as u64)
}
}