22use crate :: cp437:: FromCp437 ;
33use crate :: write:: { FileOptionExtension , FileOptions } ;
44use path:: { Component , Path , PathBuf } ;
5+ use std:: cmp:: Ordering ;
56use std:: fmt;
67use std:: fmt:: { Debug , Formatter } ;
78use 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 ) ]
8384pub 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
9289impl 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+
104133impl 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) ]
121150impl 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