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}