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