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( all( unix, not( target_os = "redox" ) ) ) ]
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)
@@ -283,6 +298,7 @@ pub fn uu_app() -> Command {
283298 . long ( OPT_FILE )
284299 . value_name ( "DATEFILE" )
285300 . value_hint ( clap:: ValueHint :: FilePath )
301+ . conflicts_with ( OPT_DATE )
286302 . help ( translate ! ( "date-help-file" ) ) ,
287303 )
288304 . arg (
@@ -297,6 +313,14 @@ pub fn uu_app() -> Command {
297313 . default_missing_value ( OPT_DATE )
298314 . help ( translate ! ( "date-help-iso-8601" ) ) ,
299315 )
316+ . arg (
317+ Arg :: new ( OPT_RESOLUTION )
318+ . long ( OPT_RESOLUTION )
319+ . conflicts_with_all ( [ OPT_DATE , OPT_FILE ] )
320+ . overrides_with ( OPT_RESOLUTION )
321+ . help ( translate ! ( "date-help-resolution" ) )
322+ . action ( ArgAction :: SetTrue ) ,
323+ )
300324 . arg (
301325 Arg :: new ( OPT_RFC_EMAIL )
302326 . short ( 'R' )
@@ -325,6 +349,7 @@ pub fn uu_app() -> Command {
325349 . long ( OPT_REFERENCE )
326350 . value_name ( "FILE" )
327351 . value_hint ( clap:: ValueHint :: AnyPath )
352+ . conflicts_with_all ( [ OPT_DATE , OPT_FILE , OPT_RESOLUTION ] )
328353 . help ( translate ! ( "date-help-reference" ) ) ,
329354 )
330355 . arg (
@@ -374,6 +399,7 @@ fn make_format_string(settings: &Settings) -> &str {
374399 Rfc3339Format :: Seconds => "%F %T%:z" ,
375400 Rfc3339Format :: Ns => "%F %T.%N%:z" ,
376401 } ,
402+ Format :: Resolution => "%s.%N" ,
377403 Format :: Custom ( ref fmt) => fmt,
378404 Format :: Default => "%a %b %e %X %Z %Y" ,
379405 }
@@ -398,6 +424,50 @@ fn parse_date<S: AsRef<str> + Clone>(
398424 }
399425}
400426
427+ #[ cfg( not( any( unix, windows) ) ) ]
428+ fn get_clock_resolution ( ) -> Timestamp {
429+ unimplemented ! ( "getting clock resolution not implemented (unsupported target)" ) ;
430+ }
431+
432+ #[ cfg( all( unix, not( target_os = "redox" ) ) ) ]
433+ fn get_clock_resolution ( ) -> Timestamp {
434+ let mut timespec = timespec {
435+ tv_sec : 0 ,
436+ tv_nsec : 0 ,
437+ } ;
438+ unsafe {
439+ // SAFETY: the timespec struct lives for the full duration of this function call.
440+ //
441+ // The clock_getres function can only fail if the passed clock_id is not
442+ // a known clock. All compliant posix implementors must support
443+ // CLOCK_REALTIME, therefore this function call cannot fail on any
444+ // compliant posix implementation.
445+ //
446+ // See more here:
447+ // https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
448+ clock_getres ( CLOCK_REALTIME , & raw mut timespec) ;
449+ }
450+ #[ allow( clippy:: unnecessary_cast) ] // Cast required on 32-bit platforms
451+ Timestamp :: constant ( timespec. tv_sec as i64 , timespec. tv_nsec as i32 )
452+ }
453+
454+ #[ cfg( all( unix, target_os = "redox" ) ) ]
455+ fn get_clock_resolution ( ) -> Timestamp {
456+ // Redox OS does not support the posix clock_getres function, however
457+ // internally it uses a resolution of 1ns to represent timestamps.
458+ // https://gitlab.redox-os.org/redox-os/kernel/-/blob/master/src/time.rs
459+ Timestamp :: constant ( 0 , 1 )
460+ }
461+
462+ #[ cfg( windows) ]
463+ fn get_clock_resolution ( ) -> Timestamp {
464+ // Windows does not expose a system call for getting the resolution of the
465+ // clock, however the FILETIME struct returned by GetSystemTimeAsFileTime,
466+ // and GetSystemTimePreciseAsFileTime has a resolution of 100ns.
467+ // https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
468+ Timestamp :: constant ( 0 , 100 )
469+ }
470+
401471#[ cfg( not( any( unix, windows) ) ) ]
402472fn set_system_datetime ( _date : Zoned ) -> UResult < ( ) > {
403473 unimplemented ! ( "setting date not implemented (unsupported target)" ) ;
0 commit comments