epoch_cli/
lib.rs

1//! epoch-cli is a tool for working with epoch timestamps.
2//!
3//! ### Features
4//!
5//! * Incredibly fast with low resource usage
6//! * Only UTC time is used
7//! * Can work with units of seconds, milliseconds, microseconds, or nanoseconds
8//! * Can convert epoch timestamps into dates and times
9//! * Can convert dates and times into epoch timestamps
10//! * Inspired by [https://www.epochconverter.com/](https://docs.rs/epoch-cli)
11//!
12//! ### Documentation
13//!
14//! Full documentation, instructions, and API are available at: [https://docs.rs/epoch-cli](https://docs.rs/epoch-cli)
15//!
16//! ### Installing with cargo
17//!
18//! 1. [Install rust](https://www.rust-lang.org/tools/install)
19//! 2. Run `cargo install epoch-cli`
20//!
21//! This will install a binary on your system named `epoch`.
22//!
23//! ### Displaying the current epoch time
24//!
25//! ```text
26//! $ epoch
27//! 1585796573
28//!
29//! $ epoch --ms
30//! 1585796603436
31//!
32//! $ epoch --us
33//! 1585796667156364
34//!
35//! $ epoch --ns
36//! 1585796681774366974
37//!```
38//!
39//! ### Converting an epoch timestamp to a datetime
40//!
41//! ```text
42//! $ epoch 1585796573
43//! 2020-04-02 03:02:53 UTC
44//!
45//! $ epoch --ms 1585796603436
46//! 2020-04-02 03:03:23.436 UTC
47//!
48//! $ epoch --us 1585796667156364
49//! 2020-04-02 03:04:27.156364 UTC
50//!
51//! $ epoch --ns 1585796681774366974
52//! 2020-04-02 03:04:41.774366974 UTC
53//!```
54//!
55//! ### Converting parts of a datetime into an epoch
56//!
57//! The full usage looks like `epoch --dt year month day [hour] [minute] [second] [millisecond] [microsecond] [nanosecond]`
58//!
59//! Only year, month, and day are required.
60//!
61//! ```text
62//! $ epoch --dt 2020 04 01 17 08 55 20 30 40
63//! 1585760935
64//!
65//! $ epoch --ns --dt 2020 04 01 17 08 55 20 30 40
66//! 1585760935020030040
67//!
68//! $ epoch --ms --dt 2020 04 01
69//! 1585699200000
70//!
71//! $ epoch --dt 2020 04 01 23 00 30
72//! 1585782030
73//!```
74
75use crate::errors::{EpochError, Result};
76use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time};
77
78pub mod conversions;
79pub mod errors;
80
81/// The parts that make up a date/time.
82#[derive(Debug, Default)]
83pub struct DateTimeParts {
84    pub year: i32,
85    pub month: u8,
86    pub day: u8,
87    pub hour: u8,
88    pub minute: u8,
89    pub second: u8,
90    pub millisecond: u32,
91    pub microsecond: u32,
92    pub nanosecond: u32,
93}
94
95impl DateTimeParts {
96    /// Computes the total number of nanoseconds from summing the millisecond, microsecond,
97    /// and nanosecond parts.
98    pub fn total_ns(&self) -> Result<u32> {
99        conversions::ms_to_ns_u32(self.millisecond)?
100            .checked_add(conversions::us_to_ns_u32(self.microsecond)?)
101            .ok_or_else(|| EpochError::numeric_precision("When summing total ns"))?
102            .checked_add(self.nanosecond)
103            .ok_or_else(|| EpochError::numeric_precision("When summing total ns"))
104    }
105}
106
107/// A struct that provides conversions to/from epochs/date/time.
108#[derive(Debug)]
109pub struct Epoch {
110    datetime: OffsetDateTime,
111}
112
113impl TryFrom<DateTimeParts> for Epoch {
114    type Error = EpochError;
115    /// Attempts to convert DateTimeParts into an Epoch
116    fn try_from(parts: DateTimeParts) -> Result<Self> {
117        let datetime = PrimitiveDateTime::new(
118            Date::from_calendar_date(parts.year, parse_month(parts.month)?, parts.day)?,
119            Time::from_hms_nano(parts.hour, parts.minute, parts.second, parts.total_ns()?)?,
120        )
121        .assume_utc();
122
123        Ok(Epoch::new(datetime))
124    }
125}
126
127/// Converts an integral month value into a time "Month"
128fn parse_month(value: u8) -> Result<Month> {
129    match value {
130        1 => Ok(Month::January),
131        2 => Ok(Month::February),
132        3 => Ok(Month::March),
133        4 => Ok(Month::April),
134        5 => Ok(Month::May),
135        6 => Ok(Month::June),
136        7 => Ok(Month::July),
137        8 => Ok(Month::August),
138        9 => Ok(Month::September),
139        10 => Ok(Month::October),
140        11 => Ok(Month::November),
141        12 => Ok(Month::December),
142        i => Err(EpochError {
143            err: format!("Illegal month integral={}. Valid values are [1-12].", i),
144        }),
145    }
146}
147
148impl Epoch {
149    /// Instantiates a new Epoch
150    pub const fn new(datetime: OffsetDateTime) -> Epoch {
151        Epoch { datetime }
152    }
153
154    /// Creates a new Epoch from DateTimeParts
155    pub fn from_parts(parts: DateTimeParts) -> Result<Epoch> {
156        parts.try_into()
157    }
158
159    /// Creates an epoch from seconds
160    pub fn from_epoch_s(epoch_s: i64) -> Result<Epoch> {
161        let datetime = OffsetDateTime::from_unix_timestamp(epoch_s)?;
162        Ok(Epoch::new(datetime))
163    }
164
165    /// Creates an epoch from milliseconds
166    pub fn from_epoch_ms(epoch_ms: i128) -> Result<Epoch> {
167        Epoch::from_epoch_ns(conversions::ms_to_ns_i128(epoch_ms)?)
168    }
169
170    /// Creates an epoch from microseconds
171    pub fn from_epoch_us(epoch_us: i128) -> Result<Epoch> {
172        Epoch::from_epoch_ns(conversions::us_to_ns_i128(epoch_us)?)
173    }
174
175    /// Creates an epoch from nanoseconds
176    pub fn from_epoch_ns(epoch_ns: i128) -> Result<Epoch> {
177        let datetime = OffsetDateTime::from_unix_timestamp_nanos(epoch_ns)?;
178        Ok(Epoch::new(datetime))
179    }
180
181    /// Returns the epoch as seconds
182    pub fn epoch_s(&self) -> i64 {
183        self.datetime.unix_timestamp()
184    }
185
186    /// Returns the epoch as milliseconds
187    pub fn epoch_ms(&self) -> Result<i128> {
188        conversions::ns_to_ms_i128(self.epoch_ns())
189    }
190
191    /// Returns the epoch as microseconds
192    pub fn epoch_us(&self) -> Result<i128> {
193        conversions::ns_to_us_i128(self.epoch_ns())
194    }
195
196    /// Returns the epoch as nanoseconds
197    pub fn epoch_ns(&self) -> i128 {
198        self.datetime.unix_timestamp_nanos()
199    }
200
201    /// Formats the epoch as a date/time
202    pub fn fmt(&self) -> String {
203        self.datetime.to_string()
204    }
205}
206
207impl Default for Epoch {
208    /// Returns the current epoch
209    fn default() -> Self {
210        Self {
211            datetime: OffsetDateTime::now_utc(),
212        }
213    }
214}