1 //! [![GitHub time-rs/time](https://img.shields.io/badge/GitHub-time--rs%2Ftime-9b88bb?logo=github&style=for-the-badge)](https://github.com/time-rs/time)
2 //! ![license MIT or Apache-2.0](https://img.shields.io/badge/license-MIT%20or%20Apache--2.0-779a6b?style=for-the-badge)
3 //! [![minimum rustc 1.32.0](https://img.shields.io/badge/minimum%20rustc-1.32.0-c18170?logo=rust&style=for-the-badge)](https://www.whatrustisit.com)
4 //!
5 //! # Feature flags
6 //!
7 //! This crate exposes a number of features. These can be enabled or disabled as
8 //! shown [in Cargo's documentation](https://doc.rust-lang.org/cargo/reference/features.html).
9 //! Features are _disabled_ by default unless otherwise noted.
10 //!
11 //! Reliance on a given feature is always indicated alongside the item
12 //! definition.
13 //!
14 //! - `std` (_enabled by default_)
15 //!
16 //! This enables a number of features that depend on the standard library.
17 //! [`Instant`] is the primary item that requires this feature, though some
18 //! others methods may rely on [`Instant`] internally.
19 //!
20 //! This crate currently requires a global allocator be present even if this
21 //! feature is disabled.
22 //!
23 //! - `serde`
24 //!
25 //! Enables [serde](https://docs.rs/serde) support for all types.
26 //!
27 //! - `rand`
28 //!
29 //! Enables [rand](https://docs.rs/rand) support for all types.
30 //!
31 //! - `deprecated` (_enabled by default_)
32 //!
33 //! Allows using certain deprecated functions from time 0.1.
34 //!
35 //! - `panicking-api`
36 //!
37 //! Non-panicking APIs are provided, and should generally be preferred.
38 //! However, there are some situations where avoiding `.unwrap()` may be
39 //! desired. Generally speaking, macros should be used in these situations.
40 //! Library authors should avoid using this feature.
41 //!
42 //! # Formatting
43 //!
44 //! Time's formatting behavior is based on `strftime` in C, though it is
45 //! explicitly _not_ compatible. Specifiers may be missing, added, or have
46 //! different behavior than in C. As such, you should use the table below, which
47 //! is an up-to-date reference on what each specifier does.
48 //!
49 //! <style>
50 //! summary, details:not([open]) { cursor: pointer; }
51 //! summary { display: list-item; }
52 //! summary::marker { content: '▶ '; }
53 //! details[open] summary::marker { content: '▼ '; }
54 //! </style>
55 //!
56 //! <details>
57 //! <summary>Specifiers</summary>
58 //!
59 //! | Specifier | Replaced by | Example |
60 //! |-----------|------------------------------------------------------------------------|----------------------------|
61 //! | `%a` | Abbreviated weekday name | `Thu` |
62 //! | `%A` | Full weekday name | `Thursday` |
63 //! | `%b` | Abbreviated month name | `Aug` |
64 //! | `%B` | Full month name | `August` |
65 //! | `%c` | Date and time representation, equivalent to `%a %b %-d %-H:%M:%S %-Y` | `Thu Aug 23 14:55:02 2001` |
66 //! | `%C` | Year divided by 100 and truncated to integer (`00`-`999`) | `20` |
67 //! | `%d` | Day of the month, zero-padded (`01`-`31`) | `23` |
68 //! | `%D` | Short MM/DD/YY date, equivalent to `%-m/%d/%y` | `8/23/01` |
69 //! | `%F` | Short YYYY-MM-DD date, equivalent to `%-Y-%m-%d` | `2001-08-23` |
70 //! | `%g` | Week-based year, last two digits (`00`-`99`) | `01` |
71 //! | `%G` | Week-based year | `2001` |
72 //! | `%H` | Hour in 24h format (`00`-`23`) | `14` |
73 //! | `%I` | Hour in 12h format (`01`-`12`) | `02` |
74 //! | `%j` | Day of the year (`001`-`366`) | `235` |
75 //! | `%m` | Month as a decimal number (`01`-`12`) | `08` |
76 //! | `%M` | Minute (`00`-`59`) | `55` |
77 //! | `%N` | Subsecond nanoseconds. Always 9 digits | `012345678` |
78 //! | `%p` | `am` or `pm` designation | `pm` |
79 //! | `%P` | `AM` or `PM` designation | `PM` |
80 //! | `%r` | 12-hour clock time, equivalent to `%-I:%M:%S %p` | `2:55:02 pm` |
81 //! | `%R` | 24-hour HH:MM time, equivalent to `%-H:%M` | `14:55` |
82 //! | `%S` | Second (`00`-`59`) | `02` |
83 //! | `%T` | 24-hour clock time with seconds, equivalent to `%-H:%M:%S` | `14:55:02` |
84 //! | `%u` | ISO 8601 weekday as number with Monday as 1 (`1`-`7`) | `4` |
85 //! | `%U` | Week number with the first Sunday as the start of week one (`00`-`53`) | `33` |
86 //! | `%V` | ISO 8601 week number (`01`-`53`) | `34` |
87 //! | `%w` | Weekday as a decimal number with Sunday as 0 (`0`-`6`) | `4` |
88 //! | `%W` | Week number with the first Monday as the start of week one (`00`-`53`) | `34` |
89 //! | `%y` | Year, last two digits (`00`-`99`) | `01` |
90 //! | `%Y` | Full year, including `+` if ≥10,000 | `2001` |
91 //! | `%z` | ISO 8601 offset from UTC in timezone (+HHMM) | `+0100` |
92 //! | `%%` | Literal `%` | `%` |
93 //!
94 //! </details>
95 //!
96 //! ## Modifiers
97 //!
98 //! All specifiers that are strictly numerical have modifiers for formatting.
99 //! Adding a modifier to a non-supporting specifier is a no-op.
100 //!
101 //! <!-- rust-lang/rust#65613 -->
102 //! <style>.docblock code { white-space: pre-wrap; }</style>
103 //!
104 //! | Modifier | Behavior | Example |
105 //! |------------------|-----------------|---------------|
106 //! | `-` (dash) | No padding | `%-d` => `5` |
107 //! | `_` (underscore) | Pad with spaces | `%_d` => ` 5` |
108 //! | `0` | Pad with zeros | `%0d` => `05` |
109
110 #![cfg_attr(__time_02_docs, feature(doc_cfg))]
111 #![cfg_attr(not(feature = "std"), no_std)]
112 #![deny(
113 anonymous_parameters,
114 clippy::all,
115 const_err,
116 illegal_floating_point_literal_pattern,
117 late_bound_lifetime_arguments,
118 path_statements,
119 patterns_in_fns_without_body,
120 rust_2018_idioms,
121 trivial_casts,
122 trivial_numeric_casts,
123 unreachable_pub,
124 unsafe_code,
125 unused_extern_crates
126 )]
127 #![warn(
128 clippy::dbg_macro,
129 clippy::decimal_literal_representation,
130 clippy::get_unwrap,
131 clippy::missing_docs_in_private_items,
132 clippy::nursery,
133 clippy::pedantic,
134 clippy::print_stdout,
135 clippy::todo,
136 clippy::unimplemented,
137 clippy::unwrap_used,
138 clippy::use_debug,
139 missing_copy_implementations,
140 missing_debug_implementations,
141 single_use_lifetimes,
142 unused_qualifications,
143 variant_size_differences
144 )]
145 #![allow(
146 clippy::cast_lossless,
147 clippy::cast_possible_truncation,
148 clippy::cast_possible_wrap,
149 clippy::cast_precision_loss,
150 clippy::cast_sign_loss,
151 clippy::enum_glob_use,
152 clippy::inline_always,
153 clippy::map_err_ignore,
154 clippy::missing_errors_doc,
155 clippy::missing_panics_doc,
156 clippy::module_name_repetitions,
157 clippy::must_use_candidate,
158 clippy::redundant_pub_crate,
159 clippy::suspicious_arithmetic_impl,
160 clippy::suspicious_op_assign_impl,
161 clippy::use_self,
162 clippy::wildcard_imports,
163 clippy::zero_prefixed_literal,
164 unstable_name_collisions
165 )]
166 #![cfg_attr(
167 test,
168 allow(
169 clippy::cognitive_complexity,
170 clippy::similar_names,
171 clippy::too_many_lines,
172 )
173 )]
174 #![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
175 #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
176 #![doc(test(attr(deny(warnings))))]
177
178 #[cfg(not(feature = "std"))]
179 extern crate alloc;
180
181 #[cfg(feature = "panicking-api")]
182 #[cfg_attr(__time_02_docs, doc(cfg(feature = "panicking-api")))]
183 macro_rules! format_conditional {
184 ($conditional:ident) => {{
185 #[cfg(not(feature = "std"))]
186 use alloc::format;
187
188 format!(concat!(stringify!($conditional), "={}"), $conditional)
189 }};
190
191 ($first_conditional:ident, $($conditional:ident),*) => {{
192 #[cfg(not(feature = "std"))]
193 use alloc::{format, string::String};
194
195 let mut s = String::new();
196 s.push_str(&format_conditional!($first_conditional));
197 $(s.push_str(&format!(concat!(", ", stringify!($conditional), "={}"), $conditional));)*
198 s
199 }}
200 }
201
202 /// Panic if the value is not in range.
203 #[cfg(feature = "panicking-api")]
204 #[cfg_attr(__time_02_docs, doc(cfg(feature = "panicking-api")))]
205 macro_rules! assert_value_in_range {
206 ($value:ident in $start:expr => $end:expr) => {{
207 #[allow(unused_imports)]
208 use standback::prelude::*;
209
210 if !($start..=$end).contains(&$value) {
211 panic!(
212 concat!(stringify!($value), " must be in the range {}..={} (was {})"),
213 $start,
214 $end,
215 $value,
216 );
217 }
218 }};
219
220 ($value:ident in $start:expr => $end:expr, given $($conditional:ident),+ $(,)?) => {{
221 #[allow(unused_imports)]
222 use standback::prelude::*;
223
224 if !($start..=$end).contains(&$value) {
225 panic!(
226 concat!(stringify!($value), " must be in the range {}..={} given{} (was {})"),
227 $start,
228 $end,
229 &format_conditional!($($conditional),+),
230 $value,
231 );
232 }
233 }};
234 }
235
236 /// Returns `Err(error::ComponentRange)` if the value is not in range.
237 macro_rules! ensure_value_in_range {
238 ($value:ident in $start:expr => $end:expr) => {{
239 #![allow(
240 trivial_numeric_casts,
241 unused_comparisons,
242 clippy::manual_range_contains
243 )]
244 if $value < $start || $value > $end {
245 return Err(crate::error::ComponentRange {
246 name: stringify!($value),
247 minimum: $start as i64,
248 maximum: $end as i64,
249 value: $value as i64,
250 conditional_range: false,
251 #[cfg(not(__time_02_supports_non_exhaustive))]
252 __non_exhaustive: (),
253 });
254 }
255 }};
256
257 ($value:ident conditionally in $start:expr => $end:expr) => {{
258 #![allow(
259 trivial_numeric_casts,
260 unused_comparisons,
261 clippy::manual_range_contains
262 )]
263 if $value < $start || $value > $end {
264 return Err(crate::error::ComponentRange {
265 name: stringify!($value),
266 minimum: $start as i64,
267 maximum: $end as i64,
268 value: $value as i64,
269 conditional_range: true,
270 #[cfg(not(__time_02_supports_non_exhaustive))]
271 __non_exhaustive: (),
272 });
273 }
274 }};
275 }
276
277 /// Try to unwrap an expression, returning if not possible.
278 ///
279 /// This is similar to the `?` operator, but does not perform `.into()`. Because
280 /// of this, it is usable in `const` contexts.
281 macro_rules! const_try {
282 ($e:expr) => {
283 match $e {
284 Ok(value) => value,
285 Err(error) => return Err(error),
286 }
287 };
288 }
289
290 /// The `Date` struct and its associated `impl`s.
291 mod date;
292 /// The `Duration` struct and its associated `impl`s.
293 mod duration;
294 /// Various error types returned by methods in the time crate.
295 pub mod error;
296 /// Extension traits.
297 pub mod ext;
298 mod format;
299 /// The `Instant` struct and its associated `impl`s.
300 #[cfg(feature = "std")]
301 mod instant;
302 pub mod internals;
303 /// The `OffsetDateTime` struct and its associated `impl`s.
304 mod offset_date_time;
305 /// The `PrimitiveDateTime` struct and its associated `impl`s.
306 mod primitive_date_time;
307 #[cfg(feature = "rand")]
308 mod rand;
309 #[cfg(feature = "serde")]
310 #[allow(missing_copy_implementations, missing_debug_implementations)]
311 pub mod serde;
312 /// The `Sign` struct and its associated `impl`s.
313 mod sign;
314 /// The `Time` struct and its associated `impl`s.
315 mod time_mod;
316 /// The `UtcOffset` struct and its associated `impl`s.
317 mod utc_offset;
318 pub mod util;
319 /// Days of the week.
320 mod weekday;
321
322 pub use date::Date;
323 pub use duration::Duration;
324 pub use error::Error;
325 #[deprecated(
326 since = "0.2.23",
327 note = "Errors have been moved to the `error` module."
328 )]
329 pub use error::{
330 ComponentRange as ComponentRangeError, ConversionRange as ConversionRangeError,
331 IndeterminateOffset as IndeterminateOffsetError, Parse as ParseError,
332 };
333 #[deprecated(
334 since = "0.2.23",
335 note = "Extension traits have been moved to the `ext` module."
336 )]
337 pub use ext::{NumericalDuration, NumericalStdDuration, NumericalStdDurationShort};
338 pub(crate) use format::DeferredFormat;
339 pub use format::Format;
340 use format::ParseResult;
341 #[cfg(feature = "std")]
342 pub use instant::Instant;
343 #[deprecated(
344 since = "0.2.23",
345 note = "Macros have been moved to the `macros` module."
346 )]
347 pub use macros::{date, offset, time};
348 pub use offset_date_time::OffsetDateTime;
349 pub use primitive_date_time::PrimitiveDateTime;
350 #[allow(deprecated)]
351 pub use sign::Sign;
352 #[allow(unused_imports)]
353 use standback::prelude::*;
354 pub use time_mod::Time;
355 pub use utc_offset::UtcOffset;
356 #[deprecated(
357 since = "0.2.23",
358 note = "This function has been moved to the `util` module."
359 )]
360 pub use util::{days_in_year, is_leap_year, validate_format_string, weeks_in_year};
361 pub use weekday::Weekday;
362
363 /// An alias for `Result` with a generic error from the time crate.
364 pub type Result<T> = core::result::Result<T, Error>;
365
366 /// A collection of imports that are widely useful.
367 ///
368 /// Unlike the standard library, this must be explicitly imported:
369 ///
370 /// ```rust,no_run
371 /// # #[allow(unused_imports)]
372 /// use time::prelude::*;
373 /// ```
374 ///
375 /// The prelude may grow in minor releases. Any removals will only occur in
376 /// major releases.
377 pub mod prelude {
378 // Rename traits to `_` if possible to avoid any potential name conflicts.
379 #[cfg(not(__time_02_use_trait_as_underscore))]
380 pub use crate::ext::{NumericalDuration, NumericalStdDuration};
381 #[cfg(__time_02_use_trait_as_underscore)]
382 pub use crate::ext::{NumericalDuration as _, NumericalStdDuration as _};
383 // We need to re-export from the macros crate again (and not just do
384 // `crate::foo`) because of the way name resolution works in Rust. It's not
385 // currently possible to import _only_ the macro, so doing `use crate::time`
386 // also pulls in the `time` _crate_ (due to `extern crate self as time`).
387 //
388 // As a side note, doing `use crate::time` causes a stack overflow in
389 // rustc <= 1.37.0.
390 pub use time_macros::{date, offset, time};
391 }
392
393 #[allow(clippy::missing_docs_in_private_items)]
394 mod private {
395 use super::*;
396
397 macro_rules! parsable {
398 ($($type:ty),* $(,)?) => {
399 $(
400 impl Parsable for $type {
401 fn parse(s: impl AsRef<str>, format: impl AsRef<str>) -> ParseResult<Self> {
402 Self::parse(s, format)
403 }
404 }
405 )*
406 };
407 }
408
409 pub trait Parsable: Sized {
parse(s: impl AsRef<str>, format: impl AsRef<str>) -> ParseResult<Self>410 fn parse(s: impl AsRef<str>, format: impl AsRef<str>) -> ParseResult<Self>;
411 }
412
413 parsable![Time, Date, UtcOffset, PrimitiveDateTime, OffsetDateTime];
414 }
415
416 /// Parse any parsable type from the time crate.
417 ///
418 /// This is identical to calling `T::parse(s, format)`, but allows the use of
419 /// type inference where possible.
420 ///
421 /// ```rust
422 /// use time::Time;
423 ///
424 /// #[derive(Debug)]
425 /// struct Foo(Time);
426 ///
427 /// fn main() -> time::Result<()> {
428 /// // We don't need to tell the compiler what type we need!
429 /// let foo = Foo(time::parse("14:55:02", "%T")?);
430 /// println!("{:?}", foo);
431 /// Ok(())
432 /// }
433 /// ```
parse<T: private::Parsable>(s: impl AsRef<str>, format: impl AsRef<str>) -> ParseResult<T>434 pub fn parse<T: private::Parsable>(s: impl AsRef<str>, format: impl AsRef<str>) -> ParseResult<T> {
435 private::Parsable::parse(s, format)
436 }
437
438 /// Macros to statically construct values that are known to be valid.
439 pub mod macros {
440
441 /// Construct a [`Date`](crate::Date) with a statically known value.
442 ///
443 /// The resulting expression can be used in `const` or `static` declarations.
444 ///
445 /// Three formats are supported: year-week-weekday, year-ordinal, and
446 /// year-month-day.
447 ///
448 /// ```rust
449 /// # use time::{Date, macros::date, Weekday::*};
450 /// # fn main() -> time::Result<()> {
451 /// assert_eq!(date!(2020-W01-3), Date::try_from_iso_ywd(2020, 1, Wednesday)?);
452 /// assert_eq!(date!(2020-001), Date::try_from_yo(2020, 1)?);
453 /// assert_eq!(date!(2020-01-01), Date::try_from_ymd(2020, 1, 1)?);
454 /// # Ok(())
455 /// # }
456 /// ```
457 pub use time_macros::date;
458 /// Construct a [`UtcOffset`](crate::UtcOffset) with a statically known value.
459 ///
460 /// The resulting expression can be used in `const` or `static` declarations.
461 ///
462 /// A sign and the hour must be provided; minutes and seconds default to zero.
463 /// `UTC` (both uppercase and lowercase) is also allowed.
464 ///
465 /// ```rust
466 /// # use time::{macros::offset, UtcOffset};
467 /// assert_eq!(offset!(UTC), UtcOffset::hours(0));
468 /// assert_eq!(offset!(utc), UtcOffset::hours(0));
469 /// assert_eq!(offset!(+0), UtcOffset::hours(0));
470 /// assert_eq!(offset!(+1), UtcOffset::hours(1));
471 /// assert_eq!(offset!(-1), UtcOffset::hours(-1));
472 /// assert_eq!(offset!(+1:30), UtcOffset::minutes(90));
473 /// assert_eq!(offset!(-1:30), UtcOffset::minutes(-90));
474 /// assert_eq!(offset!(+1:30:59), UtcOffset::seconds(5459));
475 /// assert_eq!(offset!(-1:30:59), UtcOffset::seconds(-5459));
476 /// assert_eq!(offset!(+23:59:59), UtcOffset::seconds(86_399));
477 /// assert_eq!(offset!(-23:59:59), UtcOffset::seconds(-86_399));
478 /// ```
479 pub use time_macros::offset;
480 /// Construct a [`Time`](crate::Time) with a statically known value.
481 ///
482 /// The resulting expression can be used in `const` or `static` declarations.
483 ///
484 /// Hours and minutes must be provided, while seconds defaults to zero. AM/PM is
485 /// allowed (either uppercase or lowercase). Any number of subsecond digits may
486 /// be provided (though any past nine will be discarded).
487 ///
488 /// All components are validated at compile-time. An error will be raised if any
489 /// value is invalid.
490 ///
491 /// ```rust
492 /// # use time::{Time, macros::time};
493 /// # fn main() -> time::Result<()> {
494 /// assert_eq!(time!(0:00), Time::try_from_hms(0, 0, 0)?);
495 /// assert_eq!(time!(1:02:03), Time::try_from_hms(1, 2, 3)?);
496 /// assert_eq!(time!(1:02:03.004_005_006), Time::try_from_hms_nano(1, 2, 3, 4_005_006)?);
497 /// assert_eq!(time!(12:00 am), Time::try_from_hms(0, 0, 0)?);
498 /// assert_eq!(time!(1:02:03 am), Time::try_from_hms(1, 2, 3)?);
499 /// assert_eq!(time!(1:02:03.004_005_006 am), Time::try_from_hms_nano(1, 2, 3, 4_005_006)?);
500 /// assert_eq!(time!(12:00 pm), Time::try_from_hms(12, 0, 0)?);
501 /// assert_eq!(time!(1:02:03 pm), Time::try_from_hms(13, 2, 3)?);
502 /// assert_eq!(time!(1:02:03.004_005_006 pm), Time::try_from_hms_nano(13, 2, 3, 4_005_006)?);
503 /// # Ok(())
504 /// # }
505 /// ```
506 pub use time_macros::time;
507 }
508
509 // For some back-compatibility, we're also implementing some deprecated types
510 // and methods. They will be removed completely in 0.3.
511
512 #[cfg(all(feature = "std", feature = "deprecated"))]
513 #[allow(clippy::missing_docs_in_private_items)]
514 #[deprecated(since = "0.2.0", note = "Use `Instant`")]
515 pub type PreciseTime = Instant;
516
517 #[cfg(all(feature = "std", feature = "deprecated"))]
518 #[allow(clippy::missing_docs_in_private_items)]
519 #[deprecated(since = "0.2.0", note = "Use `Instant`")]
520 pub type SteadyTime = Instant;
521
522 #[cfg(all(feature = "std", feature = "deprecated"))]
523 #[allow(clippy::missing_docs_in_private_items)]
524 #[deprecated(
525 since = "0.2.0",
526 note = "Use `OffsetDateTime::now() - OffsetDateTime::unix_epoch()` to get a `Duration` since \
527 a known epoch."
528 )]
precise_time_ns() -> u64529 pub fn precise_time_ns() -> u64 {
530 use standback::convert::TryInto;
531 use std::time::SystemTime;
532
533 (SystemTime::now().duration_since(SystemTime::UNIX_EPOCH))
534 .expect("System clock was before 1970.")
535 .as_nanos()
536 .try_into()
537 .expect("This function will be removed long before this is an issue.")
538 }
539
540 #[cfg(all(feature = "std", feature = "deprecated"))]
541 #[allow(clippy::missing_docs_in_private_items)]
542 #[deprecated(
543 since = "0.2.0",
544 note = "Use `OffsetDateTime::now() - OffsetDateTime::unix_epoch()` to get a `Duration` since \
545 a known epoch."
546 )]
precise_time_s() -> f64547 pub fn precise_time_s() -> f64 {
548 use std::time::SystemTime;
549
550 (SystemTime::now().duration_since(SystemTime::UNIX_EPOCH))
551 .expect("System clock was before 1970.")
552 .as_secs_f64()
553 }
554