1+ use pg_interval:: Interval ;
2+ use interval_norm:: IntervalNorm ;
3+ use chrono:: Duration ;
4+
5+ const NANOS_PER_SEC : i64 = 1_000_000_000 ;
6+ const NANOS_PER_MICRO : i64 = 1000 ;
7+
8+ impl Interval {
9+ /// Tries to convert from the `Duration` type to a `Interval`. Will
10+ /// return `None` on a overflow. This is a lossy conversion in that
11+ /// any units smaller than a microsecond will be lost.
12+ pub fn from_duration ( duration : Duration ) -> Option < Interval > {
13+ let mut days = duration. num_days ( ) ;
14+ let mut new_dur = duration - Duration :: days ( days) ;
15+ let mut hours = duration. num_hours ( ) ;
16+ new_dur = new_dur - Duration :: hours ( hours) ;
17+ let minutes = new_dur. num_minutes ( ) ;
18+ new_dur = new_dur - Duration :: minutes ( minutes) ;
19+ let nano_secs = new_dur. num_nanoseconds ( ) ?;
20+ if days > ( i32:: max_value ( ) as i64 ) {
21+ let overflow_days = days - ( i32:: max_value ( ) as i64 ) ;
22+ let added_hours = overflow_days. checked_mul ( 24 ) ?;
23+ hours = hours. checked_add ( added_hours) ?;
24+ days -= overflow_days;
25+ }
26+ let ( seconds, remaining_nano) = reduce_by_units ( nano_secs, NANOS_PER_SEC ) ;
27+ // We have to discard any remaining nanoseconds
28+ let ( microseconds, _remaining_nano) = reduce_by_units ( remaining_nano, NANOS_PER_MICRO ) ;
29+ let norm_interval = IntervalNorm {
30+ years : 0 ,
31+ months : 0 ,
32+ days : days as i32 ,
33+ hours : hours,
34+ minutes : minutes,
35+ seconds : seconds,
36+ microseconds : microseconds,
37+ } ;
38+ norm_interval. try_into_interval ( ) . ok ( )
39+ }
40+ }
41+
42+ fn reduce_by_units ( nano_secs : i64 , unit : i64 ) -> ( i64 , i64 ) {
43+ let new_time_unit = ( nano_secs - ( nano_secs % unit) ) / unit;
44+ let remaining_nano = nano_secs - ( new_time_unit * unit) ;
45+ ( new_time_unit, remaining_nano)
46+ }
47+
48+ #[ cfg( test) ]
49+ mod tests {
50+ use pg_interval:: Interval ;
51+ use chrono:: Duration ;
52+
53+
54+ #[ test]
55+ fn can_convert_small_amount_of_days ( ) {
56+ let dur = Duration :: days ( 5 ) ;
57+ let interval = Interval :: from_duration ( dur) ;
58+ assert_eq ! ( interval, Some ( Interval :: new( 0 , 5 , 0 ) ) )
59+ }
60+
61+ #[ test]
62+ fn overflow_on_days ( ) {
63+ let dur = Duration :: days ( 100000000000 ) ;
64+ let interval = Interval :: from_duration ( dur) ;
65+ assert_eq ! ( interval, None )
66+ }
67+
68+ #[ test]
69+ fn can_convert_small_amount_of_secs ( ) {
70+ let dur = Duration :: seconds ( 1 ) ;
71+ let interval = Interval :: from_duration ( dur) ;
72+ assert_eq ! ( interval, Some ( Interval :: new( 0 , 0 , 1_000_000 ) ) )
73+ }
74+
75+ #[ test]
76+ fn can_convert_one_micro ( ) {
77+ let dur = Duration :: nanoseconds ( 1000 ) ;
78+ let interval = Interval :: from_duration ( dur) ;
79+ assert_eq ! ( interval, Some ( Interval :: new( 0 , 0 , 1 ) ) )
80+ }
81+ }
0 commit comments