1use std::time::{SystemTime, UNIX_EPOCH};
2use std::fmt;
3
4#[derive(Debug)]
6pub struct Date {
7 year: u16,
8 month: u8,
9 day: u8,
10}
11
12impl Date {
13 pub fn new(year: u16, month: u8, day: u8) -> Option<Self> {
15 if month < 1 || month > 12 || day < 1 || day > 31 {
16 return None;
17 }
18 Some(Date { year, month, day })
19 }
20}
21
22impl fmt::Display for Date {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
25 }
26}
27
28
29fn system_time_to_units(time: SystemTime) -> (u16, u8, u8, u8, u8, u8, u16) {
31 let duration = time.duration_since(UNIX_EPOCH).expect("Time went backwards");
32 let secs = duration.as_secs();
33 let nanos = duration.subsec_nanos();
34
35 let days_since_epoch = secs / 86400;
37 let remaining_secs = secs % 86400;
38
39 let mut year = 1970;
41 let mut remaining_days = days_since_epoch as i64;
42
43 loop {
44 let is_leap_year = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
45 let days_in_year = if is_leap_year { 366 } else { 365 };
46
47 if remaining_days < days_in_year {
48 break;
49 }
50
51 remaining_days -= days_in_year;
52 year += 1;
53 }
54
55 let is_leap_year = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
56 let days_in_month = [
57 31,
58 if is_leap_year { 29 } else { 28 },
59 31,
60 30,
61 31,
62 30,
63 31,
64 31,
65 30,
66 31,
67 30,
68 31,
69 ];
70
71 let mut month = 1;
72 for &days in &days_in_month {
73 if remaining_days < days as i64 {
74 break;
75 }
76 remaining_days -= days as i64;
77 month += 1;
78 }
79
80 let day = remaining_days as u8 + 1;
81
82 let hour = (remaining_secs / 3600) as u8;
84 let minute = ((remaining_secs % 3600) / 60) as u8;
85 let second = (remaining_secs % 60) as u8;
86
87 let millisecond = (nanos / 1_000_000) as u16;
89
90 (year as u16, month as u8, day, hour, minute, second, millisecond)
91}
92
93pub fn format_date(time: SystemTime, format: &str) -> String {
95 let (year, month, day,_,_,_,_) = system_time_to_units(time);
96
97 format
99 .replace("%Y", &year.to_string())
100 .replace("%m", &format!("{:02}", month))
101 .replace("%d", &format!("{:02}", day))
102}
103
104pub fn parse_date(date_str: &str) -> Result<Date, &'static str> {
106 let parts: Vec<&str> = date_str.split('-').collect();
107 if parts.len() != 3 {
108 return Err("Invalid date format");
109 }
110
111 let year = parts[0].parse().map_err(|_| "Invalid year")?;
112 let month = parts[1].parse().map_err(|_| "Invalid month")?;
113 let day = parts[2].parse().map_err(|_| "Invalid day")?;
114
115 Date::new(year, month, day).ok_or("Invalid date")
116}
117
118pub fn days_between(time1: SystemTime, time2: SystemTime) -> i64 {
120 let duration1 = time1
121 .duration_since(UNIX_EPOCH)
122 .expect("Time went backwards");
123 let duration2 = time2
124 .duration_since(UNIX_EPOCH)
125 .expect("Time went backwards");
126
127 let secs1 = duration1.as_secs();
128 let secs2 = duration2.as_secs();
129
130 let days1 = secs1 / 86400;
131 let days2 = secs2 / 86400;
132
133 (days1 as i64 - days2 as i64).abs()
134}
135
136
137pub enum DateFormat {
141 YearMonthDay, YearMonthDayNoLine, YearMonthDayChinese, MonthDayYear, DayMonthYear, DateTime, DateTimeWithMillis, }
149
150use chrono::{DateTime, Local, TimeZone, Utc};
152pub fn format_timestamp(timestamp: u64, format: DateFormat) -> String {
153 let utc_datetime: DateTime<Utc> = Utc.timestamp_opt(timestamp as i64, 0).unwrap();
155 let local_datetime: DateTime<Local> = utc_datetime.with_timezone(&Local);
157 match format {
158 DateFormat::YearMonthDay => local_datetime.format("%Y-%m-%d").to_string(),
159 DateFormat::YearMonthDayNoLine => local_datetime.format("%Y%m%d").to_string(),
160 DateFormat::YearMonthDayChinese => local_datetime.format("%Y年%m月%d日").to_string(),
161 DateFormat::MonthDayYear => local_datetime.format("%m/%d/%Y").to_string(),
162 DateFormat::DayMonthYear => local_datetime.format("%d-%m-%Y").to_string(),
163 DateFormat::DateTime => local_datetime.format("%Y-%m-%d %H:%M:%S").to_string(),
164 DateFormat::DateTimeWithMillis => local_datetime.format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
165 }
166}