file_system/
time.rs

1//! Time representation and utilities for file system operations.
2//!
3//! This module provides time-related types and functionality specifically designed
4//! for file system metadata operations, including creation times, modification times,
5//! and access times.
6
7use core::fmt::{self, Display, Formatter};
8use core::time::Duration;
9use shared::unix_to_human_time;
10
11/// Represents a point in time for file system operations.
12///
13/// `Time_type` stores time as seconds since the Unix epoch (January 1, 1970).
14/// It's used throughout the file system for tracking file creation, modification,
15/// and access times. The type is designed to be efficient for storage and comparison.
16///
17/// # Examples
18///
19/// ```rust
20/// # extern crate alloc;
21/// use file_system::Time_type;
22///
23/// // Create a time representing the Unix epoch
24/// let epoch = Time_type::new(0);
25/// assert_eq!(epoch.As_u64(), 0);
26///
27/// // Create a time for a specific moment
28/// let time = Time_type::new(1642684800); // January 20, 2022
29/// ```
30///
31/// # Storage
32///
33/// Times are stored as 64-bit unsigned integers representing seconds, providing
34/// a range from 1970 to approximately year 584 billion, which is sufficient
35/// for any practical file system use.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37#[repr(transparent)]
38pub struct Time {
39    seconds: u64,
40}
41
42impl Time {
43    /// Create a new time from seconds since Unix epoch.
44    ///
45    /// # Arguments
46    ///
47    /// * `Seconds` - Number of seconds since January 1, 1970 00:00:00 UTC
48    ///
49    /// # Examples
50    ///
51    /// ```rust
52    /// # extern crate alloc;
53    /// use file_system::Time_type;
54    ///
55    /// let time = Time_type::new(1640995200); // January 1, 2022
56    /// ```
57    pub const fn new(seconds: u64) -> Self {
58        Self { seconds }
59    }
60
61    /// Get the time as seconds since Unix epoch.
62    ///
63    /// # Returns
64    ///
65    /// Number of seconds since January 1, 1970 00:00:00 UTC.
66    ///
67    /// # Examples
68    ///
69    /// ```rust
70    /// # extern crate alloc;
71    /// use file_system::Time_type;
72    ///
73    /// let time = Time_type::new(1640995200);
74    /// assert_eq!(time.As_u64(), 1640995200);
75    /// ```
76    pub const fn as_u64(self) -> u64 {
77        self.seconds
78    }
79}
80
81/// Convert from a duration to a time.
82///
83/// This treats the duration as an absolute time since the Unix epoch.
84impl From<Duration> for Time {
85    fn from(duration: Duration) -> Self {
86        Self {
87            seconds: duration.as_secs(),
88        }
89    }
90}
91
92/// Convert from a time to a duration.
93///
94/// This converts the absolute time to a duration since the Unix epoch.
95impl From<Time> for Duration {
96    fn from(time: Time) -> Self {
97        Duration::new(time.seconds, 0)
98    }
99}
100
101impl Display for Time {
102    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
103        let (year, month, day, hour, minute, second) = unix_to_human_time(self.seconds as i64);
104
105        write!(
106            f,
107            "{year:04}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02}",
108        )
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use alloc::{format, vec};
116
117    #[test]
118    fn test_time_creation() {
119        let time = Time::new(1640995200); // January 1, 2022
120        assert_eq!(time.as_u64(), 1640995200);
121    }
122
123    #[test]
124    fn test_time_epoch() {
125        let epoch = Time::new(0);
126        assert_eq!(epoch.as_u64(), 0);
127    }
128
129    #[test]
130    fn test_time_const_operations() {
131        // Test that New and As_u64 are const functions
132        const TIME: Time = Time::new(1234567890);
133        const SECONDS: u64 = TIME.as_u64();
134
135        assert_eq!(SECONDS, 1234567890);
136        assert_eq!(TIME.as_u64(), 1234567890);
137    }
138
139    #[test]
140    fn test_time_comparison() {
141        let early = Time::new(1000);
142        let late = Time::new(2000);
143
144        assert!(early < late);
145        assert!(late > early);
146        assert!(early <= late);
147        assert!(late >= early);
148        assert!(early <= early);
149        assert!(late >= late);
150        assert_eq!(early, early);
151        assert_ne!(early, late);
152    }
153
154    #[test]
155    fn test_time_ordering() {
156        let mut times = [
157            Time::new(3000),
158            Time::new(1000),
159            Time::new(2000),
160            Time::new(500),
161        ];
162
163        times.sort();
164
165        assert_eq!(times[0], Time::new(500));
166        assert_eq!(times[1], Time::new(1000));
167        assert_eq!(times[2], Time::new(2000));
168        assert_eq!(times[3], Time::new(3000));
169    }
170
171    #[test]
172    fn test_time_clone_copy() {
173        let original = Time::new(999);
174        let cloned = original;
175        let copied = original;
176
177        assert_eq!(original, cloned);
178        assert_eq!(original, copied);
179        assert_eq!(cloned, copied);
180
181        // Test that we can still use original after copying
182        assert_eq!(original.as_u64(), 999);
183    }
184
185    #[test]
186    fn test_time_debug() {
187        let time = Time::new(1640995200);
188        let debug_str = format!("{time:?}");
189        assert!(debug_str.contains("Time_type"));
190        assert!(debug_str.contains("1640995200"));
191    }
192
193    #[test]
194    fn test_time_hash() {
195        use alloc::collections::BTreeMap;
196
197        let time1 = Time::new(12345);
198        let time2 = Time::new(12345);
199        let time3 = Time::new(54321);
200
201        // Test that equal times can be used as keys in collections
202        let mut map = BTreeMap::new();
203        map.insert(time1, "first");
204        map.insert(time2, "second"); // Should overwrite first
205        map.insert(time3, "third");
206
207        assert_eq!(map.len(), 2); // time1 and time2 are equal, so only 2 entries
208        assert_eq!(map.get(&time1), Some(&"second"));
209        assert_eq!(map.get(&time3), Some(&"third"));
210    }
211
212    #[test]
213    fn test_time_from_duration() {
214        let duration = Duration::new(1640995200, 0);
215        let time: Time = duration.into();
216        assert_eq!(time.as_u64(), 1640995200);
217    }
218
219    #[test]
220    fn test_time_to_duration() {
221        let time = Time::new(1640995200);
222        let duration: Duration = time.into();
223        assert_eq!(duration.as_secs(), 1640995200);
224    }
225
226    #[test]
227    fn test_time_display_formatting() {
228        // Test display formatting
229        let time = Time::new(0); // Unix epoch
230        let display_str = format!("{time}");
231
232        // The exact format depends on Unix_to_human_time implementation
233        // We just verify it produces some reasonable format
234        assert!(display_str.contains("-"));
235        assert!(display_str.contains(":"));
236        assert!(display_str.len() > 10); // Should be a reasonable datetime string
237    }
238
239    #[test]
240    fn test_time_display_various_dates() {
241        // Test some known timestamps
242        let times = vec![
243            Time::new(0),          // 1970-01-01 00:00:00
244            Time::new(86400),      // 1970-01-02 00:00:00
245            Time::new(1640995200), // 2022-01-01 00:00:00 (approximately)
246        ];
247
248        for time in times {
249            let display_str = format!("{time}");
250            // Basic sanity checks
251            assert!(display_str.len() >= 19); // YYYY-MM-DD HH:MM:SS is 19 chars
252            assert!(display_str.contains("-"));
253            assert!(display_str.contains(":"));
254            assert!(display_str.contains(" "));
255        }
256    }
257
258    #[test]
259    fn test_time_max_value() {
260        let max_time = Time::new(u64::MAX);
261        assert_eq!(max_time.as_u64(), u64::MAX);
262
263        // Should still be convertible to duration
264        let duration: Duration = max_time.into();
265        assert_eq!(duration.as_secs(), u64::MAX);
266    }
267
268    #[test]
269    fn test_time_zero_and_max_comparison() {
270        let zero = Time::new(0);
271        let max = Time::new(u64::MAX);
272
273        assert!(zero < max);
274        assert!(max > zero);
275        assert_ne!(zero, max);
276    }
277
278    #[test]
279    fn test_time_round_trip_conversions() {
280        let original_seconds = 1640995200u64;
281
282        // Time -> Duration -> Time
283        let time = Time::new(original_seconds);
284        let duration: Duration = time.into();
285        let back_to_time: Time = duration.into();
286
287        assert_eq!(time, back_to_time);
288        assert_eq!(original_seconds, back_to_time.as_u64());
289    }
290
291    #[test]
292    fn test_time_type_size() {
293        use core::mem::{align_of, size_of};
294
295        // Should be same size as u64 due to repr(transparent)
296        assert_eq!(size_of::<Time>(), size_of::<u64>());
297        assert_eq!(align_of::<Time>(), align_of::<u64>());
298    }
299
300    #[test]
301    fn test_time_sequence() {
302        // Test a sequence of times
303        use alloc::vec::Vec;
304        let times: Vec<Time> = (0..10)
305            .map(|i| Time::new(i * 86400)) // Each day
306            .collect();
307
308        // Verify they're in ascending order
309        for i in 1..times.len() {
310            assert!(times[i - 1] < times[i]);
311        }
312    }
313}