Skip to content

Commit c0bd89a

Browse files
nicholasbishopGabrielMajeri
authored andcommitted
Improve Time struct
* Add a `TimeParams` struct with public fields corresponding to all the non-padding fields of `Time`, and change `Time::new` to take that as input. That improves callsites of `Time::new` so that you can see what the numeric values correspond to. See `known_disk.rs` for an example. * Add `Time::invalid` for use with `File::set_info`. When setting file info, a fully-zero-time indicates the attribute should not be updated. I think ideally we'd have `Time` always represent a valid value, and use `Option<Time>::None` to represent an invalid time similar to what we do with `Handle`s, but I don't think Rust currently allows us to indicate that all-zero-Time can be used for the Option::None niche. * Add `Time::is_invalid` to check that all fields are in the valid range. * Update Time's docstring to be more general. Although `EFI_TIME` in the spec says it's for the current time, it's used elsewhere too (like `EFI_FILE_INFO`). * Fix the docstring for `Daylight`, which was accidentally copied from `MemoryAttribute`.
1 parent be4f39c commit c0bd89a

File tree

4 files changed

+150
-53
lines changed

4 files changed

+150
-53
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
### Added
66

77
- Added `FileHandle::into_directory` and `FileHandle::into_regular_file`.
8+
- Added `TimeParams`, `Time::invalid`, and `Time::is_invalid`.
9+
10+
### Changed
11+
12+
- `Time::new` now takes a single `TimeParams` argument so that date and
13+
time fields can be explicitly named at the call site.
814

915
## uefi-macros - [Unreleased]
1016

