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
88use clap:: { Arg , ArgAction , Command } ;
99use jiff:: fmt:: strtime;
1010use jiff:: tz:: TimeZone ;
1111use 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} ;
1416use std:: fs:: File ;
1517use std:: io:: { BufRead , BufReader } ;
1618use std:: path:: PathBuf ;
@@ -35,6 +37,7 @@ const OPT_FORMAT: &str = "format";
3537const OPT_FILE : & str = "file" ;
3638const OPT_DEBUG : & str = "debug" ;
3739const OPT_ISO_8601 : & str = "iso-8601" ;
40+ const OPT_RESOLUTION : & str = "resolution" ;
3841const OPT_RFC_EMAIL : & str = "rfc-email" ;
3942const OPT_RFC_822 : & str = "rfc-822" ;
4043const 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
7378enum 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) ) ) ]
402460fn set_system_datetime ( _date : Zoned ) -> UResult < ( ) > {
403461 unimplemented ! ( "setting date not implemented (unsupported target)" ) ;
0 commit comments