aboutsummaryrefslogtreecommitdiff
path: root/src/format/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/format/mod.rs')
-rw-r--r--src/format/mod.rs738
1 files changed, 176 insertions, 562 deletions
diff --git a/src/format/mod.rs b/src/format/mod.rs
index a641f19..a822882 100644
--- a/src/format/mod.rs
+++ b/src/format/mod.rs
@@ -12,50 +12,69 @@
//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of
//! the [`Item`](./enum.Item.html) type.
//! They are generated from more readable **format strings**;
-//! currently Chrono supports [one built-in syntax closely resembling
-//! C's `strftime` format](./strftime/index.html).
-
-#![allow(ellipsis_inclusive_range_patterns)]
+//! currently Chrono supports a built-in syntax closely resembling
+//! C's `strftime` format. The available options can be found [here](./strftime/index.html).
+//!
+//! # Example
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+#![cfg_attr(feature = "std", doc = "```rust")]
+//! use chrono::{NaiveDateTime, TimeZone, Utc};
+//!
+//! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap();
+//!
+//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S"));
+//! assert_eq!(formatted, "2020-11-10 00:01:32");
+//!
+//! let parsed = NaiveDateTime::parse_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?.and_utc();
+//! assert_eq!(parsed, date_time);
+//! # Ok::<(), chrono::ParseError>(())
+//! ```
-#[cfg(feature = "alloc")]
+#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::boxed::Box;
-#[cfg(feature = "alloc")]
-use alloc::string::{String, ToString};
-#[cfg(any(feature = "alloc", feature = "std", test))]
-use core::borrow::Borrow;
use core::fmt;
use core::str::FromStr;
-#[cfg(any(feature = "std", test))]
+#[cfg(feature = "std")]
use std::error::Error;
-#[cfg(any(feature = "alloc", feature = "std", test))]
-use naive::{NaiveDate, NaiveTime};
-#[cfg(any(feature = "alloc", feature = "std", test))]
-use offset::{FixedOffset, Offset};
-#[cfg(any(feature = "alloc", feature = "std", test))]
-use {Datelike, Timelike};
-use {Month, ParseMonthError, ParseWeekdayError, Weekday};
+use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday};
-#[cfg(feature = "unstable-locales")]
+mod formatting;
+mod parsed;
+
+// due to the size of parsing routines, they are in separate modules.
+mod parse;
+pub(crate) mod scan;
+
+pub mod strftime;
+
+#[allow(unused)]
+// TODO: remove '#[allow(unused)]' once we use this module for parsing or something else that does
+// not require `alloc`.
pub(crate) mod locales;
-pub use self::parse::parse;
-pub use self::parsed::Parsed;
-pub use self::strftime::StrftimeItems;
-/// L10n locales.
+pub(crate) use formatting::write_hundreds;
+#[cfg(feature = "alloc")]
+pub(crate) use formatting::write_rfc2822;
+#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
+pub(crate) use formatting::write_rfc3339;
+pub use formatting::SecondsFormat;
+#[cfg(feature = "alloc")]
+#[allow(deprecated)]
+pub use formatting::{format, format_item, DelayedFormat};
#[cfg(feature = "unstable-locales")]
-pub use pure_rust_locales::Locale;
-
-#[cfg(not(feature = "unstable-locales"))]
-#[derive(Debug)]
-struct Locale;
+pub use locales::Locale;
+pub(crate) use parse::parse_rfc3339;
+pub use parse::{parse, parse_and_remainder};
+pub use parsed::Parsed;
+pub use strftime::StrftimeItems;
/// An uninhabited type used for `InternalNumeric` and `InternalFixed` below.
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq, Hash)]
enum Void {}
/// Padding characters for numeric items.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum Pad {
/// No padding.
None,
@@ -78,10 +97,10 @@ pub enum Pad {
/// It also trims the preceding whitespace if any.
/// It cannot parse the negative number, so some date and time cannot be formatted then
/// parsed with the same formatting items.
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum Numeric {
/// Full Gregorian year (FW=4, PW=∞).
- /// May accept years before 1 BCE or after 9999 CE, given an initial sign.
+ /// May accept years before 1 BCE or after 9999 CE, given an initial sign (+/-).
Year,
/// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year.
YearDiv100,
@@ -134,24 +153,11 @@ pub enum Numeric {
}
/// An opaque type representing numeric item types for internal uses only.
+#[derive(Clone, Eq, Hash, PartialEq)]
pub struct InternalNumeric {
_dummy: Void,
}
-impl Clone for InternalNumeric {
- fn clone(&self) -> Self {
- match self._dummy {}
- }
-}
-
-impl PartialEq for InternalNumeric {
- fn eq(&self, _other: &InternalNumeric) -> bool {
- match self._dummy {}
- }
-}
-
-impl Eq for InternalNumeric {}
-
impl fmt::Debug for InternalNumeric {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<InternalNumeric>")
@@ -162,7 +168,7 @@ impl fmt::Debug for InternalNumeric {
///
/// They have their own rules of formatting and parsing.
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum Fixed {
/// Abbreviated month names.
///
@@ -208,6 +214,18 @@ pub enum Fixed {
/// The offset is limited from `-24:00` to `+24:00`,
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetColon,
+ /// Offset from the local time to UTC with seconds (`+09:00:00` or `-04:00:00` or `+00:00:00`).
+ ///
+ /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
+ /// The offset is limited from `-24:00:00` to `+24:00:00`,
+ /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
+ TimezoneOffsetDoubleColon,
+ /// Offset from the local time to UTC without minutes (`+09` or `-04` or `+00`).
+ ///
+ /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
+ /// The offset is limited from `-24` to `+24`,
+ /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
+ TimezoneOffsetTripleColon,
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
@@ -234,12 +252,12 @@ pub enum Fixed {
}
/// An opaque type representing fixed-format item types for internal uses only.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InternalFixed {
val: InternalInternal,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum InternalInternal {
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but
/// allows missing minutes (per [ISO 8601][iso8601]).
@@ -258,18 +276,63 @@ enum InternalInternal {
Nanosecond9NoDot,
}
+/// Type for specifying the format of UTC offsets.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct OffsetFormat {
+ /// See `OffsetPrecision`.
+ pub precision: OffsetPrecision,
+ /// Separator between hours, minutes and seconds.
+ pub colons: Colons,
+ /// Represent `+00:00` as `Z`.
+ pub allow_zulu: bool,
+ /// Pad the hour value to two digits.
+ pub padding: Pad,
+}
+
+/// The precision of an offset from UTC formatting item.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum OffsetPrecision {
+ /// Format offset from UTC as only hours. Not recommended, it is not uncommon for timezones to
+ /// have an offset of 30 minutes, 15 minutes, etc.
+ /// Any minutes and seconds get truncated.
+ Hours,
+ /// Format offset from UTC as hours and minutes.
+ /// Any seconds will be rounded to the nearest minute.
+ Minutes,
+ /// Format offset from UTC as hours, minutes and seconds.
+ Seconds,
+ /// Format offset from UTC as hours, and optionally with minutes.
+ /// Any seconds will be rounded to the nearest minute.
+ OptionalMinutes,
+ /// Format offset from UTC as hours and minutes, and optionally seconds.
+ OptionalSeconds,
+ /// Format offset from UTC as hours and optionally minutes and seconds.
+ OptionalMinutesAndSeconds,
+}
+
+/// The separator between hours and minutes in an offset.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Colons {
+ /// No separator
+ None,
+ /// Colon (`:`) as separator
+ Colon,
+ /// No separator when formatting, colon allowed when parsing.
+ Maybe,
+}
+
/// A single formatting item. This is used for both formatting and parsing.
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum Item<'a> {
/// A literally printed and parsed text.
Literal(&'a str),
/// Same as `Literal` but with the string owned by the item.
- #[cfg(any(feature = "alloc", feature = "std", test))]
+ #[cfg(feature = "alloc")]
OwnedLiteral(Box<str>),
/// Whitespace. Prints literally but reads zero or more whitespace.
Space(&'a str),
/// Same as `Space` but with the string owned by the item.
- #[cfg(any(feature = "alloc", feature = "std", test))]
+ #[cfg(feature = "alloc")]
OwnedSpace(Box<str>),
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
/// the parser simply ignores any padded whitespace and zeroes.
@@ -280,49 +343,57 @@ pub enum Item<'a> {
Error,
}
-macro_rules! lit {
- ($x:expr) => {
- Item::Literal($x)
- };
+const fn num(numeric: Numeric) -> Item<'static> {
+ Item::Numeric(numeric, Pad::None)
}
-macro_rules! sp {
- ($x:expr) => {
- Item::Space($x)
- };
-}
-macro_rules! num {
- ($x:ident) => {
- Item::Numeric(Numeric::$x, Pad::None)
- };
+
+const fn num0(numeric: Numeric) -> Item<'static> {
+ Item::Numeric(numeric, Pad::Zero)
}
-macro_rules! num0 {
- ($x:ident) => {
- Item::Numeric(Numeric::$x, Pad::Zero)
- };
+
+const fn nums(numeric: Numeric) -> Item<'static> {
+ Item::Numeric(numeric, Pad::Space)
}
-macro_rules! nums {
- ($x:ident) => {
- Item::Numeric(Numeric::$x, Pad::Space)
- };
+
+const fn fixed(fixed: Fixed) -> Item<'static> {
+ Item::Fixed(fixed)
}
-macro_rules! fix {
- ($x:ident) => {
- Item::Fixed(Fixed::$x)
- };
+
+const fn internal_fixed(val: InternalInternal) -> Item<'static> {
+ Item::Fixed(Fixed::Internal(InternalFixed { val }))
}
-macro_rules! internal_fix {
- ($x:ident) => {
- Item::Fixed(Fixed::Internal(InternalFixed { val: InternalInternal::$x }))
- };
+
+impl<'a> Item<'a> {
+ /// Convert items that contain a reference to the format string into an owned variant.
+ #[cfg(any(feature = "alloc", feature = "std"))]
+ pub fn to_owned(self) -> Item<'static> {
+ match self {
+ Item::Literal(s) => Item::OwnedLiteral(Box::from(s)),
+ Item::Space(s) => Item::OwnedSpace(Box::from(s)),
+ Item::Numeric(n, p) => Item::Numeric(n, p),
+ Item::Fixed(f) => Item::Fixed(f),
+ Item::OwnedLiteral(l) => Item::OwnedLiteral(l),
+ Item::OwnedSpace(s) => Item::OwnedSpace(s),
+ Item::Error => Item::Error,
+ }
+ }
}
/// An error from the `parse` function.
-#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
pub struct ParseError(ParseErrorKind);
+impl ParseError {
+ /// The category of parse error
+ pub const fn kind(&self) -> ParseErrorKind {
+ self.0
+ }
+}
+
/// The category of parse error
-#[derive(Debug, Clone, PartialEq, Eq, Copy)]
-enum ParseErrorKind {
+#[allow(clippy::manual_non_exhaustive)]
+#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
+pub enum ParseErrorKind {
/// Given field is out of permitted range.
OutOfRange,
@@ -350,6 +421,10 @@ enum ParseErrorKind {
/// There was an error on the formatting string, or there were non-supported formating items.
BadFormat,
+
+ // TODO: Change this to `#[non_exhaustive]` (on the enum) with the next breaking release.
+ #[doc(hidden)]
+ __Nonexhaustive,
}
/// Same as `Result<T, ParseError>`.
@@ -365,11 +440,12 @@ impl fmt::Display for ParseError {
ParseErrorKind::TooShort => write!(f, "premature end of input"),
ParseErrorKind::TooLong => write!(f, "trailing input"),
ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"),
+ _ => unreachable!(),
}
}
}
-#[cfg(any(feature = "std", test))]
+#[cfg(feature = "std")]
impl Error for ParseError {
#[allow(deprecated)]
fn description(&self) -> &str {
@@ -378,465 +454,40 @@ impl Error for ParseError {
}
// to be used in this module and submodules
-const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
+pub(crate) const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible);
const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough);
const INVALID: ParseError = ParseError(ParseErrorKind::Invalid);
const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
-const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
+pub(crate) const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
-/// Formats single formatting item
-#[cfg(any(feature = "alloc", feature = "std", test))]
-pub fn format_item<'a>(
- w: &mut fmt::Formatter,
- date: Option<&NaiveDate>,
- time: Option<&NaiveTime>,
- off: Option<&(String, FixedOffset)>,
- item: &Item<'a>,
-) -> fmt::Result {
- let mut result = String::new();
- format_inner(&mut result, date, time, off, item, None)?;
- w.pad(&result)
-}
-
-#[cfg(any(feature = "alloc", feature = "std", test))]
-fn format_inner<'a>(
- result: &mut String,
- date: Option<&NaiveDate>,
- time: Option<&NaiveTime>,
- off: Option<&(String, FixedOffset)>,
- item: &Item<'a>,
- _locale: Option<Locale>,
-) -> fmt::Result {
- #[cfg(feature = "unstable-locales")]
- let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
- let locale = _locale.unwrap_or(Locale::POSIX);
- let am_pm = locales::am_pm(locale);
- (
- locales::short_months(locale),
- locales::long_months(locale),
- locales::short_weekdays(locale),
- locales::long_weekdays(locale),
- am_pm,
- &[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()],
- )
- };
- #[cfg(not(feature = "unstable-locales"))]
- let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
- (
- &["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
- &[
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December",
- ],
- &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
- &["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
- &["AM", "PM"],
- &["am", "pm"],
- )
- };
-
- use core::fmt::Write;
- use div::{div_floor, mod_floor};
-
- match *item {
- Item::Literal(s) | Item::Space(s) => result.push_str(s),
- #[cfg(any(feature = "alloc", feature = "std", test))]
- Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
-
- Item::Numeric(ref spec, ref pad) => {
- use self::Numeric::*;
-
- let week_from_sun = |d: &NaiveDate| {
- (d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 7) / 7
- };
- let week_from_mon = |d: &NaiveDate| {
- (d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7
- };
-
- let (width, v) = match *spec {
- Year => (4, date.map(|d| i64::from(d.year()))),
- YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
- YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
- IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
- IsoYearDiv100 => (2, date.map(|d| div_floor(i64::from(d.iso_week().year()), 100))),
- IsoYearMod100 => (2, date.map(|d| mod_floor(i64::from(d.iso_week().year()), 100))),
- Month => (2, date.map(|d| i64::from(d.month()))),
- Day => (2, date.map(|d| i64::from(d.day()))),
- WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
- WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
- IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
- NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))),
- WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))),
- Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
- Hour => (2, time.map(|t| i64::from(t.hour()))),
- Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
- Minute => (2, time.map(|t| i64::from(t.minute()))),
- Second => (2, time.map(|t| i64::from(t.second() + t.nanosecond() / 1_000_000_000))),
- Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
- Timestamp => (
- 1,
- match (date, time, off) {
- (Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()),
- (Some(d), Some(t), Some(&(_, off))) => {
- Some((d.and_time(*t) - off).timestamp())
- }
- (_, _, _) => None,
- },
- ),
-
- // for the future expansion
- Internal(ref int) => match int._dummy {},
- };
-
- if let Some(v) = v {
- if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
- // non-four-digit years require an explicit sign as per ISO 8601
- match *pad {
- Pad::None => write!(result, "{:+}", v),
- Pad::Zero => write!(result, "{:+01$}", v, width + 1),
- Pad::Space => write!(result, "{:+1$}", v, width + 1),
- }
- } else {
- match *pad {
- Pad::None => write!(result, "{}", v),
- Pad::Zero => write!(result, "{:01$}", v, width),
- Pad::Space => write!(result, "{:1$}", v, width),
- }
- }?
- } else {
- return Err(fmt::Error); // insufficient arguments for given format
- }
- }
-
- Item::Fixed(ref spec) => {
- use self::Fixed::*;
-
- /// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
- /// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
- fn write_local_minus_utc(
- result: &mut String,
- off: FixedOffset,
- allow_zulu: bool,
- use_colon: bool,
- ) -> fmt::Result {
- let off = off.local_minus_utc();
- if !allow_zulu || off != 0 {
- let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
- if use_colon {
- write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
- } else {
- write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
- }
- } else {
- result.push_str("Z");
- Ok(())
- }
- }
-
- let ret =
- match *spec {
- ShortMonthName => date.map(|d| {
- result.push_str(short_months[d.month0() as usize]);
- Ok(())
- }),
- LongMonthName => date.map(|d| {
- result.push_str(long_months[d.month0() as usize]);
- Ok(())
- }),
- ShortWeekdayName => date.map(|d| {
- result
- .push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]);
- Ok(())
- }),
- LongWeekdayName => date.map(|d| {
- result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]);
- Ok(())
- }),
- LowerAmPm => time.map(|t| {
- #[cfg_attr(feature = "cargo-clippy", allow(useless_asref))]
- {
- result.push_str(if t.hour12().0 {
- am_pm_lowercase[1].as_ref()
- } else {
- am_pm_lowercase[0].as_ref()
- });
- }
- Ok(())
- }),
- UpperAmPm => time.map(|t| {
- result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] });
- Ok(())
- }),
- Nanosecond => time.map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- if nano == 0 {
- Ok(())
- } else if nano % 1_000_000 == 0 {
- write!(result, ".{:03}", nano / 1_000_000)
- } else if nano % 1_000 == 0 {
- write!(result, ".{:06}", nano / 1_000)
- } else {
- write!(result, ".{:09}", nano)
- }
- }),
- Nanosecond3 => time.map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- write!(result, ".{:03}", nano / 1_000_000)
- }),
- Nanosecond6 => time.map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- write!(result, ".{:06}", nano / 1_000)
- }),
- Nanosecond9 => time.map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- write!(result, ".{:09}", nano)
- }),
- Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => time
- .map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- write!(result, "{:03}", nano / 1_000_000)
- }),
- Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => time
- .map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- write!(result, "{:06}", nano / 1_000)
- }),
- Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => time
- .map(|t| {
- let nano = t.nanosecond() % 1_000_000_000;
- write!(result, "{:09}", nano)
- }),
- TimezoneName => off.map(|&(ref name, _)| {
- result.push_str(name);
- Ok(())
- }),
- TimezoneOffsetColon => {
- off.map(|&(_, off)| write_local_minus_utc(result, off, false, true))
- }
- TimezoneOffsetColonZ => {
- off.map(|&(_, off)| write_local_minus_utc(result, off, true, true))
- }
- TimezoneOffset => {
- off.map(|&(_, off)| write_local_minus_utc(result, off, false, false))
- }
- TimezoneOffsetZ => {
- off.map(|&(_, off)| write_local_minus_utc(result, off, true, false))
- }
- Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
- panic!("Do not try to write %#z it is undefined")
- }
- RFC2822 =>
- // same as `%a, %d %b %Y %H:%M:%S %z`
- {
- if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
- let sec = t.second() + t.nanosecond() / 1_000_000_000;
- write!(
- result,
- "{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
- short_weekdays[d.weekday().num_days_from_sunday() as usize],
- d.day(),
- short_months[d.month0() as usize],
- d.year(),
- t.hour(),
- t.minute(),
- sec
- )?;
- Some(write_local_minus_utc(result, off, false, false))
- } else {
- None
- }
- }
- RFC3339 =>
- // same as `%Y-%m-%dT%H:%M:%S%.f%:z`
- {
- if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
- // reuse `Debug` impls which already print ISO 8601 format.
- // this is faster in this way.
- write!(result, "{:?}T{:?}", d, t)?;
- Some(write_local_minus_utc(result, off, false, true))
- } else {
- None
- }
- }
- };
-
- match ret {
- Some(ret) => ret?,
- None => return Err(fmt::Error), // insufficient arguments for given format
- }
- }
-
- Item::Error => return Err(fmt::Error),
- }
- Ok(())
-}
-
-/// Tries to format given arguments with given formatting items.
-/// Internally used by `DelayedFormat`.
-#[cfg(any(feature = "alloc", feature = "std", test))]
-pub fn format<'a, I, B>(
- w: &mut fmt::Formatter,
- date: Option<&NaiveDate>,
- time: Option<&NaiveTime>,
- off: Option<&(String, FixedOffset)>,
- items: I,
-) -> fmt::Result
-where
- I: Iterator<Item = B> + Clone,
- B: Borrow<Item<'a>>,
-{
- let mut result = String::new();
- for item in items {
- format_inner(&mut result, date, time, off, item.borrow(), None)?;
- }
- w.pad(&result)
-}
-
-mod parsed;
-
-// due to the size of parsing routines, they are in separate modules.
-mod parse;
-mod scan;
-
-pub mod strftime;
-
-/// A *temporary* object which can be used as an argument to `format!` or others.
-/// This is normally constructed via `format` methods of each date and time type.
-#[cfg(any(feature = "alloc", feature = "std", test))]
-#[derive(Debug)]
-pub struct DelayedFormat<I> {
- /// The date view, if any.
- date: Option<NaiveDate>,
- /// The time view, if any.
- time: Option<NaiveTime>,
- /// The name and local-to-UTC difference for the offset (timezone), if any.
- off: Option<(String, FixedOffset)>,
- /// An iterator returning formatting items.
- items: I,
- /// Locale used for text.
- locale: Option<Locale>,
-}
-
-#[cfg(any(feature = "alloc", feature = "std", test))]
-impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
- /// Makes a new `DelayedFormat` value out of local date and time.
- pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
- DelayedFormat { date: date, time: time, off: None, items: items, locale: None }
- }
-
- /// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
- pub fn new_with_offset<Off>(
- date: Option<NaiveDate>,
- time: Option<NaiveTime>,
- offset: &Off,
- items: I,
- ) -> DelayedFormat<I>
- where
- Off: Offset + fmt::Display,
- {
- let name_and_diff = (offset.to_string(), offset.fix());
- DelayedFormat {
- date: date,
- time: time,
- off: Some(name_and_diff),
- items: items,
- locale: None,
- }
- }
-
- /// Makes a new `DelayedFormat` value out of local date and time and locale.
- #[cfg(feature = "unstable-locales")]
- pub fn new_with_locale(
- date: Option<NaiveDate>,
- time: Option<NaiveTime>,
- items: I,
- locale: Locale,
- ) -> DelayedFormat<I> {
- DelayedFormat { date: date, time: time, off: None, items: items, locale: Some(locale) }
- }
-
- /// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale.
- #[cfg(feature = "unstable-locales")]
- pub fn new_with_offset_and_locale<Off>(
- date: Option<NaiveDate>,
- time: Option<NaiveTime>,
- offset: &Off,
- items: I,
- locale: Locale,
- ) -> DelayedFormat<I>
- where
- Off: Offset + fmt::Display,
- {
- let name_and_diff = (offset.to_string(), offset.fix());
- DelayedFormat {
- date: date,
- time: time,
- off: Some(name_and_diff),
- items: items,
- locale: Some(locale),
- }
- }
-}
-
-#[cfg(any(feature = "alloc", feature = "std", test))]
-impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- #[cfg(feature = "unstable-locales")]
- {
- if let Some(locale) = self.locale {
- return format_localized(
- f,
- self.date.as_ref(),
- self.time.as_ref(),
- self.off.as_ref(),
- self.items.clone(),
- locale,
- );
- }
- }
-
- format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
- }
-}
-
// this implementation is here only because we need some private code from `scan`
-/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
+/// Parsing a `str` into a `Weekday` uses the format [`%A`](./format/strftime/index.html).
///
/// # Example
///
-/// ~~~~
+/// ```
/// use chrono::Weekday;
///
/// assert_eq!("Sunday".parse::<Weekday>(), Ok(Weekday::Sun));
/// assert!("any day".parse::<Weekday>().is_err());
-/// ~~~~
+/// ```
///
/// The parsing is case-insensitive.
///
-/// ~~~~
+/// ```
/// # use chrono::Weekday;
/// assert_eq!("mON".parse::<Weekday>(), Ok(Weekday::Mon));
-/// ~~~~
+/// ```
///
/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted.
///
-/// ~~~~
+/// ```
/// # use chrono::Weekday;
/// assert!("thurs".parse::<Weekday>().is_err());
-/// ~~~~
+/// ```
impl FromStr for Weekday {
type Err = ParseWeekdayError;
@@ -849,68 +500,31 @@ impl FromStr for Weekday {
}
}
-/// Formats single formatting item
-#[cfg(feature = "unstable-locales")]
-pub fn format_item_localized<'a>(
- w: &mut fmt::Formatter,
- date: Option<&NaiveDate>,
- time: Option<&NaiveTime>,
- off: Option<&(String, FixedOffset)>,
- item: &Item<'a>,
- locale: Locale,
-) -> fmt::Result {
- let mut result = String::new();
- format_inner(&mut result, date, time, off, item, Some(locale))?;
- w.pad(&result)
-}
-
-/// Tries to format given arguments with given formatting items.
-/// Internally used by `DelayedFormat`.
-#[cfg(feature = "unstable-locales")]
-pub fn format_localized<'a, I, B>(
- w: &mut fmt::Formatter,
- date: Option<&NaiveDate>,
- time: Option<&NaiveTime>,
- off: Option<&(String, FixedOffset)>,
- items: I,
- locale: Locale,
-) -> fmt::Result
-where
- I: Iterator<Item = B> + Clone,
- B: Borrow<Item<'a>>,
-{
- let mut result = String::new();
- for item in items {
- format_inner(&mut result, date, time, off, item.borrow(), Some(locale))?;
- }
- w.pad(&result)
-}
-
-/// Parsing a `str` into a `Month` uses the format [`%W`](./format/strftime/index.html).
+/// Parsing a `str` into a `Month` uses the format [`%B`](./format/strftime/index.html).
///
/// # Example
///
-/// ~~~~
+/// ```
/// use chrono::Month;
///
/// assert_eq!("January".parse::<Month>(), Ok(Month::January));
/// assert!("any day".parse::<Month>().is_err());
-/// ~~~~
+/// ```
///
/// The parsing is case-insensitive.
///
-/// ~~~~
+/// ```
/// # use chrono::Month;
/// assert_eq!("fEbruARy".parse::<Month>(), Ok(Month::February));
-/// ~~~~
+/// ```
///
/// Only the shortest form (e.g. `jan`) and the longest form (e.g. `january`) is accepted.
///
-/// ~~~~
+/// ```
/// # use chrono::Month;
/// assert!("septem".parse::<Month>().is_err());
/// assert!("Augustin".parse::<Month>().is_err());
-/// ~~~~
+/// ```
impl FromStr for Month {
type Err = ParseMonthError;