Module strtime
dep_jiff
and alloc
only.Expand description
Support for “printf”-style parsing and formatting.
While the routines exposed in this module very closely resemble the
corresponding strptime
and strftime
POSIX functions, it is not a goal
for the formatting machinery to precisely match POSIX semantics.
If there is a conversion specifier you need that Jiff doesn’t support, please create a new issue.
The formatting and parsing in this module does not currently support any form of localization. Please see this issue about the topic of localization in Jiff.
§Example
This shows how to parse a civil date and its weekday:
use jiff::civil::Date;
let date = Date::strptime("%Y-%m-%d is a %A", "2024-07-15 is a Monday")?;
assert_eq!(date.to_string(), "2024-07-15");
// Leading zeros are optional for numbers in all cases:
let date = Date::strptime("%Y-%m-%d is a %A", "2024-07-15 is a Monday")?;
assert_eq!(date.to_string(), "2024-07-15");
// Parsing does error checking! 2024-07-15 was not a Tuesday.
assert!(Date::strptime("%Y-%m-%d is a %A", "2024-07-15 is a Tuesday").is_err());
And this shows how to format a zoned datetime with a time zone abbreviation:
use jiff::civil::date;
let zdt = date(2024, 7, 15).at(17, 30, 59, 0).intz("Australia/Tasmania")?;
// %-I instead of %I means no padding.
let string = zdt.strftime("%A, %B %d, %Y at %-I:%M%P %Z").to_string();
assert_eq!(string, "Monday, July 15, 2024 at 5:30pm AEST");
Or parse a zoned datetime with an IANA time zone identifier:
use jiff::{civil::date, Zoned};
let zdt = Zoned::strptime(
"%A, %B %d, %Y at %-I:%M%P %:V",
"Monday, July 15, 2024 at 5:30pm Australia/Tasmania",
)?;
assert_eq!(
zdt,
date(2024, 7, 15).at(17, 30, 0, 0).intz("Australia/Tasmania")?,
);
§Usage
For most cases, you can use the strptime
and strftime
methods on the
corresponding datetime type. For example, Zoned::strptime
and
Zoned::strftime
. However, the BrokenDownTime
type in this module
provides a little more control.
For example, assuming t
is a civil::Time
, then
t.strftime("%Y").to_string()
will actually panic because a civil::Time
does
not have a year. While the underlying formatting machinery actually returns
an error, this error gets turned into a panic by virtue of going through the
std::fmt::Display
and std::string::ToString
APIs.
In contrast, BrokenDownTime::format
(or just format
) can
report the error to you without any panicking:
use jiff::{civil::time, fmt::strtime};
let t = time(23, 59, 59, 0);
assert_eq!(
strtime::format("%Y", t).unwrap_err().to_string(),
"strftime formatting failed: %Y failed: requires date to format year",
);
§Advice
The formatting machinery supported by this module is not especially expressive. The pattern language is a simple sequence of conversion specifiers interspersed by literals and arbitrary whitespace. This means that you sometimes need delimiters or spaces between components. For example, this is fine:
use jiff::fmt::strtime;
let date = strtime::parse("%Y%m%d", "20240715")?.to_date()?;
assert_eq!(date.to_string(), "2024-07-15");
But this is ambiguous (is the year 999
or 9990
?):
use jiff::fmt::strtime;
assert!(strtime::parse("%Y%m%d", "9990715").is_err());
In this case, since years greedily consume up to 4 digits by default, 9990
is parsed as the year. And since months greedily consume up to 2 digits by
default, 71
is parsed as the month, which results in an invalid day. If you
expect your datetimes to always use 4 digits for the year, then it might be
okay to skip on the delimiters. For example, the year 999
could be written
with a leading zero:
use jiff::fmt::strtime;
let date = strtime::parse("%Y%m%d", "09990715")?.to_date()?;
assert_eq!(date.to_string(), "0999-07-15");
// Indeed, the leading zero is written by default when
// formatting, since years are padded out to 4 digits
// by default:
assert_eq!(date.strftime("%Y%m%d").to_string(), "09990715");
The main advice here is that these APIs can come in handy for ad hoc tasks that
would otherwise be annoying to deal with. For example, I once wrote a tool to
extract data from an XML dump of my SMS messages, and one of the date formats
used was Apr 1, 2022 20:46:15
. That doesn’t correspond to any standard, and
while parsing it with a regex isn’t that difficult, it’s pretty annoying,
especially because of the English abbreviated month name. That’s exactly the
kind of use case where this module shines.
If the formatting machinery in this module isn’t flexible enough for your use case and you don’t control the format, it is recommended to write a bespoke parser (possibly with regex). It is unlikely that the expressiveness of this formatting machinery will be improved much. (Although it is plausible to add new conversion specifiers.)
§Conversion specifications
This table lists the complete set of conversion specifiers supported in the format. While most conversion specifiers are supported as is in both parsing and formatting, there are some differences. Where differences occur, they are noted in the table below.
When parsing, and whenever a conversion specifier matches an enumeration of strings, the strings are matched without regard to ASCII case.
Specifier | Example | Description |
---|---|---|
%% | %% | A literal % . |
%A , %a | Sunday , Sun | The full and abbreviated weekday, respectively. |
%B , %b , %h | June , Jun , Jun | The full and abbreviated month name, respectively. |
%D | 7/14/24 | Equivalent to %m/%d/%y . |
%d , %e | 25 , 5 | The day of the month. %d is zero-padded, %e is space padded. |
%F | 2024-07-14 | Equivalent to %Y-%m-%d . |
%f | 000456 | Fractional seconds, up to nanosecond precision. |
%.f | .000456 | Optional fractional seconds, with dot, up to nanosecond precision. |
%H | 23 | The hour in a 24 hour clock. Zero padded. |
%I | 11 | The hour in a 12 hour clock. Zero padded. |
%M | 04 | The minute. Zero padded. |
%m | 01 | The month. Zero padded. |
%P | am | Whether the time is in the AM or PM, lowercase. |
%p | PM | Whether the time is in the AM or PM, uppercase. |
%S | 59 | The second. Zero padded. |
%T | 23:30:59 | Equivalent to %H:%M:%S . |
%V | America/New_York , +0530 | An IANA time zone identifier, or %z if one doesn’t exist. |
%:V | America/New_York , +05:30 | An IANA time zone identifier, or %:z if one doesn’t exist. |
%Y | 2024 | A full year, including century. Zero padded to 4 digits. |
%y | 24 | A two-digit year. Represents only 1969-2068. Zero padded. |
%Z | EDT | A time zone abbreviation. Supported when formatting only. |
%z | +0530 | A time zone offset in the format [+-]HHMM[SS] . |
%:z | +05:30 | A time zone offset in the format [+-]HH:MM[:SS] . |
When formatting, the following flags can be inserted immediately after the %
and before the directive:
_
- Pad a numeric result to the left with spaces.-
- Do not pad a numeric result.0
- Pad a numeric result to the left with zeros.^
- Use alphabetic uppercase for all relevant strings.#
- Swap the case of the result string. This is typically only useful with%p
or%Z
, since they are the only conversion specifiers that emit strings entirely in uppercase by default.
The above flags override the “default” settings of a specifier. For example,
%_d
pads with spaces instead of zeros, and %0e
pads with zeros instead of
spaces. The exceptions are the %z
and %:z
specifiers. They are unaffected
by any flags.
Moreover, any number of decimal digits can be inserted after the (possibly absent) flag and before the directive, so long as the parsed number is less than 256. The number formed by these digits will correspond to the minimum amount of padding (to the left).
The flags and padding amount above may be used when parsing as well. Most
settings are ignoring during parsing except for padding. For example, if one
wanted to parse 003
as the day 3
, then one should use %03d
. Otherwise, by
default, %d
will only try to consume at most 2 digits.
The %f
and %.f
flags also support specifying the precision, up to
nanoseconds. For example, %3f
and %.3f
will both always print a fractional
second component to exactly 3 decimal places. When no precision is specified,
then %f
will always emit at least one digit, even if it’s zero. But %.f
will emit the empty string when the fractional component is zero. Otherwise, it
will include the leading .
. For parsing, %f
does not include the leading
dot, but %.f
does. Note that all of the options above are still parsed for
%f
and %.f
, but they are all no-ops (except for the padding for %f
, which
is instead interpreted as a precision setting). When using a precision setting,
truncation is used. If you need a different rounding mode, you should use
higher level APIs like Timestamp::round
or Zoned::round
.
§Conditionally unsupported
Jiff does not support %V
or %:V
(IANA time zone identifier) when the
alloc
crate feature is not enabled. This is because a time zone identifier
is variable width data. If you have a use case for this, please
detail it in a new issue.
§Unsupported
The following things are currently unsupported:
- Parsing or formatting fractional seconds in the time time zone offset.
- Conversion specifiers related to week numbers.
- Conversion specifiers related to day-of-year numbers, like the Julian day.
- The
%s
conversion specifier, for Unix timestamps in seconds.
Structs§
- The “broken down time” used by parsing and formatting.
- A “lazy” implementation of
std::fmt::Display
forstrftime
.
Enums§
- A label to disambiguate hours on a 12-hour clock.
Functions§
- Format the given broken down time using the format string given.
- Parse the given
input
according to the givenformat
string.