@@ -25,12 +25,30 @@ fn format_zoned<W: Write>(out: &mut W, zoned: Zoned, fmt: &str) -> UResult<()> {
2525 . map_err ( |x| USimpleError :: new ( 1 , x. to_string ( ) ) )
2626}
2727
28+ /// Convert a SystemTime` to a number of seconds since UNIX_EPOCH
29+ pub fn system_time_to_sec ( time : SystemTime ) -> ( i64 , u32 ) {
30+ if time > UNIX_EPOCH {
31+ let d = time. duration_since ( UNIX_EPOCH ) . unwrap ( ) ;
32+ ( d. as_secs ( ) as i64 , d. subsec_nanos ( ) )
33+ } else {
34+ let d = UNIX_EPOCH . duration_since ( time) . unwrap ( ) ;
35+ ( -( d. as_secs ( ) as i64 ) , d. subsec_nanos ( ) )
36+ }
37+ }
38+
39+ /// Sets how `format_system_time` behaves if the time cannot be converted.
40+ pub enum FormatSystemTimeFallback {
41+ Integer , // Just print seconds since epoch (`ls`)
42+ IntegerError , // The above, and print an error (`du``)
43+ Float , // Just print seconds+nanoseconds since epoch (`stat`)
44+ }
45+
2846/// Format a `SystemTime` according to given fmt, and append to vector out.
2947pub fn format_system_time < W : Write > (
3048 out : & mut W ,
3149 time : SystemTime ,
3250 fmt : & str ,
33- show_error : bool ,
51+ mode : FormatSystemTimeFallback ,
3452) -> UResult < ( ) > {
3553 let zoned: Result < Zoned , _ > = time. try_into ( ) ;
3654 match zoned {
@@ -42,24 +60,30 @@ pub fn format_system_time<W: Write>(
4260 // but it still far enough in the future/past to be unlikely to matter:
4361 // jiff: Year between -9999 to 9999 (UTC) [-377705023201..=253402207200]
4462 // GNU: Year fits in signed 32 bits (timezone dependent)
45- let ts: i64 = if time > UNIX_EPOCH {
46- time. duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( ) as i64
47- } else {
48- -( UNIX_EPOCH . duration_since ( time) . unwrap ( ) . as_secs ( ) as i64 )
63+ let ( mut secs, mut nsecs) = system_time_to_sec ( time) ;
64+ match mode {
65+ FormatSystemTimeFallback :: Integer => out. write_all ( secs. to_string ( ) . as_bytes ( ) ) ?,
66+ FormatSystemTimeFallback :: IntegerError => {
67+ let str = secs. to_string ( ) ;
68+ show_error ! ( "time '{str}' is out of range" ) ;
69+ out. write_all ( str. as_bytes ( ) ) ?;
70+ }
71+ FormatSystemTimeFallback :: Float => {
72+ if secs < 0 && nsecs != 0 {
73+ secs -= 1 ;
74+ nsecs = 1_000_000_000 - nsecs;
75+ }
76+ out. write_fmt ( format_args ! ( "{secs}.{nsecs:09}" ) ) ?;
77+ }
4978 } ;
50- let str = ts. to_string ( ) ;
51- if show_error {
52- show_error ! ( "time '{str}' is out of range" ) ;
53- }
54- out. write_all ( str. as_bytes ( ) ) ?;
5579 Ok ( ( ) )
5680 }
5781 }
5882}
5983
6084#[ cfg( test) ]
6185mod tests {
62- use crate :: time:: format_system_time;
86+ use crate :: time:: { FormatSystemTimeFallback , format_system_time} ;
6387 use std:: time:: { Duration , UNIX_EPOCH } ;
6488
6589 // Test epoch SystemTime get printed correctly at UTC0, with 2 simple formats.
@@ -69,12 +93,23 @@ mod tests {
6993
7094 let time = UNIX_EPOCH ;
7195 let mut out = Vec :: new ( ) ;
72- format_system_time ( & mut out, time, "%Y-%m-%d %H:%M" , false ) . expect ( "Formatting error." ) ;
96+ format_system_time (
97+ & mut out,
98+ time,
99+ "%Y-%m-%d %H:%M" ,
100+ FormatSystemTimeFallback :: Integer ,
101+ )
102+ . expect ( "Formatting error." ) ;
73103 assert_eq ! ( String :: from_utf8( out) . unwrap( ) , "1970-01-01 00:00" ) ;
74104
75105 let mut out = Vec :: new ( ) ;
76- format_system_time ( & mut out, time, "%Y-%m-%d %H:%M:%S.%N %z" , false )
77- . expect ( "Formatting error." ) ;
106+ format_system_time (
107+ & mut out,
108+ time,
109+ "%Y-%m-%d %H:%M:%S.%N %z" ,
110+ FormatSystemTimeFallback :: Integer ,
111+ )
112+ . expect ( "Formatting error." ) ;
78113 assert_eq ! (
79114 String :: from_utf8( out) . unwrap( ) ,
80115 "1970-01-01 00:00:00.000000000 +0000"
@@ -86,12 +121,58 @@ mod tests {
86121 fn test_large_system_time ( ) {
87122 let time = UNIX_EPOCH + Duration :: from_secs ( 67_768_036_191_763_200 ) ;
88123 let mut out = Vec :: new ( ) ;
89- format_system_time ( & mut out, time, "%Y-%m-%d %H:%M" , false ) . expect ( "Formatting error." ) ;
124+ format_system_time (
125+ & mut out,
126+ time,
127+ "%Y-%m-%d %H:%M" ,
128+ FormatSystemTimeFallback :: Integer ,
129+ )
130+ . expect ( "Formatting error." ) ;
90131 assert_eq ! ( String :: from_utf8( out) . unwrap( ) , "67768036191763200" ) ;
91132
92133 let time = UNIX_EPOCH - Duration :: from_secs ( 67_768_040_922_076_800 ) ;
93134 let mut out = Vec :: new ( ) ;
94- format_system_time ( & mut out, time, "%Y-%m-%d %H:%M" , false ) . expect ( "Formatting error." ) ;
135+ format_system_time (
136+ & mut out,
137+ time,
138+ "%Y-%m-%d %H:%M" ,
139+ FormatSystemTimeFallback :: Integer ,
140+ )
141+ . expect ( "Formatting error." ) ;
95142 assert_eq ! ( String :: from_utf8( out) . unwrap( ) , "-67768040922076800" ) ;
96143 }
144+
145+ // Test that very large (positive or negative) lead to just the timestamp being printed.
146+ #[ test]
147+ fn test_large_system_time_float ( ) {
148+ let time =
149+ UNIX_EPOCH + Duration :: from_secs ( 67_768_036_191_763_000 ) + Duration :: from_nanos ( 123 ) ;
150+ let mut out = Vec :: new ( ) ;
151+ format_system_time (
152+ & mut out,
153+ time,
154+ "%Y-%m-%d %H:%M" ,
155+ FormatSystemTimeFallback :: Float ,
156+ )
157+ . expect ( "Formatting error." ) ;
158+ assert_eq ! (
159+ String :: from_utf8( out) . unwrap( ) ,
160+ "67768036191763000.000000123"
161+ ) ;
162+
163+ let time =
164+ UNIX_EPOCH - Duration :: from_secs ( 67_768_040_922_076_000 ) + Duration :: from_nanos ( 123 ) ;
165+ let mut out = Vec :: new ( ) ;
166+ format_system_time (
167+ & mut out,
168+ time,
169+ "%Y-%m-%d %H:%M" ,
170+ FormatSystemTimeFallback :: Float ,
171+ )
172+ . expect ( "Formatting error." ) ;
173+ assert_eq ! (
174+ String :: from_utf8( out) . unwrap( ) ,
175+ "-67768040922076000.000000123"
176+ ) ;
177+ }
97178}
0 commit comments