Skip to content

Commit e3eb2f7

Browse files
authored
refactor: Change the inner structure of DateTime (#267)
Hold the values ​​as packed date and packed time. Note that the second resolution is changed to 2 seconds.
1 parent 38bbd70 commit e3eb2f7

File tree

1 file changed

+96
-83
lines changed

1 file changed

+96
-83
lines changed

src/types.rs

Lines changed: 96 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use crate::cp437::FromCp437;
33
use crate::write::{FileOptionExtension, FileOptions};
44
use path::{Component, Path, PathBuf};
5+
use std::cmp::Ordering;
56
use std::fmt;
67
use std::fmt::{Debug, Formatter};
78
use std::mem;
@@ -79,14 +80,10 @@ impl From<System> for u8 {
7980
///
8081
/// Modern zip files store more precise timestamps; see [`crate::extra_fields::ExtendedTimestamp`]
8182
/// for details.
82-
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
83+
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
8384
pub struct DateTime {
84-
year: u16,
85-
month: u8,
86-
day: u8,
87-
hour: u8,
88-
minute: u8,
89-
second: u8,
85+
datepart: u16,
86+
timepart: u16,
9087
}
9188

9289
impl Debug for DateTime {
@@ -96,11 +93,43 @@ impl Debug for DateTime {
9693
}
9794
f.write_fmt(format_args!(
9895
"DateTime::from_date_and_time({}, {}, {}, {}, {}, {})?",
99-
self.year, self.month, self.day, self.hour, self.minute, self.second
96+
self.year(),
97+
self.month(),
98+
self.day(),
99+
self.hour(),
100+
self.minute(),
101+
self.second()
100102
))
101103
}
102104
}
103105

106+
impl Ord for DateTime {
107+
fn cmp(&self, other: &Self) -> Ordering {
108+
if let ord @ (Ordering::Less | Ordering::Greater) = self.year().cmp(&other.year()) {
109+
return ord;
110+
}
111+
if let ord @ (Ordering::Less | Ordering::Greater) = self.month().cmp(&other.month()) {
112+
return ord;
113+
}
114+
if let ord @ (Ordering::Less | Ordering::Greater) = self.day().cmp(&other.day()) {
115+
return ord;
116+
}
117+
if let ord @ (Ordering::Less | Ordering::Greater) = self.hour().cmp(&other.hour()) {
118+
return ord;
119+
}
120+
if let ord @ (Ordering::Less | Ordering::Greater) = self.minute().cmp(&other.minute()) {
121+
return ord;
122+
}
123+
self.second().cmp(&other.second())
124+
}
125+
}
126+
127+
impl PartialOrd for DateTime {
128+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
129+
Some(self.cmp(other))
130+
}
131+
}
132+
104133
impl DateTime {
105134
/// Returns the current time if possible, otherwise the default of 1980-01-01.
106135
#[cfg(feature = "time")]
@@ -120,14 +149,15 @@ impl DateTime {
120149
#[cfg(fuzzing)]
121150
impl arbitrary::Arbitrary<'_> for DateTime {
122151
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Self> {
123-
Ok(DateTime {
124-
year: u.int_in_range(1980..=2107)?,
125-
month: u.int_in_range(1..=12)?,
126-
day: u.int_in_range(1..=31)?,
127-
hour: u.int_in_range(0..=23)?,
128-
minute: u.int_in_range(0..=59)?,
129-
second: u.int_in_range(0..=58)?,
130-
})
152+
let year: u16 = u.int_in_range(1980..=2107)?;
153+
let month: u16 = u.int_in_range(1..=12)?;
154+
let day: u16 = u.int_in_range(1..=31)?;
155+
let datepart = day | (month << 5) | ((year - 1980) << 9);
156+
let hour: u16 = u.int_in_range(0..=23)?;
157+
let minute: u16 = u.int_in_range(0..=59)?;
158+
let second: u16 = u.int_in_range(0..=58)?;
159+
let timepart = (second >> 1) | (minute << 5) | (hour << 11);
160+
Ok(DateTime { datepart, timepart })
131161
}
132162
}
133163

@@ -152,11 +182,18 @@ impl TryFrom<DateTime> for NaiveDateTime {
152182
type Error = DateTimeRangeError;
153183

154184
fn try_from(value: DateTime) -> Result<Self, Self::Error> {
155-
let date = NaiveDate::from_ymd_opt(value.year.into(), value.month.into(), value.day.into())
156-
.ok_or(DateTimeRangeError)?;
157-
let time =
158-
NaiveTime::from_hms_opt(value.hour.into(), value.minute.into(), value.second.into())
159-
.ok_or(DateTimeRangeError)?;
185+
let date = NaiveDate::from_ymd_opt(
186+
value.year().into(),
187+
value.month().into(),
188+
value.day().into(),
189+
)
190+
.ok_or(DateTimeRangeError)?;
191+
let time = NaiveTime::from_hms_opt(
192+
value.hour().into(),
193+
value.minute().into(),
194+
value.second().into(),
195+
)
196+
.ok_or(DateTimeRangeError)?;
160197
Ok(NaiveDateTime::new(date, time))
161198
}
162199
}
@@ -181,12 +218,8 @@ impl Default for DateTime {
181218
/// Constructs an 'default' datetime of 1980-01-01 00:00:00
182219
fn default() -> DateTime {
183220
DateTime {
184-
year: 1980,
185-
month: 1,
186-
day: 1,
187-
hour: 0,
188-
minute: 0,
189-
second: 0,
221+
datepart: 0b0000000000100001,
222+
timepart: 0,
190223
}
191224
}
192225
}
@@ -197,7 +230,12 @@ impl fmt::Display for DateTime {
197230
write!(
198231
f,
199232
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
200-
self.year, self.month, self.day, self.hour, self.minute, self.second
233+
self.year(),
234+
self.month(),
235+
self.day(),
236+
self.hour(),
237+
self.minute(),
238+
self.second()
201239
)
202240
}
203241
}
@@ -208,21 +246,7 @@ impl DateTime {
208246
/// # Safety
209247
/// The caller must ensure the date and time are valid.
210248
pub const unsafe fn from_msdos_unchecked(datepart: u16, timepart: u16) -> DateTime {
211-
let seconds = (timepart & 0b0000000000011111) << 1;
212-
let minutes = (timepart & 0b0000011111100000) >> 5;
213-
let hours = (timepart & 0b1111100000000000) >> 11;
214-
let days = datepart & 0b0000000000011111;
215-
let months = (datepart & 0b0000000111100000) >> 5;
216-
let years = (datepart & 0b1111111000000000) >> 9;
217-
218-
DateTime {
219-
year: years + 1980,
220-
month: months as u8,
221-
day: days as u8,
222-
hour: hours as u8,
223-
minute: minutes as u8,
224-
second: seconds as u8,
225-
}
249+
DateTime { datepart, timepart }
226250
}
227251

228252
/// Converts an msdos (u16, u16) pair to a DateTime object if it represents a valid date and
@@ -283,30 +307,17 @@ impl DateTime {
283307
if day > max_day {
284308
return Err(DateTimeRangeError);
285309
}
286-
Ok(DateTime {
287-
year,
288-
month,
289-
day,
290-
hour,
291-
minute,
292-
second,
293-
})
310+
let datepart = (day as u16) | ((month as u16) << 5) | ((year - 1980) << 9);
311+
let timepart = ((second as u16) >> 1) | ((minute as u16) << 5) | ((hour as u16) << 11);
312+
Ok(DateTime { datepart, timepart })
294313
} else {
295314
Err(DateTimeRangeError)
296315
}
297316
}
298317

