• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /// The datetime coordinates
2 use chrono::{Date, DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, TimeZone, Timelike};
3 use std::ops::{Add, Range, Sub};
4 
5 use crate::coord::ranged1d::{
6     AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged,
7     ReversibleRanged, ValueFormatter,
8 };
9 
10 /// The trait that describe some time value. This is the uniformed abstraction that works
11 /// for both Date, DateTime and Duration, etc.
12 pub trait TimeValue: Eq + Sized {
13     type DateType: Datelike + PartialOrd;
14 
15     /// Returns the date that is no later than the time
date_floor(&self) -> Self::DateType16     fn date_floor(&self) -> Self::DateType;
17     /// Returns the date that is no earlier than the time
date_ceil(&self) -> Self::DateType18     fn date_ceil(&self) -> Self::DateType;
19     /// Returns the maximum value that is earlier than the given date
earliest_after_date(date: Self::DateType) -> Self20     fn earliest_after_date(date: Self::DateType) -> Self;
21     /// Returns the duration between two time value
subtract(&self, other: &Self) -> Duration22     fn subtract(&self, other: &Self) -> Duration;
23     /// Add duration to time value
add(&self, duration: &Duration) -> Self24     fn add(&self, duration: &Duration) -> Self;
25     /// Instantiate a date type for current time value;
ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType26     fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType;
27     /// Cast current date type into this type
from_date(date: Self::DateType) -> Self28     fn from_date(date: Self::DateType) -> Self;
29 
30     /// Map the coord spec
map_coord(value: &Self, begin: &Self, end: &Self, limit: (i32, i32)) -> i3231     fn map_coord(value: &Self, begin: &Self, end: &Self, limit: (i32, i32)) -> i32 {
32         let total_span = end.subtract(begin);
33         let value_span = value.subtract(begin);
34 
35         // First, lets try the nanoseconds precision
36         if let Some(total_ns) = total_span.num_nanoseconds() {
37             if let Some(value_ns) = value_span.num_nanoseconds() {
38                 return (f64::from(limit.1 - limit.0) * value_ns as f64 / total_ns as f64) as i32
39                     + limit.0;
40             }
41         }
42 
43         // Yes, converting them to floating point may lose precision, but this is Ok.
44         // If it overflows, it means we have a time span nearly 300 years, we are safe to ignore the
45         // portion less than 1 day.
46         let total_days = total_span.num_days() as f64;
47         let value_days = value_span.num_days() as f64;
48 
49         (f64::from(limit.1 - limit.0) * value_days / total_days) as i32 + limit.0
50     }
51 
52     /// Map pixel to coord spec
unmap_coord(point: i32, begin: &Self, end: &Self, limit: (i32, i32)) -> Self53     fn unmap_coord(point: i32, begin: &Self, end: &Self, limit: (i32, i32)) -> Self {
54         let total_span = end.subtract(begin);
55         let offset = (point - limit.0) as i64;
56 
57         // Check if nanoseconds fit in i64
58         if let Some(total_ns) = total_span.num_nanoseconds() {
59             let pixel_span = (limit.1 - limit.0) as i64;
60             let factor = total_ns / pixel_span;
61             let remainder = total_ns % pixel_span;
62             if factor == 0
63                 || i64::MAX / factor > offset.abs()
64                 || (remainder == 0 && i64::MAX / factor >= offset.abs())
65             {
66                 let nano_seconds = offset * factor + (remainder * offset) / pixel_span;
67                 return begin.add(&Duration::nanoseconds(nano_seconds));
68             }
69         }
70 
71         // Otherwise, use days
72         let total_days = total_span.num_days() as f64;
73         let days = (((offset as f64) * total_days) / ((limit.1 - limit.0) as f64)) as i64;
74         begin.add(&Duration::days(days))
75     }
76 }
77 
78 impl TimeValue for NaiveDate {
79     type DateType = NaiveDate;
date_floor(&self) -> NaiveDate80     fn date_floor(&self) -> NaiveDate {
81         *self
82     }
date_ceil(&self) -> NaiveDate83     fn date_ceil(&self) -> NaiveDate {
84         *self
85     }
earliest_after_date(date: NaiveDate) -> Self86     fn earliest_after_date(date: NaiveDate) -> Self {
87         date
88     }
subtract(&self, other: &NaiveDate) -> Duration89     fn subtract(&self, other: &NaiveDate) -> Duration {
90         *self - *other
91     }
add(&self, other: &Duration) -> NaiveDate92     fn add(&self, other: &Duration) -> NaiveDate {
93         *self + *other
94     }
95 
ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType96     fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
97         NaiveDate::from_ymd(year, month, date)
98     }
99 
from_date(date: Self::DateType) -> Self100     fn from_date(date: Self::DateType) -> Self {
101         date
102     }
103 }
104 
105 impl<Z: TimeZone> TimeValue for Date<Z> {
106     type DateType = Date<Z>;
date_floor(&self) -> Date<Z>107     fn date_floor(&self) -> Date<Z> {
108         self.clone()
109     }
date_ceil(&self) -> Date<Z>110     fn date_ceil(&self) -> Date<Z> {
111         self.clone()
112     }
earliest_after_date(date: Date<Z>) -> Self113     fn earliest_after_date(date: Date<Z>) -> Self {
114         date
115     }
subtract(&self, other: &Date<Z>) -> Duration116     fn subtract(&self, other: &Date<Z>) -> Duration {
117         self.clone() - other.clone()
118     }
add(&self, other: &Duration) -> Date<Z>119     fn add(&self, other: &Duration) -> Date<Z> {
120         self.clone() + *other
121     }
122 
ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType123     fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
124         self.timezone().ymd(year, month, date)
125     }
126 
from_date(date: Self::DateType) -> Self127     fn from_date(date: Self::DateType) -> Self {
128         date
129     }
130 }
131 
132 impl<Z: TimeZone> TimeValue for DateTime<Z> {
133     type DateType = Date<Z>;
date_floor(&self) -> Date<Z>134     fn date_floor(&self) -> Date<Z> {
135         self.date()
136     }
date_ceil(&self) -> Date<Z>137     fn date_ceil(&self) -> Date<Z> {
138         if self.time().num_seconds_from_midnight() > 0 {
139             self.date() + Duration::days(1)
140         } else {
141             self.date()
142         }
143     }
earliest_after_date(date: Date<Z>) -> DateTime<Z>144     fn earliest_after_date(date: Date<Z>) -> DateTime<Z> {
145         date.and_hms(0, 0, 0)
146     }
147 
subtract(&self, other: &DateTime<Z>) -> Duration148     fn subtract(&self, other: &DateTime<Z>) -> Duration {
149         self.clone() - other.clone()
150     }
add(&self, other: &Duration) -> DateTime<Z>151     fn add(&self, other: &Duration) -> DateTime<Z> {
152         self.clone() + *other
153     }
154 
ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType155     fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
156         self.timezone().ymd(year, month, date)
157     }
158 
from_date(date: Self::DateType) -> Self159     fn from_date(date: Self::DateType) -> Self {
160         date.and_hms(0, 0, 0)
161     }
162 }
163 
164 impl TimeValue for NaiveDateTime {
165     type DateType = NaiveDate;
166 
date_floor(&self) -> NaiveDate167     fn date_floor(&self) -> NaiveDate {
168         self.date()
169     }
170 
date_ceil(&self) -> NaiveDate171     fn date_ceil(&self) -> NaiveDate {
172         if self.time().num_seconds_from_midnight() > 0 {
173             self.date() + Duration::days(1)
174         } else {
175             self.date()
176         }
177     }
178 
earliest_after_date(date: NaiveDate) -> NaiveDateTime179     fn earliest_after_date(date: NaiveDate) -> NaiveDateTime {
180         date.and_hms(0, 0, 0)
181     }
182 
subtract(&self, other: &NaiveDateTime) -> Duration183     fn subtract(&self, other: &NaiveDateTime) -> Duration {
184         *self - *other
185     }
add(&self, other: &Duration) -> NaiveDateTime186     fn add(&self, other: &Duration) -> NaiveDateTime {
187         *self + *other
188     }
189 
ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType190     fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
191         NaiveDate::from_ymd(year, month, date)
192     }
193 
from_date(date: Self::DateType) -> Self194     fn from_date(date: Self::DateType) -> Self {
195         date.and_hms(0, 0, 0)
196     }
197 }
198 
199 /// The ranged coordinate for date
200 #[derive(Clone)]
201 pub struct RangedDate<D: Datelike>(D, D);
202 
203 impl<D: Datelike> From<Range<D>> for RangedDate<D> {
from(range: Range<D>) -> Self204     fn from(range: Range<D>) -> Self {
205         Self(range.start, range.end)
206     }
207 }
208 
209 impl<D> Ranged for RangedDate<D>
210 where
211     D: Datelike + TimeValue + Sub<D, Output = Duration> + Add<Duration, Output = D> + Clone,
212 {
213     type FormatOption = DefaultFormatting;
214     type ValueType = D;
215 
range(&self) -> Range<D>216     fn range(&self) -> Range<D> {
217         self.0.clone()..self.1.clone()
218     }
219 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32220     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
221         TimeValue::map_coord(value, &self.0, &self.1, limit)
222     }
223 
key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>224     fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
225         let max_points = hint.max_num_points();
226         let mut ret = vec![];
227 
228         let total_days = (self.1.clone() - self.0.clone()).num_days();
229         let total_weeks = (self.1.clone() - self.0.clone()).num_weeks();
230 
231         if total_days > 0 && total_days as usize <= max_points {
232             for day_idx in 0..=total_days {
233                 ret.push(self.0.clone() + Duration::days(day_idx));
234             }
235             return ret;
236         }
237 
238         if total_weeks > 0 && total_weeks as usize <= max_points {
239             for day_idx in 0..=total_weeks {
240                 ret.push(self.0.clone() + Duration::weeks(day_idx));
241             }
242             return ret;
243         }
244 
245         // When all data is in the same week, just plot properly.
246         if total_weeks == 0 {
247             ret.push(self.0.clone());
248             return ret;
249         }
250 
251         let week_per_point = ((total_weeks as f64) / (max_points as f64)).ceil() as usize;
252 
253         for idx in 0..=(total_weeks as usize / week_per_point) {
254             ret.push(self.0.clone() + Duration::weeks((idx * week_per_point) as i64));
255         }
256 
257         ret
258     }
259 }
260 
261 impl<D> DiscreteRanged for RangedDate<D>
262 where
263     D: Datelike + TimeValue + Sub<D, Output = Duration> + Add<Duration, Output = D> + Clone,
264 {
size(&self) -> usize265     fn size(&self) -> usize {
266         ((self.1.clone() - self.0.clone()).num_days().max(-1) + 1) as usize
267     }
268 
index_of(&self, value: &D) -> Option<usize>269     fn index_of(&self, value: &D) -> Option<usize> {
270         let ret = (value.clone() - self.0.clone()).num_days();
271         if ret < 0 {
272             return None;
273         }
274         Some(ret as usize)
275     }
276 
from_index(&self, index: usize) -> Option<D>277     fn from_index(&self, index: usize) -> Option<D> {
278         Some(self.0.clone() + Duration::days(index as i64))
279     }
280 }
281 
282 impl<Z: TimeZone> AsRangedCoord for Range<Date<Z>> {
283     type CoordDescType = RangedDate<Date<Z>>;
284     type Value = Date<Z>;
285 }
286 
287 impl AsRangedCoord for Range<NaiveDate> {
288     type CoordDescType = RangedDate<NaiveDate>;
289     type Value = NaiveDate;
290 }
291 
292 /// Indicates the coord has a monthly resolution
293 ///
294 /// Note: since month doesn't have a constant duration.
295 /// We can't use a simple granularity to describe it. Thus we have
296 /// this axis decorator to make it yield monthly key-points.
297 #[derive(Clone)]
298 pub struct Monthly<T: TimeValue>(Range<T>);
299 
300 impl<T: TimeValue + Datelike + Clone> ValueFormatter<T> for Monthly<T> {
format(value: &T) -> String301     fn format(value: &T) -> String {
302         format!("{}-{}", value.year(), value.month())
303     }
304 }
305 
306 impl<T: TimeValue + Clone> Monthly<T> {
bold_key_points<H: KeyPointHint>(&self, hint: &H) -> Vec<T>307     fn bold_key_points<H: KeyPointHint>(&self, hint: &H) -> Vec<T> {
308         let max_points = hint.max_num_points();
309         let start_date = self.0.start.date_ceil();
310         let end_date = self.0.end.date_floor();
311 
312         let mut start_year = start_date.year();
313         let mut start_month = start_date.month();
314         let start_day = start_date.day();
315 
316         let end_year = end_date.year();
317         let end_month = end_date.month();
318 
319         if start_day != 1 {
320             start_month += 1;
321             if start_month == 13 {
322                 start_month = 1;
323                 start_year += 1;
324             }
325         }
326 
327         let total_month = (end_year - start_year) * 12 + end_month as i32 - start_month as i32;
328 
329         fn generate_key_points<T: TimeValue>(
330             mut start_year: i32,
331             mut start_month: i32,
332             end_year: i32,
333             end_month: i32,
334             step: u32,
335             builder: &T,
336         ) -> Vec<T> {
337             let mut ret = vec![];
338             while end_year > start_year || (end_year == start_year && end_month >= start_month) {
339                 ret.push(T::earliest_after_date(builder.ymd(
340                     start_year,
341                     start_month as u32,
342                     1,
343                 )));
344                 start_month += step as i32;
345 
346                 if start_month >= 13 {
347                     start_year += start_month / 12;
348                     start_month %= 12;
349                 }
350             }
351 
352             ret
353         }
354 
355         if total_month as usize <= max_points {
356             // Monthly
357             return generate_key_points(
358                 start_year,
359                 start_month as i32,
360                 end_year,
361                 end_month as i32,
362                 1,
363                 &self.0.start,
364             );
365         } else if total_month as usize <= max_points * 3 {
366             // Quarterly
367             return generate_key_points(
368                 start_year,
369                 start_month as i32,
370                 end_year,
371                 end_month as i32,
372                 3,
373                 &self.0.start,
374             );
375         } else if total_month as usize <= max_points * 6 {
376             // Biyearly
377             return generate_key_points(
378                 start_year,
379                 start_month as i32,
380                 end_year,
381                 end_month as i32,
382                 6,
383                 &self.0.start,
384             );
385         }
386 
387         // Otherwise we could generate the yearly keypoints
388         generate_yearly_keypoints(
389             max_points,
390             start_year,
391             start_month,
392             end_year,
393             end_month,
394             &self.0.start,
395         )
396     }
397 }
398 
399 impl<T: TimeValue + Clone> Ranged for Monthly<T>
400 where
401     Range<T>: AsRangedCoord<Value = T>,
402 {
403     type FormatOption = NoDefaultFormatting;
404     type ValueType = T;
405 
range(&self) -> Range<T>406     fn range(&self) -> Range<T> {
407         self.0.start.clone()..self.0.end.clone()
408     }
409 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32410     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
411         T::map_coord(value, &self.0.start, &self.0.end, limit)
412     }
413 
key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>414     fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
415         if hint.weight().allow_light_points() && self.size() <= hint.bold_points() * 2 {
416             let coord: <Range<T> as AsRangedCoord>::CoordDescType = self.0.clone().into();
417             let normal = coord.key_points(hint.max_num_points());
418             return normal;
419         }
420         self.bold_key_points(&hint)
421     }
422 }
423 
424 impl<T: TimeValue + Clone> DiscreteRanged for Monthly<T>
425 where
426     Range<T>: AsRangedCoord<Value = T>,
427 {
size(&self) -> usize428     fn size(&self) -> usize {
429         let (start_year, start_month) = {
430             let ceil = self.0.start.date_ceil();
431             (ceil.year(), ceil.month())
432         };
433         let (end_year, end_month) = {
434             let floor = self.0.end.date_floor();
435             (floor.year(), floor.month())
436         };
437         ((end_year - start_year).max(0) * 12
438             + (1 - start_month as i32)
439             + (end_month as i32 - 1)
440             + 1)
441         .max(0) as usize
442     }
443 
index_of(&self, value: &T) -> Option<usize>444     fn index_of(&self, value: &T) -> Option<usize> {
445         let this_year = value.date_floor().year();
446         let this_month = value.date_floor().month();
447 
448         let start_year = self.0.start.date_ceil().year();
449         let start_month = self.0.start.date_ceil().month();
450 
451         let ret = (this_year - start_year).max(0) * 12
452             + (1 - start_month as i32)
453             + (this_month as i32 - 1);
454         if ret >= 0 {
455             return Some(ret as usize);
456         }
457         None
458     }
459 
from_index(&self, index: usize) -> Option<T>460     fn from_index(&self, index: usize) -> Option<T> {
461         if index == 0 {
462             return Some(T::earliest_after_date(self.0.start.date_ceil()));
463         }
464         let index_from_start_year = index + (self.0.start.date_ceil().month() - 1) as usize;
465         let year = self.0.start.date_ceil().year() + index_from_start_year as i32 / 12;
466         let month = index_from_start_year % 12;
467         Some(T::earliest_after_date(self.0.start.ymd(
468             year,
469             month as u32 + 1,
470             1,
471         )))
472     }
473 }
474 
475 /// Indicate the coord has a yearly granularity.
476 #[derive(Clone)]
477 pub struct Yearly<T: TimeValue>(Range<T>);
478 
generate_yearly_keypoints<T: TimeValue>( max_points: usize, mut start_year: i32, start_month: u32, mut end_year: i32, end_month: u32, builder: &T, ) -> Vec<T>479 fn generate_yearly_keypoints<T: TimeValue>(
480     max_points: usize,
481     mut start_year: i32,
482     start_month: u32,
483     mut end_year: i32,
484     end_month: u32,
485     builder: &T,
486 ) -> Vec<T> {
487     if start_month > end_month {
488         end_year -= 1;
489     }
490 
491     let mut exp10 = 1;
492 
493     while (end_year - start_year + 1) as usize / (exp10 * 10) > max_points {
494         exp10 *= 10;
495     }
496 
497     let mut freq = exp10;
498 
499     for try_freq in &[1, 2, 5, 10] {
500         freq = *try_freq * exp10;
501         if (end_year - start_year + 1) as usize / (exp10 * *try_freq) <= max_points {
502             break;
503         }
504     }
505 
506     let mut ret = vec![];
507 
508     while start_year <= end_year {
509         ret.push(T::earliest_after_date(builder.ymd(
510             start_year,
511             start_month,
512             1,
513         )));
514         start_year += freq as i32;
515     }
516 
517     ret
518 }
519 
520 impl<T: TimeValue + Datelike + Clone> ValueFormatter<T> for Yearly<T> {
format(value: &T) -> String521     fn format(value: &T) -> String {
522         format!("{}-{}", value.year(), value.month())
523     }
524 }
525 
526 impl<T: TimeValue + Clone> Ranged for Yearly<T>
527 where
528     Range<T>: AsRangedCoord<Value = T>,
529 {
530     type FormatOption = NoDefaultFormatting;
531     type ValueType = T;
532 
range(&self) -> Range<T>533     fn range(&self) -> Range<T> {
534         self.0.start.clone()..self.0.end.clone()
535     }
536 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32537     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
538         T::map_coord(value, &self.0.start, &self.0.end, limit)
539     }
540 
key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>541     fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
542         if hint.weight().allow_light_points() && self.size() <= hint.bold_points() * 2 {
543             return Monthly(self.0.clone()).key_points(hint);
544         }
545         let max_points = hint.max_num_points();
546         let start_date = self.0.start.date_ceil();
547         let end_date = self.0.end.date_floor();
548 
549         let mut start_year = start_date.year();
550         let mut start_month = start_date.month();
551         let start_day = start_date.day();
552 
553         let end_year = end_date.year();
554         let end_month = end_date.month();
555 
556         if start_day != 1 {
557             start_month += 1;
558             if start_month == 13 {
559                 start_month = 1;
560                 start_year += 1;
561             }
562         }
563 
564         generate_yearly_keypoints(
565             max_points,
566             start_year,
567             start_month,
568             end_year,
569             end_month,
570             &self.0.start,
571         )
572     }
573 }
574 
575 impl<T: TimeValue + Clone> DiscreteRanged for Yearly<T>
576 where
577     Range<T>: AsRangedCoord<Value = T>,
578 {
size(&self) -> usize579     fn size(&self) -> usize {
580         let year_start = self.0.start.date_ceil().year();
581         let year_end = self.0.end.date_floor().year();
582         ((year_end - year_start).max(-1) + 1) as usize
583     }
584 
index_of(&self, value: &T) -> Option<usize>585     fn index_of(&self, value: &T) -> Option<usize> {
586         let year_start = self.0.start.date_ceil().year();
587         let year_value = value.date_floor().year();
588         let ret = year_value - year_start;
589         if ret < 0 {
590             return None;
591         }
592         Some(ret as usize)
593     }
594 
from_index(&self, index: usize) -> Option<T>595     fn from_index(&self, index: usize) -> Option<T> {
596         let year = self.0.start.date_ceil().year() + index as i32;
597         let ret = T::earliest_after_date(self.0.start.ymd(year, 1, 1));
598         if ret.date_ceil() <= self.0.start.date_floor() {
599             return Some(self.0.start.clone());
600         }
601         Some(ret)
602     }
603 }
604 
605 /// The trait that converts a normal date coord into a monthly one
606 pub trait IntoMonthly<T: TimeValue> {
607     /// Converts a normal date coord into a monthly one
monthly(self) -> Monthly<T>608     fn monthly(self) -> Monthly<T>;
609 }
610 
611 /// The trait that converts a normal date coord into a yearly one
612 pub trait IntoYearly<T: TimeValue> {
613     /// Converts a normal date coord into a yearly one
yearly(self) -> Yearly<T>614     fn yearly(self) -> Yearly<T>;
615 }
616 
617 impl<T: TimeValue> IntoMonthly<T> for Range<T> {
monthly(self) -> Monthly<T>618     fn monthly(self) -> Monthly<T> {
619         Monthly(self)
620     }
621 }
622 
623 impl<T: TimeValue> IntoYearly<T> for Range<T> {
yearly(self) -> Yearly<T>624     fn yearly(self) -> Yearly<T> {
625         Yearly(self)
626     }
627 }
628 
629 /// The ranged coordinate for the date and time
630 #[derive(Clone)]
631 pub struct RangedDateTime<DT: Datelike + Timelike + TimeValue>(DT, DT);
632 
633 impl<Z: TimeZone> AsRangedCoord for Range<DateTime<Z>> {
634     type CoordDescType = RangedDateTime<DateTime<Z>>;
635     type Value = DateTime<Z>;
636 }
637 
638 impl<Z: TimeZone> From<Range<DateTime<Z>>> for RangedDateTime<DateTime<Z>> {
from(range: Range<DateTime<Z>>) -> Self639     fn from(range: Range<DateTime<Z>>) -> Self {
640         Self(range.start, range.end)
641     }
642 }
643 
644 impl From<Range<NaiveDateTime>> for RangedDateTime<NaiveDateTime> {
from(range: Range<NaiveDateTime>) -> Self645     fn from(range: Range<NaiveDateTime>) -> Self {
646         Self(range.start, range.end)
647     }
648 }
649 
650 impl<DT> Ranged for RangedDateTime<DT>
651 where
652     DT: Datelike + Timelike + TimeValue + Clone + PartialOrd,
653     DT: Add<Duration, Output = DT>,
654     DT: Sub<DT, Output = Duration>,
655     RangedDate<DT::DateType>: Ranged<ValueType = DT::DateType>,
656 {
657     type FormatOption = DefaultFormatting;
658     type ValueType = DT;
659 
range(&self) -> Range<DT>660     fn range(&self) -> Range<DT> {
661         self.0.clone()..self.1.clone()
662     }
663 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32664     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
665         TimeValue::map_coord(value, &self.0, &self.1, limit)
666     }
667 
key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>668     fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
669         let max_points = hint.max_num_points();
670         let total_span = self.1.clone() - self.0.clone();
671 
672         if let Some(total_ns) = total_span.num_nanoseconds() {
673             if let Some(actual_ns_per_point) =
674                 compute_period_per_point(total_ns as u64, max_points, true)
675             {
676                 let start_time_ns = u64::from(self.0.num_seconds_from_midnight()) * 1_000_000_000
677                     + u64::from(self.0.nanosecond());
678 
679                 let mut start_time = DT::from_date(self.0.date_floor())
680                     + Duration::nanoseconds(if start_time_ns % actual_ns_per_point > 0 {
681                         start_time_ns + (actual_ns_per_point - start_time_ns % actual_ns_per_point)
682                     } else {
683                         start_time_ns
684                     } as i64);
685 
686                 let mut ret = vec![];
687 
688                 while start_time < self.1 {
689                     ret.push(start_time.clone());
690                     start_time = start_time + Duration::nanoseconds(actual_ns_per_point as i64);
691                 }
692 
693                 return ret;
694             }
695         }
696 
697         // Otherwise, it actually behaves like a date
698         let date_range = RangedDate(self.0.date_ceil(), self.1.date_floor());
699 
700         date_range
701             .key_points(max_points)
702             .into_iter()
703             .map(DT::from_date)
704             .collect()
705     }
706 }
707 
708 impl<DT> ReversibleRanged for RangedDateTime<DT>
709 where
710     DT: Datelike + Timelike + TimeValue + Clone + PartialOrd,
711     DT: Add<Duration, Output = DT>,
712     DT: Sub<DT, Output = Duration>,
713     RangedDate<DT::DateType>: Ranged<ValueType = DT::DateType>,
714 {
715     /// Perform the reverse mapping
unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType>716     fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType> {
717         Some(TimeValue::unmap_coord(input, &self.0, &self.1, limit))
718     }
719 }
720 
721 /// The coordinate that for duration of time
722 #[derive(Clone)]
723 pub struct RangedDuration(Duration, Duration);
724 
725 impl AsRangedCoord for Range<Duration> {
726     type CoordDescType = RangedDuration;
727     type Value = Duration;
728 }
729 
730 impl From<Range<Duration>> for RangedDuration {
from(range: Range<Duration>) -> Self731     fn from(range: Range<Duration>) -> Self {
732         Self(range.start, range.end)
733     }
734 }
735 
736 impl Ranged for RangedDuration {
737     type FormatOption = DefaultFormatting;
738     type ValueType = Duration;
739 
range(&self) -> Range<Duration>740     fn range(&self) -> Range<Duration> {
741         self.0..self.1
742     }
743 
map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32744     fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
745         let total_span = self.1 - self.0;
746         let value_span = *value - self.0;
747 
748         if let Some(total_ns) = total_span.num_nanoseconds() {
749             if let Some(value_ns) = value_span.num_nanoseconds() {
750                 return limit.0
751                     + (f64::from(limit.1 - limit.0) * value_ns as f64 / total_ns as f64 + 1e-10)
752                         as i32;
753             }
754             return limit.1;
755         }
756 
757         let total_days = total_span.num_days();
758         let value_days = value_span.num_days();
759 
760         limit.0
761             + (f64::from(limit.1 - limit.0) * value_days as f64 / total_days as f64 + 1e-10) as i32
762     }
763 
key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType>764     fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
765         let max_points = hint.max_num_points();
766         let total_span = self.1 - self.0;
767 
768         if let Some(total_ns) = total_span.num_nanoseconds() {
769             if let Some(period) = compute_period_per_point(total_ns as u64, max_points, false) {
770                 let mut start_ns = self.0.num_nanoseconds().unwrap();
771 
772                 if start_ns as u64 % period > 0 {
773                     if start_ns > 0 {
774                         start_ns += period as i64 - (start_ns % period as i64);
775                     } else {
776                         start_ns -= start_ns % period as i64;
777                     }
778                 }
779 
780                 let mut current = Duration::nanoseconds(start_ns);
781                 let mut ret = vec![];
782 
783                 while current < self.1 {
784                     ret.push(current);
785                     current += Duration::nanoseconds(period as i64);
786                 }
787 
788                 return ret;
789             }
790         }
791 
792         let begin_days = self.0.num_days();
793         let end_days = self.1.num_days();
794 
795         let mut days_per_tick = 1;
796         let mut idx = 0;
797         const MULTIPLIER: &[i32] = &[1, 2, 5];
798 
799         while (end_days - begin_days) / i64::from(days_per_tick * MULTIPLIER[idx])
800             > max_points as i64
801         {
802             idx += 1;
803             if idx == MULTIPLIER.len() {
804                 idx = 0;
805                 days_per_tick *= 10;
806             }
807         }
808 
809         days_per_tick *= MULTIPLIER[idx];
810 
811         let mut ret = vec![];
812 
813         let mut current = Duration::days(
814             self.0.num_days()
815                 + if Duration::days(self.0.num_days()) != self.0 {
816                     1
817                 } else {
818                     0
819                 },
820         );
821 
822         while current < self.1 {
823             ret.push(current);
824             current += Duration::days(i64::from(days_per_tick));
825         }
826 
827         ret
828     }
829 }
830 
831 #[allow(clippy::inconsistent_digit_grouping)]
compute_period_per_point(total_ns: u64, max_points: usize, sub_daily: bool) -> Option<u64>832 fn compute_period_per_point(total_ns: u64, max_points: usize, sub_daily: bool) -> Option<u64> {
833     let min_ns_per_point = total_ns as f64 / max_points as f64;
834     let actual_ns_per_point: u64 = (10u64).pow(min_ns_per_point.log10().floor() as u32);
835 
836     fn determine_actual_ns_per_point(
837         total_ns: u64,
838         mut actual_ns_per_point: u64,
839         units: &[u64],
840         base: u64,
841         max_points: usize,
842     ) -> u64 {
843         let mut unit_per_point_idx = 0;
844         while total_ns / actual_ns_per_point > max_points as u64 * units[unit_per_point_idx] {
845             unit_per_point_idx += 1;
846             if unit_per_point_idx == units.len() {
847                 unit_per_point_idx = 0;
848                 actual_ns_per_point *= base;
849             }
850         }
851         units[unit_per_point_idx] * actual_ns_per_point
852     }
853 
854     if actual_ns_per_point < 1_000_000_000 {
855         Some(determine_actual_ns_per_point(
856             total_ns,
857             actual_ns_per_point,
858             &[1, 2, 5],
859             10,
860             max_points,
861         ))
862     } else if actual_ns_per_point < 3600_000_000_000 {
863         Some(determine_actual_ns_per_point(
864             total_ns,
865             1_000_000_000,
866             &[1, 2, 5, 10, 15, 20, 30],
867             60,
868             max_points,
869         ))
870     } else if actual_ns_per_point < 3600_000_000_000 * 24 {
871         Some(determine_actual_ns_per_point(
872             total_ns,
873             3600_000_000_000,
874             &[1, 2, 4, 8, 12],
875             24,
876             max_points,
877         ))
878     } else if !sub_daily {
879         if actual_ns_per_point < 3600_000_000_000 * 24 * 10 {
880             Some(determine_actual_ns_per_point(
881                 total_ns,
882                 3600_000_000_000 * 24,
883                 &[1, 2, 5, 7],
884                 10,
885                 max_points,
886             ))
887         } else {
888             Some(determine_actual_ns_per_point(
889                 total_ns,
890                 3600_000_000_000 * 24 * 10,
891                 &[1, 2, 5],
892                 10,
893                 max_points,
894             ))
895         }
896     } else {
897         None
898     }
899 }
900 
901 #[cfg(test)]
902 mod test {
903     use super::*;
904     use chrono::{TimeZone, Utc};
905 
906     #[test]
test_date_range_long()907     fn test_date_range_long() {
908         let range = Utc.ymd(1000, 1, 1)..Utc.ymd(2999, 1, 1);
909 
910         let ranged_coord = Into::<RangedDate<_>>::into(range);
911 
912         assert_eq!(ranged_coord.map(&Utc.ymd(1000, 8, 10), (0, 100)), 0);
913         assert_eq!(ranged_coord.map(&Utc.ymd(2999, 8, 10), (0, 100)), 100);
914 
915         let kps = ranged_coord.key_points(23);
916 
917         assert!(kps.len() <= 23);
918         let max = kps
919             .iter()
920             .zip(kps.iter().skip(1))
921             .map(|(p, n)| (*n - *p).num_days())
922             .max()
923             .unwrap();
924         let min = kps
925             .iter()
926             .zip(kps.iter().skip(1))
927             .map(|(p, n)| (*n - *p).num_days())
928             .min()
929             .unwrap();
930         assert_eq!(max, min);
931         assert_eq!(max % 7, 0);
932     }
933 
934     #[test]
test_date_range_short()935     fn test_date_range_short() {
936         let range = Utc.ymd(2019, 1, 1)..Utc.ymd(2019, 1, 21);
937         let ranged_coord = Into::<RangedDate<_>>::into(range);
938 
939         let kps = ranged_coord.key_points(4);
940 
941         assert_eq!(kps.len(), 3);
942 
943         let max = kps
944             .iter()
945             .zip(kps.iter().skip(1))
946             .map(|(p, n)| (*n - *p).num_days())
947             .max()
948             .unwrap();
949         let min = kps
950             .iter()
951             .zip(kps.iter().skip(1))
952             .map(|(p, n)| (*n - *p).num_days())
953             .min()
954             .unwrap();
955         assert_eq!(max, min);
956         assert_eq!(max, 7);
957 
958         let kps = ranged_coord.key_points(30);
959         assert_eq!(kps.len(), 21);
960         let max = kps
961             .iter()
962             .zip(kps.iter().skip(1))
963             .map(|(p, n)| (*n - *p).num_days())
964             .max()
965             .unwrap();
966         let min = kps
967             .iter()
968             .zip(kps.iter().skip(1))
969             .map(|(p, n)| (*n - *p).num_days())
970             .min()
971             .unwrap();
972         assert_eq!(max, min);
973         assert_eq!(max, 1);
974     }
975 
976     #[test]
test_yearly_date_range()977     fn test_yearly_date_range() {
978         use crate::coord::ranged1d::BoldPoints;
979         let range = Utc.ymd(1000, 8, 5)..Utc.ymd(2999, 1, 1);
980         let ranged_coord = range.yearly();
981 
982         assert_eq!(ranged_coord.map(&Utc.ymd(1000, 8, 10), (0, 100)), 0);
983         assert_eq!(ranged_coord.map(&Utc.ymd(2999, 8, 10), (0, 100)), 100);
984 
985         let kps = ranged_coord.key_points(23);
986 
987         assert!(kps.len() <= 23);
988         let max = kps
989             .iter()
990             .zip(kps.iter().skip(1))
991             .map(|(p, n)| (*n - *p).num_days())
992             .max()
993             .unwrap();
994         let min = kps
995             .iter()
996             .zip(kps.iter().skip(1))
997             .map(|(p, n)| (*n - *p).num_days())
998             .min()
999             .unwrap();
1000         assert!(max != min);
1001 
1002         assert!(kps.into_iter().all(|x| x.month() == 9 && x.day() == 1));
1003 
1004         let range = Utc.ymd(2019, 8, 5)..Utc.ymd(2020, 1, 1);
1005         let ranged_coord = range.yearly();
1006         let kps = ranged_coord.key_points(BoldPoints(23));
1007         assert!(kps.len() == 1);
1008     }
1009 
1010     #[test]
test_monthly_date_range()1011     fn test_monthly_date_range() {
1012         let range = Utc.ymd(2019, 8, 5)..Utc.ymd(2020, 9, 1);
1013         let ranged_coord = range.monthly();
1014 
1015         use crate::coord::ranged1d::BoldPoints;
1016 
1017         let kps = ranged_coord.key_points(BoldPoints(15));
1018 
1019         assert!(kps.len() <= 15);
1020         assert!(kps.iter().all(|x| x.day() == 1));
1021         assert!(kps.into_iter().any(|x| x.month() != 9));
1022 
1023         let kps = ranged_coord.key_points(BoldPoints(5));
1024         assert!(kps.len() <= 5);
1025         assert!(kps.iter().all(|x| x.day() == 1));
1026         let kps: Vec<_> = kps.into_iter().map(|x| x.month()).collect();
1027         assert_eq!(kps, vec![9, 12, 3, 6, 9]);
1028 
1029         // TODO: Investigate why max_point = 1 breaks the contract
1030         let kps = ranged_coord.key_points(3);
1031         assert!(kps.len() == 3);
1032         assert!(kps.iter().all(|x| x.day() == 1));
1033         let kps: Vec<_> = kps.into_iter().map(|x| x.month()).collect();
1034         assert_eq!(kps, vec![9, 3, 9]);
1035     }
1036 
1037     #[test]
test_datetime_long_range()1038     fn test_datetime_long_range() {
1039         let coord: RangedDateTime<_> =
1040             (Utc.ymd(1000, 1, 1).and_hms(0, 0, 0)..Utc.ymd(3000, 1, 1).and_hms(0, 0, 0)).into();
1041 
1042         assert_eq!(
1043             coord.map(&Utc.ymd(1000, 1, 1).and_hms(0, 0, 0), (0, 100)),
1044             0
1045         );
1046         assert_eq!(
1047             coord.map(&Utc.ymd(3000, 1, 1).and_hms(0, 0, 0), (0, 100)),
1048             100
1049         );
1050 
1051         let kps = coord.key_points(23);
1052 
1053         assert!(kps.len() <= 23);
1054         let max = kps
1055             .iter()
1056             .zip(kps.iter().skip(1))
1057             .map(|(p, n)| (*n - *p).num_seconds())
1058             .max()
1059             .unwrap();
1060         let min = kps
1061             .iter()
1062             .zip(kps.iter().skip(1))
1063             .map(|(p, n)| (*n - *p).num_seconds())
1064             .min()
1065             .unwrap();
1066         assert!(max == min);
1067         assert!(max % (24 * 3600 * 7) == 0);
1068     }
1069 
1070     #[test]
test_datetime_medium_range()1071     fn test_datetime_medium_range() {
1072         let coord: RangedDateTime<_> =
1073             (Utc.ymd(2019, 1, 1).and_hms(0, 0, 0)..Utc.ymd(2019, 1, 11).and_hms(0, 0, 0)).into();
1074 
1075         let kps = coord.key_points(23);
1076 
1077         assert!(kps.len() <= 23);
1078         let max = kps
1079             .iter()
1080             .zip(kps.iter().skip(1))
1081             .map(|(p, n)| (*n - *p).num_seconds())
1082             .max()
1083             .unwrap();
1084         let min = kps
1085             .iter()
1086             .zip(kps.iter().skip(1))
1087             .map(|(p, n)| (*n - *p).num_seconds())
1088             .min()
1089             .unwrap();
1090         assert!(max == min);
1091         assert_eq!(max, 12 * 3600);
1092     }
1093 
1094     #[test]
test_datetime_short_range()1095     fn test_datetime_short_range() {
1096         let coord: RangedDateTime<_> =
1097             (Utc.ymd(2019, 1, 1).and_hms(0, 0, 0)..Utc.ymd(2019, 1, 2).and_hms(0, 0, 0)).into();
1098 
1099         let kps = coord.key_points(50);
1100 
1101         assert!(kps.len() <= 50);
1102         let max = kps
1103             .iter()
1104             .zip(kps.iter().skip(1))
1105             .map(|(p, n)| (*n - *p).num_seconds())
1106             .max()
1107             .unwrap();
1108         let min = kps
1109             .iter()
1110             .zip(kps.iter().skip(1))
1111             .map(|(p, n)| (*n - *p).num_seconds())
1112             .min()
1113             .unwrap();
1114         assert!(max == min);
1115         assert_eq!(max, 1800);
1116     }
1117 
1118     #[test]
test_datetime_nano_range()1119     fn test_datetime_nano_range() {
1120         let start = Utc.ymd(2019, 1, 1).and_hms(0, 0, 0);
1121         let end = start + Duration::nanoseconds(100);
1122         let coord: RangedDateTime<_> = (start..end).into();
1123 
1124         let kps = coord.key_points(50);
1125 
1126         assert!(kps.len() <= 50);
1127         let max = kps
1128             .iter()
1129             .zip(kps.iter().skip(1))
1130             .map(|(p, n)| (*n - *p).num_nanoseconds().unwrap())
1131             .max()
1132             .unwrap();
1133         let min = kps
1134             .iter()
1135             .zip(kps.iter().skip(1))
1136             .map(|(p, n)| (*n - *p).num_nanoseconds().unwrap())
1137             .min()
1138             .unwrap();
1139         assert!(max == min);
1140         assert_eq!(max, 2);
1141     }
1142 
1143     #[test]
test_duration_long_range()1144     fn test_duration_long_range() {
1145         let coord: RangedDuration = (Duration::days(-1000000)..Duration::days(1000000)).into();
1146 
1147         assert_eq!(coord.map(&Duration::days(-1000000), (0, 100)), 0);
1148         assert_eq!(coord.map(&Duration::days(1000000), (0, 100)), 100);
1149 
1150         let kps = coord.key_points(23);
1151 
1152         assert!(kps.len() <= 23);
1153         let max = kps
1154             .iter()
1155             .zip(kps.iter().skip(1))
1156             .map(|(p, n)| (*n - *p).num_seconds())
1157             .max()
1158             .unwrap();
1159         let min = kps
1160             .iter()
1161             .zip(kps.iter().skip(1))
1162             .map(|(p, n)| (*n - *p).num_seconds())
1163             .min()
1164             .unwrap();
1165         assert!(max == min);
1166         assert!(max % (24 * 3600 * 10000) == 0);
1167     }
1168 
1169     #[test]
test_duration_daily_range()1170     fn test_duration_daily_range() {
1171         let coord: RangedDuration = (Duration::days(0)..Duration::hours(25)).into();
1172 
1173         let kps = coord.key_points(23);
1174 
1175         assert!(kps.len() <= 23);
1176         let max = kps
1177             .iter()
1178             .zip(kps.iter().skip(1))
1179             .map(|(p, n)| (*n - *p).num_seconds())
1180             .max()
1181             .unwrap();
1182         let min = kps
1183             .iter()
1184             .zip(kps.iter().skip(1))
1185             .map(|(p, n)| (*n - *p).num_seconds())
1186             .min()
1187             .unwrap();
1188         assert!(max == min);
1189         assert_eq!(max, 3600 * 2);
1190     }
1191 
1192     #[test]
test_date_discrete()1193     fn test_date_discrete() {
1194         let coord: RangedDate<Date<_>> = (Utc.ymd(2019, 1, 1)..Utc.ymd(2019, 12, 31)).into();
1195         assert_eq!(coord.size(), 365);
1196         assert_eq!(coord.index_of(&Utc.ymd(2019, 2, 28)), Some(31 + 28 - 1));
1197         assert_eq!(coord.from_index(364), Some(Utc.ymd(2019, 12, 31)));
1198     }
1199 
1200     #[test]
test_monthly_discrete()1201     fn test_monthly_discrete() {
1202         let coord1 = (Utc.ymd(2019, 1, 10)..Utc.ymd(2019, 12, 31)).monthly();
1203         let coord2 = (Utc.ymd(2019, 1, 10)..Utc.ymd(2020, 1, 1)).monthly();
1204         assert_eq!(coord1.size(), 12);
1205         assert_eq!(coord2.size(), 13);
1206 
1207         for i in 1..=12 {
1208             assert_eq!(coord1.from_index(i - 1).unwrap().month(), i as u32);
1209             assert_eq!(
1210                 coord1.index_of(&coord1.from_index(i - 1).unwrap()).unwrap(),
1211                 i - 1
1212             );
1213         }
1214     }
1215 
1216     #[test]
test_yearly_discrete()1217     fn test_yearly_discrete() {
1218         let coord1 = (Utc.ymd(2000, 1, 10)..Utc.ymd(2019, 12, 31)).yearly();
1219         assert_eq!(coord1.size(), 20);
1220 
1221         for i in 0..20 {
1222             assert_eq!(coord1.from_index(i).unwrap().year(), 2000 + i as i32);
1223             assert_eq!(coord1.index_of(&coord1.from_index(i).unwrap()).unwrap(), i);
1224         }
1225     }
1226 
1227     #[test]
test_datetime_with_unmap()1228     fn test_datetime_with_unmap() {
1229         let start_time = Utc.ymd(2021, 1, 1).and_hms(8, 0, 0);
1230         let end_time = Utc.ymd(2023, 1, 1).and_hms(8, 0, 0);
1231         let mid = Utc.ymd(2022, 1, 1).and_hms(8, 0, 0);
1232         let coord: RangedDateTime<_> = (start_time..end_time).into();
1233         let pos = coord.map(&mid, (1000, 2000));
1234         assert_eq!(pos, 1500);
1235         let value = coord.unmap(pos, (1000, 2000));
1236         assert_eq!(value, Some(mid));
1237     }
1238 
1239     #[test]
test_naivedatetime_with_unmap()1240     fn test_naivedatetime_with_unmap() {
1241         let start_time = NaiveDate::from_ymd(2021, 1, 1).and_hms_milli(8, 0, 0, 0);
1242         let end_time = NaiveDate::from_ymd(2023, 1, 1).and_hms_milli(8, 0, 0, 0);
1243         let mid = NaiveDate::from_ymd(2022, 1, 1).and_hms_milli(8, 0, 0, 0);
1244         let coord: RangedDateTime<_> = (start_time..end_time).into();
1245         let pos = coord.map(&mid, (1000, 2000));
1246         assert_eq!(pos, 1500);
1247         let value = coord.unmap(pos, (1000, 2000));
1248         assert_eq!(value, Some(mid));
1249     }
1250 
1251     #[test]
test_date_with_unmap()1252     fn test_date_with_unmap() {
1253         let start_date = Utc.ymd(2021, 1, 1);
1254         let end_date = Utc.ymd(2023, 1, 1);
1255         let mid = Utc.ymd(2022, 1, 1);
1256         let coord: RangedDate<Date<_>> = (start_date..end_date).into();
1257         let pos = coord.map(&mid, (1000, 2000));
1258         assert_eq!(pos, 1500);
1259         let value = coord.unmap(pos, (1000, 2000));
1260         assert_eq!(value, Some(mid));
1261     }
1262 
1263     #[test]
test_naivedate_with_unmap()1264     fn test_naivedate_with_unmap() {
1265         let start_date = NaiveDate::from_ymd(2021, 1, 1);
1266         let end_date = NaiveDate::from_ymd(2023, 1, 1);
1267         let mid = NaiveDate::from_ymd(2022, 1, 1);
1268         let coord: RangedDate<NaiveDate> = (start_date..end_date).into();
1269         let pos = coord.map(&mid, (1000, 2000));
1270         assert_eq!(pos, 1500);
1271         let value = coord.unmap(pos, (1000, 2000));
1272         assert_eq!(value, Some(mid));
1273     }
1274 
1275     #[test]
test_datetime_unmap_for_nanoseconds()1276     fn test_datetime_unmap_for_nanoseconds() {
1277         let start_time = Utc.ymd(2021, 1, 1).and_hms(8, 0, 0);
1278         let end_time = start_time + Duration::nanoseconds(1900);
1279         let mid = start_time + Duration::nanoseconds(950);
1280         let coord: RangedDateTime<_> = (start_time..end_time).into();
1281         let pos = coord.map(&mid, (1000, 2000));
1282         assert_eq!(pos, 1500);
1283         let value = coord.unmap(pos, (1000, 2000));
1284         assert_eq!(value, Some(mid));
1285     }
1286 
1287     #[test]
test_datetime_unmap_for_nanoseconds_small_period()1288     fn test_datetime_unmap_for_nanoseconds_small_period() {
1289         let start_time = Utc.ymd(2021, 1, 1).and_hms(8, 0, 0);
1290         let end_time = start_time + Duration::nanoseconds(400);
1291         let coord: RangedDateTime<_> = (start_time..end_time).into();
1292         let value = coord.unmap(2000, (1000, 2000));
1293         assert_eq!(value, Some(end_time));
1294         let mid = start_time + Duration::nanoseconds(200);
1295         let value = coord.unmap(500, (0, 1000));
1296         assert_eq!(value, Some(mid));
1297     }
1298 }
1299