Struct DateTimeParser
struct DateTimeParser { ... }
A parser for Temporal datetimes.
This parser converts a machine (but also human) readable format of a
datetime to the various types found in Jiff: Zoned, Timestamp,
civil::DateTime, civil::Date or civil::Time. Note that all
of those types provide FromStr implementations
that utilize the default configuration of this parser. However, this parser
can be configured to behave differently and can also parse directly from
a &[u8].
See the fmt::temporal module documentation for
more information on the specific format used.
Example
This example shows how to parse a Zoned datetime from a byte string.
(That is, &[u8] and not a &str.)
use ;
// A parser can be created in a const context.
static PARSER: DateTimeParser = new;
let zdt = PARSER.parse_zoned?;
assert_eq!;
assert_eq!;
# Ok::
Note that an ASCII space instead of the T separator is automatically
supported too:
use ;
// A parser can be created in a const context.
static PARSER: DateTimeParser = new;
let zdt = PARSER.parse_zoned?;
assert_eq!;
assert_eq!;
# Ok::
Implementations
impl DateTimeParser
const fn new() -> DateTimeParserCreate a new Temporal datetime parser with the default configuration.
const fn offset_conflict(self: Self, strategy: OffsetConflict) -> DateTimeParserSet the conflict resolution strategy for when an offset in a datetime string is inconsistent with the time zone.
See the documentation on
OffsetConflictfor more details about the different strategies one can choose.This only applies when parsing
Zonedvalues.The default is
OffsetConflict::Reject, which results in an error whenever parsing a datetime with an offset that is inconsistent with the time zone.Example: respecting offsets even when they're invalid
use ; static PARSER: DateTimeParser = new .offset_conflict; let zdt = PARSER.parse_zoned?; // Notice that the time *and* offset have been corrected. The offset // given was invalid for `America/New_York` at the given time, so // it cannot be kept, but the instant returned is equivalent to // `2024-06-09T07:00-05`. It is just adjusted automatically to be // correct in the `America/New_York` time zone. assert_eq!; assert_eq!; # Ok::Example: all offsets are invalid for gaps in civil time by default
When parsing a datetime with an offset for a gap in civil time, the offset is treated as invalid. This results in parsing failing. For example, some parts of Indiana in the US didn't start using daylight saving time until 2006. If a datetime for 2006 were serialized before the updated daylight saving time rules were known, then this parse error will prevent you from silently changing the originally intended time:
use ; static PARSER: DateTimeParser = new; // DST in Indiana/Vevay began at 2006-04-02T02:00 local time. // The last time Indiana/Vevay observed DST was in 1972. let result = PARSER.parse_zoned; assert_eq!;If one doesn't want an error here, then you can either prioritize the instant in time by respecting the offset:
use ; static PARSER: DateTimeParser = new .offset_conflict; let zdt = PARSER.parse_zoned?; assert_eq!; # Ok::or you can force your own disambiguation rules, e.g., by taking the earlier time:
use ; static PARSER: DateTimeParser = new .disambiguation .offset_conflict; let zdt = PARSER.parse_zoned?; assert_eq!; # Ok::Example: a
Znever results in an offset conflictRFC 9557 specifies that
Zindicates that the offset from UTC to get local time is unknown. Since it doesn't prescribe a particular offset, when aZis parsed with a time zone annotation, theOffsetConflict::ALwaysOffsetstrategy is used regardless of what is set here. For example:use DateTimeParser; // NOTE: The default is reject. static PARSER: DateTimeParser = new; let zdt = PARSER.parse_zoned?; assert_eq!; # Ok::Conversely, if the
+00:00offset was used, then an error would occur because of the offset conflict:use DateTimeParser; // NOTE: The default is reject. static PARSER: DateTimeParser = new; let result = PARSER.parse_zoned; assert_eq!;const fn disambiguation(self: Self, strategy: Disambiguation) -> DateTimeParserSet the disambiguation strategy for when a datetime falls into a time zone transition "fold" or "gap."
The most common manifestation of such time zone transitions is daylight saving time. In most cases, the transition into daylight saving time moves the civil time ("the time you see on the clock") ahead one hour. This is called a "gap" because an hour on the clock is skipped. While the transition out of daylight saving time moves the civil time back one hour. This is called a "fold" because an hour on the clock is repeated.
In the case of a gap, an ambiguous datetime manifests as a time that never appears on a clock. (For example,
02:30on2024-03-10in New York.) In the case of a fold, an ambiguous datetime manifests as a time that repeats itself. (For example,01:30on2024-11-03in New York.) So when a fold occurs, you don't know whether it's the "first" occurrence of that time or the "second."Time zone transitions are not just limited to daylight saving time, although those are the most common. In other cases, a transition occurs because of a change in the offset of the time zone itself. (See the examples below.)
Example
This example shows how to set the disambiguation configuration while parsing a
Zoneddatetime. In this example, we always prefer the earlier time.use ; static PARSER: DateTimeParser = new .disambiguation; let zdt = PARSER.parse_zoned?; assert_eq!; assert_eq!; # Ok::Example: time zone offset change
In this example, we explore a time zone offset change in Hawaii that occurred on
1947-06-08. Namely, Hawaii went from a-10:30offset to a-10:00offset at02:00. This results in a 30 minute gap in civil time.use ; static PARSER: DateTimeParser = new .disambiguation; // 02:05 didn't exist on clocks on 1947-06-08. let zdt = PARSER.parse_zoned?; // Our parser is configured to select the later time, so we jump to // 02:35. But if we used `Disambiguation::Earlier`, then we'd get // 01:35. assert_eq!; assert_eq!; // If we subtract 10 minutes from 02:35, notice that we (correctly) // jump to 01:55 *and* our offset is corrected to -10:30. let zdt = zdt.checked_sub?; assert_eq!; assert_eq!; # Ok::fn parse_zoned<I: AsRef<[u8]>>(self: &Self, input: I) -> Result<Zoned, Error>Parse a datetime string with a time zone annotation into a
Zonedvalue using the system time zone database.Errors
This returns an error if the datetime string given is invalid or if it is valid but doesn't fit in the datetime range supported by Jiff.
The
DateTimeParser::offset_conflictandDateTimeParser::disambiguationsettings can also influence whether an error occurs or not. Namely, ifOffsetConflict::Rejectis used (which is the default), then an error occurs when there is an inconsistency between the offset and the time zone. And ifDisambiguation::Rejectis used, then an error occurs when the civil time in the string is ambiguous.Example: parsing without an IANA time zone
Note that when parsing a
Zonedvalue, it is required for the datetime string to contain a time zone annotation in brackets. For example, this fails to parse even though it refers to a precise instant in time:use DateTimeParser; static PARSER: DateTimeParser = new; assert!;While it is better to include a time zone name, if the only thing that's available is an offset, the offset can be repeated as a time zone annotation:
use ; static PARSER: DateTimeParser = new; let zdt = PARSER.parse_zoned?; assert_eq!; assert_eq!; # Ok::Otherwise, if you need to be able to parse something like
2024-06-08T07:00-04as-is, you should parse it into an [Timestamp]:use ; static PARSER: DateTimeParser = new; let timestamp = PARSER.parse_timestamp?; let zdt = timestamp.to_zoned; assert_eq!; assert_eq!; # Ok::If you really need to parse something like
2024-06-08T07:00-04into aZonedwith a fixed offset of-04:00as itsTimeZone, then you'll need to use lower level parsing routines. See the documentation onPiecesfor a case study of how to achieve this.fn parse_zoned_with<I: AsRef<[u8]>>(self: &Self, db: &TimeZoneDatabase, input: I) -> Result<Zoned, Error>Parse a datetime string with a time zone annotation into a
Zonedvalue using the time zone database given.Errors
This returns an error if the datetime string given is invalid or if it is valid but doesn't fit in the datetime range supported by Jiff.
The
DateTimeParser::offset_conflictandDateTimeParser::disambiguationsettings can also influence whether an error occurs or not. Namely, ifOffsetConflict::Rejectis used (which is the default), then an error occurs when there is an inconsistency between the offset and the time zone. And ifDisambiguation::Rejectis used, then an error occurs when the civil time in the string is ambiguous.Example
This example demonstrates the utility of this routine by parsing a datetime using an older copy of the IANA Time Zone Database. This example leverages the fact that the 2018 copy of
tzdbpreceded Brazil's announcement that daylight saving time would be abolished. This meant that datetimes in the future, when parsed with the older copy oftzdb, would still follow the old daylight saving time rules. But a mere update oftzdbwould otherwise change the meaning of the datetime.This scenario can come up if one stores datetimes in the future. This is also why the default offset conflict resolution strategy is
OffsetConflict::Reject, which prevents one from silently re-interpreting datetimes to a different timestamp.use jiff::{fmt::temporal::DateTimeParser, tz::{self, TimeZoneDatabase}}; static PARSER: DateTimeParser = DateTimeParser::new(); // Open a version of tzdb from before Brazil announced its abolition // of daylight saving time. let tzdb2018 = TimeZoneDatabase::from_dir("path/to/tzdb-2018b")?; // Open the system tzdb. let tzdb = tz::db(); // Parse the same datetime string with the same parser, but using two // different versions of tzdb. let dt = "2020-01-15T12:00[America/Sao_Paulo]"; let zdt2018 = PARSER.parse_zoned_with(&tzdb2018, dt)?; let zdt = PARSER.parse_zoned_with(tzdb, dt)?; // Before DST was abolished, 2020-01-15 was in DST, which corresponded // to UTC offset -02. Since DST rules applied to datetimes in the // future, the 2018 version of tzdb would lead one to interpret // 2020-01-15 as being in DST. assert_eq!(zdt2018.offset(), tz::offset(-2)); // But DST was abolished in 2019, which means that 2020-01-15 was no // no longer in DST. So after a tzdb update, the same datetime as above // now has a different offset. assert_eq!(zdt.offset(), tz::offset(-3)); // So if you try to parse a datetime serialized from an older copy of // tzdb, you'll get an error under the default configuration because // of `OffsetConflict::Reject`. This would succeed if you parsed it // using tzdb2018! assert!(PARSER.parse_zoned_with(tzdb, zdt2018.to_string()).is_err()); # Ok::<(), Box<dyn std::error::Error>>(())fn parse_timestamp<I: AsRef<[u8]>>(self: &Self, input: I) -> Result<Timestamp, Error>Parse a datetime string into a
Timestamp.The datetime string must correspond to a specific instant in time. This requires an offset in the datetime string.
Errors
This returns an error if the datetime string given is invalid or if it is valid but doesn't fit in the datetime range supported by Jiff.
Example
This shows a basic example of parsing an
Timestamp.use DateTimeParser; static PARSER: DateTimeParser = new; let timestamp = PARSER.parse_timestamp?; assert_eq!; # Ok::Example: parsing a timestamp from a datetime with a time zone
A timestamp can also be parsed fron a time zone aware datetime string. The time zone is ignored and the offset is always used.
use DateTimeParser; static PARSER: DateTimeParser = new; let timestamp = PARSER.parse_timestamp?; assert_eq!; # Ok::fn parse_datetime<I: AsRef<[u8]>>(self: &Self, input: I) -> Result<civil::DateTime, Error>Parse a civil datetime string into a
civil::DateTime.A civil datetime can be parsed from anything that contains a datetime. For example, a time zone aware string.
Errors
This returns an error if the datetime string given is invalid or if it is valid but doesn't fit in the datetime range supported by Jiff.
This also returns an error if a
Z(Zulu) offset is found, since interpreting such strings as civil time is usually a bug.Example
This shows a basic example of parsing a
civil::DateTime.use ; static PARSER: DateTimeParser = new; let datetime = PARSER.parse_datetime?; assert_eq!; # Ok::Example: parsing fails if a
Z(Zulu) offset is encounteredBecause parsing a datetime with a
Zoffset and interpreting it as a civil time is usually a bug, it is forbidden:use ; static PARSER: DateTimeParser = new; assert!; // Note though that -00 and +00 offsets parse successfully. let datetime = PARSER.parse_datetime?; assert_eq!; let datetime = PARSER.parse_datetime?; assert_eq!; # Ok::fn parse_date<I: AsRef<[u8]>>(self: &Self, input: I) -> Result<civil::Date, Error>Parse a civil date string into a
civil::Date.A civil date can be parsed from anything that contains a date. For example, a time zone aware string.
Errors
This returns an error if the date string given is invalid or if it is valid but doesn't fit in the date range supported by Jiff.
This also returns an error if a
Z(Zulu) offset is found, since interpreting such strings as civil date or time is usually a bug.Example
This shows a basic example of parsing a
civil::Date.use ; static PARSER: DateTimeParser = new; let d = PARSER.parse_date?; assert_eq!; # Ok::Example: parsing fails if a
Z(Zulu) offset is encounteredBecause parsing a date with a
Zoffset and interpreting it as a civil date or time is usually a bug, it is forbidden:use ; static PARSER: DateTimeParser = new; assert!; // Note though that -00 and +00 offsets parse successfully. let d = PARSER.parse_date?; assert_eq!; let d = PARSER.parse_date?; assert_eq!; # Ok::fn parse_time<I: AsRef<[u8]>>(self: &Self, input: I) -> Result<civil::Time, Error>Parse a civil time string into a
civil::Time.A civil time can be parsed from anything that contains a time. For example, a time zone aware string.
Errors
This returns an error if the time string given is invalid or if it is valid but doesn't fit in the time range supported by Jiff.
This also returns an error if a
Z(Zulu) offset is found, since interpreting such strings as civil time is usually a bug.Example
This shows a basic example of parsing a
civil::Time.use ; static PARSER: DateTimeParser = new; let t = PARSER.parse_time?; assert_eq!; # Ok::Example: parsing fails if a
Z(Zulu) offset is encounteredBecause parsing a time with a
Zoffset and interpreting it as a civil time is usually a bug, it is forbidden:use ; static PARSER: DateTimeParser = new; assert!; // Note though that -00 and +00 offsets parse successfully. let t = PARSER.parse_time?; assert_eq!; let t = PARSER.parse_time?; assert_eq!; # Ok::fn parse_time_zone<'i, I: AsRef<[u8]>>(self: &Self, input: I) -> Result<TimeZone, Error>Parses a string representing a time zone into a
TimeZone.This will parse one of three different categories of strings:
- An IANA Time Zone Database identifier. For example,
America/New_YorkorUTC. - A fixed offset. For example,
-05:00or-00:44:30. - A POSIX time zone string. For example,
EST5EDT,M3.2.0,M11.1.0.
Example
This shows a few examples of parsing different kinds of time zones:
use ; static PARSER: DateTimeParser = new; assert_eq!; assert_eq!; assert_eq!; assert_eq!; assert_eq!; // Some error cases! assert!; assert!; assert!; assert!; # Ok::- An IANA Time Zone Database identifier. For example,
fn parse_time_zone_with<'i, I: AsRef<[u8]>>(self: &Self, db: &TimeZoneDatabase, input: I) -> Result<TimeZone, Error>Parses a string representing a time zone into a
TimeZoneand performs any time zone database lookups using theTimeZoneDatabasegiven.This is like
DateTimeParser::parse_time_zone, but uses the time zone database given instead of the implicit global time zone database.This will parse one of three different categories of strings:
- An IANA Time Zone Database identifier. For example,
America/New_YorkorUTC. - A fixed offset. For example,
-05:00or-00:44:30. - A POSIX time zone string. For example,
EST5EDT,M3.2.0,M11.1.0.
Example
use ; static PARSER: DateTimeParser = new; let db = db; assert_eq!; # Ok::See also the example for
DateTimeParser::parse_zoned_withfor a more interesting example using a time zone database other than the default.- An IANA Time Zone Database identifier. For example,
fn parse_pieces<'i, I: ?Sized + AsRef<[u8]> + 'i>(self: &Self, input: &'i I) -> Result<Pieces<'i>, Error>Parse a Temporal datetime string into
Pieces.This is a lower level routine meant to give callers raw access to the individual "pieces" of a parsed Temporal ISO 8601 datetime string. Note that this only includes strings that have a date component.
The benefit of this routine is that it only checks that the datetime is itself valid. It doesn't do any automatic diambiguation, offset conflict resolution or attempt to prevent you from shooting yourself in the foot. For example, this routine will let you parse a fixed offset datetime into a
Zonedwithout a time zone abbreviation.Note that when using this routine, the
DateTimeParser::offset_conflictandDateTimeParser::disambiguationconfiguration knobs are completely ignored. This is because with the lower levelPieces, callers must handle offset conflict resolution (if they want it) themselves. See thePiecesdocumentation for a case study on how to do this if you need it.Errors
This returns an error if the datetime string given is invalid or if it is valid but doesn't fit in the date range supported by Jiff.
Example
This shows how to parse a fixed offset timestamp into a
Zoned.use ; static PARSER: DateTimeParser = new; let timestamp = "2025-01-02T15:13-05"; // Normally this operation will fail. assert_eq!; // But you can work-around this with `Pieces`, which gives you direct // access to the components parsed from the string. let pieces = PARSER.parse_pieces?; let time = pieces.time.unwrap_or_else; let dt = pieces.date.to_datetime; let tz = match pieces.to_time_zone? ; // We don't bother with offset conflict resolution. And note that // this uses automatic "compatible" disambiguation in the case of // discontinuities. Of course, this is all moot if `TimeZone` is // fixed. The above code handles the case where it isn't! let zdt = tz.to_zoned?; assert_eq!; # Ok::Example: work around errors when a
Z(Zulu) offset is encounteredBecause parsing a date with a
Zoffset and interpreting it as a civil date or time is usually a bug, it is forbidden:use ; static PARSER: DateTimeParser = new; assert_eq!; # Ok::But this sort of error checking doesn't happen when you parse into a
Pieces. You just get what was parsed, which lets you extract a date even if the higher level APIs forbid it:use ; static PARSER: DateTimeParser = new; let pieces = PARSER.parse_pieces?; assert_eq!; assert_eq!; assert_eq!; assert_eq!; # Ok::This is usually not the right thing to do. It isn't even suggested in the error message above. But if you know it's the right thing, then
Pieceswill let you do it.
impl Debug for DateTimeParser
fn fmt(self: &Self, f: &mut $crate::fmt::Formatter<'_>) -> $crate::fmt::Result
impl Freeze for DateTimeParser
impl RefUnwindSafe for DateTimeParser
impl Send for DateTimeParser
impl Sync for DateTimeParser
impl Unpin for DateTimeParser
impl UnwindSafe for DateTimeParser
impl<T> Any for DateTimeParser
fn type_id(self: &Self) -> TypeId
impl<T> Borrow for DateTimeParser
fn borrow(self: &Self) -> &T
impl<T> BorrowMut for DateTimeParser
fn borrow_mut(self: &mut Self) -> &mut T
impl<T> From for DateTimeParser
fn from(t: T) -> TReturns the argument unchanged.
impl<T, U> Into for DateTimeParser
fn into(self: Self) -> UCalls
U::from(self).That is, this conversion is whatever the implementation of
[From]<T> for Uchooses to do.
impl<T, U> TryFrom for DateTimeParser
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
impl<T, U> TryInto for DateTimeParser
fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>