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