1#![allow(dead_code)]
18
19pub use chrono::{DateTime, Datelike, Timelike, Utc, offset::TimeZone};
20
21#[derive(Debug)]
22pub enum SetTimeError {
23 PermissionDenied,
24 InvalidTime,
25}
26
27impl std::fmt::Display for SetTimeError {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 SetTimeError::PermissionDenied => write!(f, "Permission denied, are you running as administrator/root?"),
31 SetTimeError::InvalidTime => write!(f, "Invalid time format"),
32 }
33 }
34}
35impl std::error::Error for SetTimeError {}
36
37#[cfg(target_os = "windows")]
38fn set_time_windows<D: Datelike + Timelike>(time: D) -> Result<(), SetTimeError> {
39 use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
40 let time: SYSTEMTIME = SYSTEMTIME {
41 wYear: time.year() as u16,
42 wMonth: time.month() as u16,
43 wDayOfWeek: time.weekday().num_days_from_sunday() as u16,
44 wDay: time.day() as u16,
45 wHour: time.hour() as u16,
46 wMinute: time.minute() as u16,
47 wSecond: time.second() as u16,
48 wMilliseconds: (time.nanosecond() / 1_000_000) as u16,
49 };
50 unsafe {
51 let result = SetSystemTime(&time);
52 if result == 0 {
53 return Err(SetTimeError::PermissionDenied);
54 }
55 }
56 Ok(())
57}
58
59#[cfg(unix)]
60fn set_time_unix<D: Datelike + Timelike>(time: D) -> Result<(), SetTimeError> {
61 let naive_date = chrono::NaiveDate::from_ymd_opt(time.year(), time.month(), time.day())
62 .ok_or(SetTimeError::InvalidTime)?;
63 let naive_time = chrono::NaiveTime::from_hms_nano_opt(time.hour(), time.minute(), time.second(), time.nanosecond())
64 .ok_or(SetTimeError::InvalidTime)?;
65 let naive_dt = chrono::NaiveDateTime::new(naive_date, naive_time);
66
67 let timestamp = naive_dt.and_utc().timestamp();
69
70 let tv = libc::timeval {
71 tv_sec: timestamp as libc::time_t,
72 tv_usec: (time.nanosecond() / 1000) as libc::suseconds_t,
73 };
74
75 unsafe {
76 let result = libc::settimeofday(&tv, std::ptr::null());
77 if result != 0 {
78 return Err(SetTimeError::PermissionDenied);
79 }
80 }
81 Ok(())
82}
83
84#[cfg(target_os = "macos")]
85fn set_time_macos<D: Datelike + Timelike>(time: D) -> Result<(), SetTimeError> {
86 set_time_unix(time)
87}
88
89#[cfg(target_os = "linux")]
90fn set_time_linux<D: Datelike + Timelike>(time: D) -> Result<(), SetTimeError> {
91 set_time_unix(time)
92}
93
94pub fn set_time<D: Datelike + Timelike>(time: D) -> Result<(), SetTimeError> {
101 #[cfg(target_os = "windows")]
102 return set_time_windows(time);
103
104 #[cfg(target_os = "linux")]
105 return set_time_linux(time);
106
107 #[cfg(target_os = "macos")]
108 return set_time_macos(time);
109
110 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
111 compile_error!("Unsupported platform");
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use chrono::NaiveDateTime;
118
119 #[test]
120 fn test_set_time() {
121 let original_time = chrono::Utc::now();
122 let new_time = NaiveDateTime::parse_from_str("2020-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
123 .unwrap()
124 .and_utc();
125 let result = set_time(new_time);
126 assert!(result.is_ok());
127 let now_after = chrono::Utc::now();
128 assert_eq!(now_after.year(), 2020);
129 assert_eq!(now_after.month(), 1);
130 assert_eq!(now_after.day(), 1);
131 set_time(original_time).expect("Failed to restore original time");
132 }
133}