2
2
use crate :: cp437:: FromCp437 ;
3
3
use crate :: write:: { FileOptionExtension , FileOptions } ;
4
4
use path:: { Component , Path , PathBuf } ;
5
+ use std:: cmp:: Ordering ;
5
6
use std:: fmt;
6
7
use std:: fmt:: { Debug , Formatter } ;
7
8
use std:: mem;
@@ -79,14 +80,10 @@ impl From<System> for u8 {
79
80
///
80
81
/// Modern zip files store more precise timestamps; see [`crate::extra_fields::ExtendedTimestamp`]
81
82
/// for details.
82
- #[ derive( Clone , Copy , Eq , Hash , Ord , PartialEq , PartialOrd ) ]
83
+ #[ derive( Clone , Copy , Eq , Hash , PartialEq ) ]
83
84
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 ,
90
87
}
91
88
92
89
impl Debug for DateTime {
@@ -96,11 +93,43 @@ impl Debug for DateTime {
96
93
}
97
94
f. write_fmt ( format_args ! (
98
95
"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( )
100
102
) )
101
103
}
102
104
}
103
105
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
+
104
133
impl DateTime {
105
134
/// Returns the current time if possible, otherwise the default of 1980-01-01.
106
135
#[ cfg( feature = "time" ) ]
@@ -120,14 +149,15 @@ impl DateTime {
120
149
#[ cfg( fuzzing) ]
121
150
impl arbitrary:: Arbitrary < ' _ > for DateTime {
122
151
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 } )
131
161
}
132
162
}
133
163
@@ -152,11 +182,18 @@ impl TryFrom<DateTime> for NaiveDateTime {
152
182
type Error = DateTimeRangeError ;
153
183
154
184
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 ) ?;
160
197
Ok ( NaiveDateTime :: new ( date, time) )
161
198
}
162
199
}
@@ -181,12 +218,8 @@ impl Default for DateTime {
181
218
/// Constructs an 'default' datetime of 1980-01-01 00:00:00
182
219
fn default ( ) -> DateTime {
183
220
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 ,
190
223
}
191
224
}
192
225
}
@@ -197,7 +230,12 @@ impl fmt::Display for DateTime {
197
230
write ! (
198
231
f,
199
232
"{: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( )
201
239
)
202
240
}
203
241
}
@@ -208,21 +246,7 @@ impl DateTime {
208
246
/// # Safety
209
247
/// The caller must ensure the date and time are valid.
210
248
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 }
226
250
}
227
251
228
252
/// Converts an msdos (u16, u16) pair to a DateTime object if it represents a valid date and
@@ -283,30 +307,17 @@ impl DateTime {
283
307
if day > max_day {
284
308
return Err ( DateTimeRangeError ) ;
285
309
}
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 } )
294
313
} else {
295
314
Err ( DateTimeRangeError )
296
315
}
297
316
}
298
317
299
318
/// Indicates whether this date and time can be written to a zip archive.
300
319
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 ( )
310
321
}
311
322
312
323
#[ cfg( feature = "time" ) ]
@@ -320,12 +331,12 @@ impl DateTime {
320
331
321
332
/// Gets the time portion of this datetime in the msdos representation
322
333
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
324
335
}
325
336
326
337
/// Gets the date portion of this datetime in the msdos representation
327
338
pub const fn datepart ( & self ) -> u16 {
328
- ( self . day as u16 ) | ( ( self . month as u16 ) << 5 ) | ( ( self . year - 1980 ) << 9 )
339
+ self . datepart
329
340
}
330
341
331
342
#[ cfg( feature = "time" ) ]
@@ -337,7 +348,7 @@ impl DateTime {
337
348
338
349
/// Get the year. There is no epoch, i.e. 2018 will be returned as 2018.
339
350
pub const fn year ( & self ) -> u16 {
340
- self . year
351
+ ( self . datepart >> 9 ) + 1980
341
352
}
342
353
343
354
/// Get the month, where 1 = january and 12 = december
@@ -346,7 +357,7 @@ impl DateTime {
346
357
///
347
358
/// When read from a zip file, this may not be a reasonable value
348
359
pub const fn month ( & self ) -> u8 {
349
- self . month
360
+ ( ( self . datepart & 0b0000000111100000 ) >> 5 ) as u8
350
361
}
351
362
352
363
/// Get the day
@@ -355,7 +366,7 @@ impl DateTime {
355
366
///
356
367
/// When read from a zip file, this may not be a reasonable value
357
368
pub const fn day ( & self ) -> u8 {
358
- self . day
369
+ ( self . datepart & 0b0000000000011111 ) as u8
359
370
}
360
371
361
372
/// Get the hour
@@ -364,7 +375,7 @@ impl DateTime {
364
375
///
365
376
/// When read from a zip file, this may not be a reasonable value
366
377
pub const fn hour ( & self ) -> u8 {
367
- self . hour
378
+ ( self . timepart >> 11 ) as u8
368
379
}
369
380
370
381
/// Get the minute
@@ -373,7 +384,7 @@ impl DateTime {
373
384
///
374
385
/// When read from a zip file, this may not be a reasonable value
375
386
pub const fn minute ( & self ) -> u8 {
376
- self . minute
387
+ ( ( self . timepart & 0b0000011111100000 ) >> 5 ) as u8
377
388
}
378
389
379
390
/// Get the second
@@ -382,7 +393,7 @@ impl DateTime {
382
393
///
383
394
/// When read from a zip file, this may not be a reasonable value
384
395
pub const fn second ( & self ) -> u8 {
385
- self . second
396
+ ( ( self . timepart & 0b0000000000011111 ) << 1 ) as u8
386
397
}
387
398
}
388
399
@@ -391,18 +402,14 @@ impl TryFrom<OffsetDateTime> for DateTime {
391
402
type Error = DateTimeRangeError ;
392
403
393
404
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
+ )
406
413
}
407
414
}
408
415
@@ -411,8 +418,9 @@ impl TryFrom<DateTime> for OffsetDateTime {
411
418
type Error = ComponentRange ;
412
419
413
420
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 ( ) ) ?;
416
424
Ok ( PrimitiveDateTime :: new ( date, time) . assume_utc ( ) )
417
425
}
418
426
}
@@ -1187,8 +1195,13 @@ mod test {
1187
1195
assert ! ( dt < DateTime :: from_date_and_time( 2018 , 11 , 17 , 10 , 39 , 30 ) . unwrap( ) ) ;
1188
1196
assert ! ( dt > DateTime :: from_date_and_time( 2018 , 11 , 17 , 10 , 37 , 30 ) . unwrap( ) ) ;
1189
1197
// 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
+ ) ;
1191
1203
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( ) ) ;
1192
1205
}
1193
1206
1194
1207
#[ test]
0 commit comments