Skip to content

Commit 5acfdca

Browse files
committed
date: implement --resolution flag
1 parent c229ff8 commit 5acfdca

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

src/uu/date/locales/en-US.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ date-help-iso-8601 = output date/time in ISO 8601 format.
8282
'hours', 'minutes', 'seconds', or 'ns'
8383
for date and time to the indicated precision.
8484
Example: 2006-08-14T02:34:56-06:00
85+
date-help-resolution = output the available resolution of timestamps
86+
Example: 0.000000001
8587
date-help-rfc-email = output date and time in RFC 5322 format.
8688
Example: Mon, 14 Aug 2006 02:34:56 -0600
8789
date-help-rfc-3339 = output date/time in RFC 3339 format.

src/uu/date/src/date.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
// spell-checker:ignore strtime ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
6+
// spell-checker:ignore strtime ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes getres
77

88
use clap::{Arg, ArgAction, Command};
99
use jiff::fmt::strtime;
1010
use jiff::tz::TimeZone;
1111
use jiff::{Timestamp, Zoned};
1212
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
13-
use libc::{CLOCK_REALTIME, clock_settime, timespec};
13+
use libc::clock_settime;
14+
#[cfg(unix)]
15+
use libc::{CLOCK_REALTIME, clock_getres, timespec};
1416
use std::fs::File;
1517
use std::io::{BufRead, BufReader};
1618
use std::path::PathBuf;
@@ -35,6 +37,7 @@ const OPT_FORMAT: &str = "format";
3537
const OPT_FILE: &str = "file";
3638
const OPT_DEBUG: &str = "debug";
3739
const OPT_ISO_8601: &str = "iso-8601";
40+
const OPT_RESOLUTION: &str = "resolution";
3841
const OPT_RFC_EMAIL: &str = "rfc-email";
3942
const OPT_RFC_822: &str = "rfc-822";
4043
const OPT_RFC_2822: &str = "rfc-2822";
@@ -57,6 +60,7 @@ enum Format {
5760
Iso8601(Iso8601Format),
5861
Rfc5322,
5962
Rfc3339(Rfc3339Format),
63+
Resolution,
6064
Custom(String),
6165
Default,
6266
}
@@ -68,6 +72,7 @@ enum DateSource {
6872
FileMtime(PathBuf),
6973
Stdin,
7074
Human(String),
75+
Resolution,
7176
}
7277

7378
enum Iso8601Format {
@@ -136,6 +141,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
136141
.map(|s| s.as_str().into())
137142
{
138143
Format::Rfc3339(fmt)
144+
} else if matches.get_flag(OPT_RESOLUTION) {
145+
Format::Resolution
139146
} else {
140147
Format::Default
141148
};
@@ -149,6 +156,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
149156
}
150157
} else if let Some(file) = matches.get_one::<String>(OPT_REFERENCE) {
151158
DateSource::FileMtime(file.into())
159+
} else if matches.get_flag(OPT_RESOLUTION) {
160+
DateSource::Resolution
152161
} else {
153162
DateSource::Now
154163
};
@@ -230,6 +239,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
230239
let iter = std::iter::once(Ok(date));
231240
Box::new(iter)
232241
}
242+
DateSource::Resolution => {
243+
let resolution = get_clock_resolution();
244+
let date = resolution.to_zoned(TimeZone::system());
245+
let iter = std::iter::once(Ok(date));
246+
Box::new(iter)
247+
}
233248
DateSource::Now => {
234249
let iter = std::iter::once(Ok(now));
235250
Box::new(iter)
@@ -297,6 +312,13 @@ pub fn uu_app() -> Command {
297312
.default_missing_value(OPT_DATE)
298313
.help(translate!("date-help-iso-8601")),
299314
)
315+
.arg(
316+
Arg::new(OPT_RESOLUTION)
317+
.long(OPT_RESOLUTION)
318+
.overrides_with(OPT_RESOLUTION)
319+
.help(translate!("date-help-resolution"))
320+
.action(ArgAction::SetTrue),
321+
)
300322
.arg(
301323
Arg::new(OPT_RFC_EMAIL)
302324
.short('R')
@@ -374,6 +396,7 @@ fn make_format_string(settings: &Settings) -> &str {
374396
Rfc3339Format::Seconds => "%F %T%:z",
375397
Rfc3339Format::Ns => "%F %T.%N%:z",
376398
},
399+
Format::Resolution => "%s.%N",
377400
Format::Custom(ref fmt) => fmt,
378401
Format::Default => "%a %b %e %X %Z %Y",
379402
}
@@ -398,6 +421,41 @@ fn parse_date<S: AsRef<str> + Clone>(
398421
}
399422
}
400423

424+
#[cfg(not(any(unix, windows)))]
425+
fn get_clock_resolution() -> Timestamp {
426+
unimplemented!("getting clock resolution not implemented (unsupported target)");
427+
}
428+
429+
#[cfg(unix)]
430+
fn get_clock_resolution() -> Timestamp {
431+
let mut timespec = timespec {
432+
tv_sec: 0,
433+
tv_nsec: 0,
434+
};
435+
unsafe {
436+
// SAFETY: the timespec struct lives for the full duration of this function call.
437+
//
438+
// The clock_getres function can only fail if the passed clock_id is not
439+
// a known clock. All compliant posix implementors must support
440+
// CLOCK_REALTIME, therefore this function call cannot fail on any
441+
// compliant posix implementation.
442+
//
443+
// See more here:
444+
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
445+
clock_getres(CLOCK_REALTIME, &raw mut timespec);
446+
}
447+
Timestamp::constant(timespec.tv_sec, timespec.tv_nsec as i32)
448+
}
449+
450+
#[cfg(windows)]
451+
fn get_clock_resolution() -> Timestamp {
452+
// Windows does not expose a system call for getting the resolution of the
453+
// clock, however the FILETIME struct returned by GetSystemTimeAsFileTime,
454+
// and GetSystemTimePreciseAsFileTime has a resolution of 100ns.
455+
// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
456+
Timestamp::constant(0, 100)
457+
}
458+
401459
#[cfg(not(any(unix, windows)))]
402460
fn set_system_datetime(_date: Zoned) -> UResult<()> {
403461
unimplemented!("setting date not implemented (unsupported target)");

0 commit comments

Comments
 (0)