1use std::{ops::{Add, AddAssign, Sub}, time::{Duration, SystemTime, UNIX_EPOCH}};
2
3#[derive(Clone, Default, Copy)]
12pub struct WinFiletime(pub u64);
13
14
15#[derive(Clone, Default, Copy)]
23pub struct UnixTimestamp(pub u64);
24
25#[derive(Clone, Default, Copy)]
37pub struct Filetime {
38 original : u64,
39 year : u16,
40 month : u8,
41 day : u8,
42 hour : u8,
43 minute : u8,
44 second : u8,
45 nanos : u32
46}
47
48impl Filetime {
49 pub fn new(timestap : u64) -> Self {
51 let nanoseconds_since_beginning = (timestap as u128) * 100;
52 let days_since_beginning = nanoseconds_since_beginning.div_euclid(60 * 60 * 24 * 1_000_000_000);
53 let nanoseconds_in_day = nanoseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1_000_000_000;
54 let (year, restant_days) = to_years(days_since_beginning);
55 let (month, acumulated_day_month) = if is_leap_year(year) {
56 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
57 .iter()
58 .position(|&v| v > restant_days)
59 .map(|pos| {
60 (
61 pos,
62 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][pos - 1],
63 )
64 })
65 } else {
66 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
67 .iter()
68 .position(|&v| v > restant_days)
69 .map(|pos| {
70 (
71 pos,
72 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][pos - 1],
73 )
74 })
75 }
76 .unwrap_or((12, 335));
77 let day = restant_days.saturating_sub(acumulated_day_month) + 1;
78 let hour = nanoseconds_in_day.div_euclid(60 * 60 * 1_000_000_000);
79 let rest_nanos = nanoseconds_in_day - hour * 60*60*1_000_000_000;
80 let minute = rest_nanos.div_euclid(60 * 1_000_000_000);
81 let rest_nanos = rest_nanos - minute * 60 * 1_000_000_000;
82 let second = rest_nanos.div_euclid(1_000_000_000);
83 let nanos = rest_nanos - second*1_000_000_000;
84 Self {
85 original : timestap,
86 year : year as u16,
87 month: month as u8,
88 day: day as u8,
89 hour: hour as u8,
90 minute: minute as u8,
91 second: second as u8,
92 nanos: nanos as u32,
93 }
94 }
95 pub fn with_ymd_and_hms(year : u16, month : u8, day : u8, hour : u8, minute : u8, second : u8, nanos : u32 ) -> Self {
97 let days_since_begining = days_from_year(year) as u64;
98 let days_since_start_year = acumulated_day_month(month, year) as u64;
99 let days = days_since_begining + days_since_start_year + day as u64 - 1;
100 let original = (nanos as u64 / 100) + ((second as u64 + (60u64 * (minute as u64 + (60u64 * (hour as u64 + 24 * days))))) * 10_000_000u64);
101
102 Self {
103 year,
104 month,
105 day,
106 hour,
107 minute,
108 second,
109 nanos,
110 original
111 }
112 }
113
114 pub fn year(&self) -> u16 {
122 self.year
123 }
124 pub fn month(&self) -> u8 {
132 self.month
133 }
134 pub fn day(&self) -> u8 {
142 self.day
143 }
144 pub fn hour(&self) -> u8 {
152 self.hour
153 }
154 pub fn minute(&self) -> u8 {
162 self.minute
163 }
164 pub fn second(&self) -> u8 {
172 self.second
173 }
174 pub fn millis(&self) -> u32 {
182 self.nanos / 1_000_000
183 }
184 pub fn nanoseconds(&self) -> u32 {
192 self.nanos
193 }
194 pub fn filetime(&self) -> u64 {
201 self.original
202 }
203
204 pub fn duration_since(&self, earlier : SystemTime) -> Result<Duration, Duration> {
212 let nano_epoch = earlier.duration_since(UNIX_EPOCH).map_err(|e| e.duration())?;
213 let nanos = nano_epoch.as_nanos();
214 let self_nanos = self.original as u128 * 100;
215
216 if nanos > self_nanos {
217 return Err(Duration::from_nanos((nanos - self_nanos) as u64))
218 }
219 Ok(Duration::from_nanos((self_nanos - nanos) as u64))
220 }
221}
222
223impl std::fmt::Debug for Filetime {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 if self.nanos == 0 {
226 f.write_fmt(format_args!(
227 "{:02}-{:02}-{:04} {:02}:{:02}:{:02}", self.day, self.month, self.year, self.hour, self.minute, self.second
228 ))
229 }else {
230 f.write_fmt(format_args!(
231 "{:02}-{:02}-{:04} {:02}:{:02}:{:02}.{:03}", self.day, self.month, self.year, self.hour, self.minute, self.second, self.nanos / 1_000_000
232 ))
233 }
234 }
235}
236
237impl std::fmt::Display for Filetime {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 write!(f, "{:?}", self)
240 }
241}
242
243impl Add<Duration> for Filetime {
244 type Output = Filetime;
245
246 fn add(self, rhs: Duration) -> Self::Output {
247 let nanos = rhs.as_nanos();
248 Self::new(((self.original as u128) * 100 + nanos).div_euclid(100) as u64)
249 }
250}
251
252impl AddAssign<Duration> for Filetime {
253 fn add_assign(&mut self, rhs: Duration) {
254 let nanos = rhs.as_nanos();
255 let nw = Self::new(((self.original as u128) * 100 + nanos).div_euclid(100) as u64);
256 self.hour = nw.hour;
257 self.day = nw.day;
258 self.minute = nw.minute;
259 self.nanos = nw.nanos;
260 self.second = nw.second;
261 self.year = nw.year;
262 self.original = nw.original;
263 }
264}
265
266impl Sub<Duration> for Filetime {
267 type Output = Filetime;
268
269 fn sub(self, rhs: Duration) -> Self::Output {
270 let nanos = rhs.as_nanos();
271 Self::new(((self.original as u128) * 100 - nanos).div_euclid(100) as u64)
272 }
273}
274
275
276pub fn filetime_to_unix_timestamp(filetime: u64) -> u64 {
284 (filetime as u128)
285 .div_ceil(10_000u128)
286 .saturating_sub(11644473600000u128) as u64
287}
288
289pub fn filetime_to_system_time(filetime: u64) -> std::time::SystemTime {
298 UNIX_EPOCH + std::time::Duration::from_millis(filetime_to_unix_timestamp(filetime))
299}
300
301impl From<u64> for WinFiletime {
302 fn from(value: u64) -> Self {
303 WinFiletime(value)
304 }
305}
306impl From<u64> for UnixTimestamp {
307 fn from(value: u64) -> Self {
308 UnixTimestamp(value)
309 }
310}
311
312
313impl std::fmt::Debug for WinFiletime {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
316 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
317 let milliseconds_in_day = milliseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1000;
318 let (year, restant_days) = to_years(days_since_beginning);
319 let (month, acumulated_day_month) = if is_leap_year(year) {
320 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
321 .iter()
322 .position(|&v| v > restant_days)
323 .map(|pos| {
324 (
325 pos,
326 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][pos - 1],
327 )
328 })
329 } else {
330 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
331 .iter()
332 .position(|&v| v > restant_days)
333 .map(|pos| {
334 (
335 pos,
336 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][pos - 1],
337 )
338 })
339 }
340 .unwrap_or((12, 335));
341 let day = restant_days.saturating_sub(acumulated_day_month) + 1;
342 let hours = milliseconds_in_day.div_euclid(60 * 60 * 1000);
343 let rest_millis = milliseconds_in_day - hours * 60*60*1000;
344 let minute = rest_millis.div_euclid(60 * 1000);
345 let rest_millis = rest_millis - minute * 60 * 1000;
346 let seconds = rest_millis.div_euclid(1000);
347 let millis = rest_millis - seconds*1000;
348 if millis == 0 {
349 f.write_fmt(format_args!(
350 "{:02}-{:02}-{:04} {:02}:{:02}:{:02}", day, month,year, hours, minute, seconds
351 ))
352 }else {
353 f.write_fmt(format_args!(
354 "{:02}-{:02}-{:04} {:02}:{:02}:{:02}.{:03}", day, month,year, hours, minute, seconds, millis
355 ))
356 }
357 }
358}
359
360impl std::fmt::Debug for UnixTimestamp {
361 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362 let milliseconds_since_beginning = self.0 as u128;
363 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
364 let milliseconds_in_day = milliseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1000;
365 let (year, restant_days) = to_years_unix(days_since_beginning);
366 let (month, acumulated_day_month) = if is_leap_year(year) {
367 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
368 .iter()
369 .position(|&v| v > restant_days)
370 .map(|pos| {
371 (
372 pos,
373 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][pos - 1],
374 )
375 })
376 } else {
377 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
378 .iter()
379 .position(|&v| v > restant_days)
380 .map(|pos| {
381 (
382 pos,
383 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][pos - 1],
384 )
385 })
386 }
387 .unwrap_or((12, 335));
388 let day = restant_days.saturating_sub(acumulated_day_month) + 1;
389 let hours = milliseconds_in_day.div_euclid(60 * 60 * 1000);
390 let rest_millis = milliseconds_in_day - hours * 60*60*1000;
391 let minute = rest_millis.div_euclid(60 * 1000);
392 let rest_millis = rest_millis - minute * 60 * 1000;
393 let seconds = rest_millis.div_euclid(1000);
394 let millis = rest_millis - seconds*1000;
395 if millis == 0 {
396 f.write_fmt(format_args!(
397 "{:02}-{:02}-{:04} {:02}:{:02}:{:02}", day, month,year, hours, minute, seconds
398 ))
399 }else {
400 f.write_fmt(format_args!(
401 "{:02}-{:02}-{:04} {:02}:{:02}:{:02}.{:03}", day, month,year, hours, minute, seconds, millis
402 ))
403 }
404 }
405}
406
407fn acumulated_day_month(month : u8, year : u16) -> u16 {
408 if is_leap_year(year) {
409 if month >= 12 {
410 return 366u16
411 }
412 [0,0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][month as usize]
413 } else {
414 if month >= 12 {
415 return 365u16
416 }
417 [0,0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month as usize]
418 }
419}
420
421fn is_leap_year(year: u16) -> bool {
422 (year % 4 == 0 && year % 100 != 0) || (year % 100 == 0 && year % 400 == 0)
423}
424fn to_years(mut days : u128) -> (u16, u128) {
425 let mut year = 1601;
426 while days >= 365 {
427 days -= 365;
428 year += 1;
429 if days < 365 {
430 break
431 }
432 if is_leap_year(year) {
433 days -= 1;
434 }
435 }
436 (year, days)
437}
438fn days_from_year(year : u16) -> u128 {
439 let mut days = 0;
440 if year <= 1601 {
441 return 0
442 }
443 for yr in 1601..year {
444 if is_leap_year(yr) {
445 days += 1;
446 }
447 days += 365;
448 }
449 days
450}
451fn to_years_unix(mut days : u128) -> (u16, u128) {
452 let mut year = 1970;
453 while days >= 365 {
454 days -= 365;
455 year += 1;
456 if days < 365 {
457 break
458 }
459 if is_leap_year(year) {
460 days -= 1;
461 }
462 }
463 (year, days)
464}
465
466impl From<WinFiletime> for SystemTime {
467 fn from(val: WinFiletime) -> Self {
468 filetime_to_system_time(val.0)
469 }
470}
471impl From<&WinFiletime> for SystemTime {
472 fn from(val: &WinFiletime) -> Self {
473 filetime_to_system_time(val.0)
474 }
475}
476impl From<Filetime> for SystemTime {
477 fn from(val: Filetime) -> Self {
478 filetime_to_system_time(val.original)
479 }
480}
481impl From<&Filetime> for SystemTime {
482 fn from(val: &Filetime) -> Self {
483 filetime_to_system_time(val.original)
484 }
485}
486
487impl WinFiletime {
488 pub fn new() -> Self {
489 Self(0)
490 }
491
492 pub fn year(&self) -> u32 {
500 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
501 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
502 let (year, _) = to_years(days_since_beginning);
503 year as u32
504 }
505
506 pub fn month(&self) -> u32 {
514 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
515 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
516 let (year, restant_days) = to_years(days_since_beginning);
517 let (month, _acumulated_day_month) = if is_leap_year(year) {
518 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
519 .iter()
520 .position(|&v| v > restant_days)
521 .map(|pos| {
522 (
523 pos,
524 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][pos - 1],
525 )
526 })
527 } else {
528 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
529 .iter()
530 .position(|&v| v > restant_days)
531 .map(|pos| {
532 (
533 pos,
534 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][pos - 1],
535 )
536 })
537 }
538 .unwrap_or((12, 335));
539 month as u32
540 }
541
542 pub fn day(&self) -> u32 {
550 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
551 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
552 let (year, restant_days) = to_years(days_since_beginning);
553 let (_month, acumulated_day_month) = if is_leap_year(year) {
554 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
555 .iter()
556 .position(|&v| v > restant_days)
557 .map(|pos| {
558 (
559 pos,
560 [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][pos - 1],
561 )
562 })
563 } else {
564 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
565 .iter()
566 .position(|&v| v > restant_days)
567 .map(|pos| {
568 (
569 pos,
570 [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][pos - 1],
571 )
572 })
573 }
574 .unwrap_or((12, 335));
575 let day = restant_days.saturating_sub(acumulated_day_month) + 1;
576 day as u32
577 }
578
579 pub fn hour(&self) -> u32 {
587 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
588 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
589 let milliseconds_in_day = milliseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1000;
590 let hours = milliseconds_in_day.div_euclid(60 * 60 * 1000);
591 hours as u32
592 }
593
594 pub fn minute(&self) -> u32 {
602 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
603 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
604 let milliseconds_in_day = milliseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1000;
605 let hours = milliseconds_in_day.div_euclid(60 * 60 * 1000);
606 let rest_millis = milliseconds_in_day - hours * 60*60*1000;
607 let minute = rest_millis.div_euclid(60 * 1000);
608 minute as u32
609 }
610 pub fn second(&self) -> u32 {
618 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
619 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
620 let milliseconds_in_day = milliseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1000;
621 let hours = milliseconds_in_day.div_euclid(60 * 60 * 1000);
622 let rest_millis = milliseconds_in_day - hours * 60*60*1000;
623 let minute = rest_millis.div_euclid(60 * 1000);
624 let rest_millis = rest_millis - minute * 60 * 1000;
625 let seconds = rest_millis.div_euclid(1000);
626 seconds as u32
627 }
628 pub fn milliseconds(&self) -> u32 {
636 let milliseconds_since_beginning = (self.0 as u128).div_euclid(10_000u128);
637 let days_since_beginning = milliseconds_since_beginning.div_euclid(60 * 60 * 24 * 1000);
638 let milliseconds_in_day = milliseconds_since_beginning - days_since_beginning *60 * 60 * 24 * 1000;
639 let hours = milliseconds_in_day.div_euclid(60 * 60 * 1000);
640 let rest_millis = milliseconds_in_day - hours * 60*60*1000;
641 let minute = rest_millis.div_euclid(60 * 1000);
642 let rest_millis = rest_millis - minute * 60 * 1000;
643 let seconds = rest_millis.div_euclid(1000);
644 let millis = rest_millis - seconds*1000;
645 millis as u32
646 }
647
648}
649
650impl PartialEq for Filetime {
651 fn eq(&self, other: &Self) -> bool {
652 self.original == other.original
653 }
654}
655impl Eq for Filetime {}
656
657impl PartialOrd for Filetime {
658 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
659 Some(self.original.cmp(&other.original))
660 }
661}
662
663impl Ord for Filetime {
664 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
665 self.original.cmp(&other.original)
666 }
667}
668
669#[test]
670fn should_generate_valid_windows_timestamps() {
671 assert_eq!(
672 1706969423596,
673 filetime_to_unix_timestamp(133514430235959706u64)
674 );
675 let time = filetime_to_system_time(133514430235959706u64); assert_eq!(
677 1706969423,
678 time.duration_since(UNIX_EPOCH).unwrap().as_secs()
679 );
680 assert_eq!(
681 1706969423596,
682 time.duration_since(UNIX_EPOCH).unwrap().as_millis()
683 );
684 println!("{:?}", time.duration_since(UNIX_EPOCH).unwrap().as_millis());
685}
686
687#[test]
688fn should_transform_to_calendar() {
689 assert_eq!("01-02-2024 00:00:00", format!("{:?}", WinFiletime(133512192000000000)));
690 assert_eq!("01-01-2024 14:10:23", format!("{:?}", WinFiletime(133485918230000000)));
691 assert_eq!("03-02-2024 14:10:23", format!("{:?}", WinFiletime(133514430230000000)));
692 assert_eq!("03-02-2024 14:10:23", format!("{:?}", WinFiletime(133514430230000000)));
693 assert_eq!("01-01-1601 00:00:00", format!("{:?}", WinFiletime(0)));
694 assert_eq!("01-01-1602 00:00:00", format!("{:?}", WinFiletime(315360000000000)));
695 assert_eq!("01-01-1605 00:00:00", format!("{:?}", WinFiletime(1262304000000000)));
696 assert_eq!("14-11-1999 18:27:59", format!("{:?}", WinFiletime(125870776790000000)));
697 assert_eq!("14-11-2000 18:27:59", format!("{:?}", WinFiletime(126187000790000000)));
698 assert_eq!("29-02-2000 18:27:59.001", format!("{:?}", WinFiletime(125963224790010000)));
700 assert_eq!("01-03-1900 18:27:59", format!("{:?}", WinFiletime(94406488790000000)));
702 assert_eq!("28-02-1900 18:27:59", format!("{:?}", WinFiletime(94405624790000000)));
703
704 let time = WinFiletime(125963224790010000);
705 assert_eq!(29, time.day());
706 assert_eq!(2, time.month());
707 assert_eq!(2000, time.year());
708 assert_eq!(18, time.hour());
709 assert_eq!(27, time.minute());
710 assert_eq!(59, time.second());
711 assert_eq!(1, time.milliseconds());
712}
713
714#[test]
715fn should_transform_unix_to_calendar() {
716 assert_eq!("01-02-2024 00:00:00", format!("{:?}", UnixTimestamp(1706745600000)));
717 assert_eq!("01-01-2024 14:10:23", format!("{:?}", UnixTimestamp(1704118223000)));
718 assert_eq!("03-02-2024 14:10:23", format!("{:?}", UnixTimestamp(1706969423000)));
719 assert_eq!("01-01-1970 00:00:00", format!("{:?}", UnixTimestamp(0)));
720 assert_eq!("01-01-1972 00:00:00", format!("{:?}", UnixTimestamp(63072000000)));
721 assert_eq!("14-11-1999 18:27:59", format!("{:?}", UnixTimestamp(942604079000)));
722 assert_eq!("14-11-2000 18:27:59", format!("{:?}", UnixTimestamp(974226479000)));
723 assert_eq!("29-02-2000 18:27:59.001", format!("{:?}", UnixTimestamp(951848879001)));
725}
726
727#[test]
728fn should_generate_valid_filetime() {
729 let time = Filetime::new(125963224790010000);
730 assert_eq!("29-02-2000 18:27:59.001", &format!("{}", time));
731 assert_eq!(time, Filetime::with_ymd_and_hms(2000, 2, 29, 18, 27, 59, 1000000));
732 let time = Filetime::new(94405624790010000);
733 assert_eq!(time, Filetime::with_ymd_and_hms(1900, 2, 28, 18, 27, 59, 1000000));
734 assert_eq!("28-02-1900 18:27:59.001", format!("{}", time));
735}