dia_time/
lib.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4Dia-Time
5
6Copyright (C) 2018-2022, 2024  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2018-2022".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # A small time kit
28//!
29//! ## Project
30//!
31//! - License: GNU Lesser General Public License, either version 3, or (at your option) any later version.
32//! - _This project follows [Semantic Versioning 2.0.0]_
33//!
34//! ## Features
35//!
36//! - Constants as seconds: [`MINUTE`][const:MINUTE], [`HOUR`][const:HOUR], [`DAY`][const:DAY], [`WEEK`][const:WEEK].
37//! - Constants as milliseconds: [`millis::SECOND`][const:millis/SECOND], [`millis::MINUTE`][const:millis/MINUTE],
38//!   [`millis::HOUR`][const:millis/HOUR], [`millis::DAY`][const:millis/DAY], [`millis::WEEK`][const:millis/WEEK].
39//! - Other modules: [`micros`][mod:micros], [`nanos`][mod:nanos], [`decis`][mod:decis], [`centis`][mod:centis], [`picos`][mod:picos],
40//!   [`femtos`][mod:femtos], [`attos`][mod:attos], [`zeptos`][mod:zeptos], [`yoctos`][mod:yoctos].
41//! - And some helper functions for formatting time...
42//!
43//! Some components require `lib-c` feature. If you can't access them, you should enable that feature. For example:
44//!
45//! ```toml
46//! [dependencies]
47//! dia-time = { version='...', features=['lib-c'] }
48//! ```
49//!
50//! ## Notes
51//!
52//! Documentation is built with all features. Some of them are optional. If you see components from other crates, you can view source to see
53//! what features are required.
54//!
55//! ## References
56//!
57//! - Wikipedia: <https://en.wikipedia.org/wiki/Second#SI_multiples>
58//!
59//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html
60//! [const:MINUTE]: constant.MINUTE.html
61//! [const:HOUR]: constant.HOUR.html
62//! [const:DAY]: constant.DAY.html
63//! [const:WEEK]: constant.WEEK.html
64//! [mod:micros]: micros/index.html
65//! [mod:nanos]: nanos/index.html
66//! [mod:decis]: decis/index.html
67//! [mod:centis]: centis/index.html
68//! [mod:picos]: picos/index.html
69//! [mod:femtos]: femtos/index.html
70//! [mod:attos]: attos/index.html
71//! [mod:zeptos]: zeptos/index.html
72//! [mod:yoctos]: yoctos/index.html
73//! [const:millis/SECOND]: millis/constant.SECOND.html
74//! [const:millis/MINUTE]: millis/constant.MINUTE.html
75//! [const:millis/HOUR]: millis/constant.HOUR.html
76//! [const:millis/DAY]: millis/constant.DAY.html
77//! [const:millis/WEEK]: millis/constant.WEEK.html
78
79#![warn(missing_docs)]
80#![no_std]
81#![feature(doc_cfg)]
82
83// ╔═════════════════╗
84// ║   IDENTIFIERS   ║
85// ╚═════════════════╝
86
87macro_rules! crate_code_name    { () => { "dia-time" }}
88macro_rules! crate_version      { () => { "9.0.0" }}
89
90/// # Crate name
91pub const NAME: &str = "Dia-Time";
92
93/// # Crate code name
94pub const CODE_NAME: &str = crate_code_name!();
95
96/// # ID of this crate
97pub const ID: &str = concat!(
98    "88754f65-f8ad90df-c6f5d48f-3b7ec1e3-43dd5631-337efed4-cda6b228-5fb13c39-",
99    "c88959ab-59b4eb2d-b6ea09d6-c3fbc084-c3b4f246-ce05808f-91bdb0dc-a06f9b1e",
100);
101
102/// # Crate version
103pub const VERSION: &str = crate_version!();
104
105/// # Crate release date (year/month/day)
106pub const RELEASE_DATE: (u16, u8, u8) = (2024, 4, 18);
107
108/// # Tag, which can be used for logging...
109pub const TAG: &str = concat!(crate_code_name!(), "::88754f65::", crate_version!());
110
111// ╔════════════════════╗
112// ║   IMPLEMENTATION   ║
113// ╚════════════════════╝
114
115#[macro_use]
116extern crate alloc;
117
118#[cfg(feature="std")]
119extern crate std;
120
121use alloc::string::String;
122
123#[test]
124fn test_crate_version() {
125    assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
126}
127
128use core::time::Duration;
129
130/// # Makes new Error with formatted string, or without one
131macro_rules! err {
132    () => {
133        $crate::Error::new(line!(), module_path!(), None)
134    };
135    ($s: literal) => {
136        $crate::Error::new(line!(), module_path!(), Some(alloc::borrow::Cow::Borrowed($s)))
137    };
138    ($s: literal, $($arg: tt)+) => {
139        $crate::Error::new(line!(), module_path!(), Some(alloc::borrow::Cow::Owned(alloc::format!($s, $($arg)+))))
140    };
141}
142
143#[test]
144fn test_macro_err() {
145    use alloc::borrow::Cow;
146
147    macro_rules! s_test { () => { "test" }}
148
149    fn eq(first: Error, second: Error) -> bool {
150        first.line() == second.line() && first.module_path() == second.module_path() && first.msg() == second.msg()
151    }
152
153    assert!(eq(err!(), Error::new(line!(), module_path!(), None)));
154    assert!(eq(err!("test"), Error::new(line!(), module_path!(), Some(Cow::Borrowed(s_test!())))));
155    assert!(eq(err!("{s:?}", s=s_test!()), Error::new(line!(), module_path!(), Some(Cow::Owned(alloc::format!("{:?}", s_test!()))))));
156}
157
158mod error;
159mod month;
160mod time;
161mod weekday;
162
163pub use self::{
164    error::*,
165    month::*,
166    time::*,
167    weekday::*,
168};
169
170pub mod debts;
171pub mod decis;
172pub mod centis;
173pub mod millis;
174pub mod micros;
175pub mod nanos;
176pub mod picos;
177pub mod femtos;
178pub mod attos;
179pub mod zeptos;
180pub mod yoctos;
181pub mod symbols;
182
183pub mod version_info;
184
185/// # Result type used in this crate
186pub type Result<T> = core::result::Result<T, Error>;
187
188/// # 1 second
189pub const SECOND: u64 = 1;
190
191/// # 1 minute in seconds
192pub const MINUTE: u64 = 60;
193
194/// # 1 hour in seconds
195pub const HOUR: u64 = 3_600;
196
197/// # 1 day in seconds
198pub const DAY: u64 = 86_400;
199
200/// # 1 week in seconds
201pub const WEEK: u64 = 604_800;
202
203/// # 1 decasecond
204pub const DECASECOND: u64 = 10;
205
206/// # 1 hectosecond
207pub const HECTOSECOND: u64 = 100;
208
209/// # 1 kilosecond
210pub const KILOSECOND: u64 = 1_000;
211
212/// # 1 megasecond
213pub const MEGASECOND: u64 = 1_000_000;
214
215/// # 1 gigasecond
216pub const GIGASECOND: u64 = 1_000_000_000;
217
218/// # 1 terasecond
219pub const TERASECOND: u64 = 1_000_000_000_000;
220
221/// # 1 petasecond
222pub const PETASECOND: u64 = 1_000_000_000_000_000;
223
224/// # 1 exasecond
225pub const EXASECOND: u64 = 1_000_000_000_000_000_000;
226
227/// # 1 zettasecond
228pub const ZETTASECOND: u128 = 1_000_000_000_000_000_000_000;
229
230/// # 1 yottasecond
231pub const YOTTASECOND: u128 = 1_000_000_000_000_000_000_000_000;
232
233/// # Converts duration to days, hours, minutes, seconds.
234///
235/// # Examples
236///
237/// ```
238/// use std::time::Duration;
239/// use dia_time::{self, HOUR, MINUTE};
240///
241/// assert_eq!(
242///     dia_time::duration_to_dhms(&Duration::from_secs(HOUR + MINUTE)),
243///     (0, 1, 1, 0)
244/// );
245/// ```
246pub fn duration_to_dhms(duration: &Duration) -> (u64, u64, u64, u64) {
247    let seconds = duration.as_secs();
248    let (minutes, seconds) = {
249        let minutes = seconds / 60;
250        (minutes, seconds - minutes * 60)
251    };
252    let (hours, minutes) = {
253        let hours = minutes / 60;
254        (hours, minutes - hours * 60)
255    };
256    let (days, hours) = {
257        let days = hours / 24;
258        (days, hours - days * 24)
259    };
260
261    (days as u64, hours as u64, minutes as u64, seconds as u64)
262}
263
264/// # Examples: `02:53:58.018`
265///
266/// ```
267/// use std::time::Duration;
268/// use dia_time::{self, HOUR, MINUTE};
269///
270/// assert_eq!(
271///     dia_time::format_hms_ms(&Duration::from_secs(HOUR * 2 + MINUTE * 53 + 58)),
272///     "02:53:58.000"
273/// );
274/// ```
275pub fn format_hms_ms(duration: &Duration) -> String {
276    let (_, h, m, s) = duration_to_dhms(duration);
277    format!("{h:02}:{m:02}:{s:02}.{ms:03}", h=h, m=m, s=s, ms=duration.subsec_millis())
278}
279
280/// # Examples: `3d, 02:53:58.018`
281///
282/// ```
283/// use std::time::Duration;
284/// use dia_time::{self, DAY, HOUR, MINUTE};
285///
286/// assert_eq!(
287///     dia_time::format_day_hms_ms(&Duration::from_secs(DAY * 3 + HOUR * 2 + MINUTE * 53 + 58)),
288///     "3d, 02:53:58.000"
289/// );
290/// ```
291pub fn format_day_hms_ms(duration: &Duration) -> String {
292    format!("{d}d, {t}", d=duration.as_secs() / DAY, t=format_hms_ms(duration))
293}
294
295/// # If the duration is within a day, forwards to [`::format_hms_ms()`]; otherwise, forwards to [`::format_day_hms_ms()`].
296///
297/// # Examples
298///
299/// ```
300/// use std::time::Duration;
301/// use dia_time::{self, DAY, HOUR, MINUTE};
302///
303/// assert_eq!(
304///     dia_time::smart_format_day_hms_ms(&Duration::from_secs(HOUR * 2 + MINUTE * 53 + 58)),
305///     "02:53:58.000"
306/// );
307/// assert_eq!(dia_time::smart_format_day_hms_ms(&Duration::from_secs(DAY * 9 + HOUR * 2 + MINUTE * 53 + 58)), "9d, 02:53:58.000");
308/// ```
309///
310/// [`::format_hms_ms()`]: fn.format_hms_ms.html
311/// [`::format_day_hms_ms()`]: fn.format_day_hms_ms.html
312pub fn smart_format_day_hms_ms(duration: &Duration) -> String {
313    match duration.as_secs() / DAY {
314        0 => format_hms_ms(duration),
315        _ => format_day_hms_ms(duration),
316    }
317}
318
319/// # Examples: `02:53:58`
320///
321/// ```
322/// use std::time::Duration;
323/// use dia_time::{self, HOUR, MINUTE};
324///
325/// assert_eq!(
326///     dia_time::format_hms(&Duration::from_secs(HOUR * 2 + MINUTE * 53 + 58)),
327///     "02:53:58"
328/// );
329/// ```
330pub fn format_hms(duration: &Duration) -> String {
331    let (_, h, m, s) = duration_to_dhms(duration);
332    format!("{h:02}:{m:02}:{s:02}", h=h, m=m, s=s)
333}
334
335/// # Examples: `9d, 02:53:58`
336///
337/// ```
338/// use std::time::Duration;
339/// use dia_time::{self, DAY, HOUR, MINUTE};
340///
341/// assert_eq!(
342///     dia_time::format_day_hms(&Duration::from_secs(DAY * 9 + HOUR * 2 + MINUTE * 53 + 58)),
343///     "9d, 02:53:58"
344/// );
345/// ```
346pub fn format_day_hms(duration: &Duration) -> String {
347    format!("{d}d, {t}", d=duration.as_secs() / DAY, t=format_hms(duration))
348}
349
350/// # If the duration is within a day, forwards to [`::format_hms()`]; otherwise, forwards to [`::format_day_hms()`].
351///
352/// # Examples
353///
354/// ```
355/// use std::time::Duration;
356/// use dia_time::{self, DAY, HOUR, MINUTE};
357///
358/// assert_eq!(
359///     dia_time::smart_format_day_hms(&Duration::from_secs(HOUR * 2 + MINUTE * 53 + 58)),
360///     "02:53:58"
361/// );
362/// assert_eq!(
363///     dia_time::smart_format_day_hms(&Duration::from_secs(DAY * 9 + HOUR * 2 + MINUTE * 53 + 58)),
364///     "9d, 02:53:58"
365/// );
366/// ```
367///
368/// [`::format_hms()`]: fn.format_hms.html
369/// [`::format_day_hms()`]: fn.format_day_hms.html
370pub fn smart_format_day_hms(duration: &Duration) -> String {
371    match duration.as_secs() / DAY {
372        0 => format_hms(duration),
373        _ => format_day_hms(duration),
374    }
375}