1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::convert::TryFrom;
use std::time::Duration;

use serde::{Deserialize, Serialize};

use crate::error::{Error, ErrorKind};

/// Different resolutions supported by the time related
/// metric types (e.g. DatetimeMetric).
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[repr(i32)] // use i32 to be compatible with our JNA definition
pub enum TimeUnit {
    /// Truncate to nanosecond precision.
    Nanosecond,
    /// Truncate to microsecond precision.
    Microsecond,
    /// Truncate to millisecond precision.
    Millisecond,
    /// Truncate to second precision.
    Second,
    /// Truncate to minute precision.
    Minute,
    /// Truncate to hour precision.
    Hour,
    /// Truncate to day precision.
    Day,
}

impl TimeUnit {
    /// Formats the given time unit, truncating the time if needed.
    pub fn format_pattern(self) -> &'static str {
        use TimeUnit::*;
        match self {
            Nanosecond => "%Y-%m-%dT%H:%M:%S%.f%:z",
            Microsecond => "%Y-%m-%dT%H:%M:%S%.6f%:z",
            Millisecond => "%Y-%m-%dT%H:%M:%S%.3f%:z",
            Second => "%Y-%m-%dT%H:%M:%S%:z",
            Minute => "%Y-%m-%dT%H:%M%:z",
            Hour => "%Y-%m-%dT%H%:z",
            Day => "%Y-%m-%d%:z",
        }
    }

    /// Converts a duration to the requested time unit.
    ///
    /// # Arguments
    ///
    /// * `duration` - the duration to convert.
    ///
    /// # Returns
    ///
    /// The integer representation of the converted duration.
    pub fn duration_convert(self, duration: Duration) -> u64 {
        use TimeUnit::*;
        match self {
            Nanosecond => duration.as_nanos() as u64,
            Microsecond => duration.as_micros() as u64,
            Millisecond => duration.as_millis() as u64,
            Second => duration.as_secs(),
            Minute => duration.as_secs() / 60,
            Hour => duration.as_secs() / 60 / 60,
            Day => duration.as_secs() / 60 / 60 / 24,
        }
    }

    /// Converts a duration in the given unit to nanoseconds.
    ///
    /// # Arguments
    ///
    /// * `duration` - the duration to convert.
    ///
    /// # Returns
    ///
    /// The integer representation of the nanosecond duration.
    pub fn as_nanos(self, duration: u64) -> u64 {
        use TimeUnit::*;
        let duration = match self {
            Nanosecond => Duration::from_nanos(duration),
            Microsecond => Duration::from_micros(duration),
            Millisecond => Duration::from_millis(duration),
            Second => Duration::from_secs(duration),
            Minute => Duration::from_secs(duration * 60),
            Hour => Duration::from_secs(duration * 60 * 60),
            Day => Duration::from_secs(duration * 60 * 60 * 24),
        };

        duration.as_nanos() as u64
    }
}

/// Trait implementation for converting an integer value to a TimeUnit.
///
/// This is used in the FFI code.
///
/// Please note that values should match the ordering of the
/// platform specific side of things (e.g. Kotlin implementation).
impl TryFrom<i32> for TimeUnit {
    type Error = Error;

    fn try_from(value: i32) -> Result<TimeUnit, Self::Error> {
        match value {
            0 => Ok(TimeUnit::Nanosecond),
            1 => Ok(TimeUnit::Microsecond),
            2 => Ok(TimeUnit::Millisecond),
            3 => Ok(TimeUnit::Second),
            4 => Ok(TimeUnit::Minute),
            5 => Ok(TimeUnit::Hour),
            6 => Ok(TimeUnit::Day),
            e => Err(ErrorKind::TimeUnit(e).into()),
        }
    }
}