@@ -37,87 +37,90 @@ use winnow::{
3737 ModalResult , Parser ,
3838} ;
3939
40- use super :: {
41- ordinal:: ordinal,
42- primitive:: { float, s} ,
43- } ;
40+ use super :: { epoch:: sec_and_nsec, ordinal:: ordinal, primitive:: s} ;
4441
4542#[ derive( Clone , Copy , Debug , PartialEq ) ]
46- pub enum Relative {
43+ pub ( crate ) enum Relative {
4744 Years ( i32 ) ,
4845 Months ( i32 ) ,
4946 Days ( i32 ) ,
5047 Hours ( i32 ) ,
5148 Minutes ( i32 ) ,
52- // Seconds are special because they can be given as a float
53- Seconds ( f64 ) ,
49+ Seconds ( i64 , u32 ) ,
5450}
5551
56- impl Relative {
57- // TODO: determine how to handle multiplication overflows,
58- // using saturating_mul for now.
59- fn mul ( self , n : i32 ) -> Self {
60- match self {
61- Self :: Years ( x ) => Self :: Years ( n . saturating_mul ( x ) ) ,
62- Self :: Months ( x ) => Self :: Months ( n . saturating_mul ( x ) ) ,
63- Self :: Days ( x ) => Self :: Days ( n . saturating_mul ( x ) ) ,
64- Self :: Hours ( x ) => Self :: Hours ( n . saturating_mul ( x ) ) ,
65- Self :: Minutes ( x ) => Self :: Minutes ( n . saturating_mul ( x ) ) ,
66- Self :: Seconds ( x ) => Self :: Seconds ( f64 :: from ( n ) * x ) ,
52+ impl From < Relative > for jiff :: Span {
53+ fn from ( relative : Relative ) -> Self {
54+ match relative {
55+ Relative :: Years ( years ) => jiff :: Span :: new ( ) . years ( years ) ,
56+ Relative :: Months ( months ) => jiff :: Span :: new ( ) . months ( months ) ,
57+ Relative :: Days ( days ) => jiff :: Span :: new ( ) . days ( days ) ,
58+ Relative :: Hours ( hours ) => jiff :: Span :: new ( ) . hours ( hours ) ,
59+ Relative :: Minutes ( minutes ) => jiff :: Span :: new ( ) . minutes ( minutes ) ,
60+ Relative :: Seconds ( seconds , nanoseconds ) => {
61+ jiff :: Span :: new ( ) . seconds ( seconds ) . nanoseconds ( nanoseconds )
62+ }
6763 }
6864 }
6965}
7066
71- pub fn parse ( input : & mut & str ) -> ModalResult < Relative > {
67+ pub ( super ) fn parse ( input : & mut & str ) -> ModalResult < Relative > {
7268 alt ( (
7369 s ( "tomorrow" ) . value ( Relative :: Days ( 1 ) ) ,
7470 s ( "yesterday" ) . value ( Relative :: Days ( -1 ) ) ,
7571 // For "today" and "now", the unit is arbitrary
7672 s ( "today" ) . value ( Relative :: Days ( 0 ) ) ,
7773 s ( "now" ) . value ( Relative :: Days ( 0 ) ) ,
7874 seconds,
79- other ,
75+ displacement ,
8076 ) )
8177 . parse_next ( input)
8278}
8379
8480fn seconds ( input : & mut & str ) -> ModalResult < Relative > {
8581 (
86- opt ( alt ( ( s ( float) , ordinal. map ( |x| x as f64 ) ) ) ) ,
82+ opt ( alt ( ( s ( '+' ) . value ( 1 ) , s ( '-' ) . value ( -1 ) ) ) ) ,
83+ sec_and_nsec,
8784 s ( alpha1) . verify ( |s : & str | matches ! ( s, "seconds" | "second" | "sec" | "secs" ) ) ,
8885 ago,
8986 )
90- . map ( |( n, _, ago) | Relative :: Seconds ( n. unwrap_or ( 1.0 ) * if ago { -1.0 } else { 1.0 } ) )
87+ . verify_map ( |( sign, ( sec, nsec) , _, ago) | {
88+ let sec = i64:: try_from ( sec) . ok ( ) ?;
89+ let sign = sign. unwrap_or ( 1 ) * if ago { -1 } else { 1 } ;
90+ let ( second, nanosecond) = match ( sign, nsec) {
91+ ( -1 , 0 ) => ( -sec, 0 ) ,
92+ // Truncate towards minus infinity.
93+ ( -1 , _) => ( ( -sec) . checked_sub ( 1 ) ?, 1_000_000_000 - nsec) ,
94+ _ => ( sec, nsec) ,
95+ } ;
96+ Some ( Relative :: Seconds ( second, nanosecond) )
97+ } )
9198 . parse_next ( input)
9299}
93100
94- fn other ( input : & mut & str ) -> ModalResult < Relative > {
95- ( opt ( ordinal) , integer_unit, ago)
96- . map ( |( n, unit, ago) | unit. mul ( n. unwrap_or ( 1 ) * if ago { -1 } else { 1 } ) )
101+ fn displacement ( input : & mut & str ) -> ModalResult < Relative > {
102+ ( opt ( ordinal) , s ( alpha1) , ago)
103+ . verify_map ( |( n, unit, ago) : ( Option < i32 > , & str , bool ) | {
104+ let multipler = n. unwrap_or ( 1 ) * if ago { -1 } else { 1 } ;
105+ Some ( match unit. strip_suffix ( 's' ) . unwrap_or ( unit) {
106+ "year" => Relative :: Years ( multipler) ,
107+ "month" => Relative :: Months ( multipler) ,
108+ "fortnight" => Relative :: Days ( 14 * multipler) ,
109+ "week" => Relative :: Days ( 7 * multipler) ,
110+ "day" => Relative :: Days ( multipler) ,
111+ "hour" => Relative :: Hours ( multipler) ,
112+ "minute" | "min" => Relative :: Minutes ( multipler) ,
113+ "second" | "sec" => Relative :: Seconds ( multipler as i64 , 0 ) ,
114+ _ => return None ,
115+ } )
116+ } )
97117 . parse_next ( input)
98118}
99119
100120fn ago ( input : & mut & str ) -> ModalResult < bool > {
101121 opt ( s ( "ago" ) ) . map ( |o| o. is_some ( ) ) . parse_next ( input)
102122}
103123
104- fn integer_unit ( input : & mut & str ) -> ModalResult < Relative > {
105- s ( alpha1)
106- . verify_map ( |s : & str | {
107- Some ( match s. strip_suffix ( 's' ) . unwrap_or ( s) {
108- "year" => Relative :: Years ( 1 ) ,
109- "month" => Relative :: Months ( 1 ) ,
110- "fortnight" => Relative :: Days ( 14 ) ,
111- "week" => Relative :: Days ( 7 ) ,
112- "day" => Relative :: Days ( 1 ) ,
113- "hour" => Relative :: Hours ( 1 ) ,
114- "minute" | "min" => Relative :: Minutes ( 1 ) ,
115- _ => return None ,
116- } )
117- } )
118- . parse_next ( input)
119- }
120-
121124#[ cfg( test) ]
122125mod tests {
123126 use super :: { parse, Relative } ;
@@ -126,16 +129,17 @@ mod tests {
126129 fn all ( ) {
127130 for ( s, rel) in [
128131 // Seconds
129- ( "second" , Relative :: Seconds ( 1.0 ) ) ,
130- ( "sec" , Relative :: Seconds ( 1.0 ) ) ,
131- ( "seconds" , Relative :: Seconds ( 1.0 ) ) ,
132- ( "secs" , Relative :: Seconds ( 1.0 ) ) ,
133- ( "second ago" , Relative :: Seconds ( -1.0 ) ) ,
134- ( "3 seconds" , Relative :: Seconds ( 3.0 ) ) ,
135- ( "3.5 seconds" , Relative :: Seconds ( 3.5 ) ) ,
136- // ("+3.5 seconds", Relative::Seconds(3.5)),
137- ( "3.5 seconds ago" , Relative :: Seconds ( -3.5 ) ) ,
138- ( "-3.5 seconds ago" , Relative :: Seconds ( 3.5 ) ) ,
132+ ( "second" , Relative :: Seconds ( 1 , 0 ) ) ,
133+ ( "sec" , Relative :: Seconds ( 1 , 0 ) ) ,
134+ ( "seconds" , Relative :: Seconds ( 1 , 0 ) ) ,
135+ ( "secs" , Relative :: Seconds ( 1 , 0 ) ) ,
136+ ( "second ago" , Relative :: Seconds ( -1 , 0 ) ) ,
137+ ( "3 seconds" , Relative :: Seconds ( 3 , 0 ) ) ,
138+ ( "3.5 seconds" , Relative :: Seconds ( 3 , 500_000_000 ) ) ,
139+ ( "-3.5 seconds" , Relative :: Seconds ( -4 , 500_000_000 ) ) ,
140+ ( "+3.5 seconds" , Relative :: Seconds ( 3 , 500_000_000 ) ) ,
141+ ( "3.5 seconds ago" , Relative :: Seconds ( -4 , 500_000_000 ) ) ,
142+ ( "-3.5 seconds ago" , Relative :: Seconds ( 3 , 500_000_000 ) ) ,
139143 // Minutes
140144 ( "minute" , Relative :: Minutes ( 1 ) ) ,
141145 ( "minutes" , Relative :: Minutes ( 1 ) ) ,
@@ -181,7 +185,7 @@ mod tests {
181185 ( "now" , Relative :: Days ( 0 ) ) ,
182186 // This something
183187 ( "this day" , Relative :: Days ( 0 ) ) ,
184- ( "this second" , Relative :: Seconds ( 0. 0 ) ) ,
188+ ( "this second" , Relative :: Seconds ( 0 , 0 ) ) ,
185189 ( "this year" , Relative :: Years ( 0 ) ) ,
186190 // Weird stuff
187191 ( "next week ago" , Relative :: Days ( -7 ) ) ,
0 commit comments