299318
/// Indicates whether this date and time can be written to a zip archive.
300319
pub fn is_valid(&self) -> bool {
301-
DateTime::from_date_and_time(
302-
self.year,
303-
self.month,
304-
self.day,
305-
self.hour,
306-
self.minute,
307-
self.second,
308-
)
309-
.is_ok()
320+
Self::try_from_msdos(self.datepart, self.timepart).is_ok()
310321
}
311322

312323
#[cfg(feature = "time")]
@@ -320,12 +331,12 @@ impl DateTime {
320331

321332
/// Gets the time portion of this datetime in the msdos representation
322333
pub const fn timepart(&self) -> u16 {
323-
((self.second as u16) >> 1) | ((self.minute as u16) << 5) | ((self.hour as u16) << 11)
334+
self.timepart
324335
}
325336

326337
/// Gets the date portion of this datetime in the msdos representation
327338
pub const fn datepart(&self) -> u16 {
328-
(self.day as u16) | ((self.month as u16) << 5) | ((self.year - 1980) << 9)
339+
self.datepart
329340
}
330341

331342
#[cfg(feature = "time")]
@@ -337,7 +348,7 @@ impl DateTime {
337348

338349
/// Get the year. There is no epoch, i.e. 2018 will be returned as 2018.
339350
pub const fn year(&self) -> u16 {
340-
self.year
351+
(self.datepart >> 9) + 1980
341352
}
342353

343354
/// Get the month, where 1 = january and 12 = december
@@ -346,7 +357,7 @@ impl DateTime {
346357
///
347358
/// When read from a zip file, this may not be a reasonable value
348359
pub const fn month(&self) -> u8 {
349-
self.month
360+
((self.datepart & 0b0000000111100000) >> 5) as u8
350361
}
351362

352363
/// Get the day
@@ -355,7 +366,7 @@ impl DateTime {
355366
///
356367
/// When read from a zip file, this may not be a reasonable value
357368
pub const fn day(&self) -> u8 {
358-
self.day
369+
(self.datepart & 0b0000000000011111) as u8
359370
}
360371

361372
/// Get the hour
@@ -364,7 +375,7 @@ impl DateTime {
364375
///
365376
/// When read from a zip file, this may not be a reasonable value
366377
pub const fn hour(&self) -> u8 {
367-
self.hour
378+
(self.timepart >> 11) as u8
368379
}
369380

370381
/// Get the minute
@@ -373,7 +384,7 @@ impl DateTime {
373384
///
374385
/// When read from a zip file, this may not be a reasonable value
375386
pub const fn minute(&self) -> u8 {
376-
self.minute
387+
((self.timepart & 0b0000011111100000) >> 5) as u8
377388
}
378389

379390
/// Get the second
@@ -382,7 +393,7 @@ impl DateTime {
382393
///
383394
/// When read from a zip file, this may not be a reasonable value
384395
pub const fn second(&self) -> u8 {
385-
self.second
396+
((self.timepart & 0b0000000000011111) << 1) as u8
386397
}
387398
}
388399

@@ -391,18 +402,14 @@ impl TryFrom<OffsetDateTime> for DateTime {
391402
type Error = DateTimeRangeError;
392403

393404
fn try_from(dt: OffsetDateTime) -> Result<Self, Self::Error> {
394-
if dt.year() >= 1980 && dt.year() <= 2107 {
395-
Ok(DateTime {
396-
year: dt.year().try_into()?,
397-
month: dt.month().into(),
398-
day: dt.day(),
399-
hour: dt.hour(),
400-
minute: dt.minute(),
401-
second: dt.second(),
402-
})
403-
} else {
404-
Err(DateTimeRangeError)
405-
}
405+
Self::from_date_and_time(
406+
dt.year().try_into()?,
407+
dt.month().into(),
408+
dt.day(),
409+
dt.hour(),
410+
dt.minute(),
411+
dt.second(),
412+
)
406413
}
407414
}
408415

@@ -411,8 +418,9 @@ impl TryFrom<DateTime> for OffsetDateTime {
411418
type Error = ComponentRange;
412419

413420
fn try_from(dt: DateTime) -> Result<Self, Self::Error> {
414-
let date = Date::from_calendar_date(dt.year as i32, Month::try_from(dt.month)?, dt.day)?;
415-
let time = Time::from_hms(dt.hour, dt.minute, dt.second)?;
421+
let date =
422+
Date::from_calendar_date(dt.year() as i32, Month::try_from(dt.month())?, dt.day())?;
423+
let time = Time::from_hms(dt.hour(), dt.minute(), dt.second())?;
416424
Ok(PrimitiveDateTime::new(date, time).assume_utc())
417425
}
418426
}
@@ -1187,8 +1195,13 @@ mod test {
11871195
assert!(dt < DateTime::from_date_and_time(2018, 11, 17, 10, 39, 30).unwrap());
11881196
assert!(dt > DateTime::from_date_and_time(2018, 11, 17, 10, 37, 30).unwrap());
11891197
// second
1190-
assert!(dt < DateTime::from_date_and_time(2018, 11, 17, 10, 38, 31).unwrap());
1198+
assert!(dt < DateTime::from_date_and_time(2018, 11, 17, 10, 38, 32).unwrap());
1199+
assert_eq!(
1200+
dt.cmp(&DateTime::from_date_and_time(2018, 11, 17, 10, 38, 31).unwrap()),
1201+
Ordering::Equal
1202+
);
11911203
assert!(dt > DateTime::from_date_and_time(2018, 11, 17, 10, 38, 29).unwrap());
1204+
assert!(dt > DateTime::from_date_and_time(2018, 11, 17, 10, 38, 28).unwrap());
11921205
}
11931206

11941207
#[test]

0 commit comments

Comments
 (0)