tzdb/now.rs
1//! Get the current time in some time zone
2
3extern crate std;
4
5use core::convert::TryFrom;
6use core::fmt;
7use std::time::{SystemTime, SystemTimeError};
8
9use tz::{DateTime, TimeZoneRef, TzError};
10
11#[cfg(not(feature = "local"))]
12mod iana_time_zone {
13 #[allow(missing_copy_implementations)] // intentionally omitted
14 #[derive(Debug)]
15 #[non_exhaustive]
16 pub struct GetTimezoneError(Impossible);
17
18 #[derive(Debug, Clone, Copy)]
19 enum Impossible {}
20}
21
22/// An error as returned by [`local()`] and similar functions
23///
24/// # See also:
25///
26/// * [`local()`] / [`local_or()`]
27/// * [`in_named()`] / [`in_named_or()`]
28/// * [`in_tz()`]
29#[allow(clippy::module_name_repetitions)]
30#[derive(Debug)]
31pub enum NowError {
32 /// Could not get time zone. Only returned by [`local()`].
33 TimeZone(iana_time_zone::GetTimezoneError),
34 /// Unknown system time zone. Only returned by [`local()`], and [`in_named()`].
35 UnknownTimezone,
36 /// Could not project timestamp.
37 ProjectDateTime(TzError),
38 /// Could not get current system time.
39 Utcnow(SystemTimeError),
40}
41
42impl fmt::Display for NowError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 f.write_str(match self {
45 Self::TimeZone(_) => "could not get time zone",
46 Self::UnknownTimezone => "unknown system time zone",
47 Self::ProjectDateTime(_) => "could not project timestamp",
48 Self::Utcnow(_) => "could not get current system time",
49 })
50 }
51}
52
53impl core::error::Error for NowError {
54 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
55 match self {
56 #[cfg(feature = "local")]
57 Self::TimeZone(err) => Some(err),
58 #[cfg(not(feature = "local"))]
59 Self::TimeZone(_) => None,
60 Self::UnknownTimezone => None,
61 Self::ProjectDateTime(err) => Some(err),
62 Self::Utcnow(err) => Some(err),
63 }
64 }
65}
66
67/// Get the current time in the local system time zone
68///
69/// # Errors
70///
71/// Possible errors include:
72///
73/// * The current [Unix time](https://en.wikipedia.org/w/index.php?title=Unix_time&oldid=1101650731)
74/// could not be determined.
75/// * The current Unix time could not be projected into the time zone.
76/// Most likely the system time is off, or you are a time traveler trying run this code a few billion years in the future or past.
77/// * The local time zone could not be determined.
78/// * The local time zone is not a valid [IANA time zone](https://www.iana.org/time-zones).
79///
80/// # Example
81///
82/// ```rust
83/// # fn main() -> Result<(), tzdb::now::NowError> {
84/// // Query the time zone of the local system:
85/// let now = tzdb::now::local()?;
86/// # Ok(()) }
87/// ```
88///
89/// In most cases you will want to default to a specified time zone if the system timezone
90/// could not be determined. Then use e.g.
91///
92/// ```rust
93/// # fn main() -> Result<(), tzdb::now::NowError> {
94/// let now = tzdb::now::local_or(tzdb::time_zone::GMT)?;
95/// # Ok(()) }
96/// ```
97///
98/// # See also:
99///
100/// * `local()` / [`local_or()`]
101/// * [`in_named()`] / [`in_named_or()`]
102/// * [`in_tz()`]
103#[cfg(feature = "local")]
104#[cfg_attr(docsrs, doc(cfg(feature = "local")))]
105pub fn local() -> Result<DateTime, NowError> {
106 in_named(iana_time_zone::get_timezone().map_err(NowError::TimeZone)?)
107}
108
109/// Get the current time in the local system time zone with a fallback time zone
110///
111/// # Errors
112///
113/// Possible errors include:
114///
115/// * The current [Unix time](https://en.wikipedia.org/w/index.php?title=Unix_time&oldid=1101650731)
116/// could not be determined.
117/// * The current Unix time could not be projected into the time zone.
118/// Most likely the system time is off, or you are a time traveler trying run this code a few billion years in the future or past.
119///
120/// # Example
121///
122/// ```rust
123/// # fn main() -> Result<(), tzdb::now::NowError> {
124/// // Query the time zone of the local system, or use GMT as default:
125/// let now = tzdb::now::local_or(tzdb::time_zone::GMT)?;
126/// # Ok(()) }
127/// ```
128///
129/// # See also:
130///
131/// * [`local()`] / `local_or()`
132/// * [`in_named()`] / [`in_named_or()`]
133/// * [`in_tz()`]
134#[cfg(feature = "local")]
135#[cfg_attr(docsrs, doc(cfg(feature = "local")))]
136pub fn local_or(default: TimeZoneRef<'_>) -> Result<DateTime, NowError> {
137 let tz = iana_time_zone::get_timezone()
138 .ok()
139 .and_then(crate::tz_by_name)
140 .unwrap_or(default);
141 in_tz(tz)
142}
143
144/// Get the current time a given time zone
145///
146/// # Errors
147///
148/// Possible errors include:
149///
150/// * The current [Unix time](https://en.wikipedia.org/w/index.php?title=Unix_time&oldid=1101650731)
151/// could not be determined.
152/// * The current Unix time could not be projected into the time zone.
153/// Most likely the system time is off, or you are a time traveler trying run this code a few billion years in the future or past.
154///
155/// # Example
156///
157/// ```rust
158/// # fn main() -> Result<(), tzdb::now::NowError> {
159/// // What is the time in Berlin?
160/// let now = tzdb::now::in_tz(tzdb::time_zone::europe::BERLIN)?;
161/// # Ok(()) }
162/// ```
163///
164/// # See also:
165///
166/// * [`local()`] / [`local_or()`]
167/// * [`in_named()`] / [`in_named_or()`]
168/// * `in_tz()`
169pub fn in_tz(time_zone_ref: TimeZoneRef<'_>) -> Result<DateTime, NowError> {
170 let now = SystemTime::now()
171 .duration_since(SystemTime::UNIX_EPOCH)
172 .map_err(NowError::Utcnow)?;
173 let secs =
174 i64::try_from(now.as_secs()).map_err(|_| NowError::ProjectDateTime(TzError::OutOfRange))?;
175 let nanos = now.subsec_nanos();
176 DateTime::from_timespec(secs, nanos, time_zone_ref).map_err(NowError::ProjectDateTime)
177}
178
179/// Get the current time in a given time zone, by name
180///
181/// # Errors
182///
183/// Possible errors include:
184///
185/// * The current [Unix time](https://en.wikipedia.org/w/index.php?title=Unix_time&oldid=1101650731)
186/// could not be determined.
187/// * The current Unix time could not be projected into the time zone.
188/// Most likely the system time is off, or you are a time traveler trying run this code a few billion years in the future or past.
189/// * The time zone is not a valid [IANA time zone](https://www.iana.org/time-zones).
190///
191/// # Example
192///
193/// ```rust
194/// # fn main() -> Result<(), tzdb::now::NowError> {
195/// // What is the time in Berlin?
196/// let now = tzdb::now::in_named("Europe/Berlin")?;
197/// # Ok(()) }
198/// ```
199///
200/// In most cases you will want to default to a specified time zone if the time zone was not found.
201/// Then use e.g.
202///
203/// ```rust
204/// # fn main() -> Result<(), tzdb::now::NowError> {
205/// let now = tzdb::now::in_named_or(tzdb::time_zone::GMT, "Some/City")?;
206/// # Ok(()) }
207/// ```
208///
209/// # See also:
210///
211/// * [`local()`] / [`local_or()`]
212/// * `in_named()` / [`in_named_or()`]
213/// * [`in_tz()`]
214pub fn in_named(tz: impl AsRef<[u8]>) -> Result<DateTime, NowError> {
215 in_tz(crate::tz_by_name(tz).ok_or(NowError::UnknownTimezone)?)
216}
217
218/// Get the current time in a given time zone, by name, or default to some static time zone
219///
220/// # Errors
221///
222/// Possible errors include:
223///
224/// * The current [Unix time](https://en.wikipedia.org/w/index.php?title=Unix_time&oldid=1101650731)
225/// could not be determined.
226/// * The current Unix time could not be projected into the time zone.
227/// Most likely the system time is off, or you are a time traveler trying run this code a few billion years in the future or past.
228///
229/// # Example
230///
231/// ```rust
232/// # fn main() -> Result<(), tzdb::now::NowError> {
233/// // What is the time in Some City?
234/// let now = tzdb::now::in_named_or(tzdb::time_zone::GMT, "Some/City")?;
235/// # Ok(()) }
236/// ```
237///
238/// # See also:
239///
240/// * [`local()`] / [`local_or()`]
241/// * [`in_named()`] / `in_named_or()`
242/// * [`in_tz()`]
243pub fn in_named_or(default: TimeZoneRef<'_>, tz: impl AsRef<[u8]>) -> Result<DateTime, NowError> {
244 in_tz(crate::tz_by_name(tz).unwrap_or(default))
245}