src/proto/media/file/info.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ impl FileProtocolInfo for FileSystemVolumeLabel {}
375375
mod tests {
376376
use super::*;
377377
use crate::alloc_api::vec;
378+
use crate::table::runtime::TimeParams;
378379
use crate::table::runtime::{Daylight, Time};
379380
use crate::CString16;
380381

@@ -394,9 +395,20 @@ mod tests {
394395

395396
let file_size = 123;
396397
let physical_size = 456;
397-
let create_time = Time::new(1970, 1, 1, 0, 0, 0, 0, 0, Daylight::IN_DAYLIGHT);
398-
let last_access_time = Time::new(1971, 1, 1, 0, 0, 0, 0, 0, Daylight::IN_DAYLIGHT);
399-
let modification_time = Time::new(1972, 1, 1, 0, 0, 0, 0, 0, Daylight::IN_DAYLIGHT);
398+
let tp = TimeParams {
399+
year: 1970,
400+
month: 1,
401+
day: 1,
402+
hour: 0,
403+
minute: 0,
404+
second: 0,
405+
nanosecond: 0,
406+
time_zone: None,
407+
daylight: Daylight::IN_DAYLIGHT,
408+
};
409+
let create_time = Time::new(tp).unwrap();
410+
let last_access_time = Time::new(TimeParams { year: 1971, ..tp }).unwrap();
411+
let modification_time = Time::new(TimeParams { year: 1972, ..tp }).unwrap();
400412
let attribute = FileAttribute::READ_ONLY;
401413
let name = CString16::try_from("test_name").unwrap();
402414
let info = FileInfo::new(

src/table/runtime.rs

+102-43
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ impl Debug for RuntimeServices {
263263
}
264264
}
265265

266-
/// The current time information
266+
/// Date and time representation.
267267
#[derive(Copy, Clone)]
268268
#[repr(C)]
269269
pub struct Time {
@@ -280,92 +280,151 @@ pub struct Time {
280280
_pad2: u8,
281281
}
282282

283+
/// Input parameters for [`Time::new`].
284+
#[derive(Copy, Clone)]
285+
pub struct TimeParams {
286+
/// Year in the range `1900..=9999`.
287+
pub year: u16,
288+
289+
/// Month in the range `1..=12`.
290+
pub month: u8,
291+
292+
/// Day in the range `1..=31`.
293+
pub day: u8,
294+
295+
/// Hour in the range `0.=23`.
296+
pub hour: u8,
297+
298+
/// Minute in the range `0..=59`.
299+
pub minute: u8,
300+
301+
/// Second in the range `0..=59`.
302+
pub second: u8,
303+
304+
/// Fraction of a second represented as nanoseconds in the range
305+
/// `0..=999_999_999`.
306+
pub nanosecond: u32,
307+
308+
/// Offset in minutes from UTC in the range `-1440..=1440`, or
309+
/// local time if `None`.
310+
pub time_zone: Option<i16>,
311+
312+
/// Daylight savings time information.
313+
pub daylight: Daylight,
314+
}
315+
283316
bitflags! {
284-
/// Flags describing the capabilities of a memory range.
317+
/// A bitmask containing daylight savings time information.
285318
pub struct Daylight: u8 {
286-
/// Time is affected by daylight savings time
319+
/// Time is affected by daylight savings time.
287320
const ADJUST_DAYLIGHT = 0x01;
288-
/// Time has been adjusted for daylight savings time
321+
/// Time has been adjusted for daylight savings time.
289322
const IN_DAYLIGHT = 0x02;
290323
}
291324
}
292325

326+
/// Error returned by [`Time`] methods if the input is outside the valid range.
327+
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
328+
pub struct TimeError;
329+
293330
impl Time {
294331
/// Unspecified Timezone/local time.
295332
const UNSPECIFIED_TIMEZONE: i16 = 0x07ff;
296333

297-
/// Build an UEFI time struct
298-
#[allow(clippy::too_many_arguments)]
299-
pub fn new(
300-
year: u16,
301-
month: u8,
302-
day: u8,
303-
hour: u8,
304-
minute: u8,
305-
second: u8,
306-
nanosecond: u32,
307-
time_zone: i16,
308-
daylight: Daylight,
309-
) -> Self {
310-
assert!((1900..=9999).contains(&year));
311-
assert!((1..=12).contains(&month));
312-
assert!((1..=31).contains(&day));
313-
assert!(hour <= 23);
314-
assert!(minute <= 59);
315-
assert!(second <= 59);
316-
assert!(nanosecond <= 999_999_999);
317-
assert!((time_zone >= -1440 && time_zone <= 1440) || time_zone == 2047);
334+
/// Create a `Time` value. If a field is not in the valid range,
335+
/// [`TimeError`] is returned.
336+
pub fn new(params: TimeParams) -> core::result::Result<Self, TimeError> {
337+
let time = Self {
338+
year: params.year,
339+
month: params.month,
340+
day: params.day,
341+
hour: params.hour,
342+
minute: params.minute,
343+
second: params.second,
344+
_pad1: 0,
345+
nanosecond: params.nanosecond,
346+
time_zone: params.time_zone.unwrap_or(Self::UNSPECIFIED_TIMEZONE),
347+
daylight: params.daylight,
348+
_pad2: 0,
349+
};
350+
if time.is_valid() {
351+
Ok(time)
352+
} else {
353+
Err(TimeError)
354+
}
355+
}
356+
357+
/// Create an invalid `Time` with all fields set to zero. This can
358+
/// be used with [`FileInfo`] to indicate a field should not be
359+
/// updated when calling [`File::set_info`].
360+
///
361+
/// [`FileInfo`]: uefi::proto::media::file::FileInfo
362+
/// [`File::set_info`]: uefi::proto::media::file::File::set_info
363+
pub fn invalid() -> Self {
318364
Self {
319-
year,
320-
month,
321-
day,
322-
hour,
323-
minute,
324-
second,
365+
year: 0,
366+
month: 0,
367+
day: 0,
368+
hour: 0,
369+
minute: 0,
370+
second: 0,
325371
_pad1: 0,
326-
nanosecond,
327-
time_zone,
328-
daylight,
372+
nanosecond: 0,
373+
time_zone: 0,
374+
daylight: Daylight::empty(),
329375
_pad2: 0,
330376
}
331377
}
332378

333-
/// Query the year
379+
/// True if all fields are within valid ranges, false otherwise.
380+
pub fn is_valid(&self) -> bool {
381+
(1900..=9999).contains(&self.year)
382+
&& (1..=12).contains(&self.month)
383+
&& (1..=31).contains(&self.day)
384+
&& self.hour <= 23
385+
&& self.minute <= 59
386+
&& self.second <= 59
387+
&& self.nanosecond <= 999_999_999
388+
&& ((-1440..=1440).contains(&self.time_zone)
389+
|| self.time_zone == Self::UNSPECIFIED_TIMEZONE)
390+
}
391+
392+
/// Query the year.
334393
pub fn year(&self) -> u16 {
335394
self.year
336395
}
337396

338-
/// Query the month
397+
/// Query the month.
339398
pub fn month(&self) -> u8 {
340399
self.month
341400
}
342401

343-
/// Query the day
402+
/// Query the day.
344403
pub fn day(&self) -> u8 {
345404
self.day
346405
}
347406

348-
/// Query the hour
407+
/// Query the hour.
349408
pub fn hour(&self) -> u8 {
350409
self.hour
351410
}
352411

353-
/// Query the minute
412+
/// Query the minute.
354413
pub fn minute(&self) -> u8 {
355414
self.minute
356415
}
357416

358-
/// Query the second
417+
/// Query the second.
359418
pub fn second(&self) -> u8 {
360419
self.second
361420
}
362421

363-
/// Query the nanosecond
422+
/// Query the nanosecond.
364423
pub fn nanosecond(&self) -> u32 {
365424
self.nanosecond
366425
}
367426

368-
/// Query the time offset in minutes from UTC, or None if using local time
427+
/// Query the time offset in minutes from UTC, or None if using local time.
369428
pub fn time_zone(&self) -> Option<i16> {
370429
if self.time_zone == 2047 {
371430
None
@@ -374,7 +433,7 @@ impl Time {
374433
}
375434
}
376435

377-
/// Query the daylight savings time information
436+
/// Query the daylight savings time information.
378437
pub fn daylight(&self) -> Daylight {
379438
self.daylight
380439
}

uefi-test-runner/src/proto/media/known_disk.rs

+27-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use uefi::proto::media::file::{
55
};
66
use uefi::proto::media::fs::SimpleFileSystem;
77
use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams};
8-
use uefi::table::runtime::{Daylight, Time};
8+
use uefi::table::runtime::{Daylight, Time, TimeParams};
99
use uefi::CString16;
1010

1111
/// Test directory entry iteration.
@@ -78,17 +78,37 @@ fn test_existing_file(directory: &mut Directory) {
7878
let info = file.get_info::<FileInfo>(&mut info_buffer).unwrap();
7979
assert_eq!(info.file_size(), 15);
8080
assert_eq!(info.physical_size(), 512);
81-
assert_eq!(
82-
*info.create_time(),
83-
Time::new(2000, 1, 24, 0, 0, 0, 0, 2047, Daylight::empty())
84-
);
81+
let tp = TimeParams {
82+
year: 2000,
83+
month: 1,
84+
day: 24,
85+
hour: 0,
86+
minute: 0,
87+
second: 0,
88+
nanosecond: 0,
89+
time_zone: None,
90+
daylight: Daylight::empty(),
91+
};
92+
assert_eq!(*info.create_time(), Time::new(tp).unwrap());
8593
assert_eq!(
8694
*info.last_access_time(),
87-
Time::new(2001, 2, 25, 0, 0, 0, 0, 2047, Daylight::empty())
95+
Time::new(TimeParams {
96+
year: 2001,
97+
month: 2,
98+
day: 25,
99+
..tp
100+
})
101+
.unwrap()
88102
);
89103
assert_eq!(
90104
*info.modification_time(),
91-
Time::new(2002, 3, 26, 0, 0, 0, 0, 2047, Daylight::empty())
105+
Time::new(TimeParams {
106+
year: 2002,
107+
month: 3,
108+
day: 26,
109+
..tp
110+
})
111+
.unwrap()
92112
);
93113
assert_eq!(info.attribute(), FileAttribute::empty());
94114
assert_eq!(

0 commit comments

Comments
 (0)