summaryrefslogtreecommitdiffstats
path: root/vendor/chrono/src/format
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/chrono/src/format
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/chrono/src/format')
-rw-r--r--vendor/chrono/src/format/mod.rs483
-rw-r--r--vendor/chrono/src/format/parse.rs97
-rw-r--r--vendor/chrono/src/format/parsed.rs160
-rw-r--r--vendor/chrono/src/format/scan.rs121
-rw-r--r--vendor/chrono/src/format/strftime.rs177
5 files changed, 678 insertions, 360 deletions
diff --git a/vendor/chrono/src/format/mod.rs b/vendor/chrono/src/format/mod.rs
index a641f196d..c05ba4d04 100644
--- a/vendor/chrono/src/format/mod.rs
+++ b/vendor/chrono/src/format/mod.rs
@@ -12,10 +12,26 @@
//! 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).
+//! currently Chrono supports a built-in syntax closely resembling
+//! C's `strftime` format. The available options can be found [here](./strftime/index.html).
+//!
+//! # Example
+//! ```rust
+//! # use std::error::Error;
+//! use chrono::prelude::*;
+//!
+//! 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 = Utc.datetime_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?;
+//! assert_eq!(parsed, date_time);
+//! # Ok::<(), chrono::ParseError>(())
+//! ```
-#![allow(ellipsis_inclusive_range_patterns)]
+#[cfg(feature = "alloc")]
+extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
@@ -24,38 +40,40 @@ use alloc::string::{String, ToString};
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::fmt;
+use core::fmt::Write;
use core::str::FromStr;
#[cfg(any(feature = "std", test))]
use std::error::Error;
#[cfg(any(feature = "alloc", feature = "std", test))]
-use naive::{NaiveDate, NaiveTime};
+use crate::naive::{NaiveDate, NaiveTime};
#[cfg(any(feature = "alloc", feature = "std", test))]
-use offset::{FixedOffset, Offset};
+use crate::offset::{FixedOffset, Offset};
#[cfg(any(feature = "alloc", feature = "std", test))]
-use {Datelike, Timelike};
-use {Month, ParseMonthError, ParseWeekdayError, Weekday};
+use crate::{Datelike, Timelike};
+use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday};
#[cfg(feature = "unstable-locales")]
pub(crate) mod locales;
-pub use self::parse::parse;
-pub use self::parsed::Parsed;
-pub use self::strftime::StrftimeItems;
+pub use parse::parse;
+pub use parsed::Parsed;
/// L10n locales.
#[cfg(feature = "unstable-locales")]
pub use pure_rust_locales::Locale;
+pub use strftime::StrftimeItems;
#[cfg(not(feature = "unstable-locales"))]
+#[allow(dead_code)]
#[derive(Debug)]
struct Locale;
/// 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 +96,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 +152,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 +167,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 +213,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 +251,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 +275,29 @@ enum InternalInternal {
Nanosecond9NoDot,
}
+#[cfg(any(feature = "alloc", feature = "std", test))]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+enum Colons {
+ None,
+ Single,
+ Double,
+ Triple,
+}
+
/// 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_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
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_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
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.
@@ -317,12 +345,19 @@ macro_rules! internal_fix {
}
/// 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 {
+#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
+pub enum ParseErrorKind {
/// Given field is out of permitted range.
OutOfRange,
@@ -350,6 +385,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) when MSRV is increased
+ #[doc(hidden)]
+ __Nonexhaustive,
}
/// Same as `Result<T, ParseError>`.
@@ -365,11 +404,13 @@ 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_attr(docsrs, doc(cfg(feature = "std")))]
impl Error for ParseError {
#[allow(deprecated)]
fn description(&self) -> &str {
@@ -386,14 +427,72 @@ const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
+#[cfg(any(feature = "alloc", feature = "std", test))]
+struct Locales {
+ short_months: &'static [&'static str],
+ long_months: &'static [&'static str],
+ short_weekdays: &'static [&'static str],
+ long_weekdays: &'static [&'static str],
+ am_pm: &'static [&'static str],
+}
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+impl Locales {
+ fn new(_locale: Option<Locale>) -> Self {
+ #[cfg(feature = "unstable-locales")]
+ {
+ let locale = _locale.unwrap_or(Locale::POSIX);
+ Self {
+ short_months: locales::short_months(locale),
+ long_months: locales::long_months(locale),
+ short_weekdays: locales::short_weekdays(locale),
+ long_weekdays: locales::long_weekdays(locale),
+ am_pm: locales::am_pm(locale),
+ }
+ }
+ #[cfg(not(feature = "unstable-locales"))]
+ Self {
+ short_months: &[
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ ],
+ long_months: &[
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ ],
+ short_weekdays: &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ long_weekdays: &[
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ ],
+ am_pm: &["AM", "PM"],
+ }
+ }
+}
+
/// Formats single formatting item
#[cfg(any(feature = "alloc", feature = "std", test))]
-pub fn format_item<'a>(
+#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
+pub fn format_item(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>,
- item: &Item<'a>,
+ item: &Item<'_>,
) -> fmt::Result {
let mut result = String::new();
format_inner(&mut result, date, time, off, item, None)?;
@@ -401,54 +500,17 @@ pub fn format_item<'a>(
}
#[cfg(any(feature = "alloc", feature = "std", test))]
-fn format_inner<'a>(
+fn format_inner(
result: &mut String,
date: Option<&NaiveDate>,
time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>,
- item: &Item<'a>,
- _locale: Option<Locale>,
+ item: &Item<'_>,
+ 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"],
- )
- };
+ let locale = Locales::new(locale);
- use core::fmt::Write;
- use div::{div_floor, mod_floor};
+ use num_integer::{div_floor, mod_floor};
match *item {
Item::Literal(s) | Item::Space(s) => result.push_str(s),
@@ -458,12 +520,8 @@ fn format_inner<'a>(
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 week_from_sun = |d: &NaiveDate| d.weeks_from(Weekday::Sun);
+ let week_from_mon = |d: &NaiveDate| d.weeks_from(Weekday::Mon);
let (width, v) = match *spec {
Year => (4, date.map(|d| i64::from(d.year()))),
@@ -501,7 +559,7 @@ fn format_inner<'a>(
};
if let Some(v) = v {
- if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
+ if (spec == &Year || spec == &IsoYear) && !(0..10_000).contains(&v) {
// non-four-digit years require an explicit sign as per ISO 8601
match *pad {
Pad::None => write!(result, "{:+}", v),
@@ -523,60 +581,41 @@ fn format_inner<'a>(
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]);
+ result.push_str(locale.short_months[d.month0() as usize]);
Ok(())
}),
LongMonthName => date.map(|d| {
- result.push_str(long_months[d.month0() as usize]);
+ result.push_str(locale.long_months[d.month0() as usize]);
Ok(())
}),
ShortWeekdayName => date.map(|d| {
- result
- .push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]);
+ result.push_str(
+ locale.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]);
+ result.push_str(
+ locale.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()
- });
+ let ampm = if t.hour12().0 { locale.am_pm[1] } else { locale.am_pm[0] };
+ for char in ampm.chars() {
+ result.extend(char.to_lowercase())
}
Ok(())
}),
UpperAmPm => time.map(|t| {
- result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] });
+ result.push_str(if t.hour12().0 {
+ locale.am_pm[1]
+ } else {
+ locale.am_pm[0]
+ });
Ok(())
}),
Nanosecond => time.map(|t| {
@@ -618,21 +657,23 @@ fn format_inner<'a>(
let nano = t.nanosecond() % 1_000_000_000;
write!(result, "{:09}", nano)
}),
- TimezoneName => off.map(|&(ref name, _)| {
+ TimezoneName => off.map(|(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))
- }
+ TimezoneOffsetColon => off
+ .map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Single)),
+ TimezoneOffsetDoubleColon => off
+ .map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Double)),
+ TimezoneOffsetTripleColon => off
+ .map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Triple)),
+ TimezoneOffsetColonZ => off
+ .map(|&(_, off)| write_local_minus_utc(result, off, true, Colons::Single)),
TimezoneOffset => {
- off.map(|&(_, off)| write_local_minus_utc(result, off, false, false))
+ off.map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::None))
}
TimezoneOffsetZ => {
- off.map(|&(_, off)| write_local_minus_utc(result, off, true, false))
+ off.map(|&(_, off)| write_local_minus_utc(result, off, true, Colons::None))
}
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
panic!("Do not try to write %#z it is undefined")
@@ -641,19 +682,7 @@ fn format_inner<'a>(
// 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))
+ Some(write_rfc2822_inner(result, d, t, off, locale))
} else {
None
}
@@ -662,10 +691,7 @@ fn format_inner<'a>(
// 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))
+ Some(write_rfc3339(result, crate::NaiveDateTime::new(*d, *t), off))
} else {
None
}
@@ -683,9 +709,114 @@ fn format_inner<'a>(
Ok(())
}
+/// 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.
+#[cfg(any(feature = "alloc", feature = "std", test))]
+fn write_local_minus_utc(
+ result: &mut String,
+ off: FixedOffset,
+ allow_zulu: bool,
+ colon_type: Colons,
+) -> fmt::Result {
+ let off = off.local_minus_utc();
+ if allow_zulu && off == 0 {
+ result.push('Z');
+ return Ok(());
+ }
+ let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
+ result.push(sign);
+
+ write_hundreds(result, (off / 3600) as u8)?;
+
+ match colon_type {
+ Colons::None => write_hundreds(result, (off / 60 % 60) as u8),
+ Colons::Single => {
+ result.push(':');
+ write_hundreds(result, (off / 60 % 60) as u8)
+ }
+ Colons::Double => {
+ result.push(':');
+ write_hundreds(result, (off / 60 % 60) as u8)?;
+ result.push(':');
+ write_hundreds(result, (off % 60) as u8)
+ }
+ Colons::Triple => Ok(()),
+ }
+}
+
+/// Writes the date, time and offset to the string. same as `%Y-%m-%dT%H:%M:%S%.f%:z`
+#[cfg(any(feature = "alloc", feature = "std", test))]
+pub(crate) fn write_rfc3339(
+ result: &mut String,
+ dt: crate::NaiveDateTime,
+ off: FixedOffset,
+) -> fmt::Result {
+ // reuse `Debug` impls which already print ISO 8601 format.
+ // this is faster in this way.
+ write!(result, "{:?}", dt)?;
+ write_local_minus_utc(result, off, false, Colons::Single)
+}
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z`
+pub(crate) fn write_rfc2822(
+ result: &mut String,
+ dt: crate::NaiveDateTime,
+ off: FixedOffset,
+) -> fmt::Result {
+ write_rfc2822_inner(result, &dt.date(), &dt.time(), off, Locales::new(None))
+}
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z`
+fn write_rfc2822_inner(
+ result: &mut String,
+ d: &NaiveDate,
+ t: &NaiveTime,
+ off: FixedOffset,
+ locale: Locales,
+) -> fmt::Result {
+ let year = d.year();
+ // RFC2822 is only defined on years 0 through 9999
+ if !(0..=9999).contains(&year) {
+ return Err(fmt::Error);
+ }
+
+ result.push_str(locale.short_weekdays[d.weekday().num_days_from_sunday() as usize]);
+ result.push_str(", ");
+ write_hundreds(result, d.day() as u8)?;
+ result.push(' ');
+ result.push_str(locale.short_months[d.month0() as usize]);
+ result.push(' ');
+ write_hundreds(result, (year / 100) as u8)?;
+ write_hundreds(result, (year % 100) as u8)?;
+ result.push(' ');
+ write_hundreds(result, t.hour() as u8)?;
+ result.push(':');
+ write_hundreds(result, t.minute() as u8)?;
+ result.push(':');
+ let sec = t.second() + t.nanosecond() / 1_000_000_000;
+ write_hundreds(result, sec as u8)?;
+ result.push(' ');
+ write_local_minus_utc(result, off, false, Colons::None)
+}
+
+/// Equivalent to `{:02}` formatting for n < 100.
+pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result {
+ if n >= 100 {
+ return Err(fmt::Error);
+ }
+
+ let tens = b'0' + n / 10;
+ let ones = b'0' + n % 10;
+ w.write_char(tens as char)?;
+ w.write_char(ones as char)
+}
+
/// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`.
#[cfg(any(feature = "alloc", feature = "std", test))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
pub fn format<'a, I, B>(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
@@ -715,6 +846,7 @@ 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))]
+#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[derive(Debug)]
pub struct DelayedFormat<I> {
/// The date view, if any.
@@ -726,6 +858,9 @@ pub struct DelayedFormat<I> {
/// An iterator returning formatting items.
items: I,
/// Locale used for text.
+ // TODO: Only used with the locale feature. We should make this property
+ // only present when the feature is enabled.
+ #[cfg(feature = "unstable-locales")]
locale: Option<Locale>,
}
@@ -733,7 +868,14 @@ pub struct DelayedFormat<I> {
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 }
+ DelayedFormat {
+ date,
+ time,
+ off: None,
+ items,
+ #[cfg(feature = "unstable-locales")]
+ locale: None,
+ }
}
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
@@ -748,27 +890,30 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
{
let name_and_diff = (offset.to_string(), offset.fix());
DelayedFormat {
- date: date,
- time: time,
+ date,
+ time,
off: Some(name_and_diff),
- items: items,
+ items,
+ #[cfg(feature = "unstable-locales")]
locale: None,
}
}
/// Makes a new `DelayedFormat` value out of local date and time and locale.
#[cfg(feature = "unstable-locales")]
+ #[cfg_attr(docsrs, doc(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) }
+ DelayedFormat { date, time, off: None, items, locale: Some(locale) }
}
/// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale.
#[cfg(feature = "unstable-locales")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
pub fn new_with_offset_and_locale<Off>(
date: Option<NaiveDate>,
time: Option<NaiveTime>,
@@ -780,13 +925,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
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),
- }
+ DelayedFormat { date, time, off: Some(name_and_diff), items, locale: Some(locale) }
}
}
@@ -817,26 +956,26 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> fmt::Display for De
///
/// # 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;
@@ -851,6 +990,7 @@ impl FromStr for Weekday {
/// Formats single formatting item
#[cfg(feature = "unstable-locales")]
+#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
pub fn format_item_localized<'a>(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
@@ -867,6 +1007,7 @@ pub fn format_item_localized<'a>(
/// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`.
#[cfg(feature = "unstable-locales")]
+#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
pub fn format_localized<'a, I, B>(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
@@ -890,27 +1031,27 @@ where
///
/// # 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;
diff --git a/vendor/chrono/src/format/parse.rs b/vendor/chrono/src/format/parse.rs
index 2fce8277b..69204d2e9 100644
--- a/vendor/chrono/src/format/parse.rs
+++ b/vendor/chrono/src/format/parse.rs
@@ -14,7 +14,7 @@ use super::scan;
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed};
use super::{ParseError, ParseErrorKind, ParseResult};
use super::{BAD_FORMAT, INVALID, NOT_ENOUGH, OUT_OF_RANGE, TOO_LONG, TOO_SHORT};
-use {DateTime, FixedOffset, Weekday};
+use crate::{DateTime, FixedOffset, Weekday};
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
p.set_weekday(match v {
@@ -53,7 +53,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
// an adapted RFC 2822 syntax from Section 3.3 and 4.3:
//
- // date-time = [ day-of-week "," ] date 1*S time *S
+ // c-char = <any char except '(', ')' and '\\'>
+ // c-escape = "\" <any char>
+ // comment = "(" *(comment / c-char / c-escape) ")" *S
+ // date-time = [ day-of-week "," ] date 1*S time *S *comment
// day-of-week = *S day-name *S
// day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
// date = day month year
@@ -79,9 +82,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
//
// - we do not recognize a folding white space (FWS) or comment (CFWS).
// for our purposes, instead, we accept any sequence of Unicode
- // white space characters (denoted here to `S`). any actual RFC 2822
- // parser is expected to parse FWS and/or CFWS themselves and replace
- // it with a single SP (`%x20`); this is legitimate.
+ // white space characters (denoted here to `S`). For comments, we accept
+ // any text within parentheses while respecting escaped parentheses.
+ // Any actual RFC 2822 parser is expected to parse FWS and/or CFWS themselves
+ // and replace it with a single SP (`%x20`); this is legitimate.
//
// - two-digit year < 50 should be interpreted by adding 2000.
// two-digit year >= 50 or three-digit year should be interpreted
@@ -117,10 +121,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
let mut year = try_consume!(scan::number(s, 2, usize::MAX));
let yearlen = prevlen - s.len();
match (yearlen, year) {
- (2, 0...49) => {
+ (2, 0..=49) => {
year += 2000;
} // 47 -> 2047, 05 -> 2005
- (2, 50...99) => {
+ (2, 50..=99) => {
year += 1900;
} // 79 -> 1979
(3, _) => {
@@ -145,6 +149,11 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
parsed.set_offset(i64::from(offset))?;
}
+ // optional comments
+ while let Ok((s_out, ())) = scan::comment_2822(s) {
+ s = s_out;
+ }
+
Ok((s, ()))
}
@@ -411,7 +420,10 @@ where
try_consume!(scan::timezone_name_skip(s));
}
- &TimezoneOffsetColon | &TimezoneOffset => {
+ &TimezoneOffsetColon
+ | &TimezoneOffsetDoubleColon
+ | &TimezoneOffsetTripleColon
+ | &TimezoneOffset => {
let offset = try_consume!(scan::timezone_offset(
s.trim_left(),
scan::colon_or_space
@@ -455,11 +467,22 @@ where
}
}
+/// Accepts a relaxed form of RFC3339.
+/// A space or a 'T' are acepted as the separator between the date and time
+/// parts. Additional spaces are allowed between each component.
+///
+/// All of these examples are equivalent:
+/// ```
+/// # use chrono::{DateTime, offset::FixedOffset};
+/// "2012-12-12T12:12:12Z".parse::<DateTime<FixedOffset>>();
+/// "2012-12-12 12:12:12Z".parse::<DateTime<FixedOffset>>();
+/// "2012- 12-12T12: 12:12Z".parse::<DateTime<FixedOffset>>();
+/// ```
impl str::FromStr for DateTime<FixedOffset> {
type Err = ParseError;
fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
- const DATE_ITEMS: &'static [Item<'static>] = &[
+ const DATE_ITEMS: &[Item<'static>] = &[
Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(""),
Item::Literal("-"),
@@ -468,7 +491,7 @@ impl str::FromStr for DateTime<FixedOffset> {
Item::Literal("-"),
Item::Numeric(Numeric::Day, Pad::Zero),
];
- const TIME_ITEMS: &'static [Item<'static>] = &[
+ const TIME_ITEMS: &[Item<'static>] = &[
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""),
Item::Literal(":"),
@@ -488,11 +511,11 @@ impl str::FromStr for DateTime<FixedOffset> {
if remainder.starts_with('T') || remainder.starts_with(' ') {
parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?;
} else {
- Err(INVALID)?;
+ return Err(INVALID);
}
}
- Err((_s, e)) => Err(e)?,
- Ok(_) => Err(NOT_ENOUGH)?,
+ Err((_s, e)) => return Err(e),
+ Ok(_) => return Err(NOT_ENOUGH),
};
parsed.to_datetime()
}
@@ -557,7 +580,7 @@ fn test_parse() {
check!(" \t987", [num!(Year)]; year: 987);
check!("5", [num!(Year)]; year: 5);
check!("5\0", [num!(Year)]; TOO_LONG);
- check!("\05", [num!(Year)]; INVALID);
+ check!("\x005", [num!(Year)]; INVALID);
check!("", [num!(Year)]; TOO_SHORT);
check!("12345", [num!(Year), lit!("5")]; year: 1234);
check!("12345", [nums!(Year), lit!("5")]; year: 1234);
@@ -798,14 +821,25 @@ fn test_parse() {
fn test_rfc2822() {
use super::NOT_ENOUGH;
use super::*;
- use offset::FixedOffset;
- use DateTime;
+ use crate::offset::FixedOffset;
+ use crate::DateTime;
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
let testdates = [
("Tue, 20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // normal case
("Fri, 2 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // folding whitespace
("Fri, 02 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // leading zero
+ ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC)", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // trailing comment
+ (
+ r"Tue, 20 Jan 2015 17:35:20 -0800 ( (UTC ) (\( (a)\(( \t ) ) \\( \) ))",
+ Ok("Tue, 20 Jan 2015 17:35:20 -0800"),
+ ), // complex trailing comment
+ (r"Tue, 20 Jan 2015 17:35:20 -0800 (UTC\)", Err(TOO_LONG)), // incorrect comment, not enough closing parentheses
+ (
+ "Tue, 20 Jan 2015 17:35:20 -0800 (UTC)\t \r\n(Anothercomment)",
+ Ok("Tue, 20 Jan 2015 17:35:20 -0800"),
+ ), // multiple comments
+ ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC) ", Err(TOO_LONG)), // trailing whitespace after comment
("20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // no day of week
("20 JAN 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // upper case month
("Tue, 20 Jan 2015 17:35 -0800", Ok("Tue, 20 Jan 2015 17:35:00 -0800")), // no second
@@ -853,12 +887,12 @@ fn test_rfc2822() {
#[cfg(test)]
#[test]
fn parse_rfc850() {
- use {TimeZone, Utc};
+ use crate::{TimeZone, Utc};
- static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT";
+ static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT";
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
- let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37);
+ let dt = Utc.with_ymd_and_hms(1994, 11, 6, 8, 49, 37).unwrap();
// Check that the format is what we expect
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
@@ -869,12 +903,21 @@ fn parse_rfc850() {
// Check that the rest of the weekdays parse correctly (this test originally failed because
// Sunday parsed incorrectly).
let testdates = [
- (Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
- (Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
- (Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
- (Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
- (Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
- (Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
+ (Utc.with_ymd_and_hms(1994, 11, 7, 8, 49, 37).unwrap(), "Monday, 07-Nov-94 08:49:37 GMT"),
+ (Utc.with_ymd_and_hms(1994, 11, 8, 8, 49, 37).unwrap(), "Tuesday, 08-Nov-94 08:49:37 GMT"),
+ (
+ Utc.with_ymd_and_hms(1994, 11, 9, 8, 49, 37).unwrap(),
+ "Wednesday, 09-Nov-94 08:49:37 GMT",
+ ),
+ (
+ Utc.with_ymd_and_hms(1994, 11, 10, 8, 49, 37).unwrap(),
+ "Thursday, 10-Nov-94 08:49:37 GMT",
+ ),
+ (Utc.with_ymd_and_hms(1994, 11, 11, 8, 49, 37).unwrap(), "Friday, 11-Nov-94 08:49:37 GMT"),
+ (
+ Utc.with_ymd_and_hms(1994, 11, 12, 8, 49, 37).unwrap(),
+ "Saturday, 12-Nov-94 08:49:37 GMT",
+ ),
];
for val in &testdates {
@@ -886,8 +929,8 @@ fn parse_rfc850() {
#[test]
fn test_rfc3339() {
use super::*;
- use offset::FixedOffset;
- use DateTime;
+ use crate::offset::FixedOffset;
+ use crate::DateTime;
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
let testdates = [
diff --git a/vendor/chrono/src/format/parsed.rs b/vendor/chrono/src/format/parsed.rs
index b8ed2d90f..6cc29e9d4 100644
--- a/vendor/chrono/src/format/parsed.rs
+++ b/vendor/chrono/src/format/parsed.rs
@@ -4,16 +4,16 @@
//! A collection of parsed date and time items.
//! They can be constructed incrementally while being checked for consistency.
+use num_integer::div_rem;
use num_traits::ToPrimitive;
-use oldtime::Duration as OldDuration;
use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
-use div::div_rem;
-use naive::{NaiveDate, NaiveDateTime, NaiveTime};
-use offset::{FixedOffset, LocalResult, Offset, TimeZone};
-use DateTime;
-use Weekday;
-use {Datelike, Timelike};
+use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
+use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone};
+use crate::oldtime::Duration as OldDuration;
+use crate::DateTime;
+use crate::Weekday;
+use crate::{Datelike, Timelike};
/// Parsed parts of date and time. There are two classes of methods:
///
@@ -22,8 +22,7 @@ use {Datelike, Timelike};
///
/// - `to_*` methods try to make a concrete date and time value out of set fields.
/// It fully checks any remaining out-of-range conditions and inconsistent/impossible fields.
-#[allow(missing_copy_implementations)]
-#[derive(Clone, PartialEq, Debug)]
+#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
pub struct Parsed {
/// Year.
///
@@ -126,34 +125,6 @@ fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<(
}
}
-impl Default for Parsed {
- fn default() -> Parsed {
- Parsed {
- year: None,
- year_div_100: None,
- year_mod_100: None,
- isoyear: None,
- isoyear_div_100: None,
- isoyear_mod_100: None,
- month: None,
- week_from_sun: None,
- week_from_mon: None,
- isoweek: None,
- weekday: None,
- ordinal: None,
- day: None,
- hour_div_12: None,
- hour_mod_12: None,
- minute: None,
- second: None,
- nanosecond: None,
- timestamp: None,
- offset: None,
- _dummy: (),
- }
- }
-}
-
impl Parsed {
/// Returns the initial value of parsed parts.
pub fn new() -> Parsed {
@@ -254,14 +225,14 @@ impl Parsed {
/// (`false` for AM, `true` for PM)
#[inline]
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
- set_if_consistent(&mut self.hour_div_12, if value { 1 } else { 0 })
+ set_if_consistent(&mut self.hour_div_12, u32::from(value))
}
/// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from
/// given hour number in 12-hour clocks.
#[inline]
pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
- if value < 1 || value > 12 {
+ if !(1..=12).contains(&value) {
return Err(OUT_OF_RANGE);
}
set_if_consistent(&mut self.hour_mod_12, value as u32 % 12)
@@ -333,7 +304,7 @@ impl Parsed {
// check if present quotient and/or modulo is consistent to the full year.
// since the presence of those fields means a positive full year,
// we should filter a negative full year first.
- (Some(y), q, r @ Some(0...99)) | (Some(y), q, r @ None) => {
+ (Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => {
if y < 0 {
return Err(OUT_OF_RANGE);
}
@@ -347,7 +318,7 @@ impl Parsed {
// the full year is missing but we have quotient and modulo.
// reconstruct the full year. make sure that the result is always positive.
- (None, Some(q), Some(r @ 0...99)) => {
+ (None, Some(q), Some(r @ 0..=99)) => {
if q < 0 {
return Err(OUT_OF_RANGE);
}
@@ -357,7 +328,7 @@ impl Parsed {
// we only have modulo. try to interpret a modulo as a conventional two-digit year.
// note: we are affected by Rust issue #18060. avoid multiple range patterns.
- (None, None, Some(r @ 0...99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })),
+ (None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })),
// otherwise it is an out-of-bound or insufficient condition.
(None, Some(_), None) => Err(NOT_ENOUGH),
@@ -408,9 +379,8 @@ impl Parsed {
// verify the ordinal and other (non-ISO) week dates.
let verify_ordinal = |date: NaiveDate| {
let ordinal = date.ordinal();
- let weekday = date.weekday();
- let week_from_sun = (ordinal as i32 - weekday.num_days_from_sunday() as i32 + 7) / 7;
- let week_from_mon = (ordinal as i32 - weekday.num_days_from_monday() as i32 + 7) / 7;
+ let week_from_sun = date.weeks_from(Weekday::Sun);
+ let week_from_mon = date.weeks_from(Weekday::Mon);
self.ordinal.unwrap_or(ordinal) == ordinal
&& self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun
&& self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon
@@ -528,32 +498,32 @@ impl Parsed {
/// It is able to handle leap seconds when given second is 60.
pub fn to_naive_time(&self) -> ParseResult<NaiveTime> {
let hour_div_12 = match self.hour_div_12 {
- Some(v @ 0...1) => v,
+ Some(v @ 0..=1) => v,
Some(_) => return Err(OUT_OF_RANGE),
None => return Err(NOT_ENOUGH),
};
let hour_mod_12 = match self.hour_mod_12 {
- Some(v @ 0...11) => v,
+ Some(v @ 0..=11) => v,
Some(_) => return Err(OUT_OF_RANGE),
None => return Err(NOT_ENOUGH),
};
let hour = hour_div_12 * 12 + hour_mod_12;
let minute = match self.minute {
- Some(v @ 0...59) => v,
+ Some(v @ 0..=59) => v,
Some(_) => return Err(OUT_OF_RANGE),
None => return Err(NOT_ENOUGH),
};
// we allow omitting seconds or nanoseconds, but they should be in the range.
let (second, mut nano) = match self.second.unwrap_or(0) {
- v @ 0...59 => (v, 0),
+ v @ 0..=59 => (v, 0),
60 => (59, 1_000_000_000),
_ => return Err(OUT_OF_RANGE),
};
nano += match self.nanosecond {
- Some(v @ 0...999_999_999) if self.second.is_some() => v,
- Some(0...999_999_999) => return Err(NOT_ENOUGH), // second is missing
+ Some(v @ 0..=999_999_999) if self.second.is_some() => v,
+ Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing
Some(_) => return Err(OUT_OF_RANGE),
None => 0,
};
@@ -655,6 +625,12 @@ impl Parsed {
let offset = self.offset.ok_or(NOT_ENOUGH)?;
let datetime = self.to_naive_datetime_with_offset(offset)?;
let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
+
+ // this is used to prevent an overflow when calling FixedOffset::from_local_datetime
+ datetime
+ .checked_sub_signed(OldDuration::seconds(i64::from(offset.local_minus_utc())))
+ .ok_or(OUT_OF_RANGE)?;
+
match offset.from_local_datetime(&datetime) {
LocalResult::None => Err(IMPOSSIBLE),
LocalResult::Single(t) => Ok(t),
@@ -721,10 +697,10 @@ impl Parsed {
mod tests {
use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
use super::Parsed;
- use naive::{NaiveDate, NaiveTime, MAX_DATE, MIN_DATE};
- use offset::{FixedOffset, TimeZone, Utc};
- use Datelike;
- use Weekday::*;
+ use crate::naive::{NaiveDate, NaiveTime};
+ use crate::offset::{FixedOffset, TimeZone, Utc};
+ use crate::Datelike;
+ use crate::Weekday::*;
#[test]
fn test_parsed_set_fields() {
@@ -805,7 +781,7 @@ mod tests {
)
}
- let ymd = |y, m, d| Ok(NaiveDate::from_ymd(y, m, d));
+ let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap());
// ymd: omission of fields
assert_eq!(parse!(), Err(NOT_ENOUGH));
@@ -851,7 +827,7 @@ mod tests {
assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE));
assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1));
assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(OUT_OF_RANGE));
- let max_year = MAX_DATE.year();
+ let max_year = NaiveDate::MAX.year();
assert_eq!(
parse!(year_div_100: max_year / 100,
year_mod_100: max_year % 100, month: 1, day: 1),
@@ -992,8 +968,8 @@ mod tests {
)
}
- let hms = |h, m, s| Ok(NaiveTime::from_hms(h, m, s));
- let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano(h, m, s, n));
+ let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap());
+ let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap());
// omission of fields
assert_eq!(parse!(), Err(NOT_ENOUGH));
@@ -1048,9 +1024,12 @@ mod tests {
($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*))
}
- let ymdhms = |y, m, d, h, n, s| Ok(NaiveDate::from_ymd(y, m, d).and_hms(h, n, s));
- let ymdhmsn =
- |y, m, d, h, n, s, nano| Ok(NaiveDate::from_ymd(y, m, d).and_hms_nano(h, n, s, nano));
+ let ymdhms = |y, m, d, h, n, s| {
+ Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap())
+ };
+ let ymdhmsn = |y, m, d, h, n, s, nano| {
+ Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap())
+ };
// omission of fields
assert_eq!(parse!(), Err(NOT_ENOUGH));
@@ -1104,14 +1083,15 @@ mod tests {
// more timestamps
let max_days_from_year_1970 =
- MAX_DATE.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1));
- let year_0_from_year_1970 =
- NaiveDate::from_ymd(0, 1, 1).signed_duration_since(NaiveDate::from_ymd(1970, 1, 1));
+ NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
+ let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1)
+ .unwrap()
+ .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
let min_days_from_year_1970 =
- MIN_DATE.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1));
+ NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap());
assert_eq!(
parse!(timestamp: min_days_from_year_1970.num_seconds()),
- ymdhms(MIN_DATE.year(), 1, 1, 0, 0, 0)
+ ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0)
);
assert_eq!(
parse!(timestamp: year_0_from_year_1970.num_seconds()),
@@ -1119,7 +1099,7 @@ mod tests {
);
assert_eq!(
parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
- ymdhms(MAX_DATE.year(), 12, 31, 23, 59, 59)
+ ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59)
);
// leap seconds #1: partial fields
@@ -1198,7 +1178,15 @@ mod tests {
}
let ymdhmsn = |y, m, d, h, n, s, nano, off| {
- Ok(FixedOffset::east(off).ymd(y, m, d).and_hms_nano(h, n, s, nano))
+ Ok(FixedOffset::east_opt(off)
+ .unwrap()
+ .from_local_datetime(
+ &NaiveDate::from_ymd_opt(y, m, d)
+ .unwrap()
+ .and_hms_nano_opt(h, n, s, nano)
+ .unwrap(),
+ )
+ .unwrap())
};
assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH));
@@ -1242,7 +1230,14 @@ mod tests {
parse!(Utc;
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
- Ok(Utc.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678))
+ Ok(Utc
+ .from_local_datetime(
+ &NaiveDate::from_ymd_opt(2014, 12, 31)
+ .unwrap()
+ .and_hms_nano_opt(4, 26, 40, 12_345_678)
+ .unwrap()
+ )
+ .unwrap())
);
assert_eq!(
parse!(Utc;
@@ -1251,31 +1246,42 @@ mod tests {
Err(IMPOSSIBLE)
);
assert_eq!(
- parse!(FixedOffset::east(32400);
+ parse!(FixedOffset::east_opt(32400).unwrap();
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
Err(IMPOSSIBLE)
);
assert_eq!(
- parse!(FixedOffset::east(32400);
+ parse!(FixedOffset::east_opt(32400).unwrap();
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
- Ok(FixedOffset::east(32400).ymd(2014, 12, 31).and_hms_nano(13, 26, 40, 12_345_678))
+ Ok(FixedOffset::east_opt(32400)
+ .unwrap()
+ .from_local_datetime(
+ &NaiveDate::from_ymd_opt(2014, 12, 31)
+ .unwrap()
+ .and_hms_nano_opt(13, 26, 40, 12_345_678)
+ .unwrap()
+ )
+ .unwrap())
);
// single result from timestamp
assert_eq!(
parse!(Utc; timestamp: 1_420_000_000, offset: 0),
- Ok(Utc.ymd(2014, 12, 31).and_hms(4, 26, 40))
+ Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap())
);
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE));
assert_eq!(
- parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0),
+ parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0),
Err(IMPOSSIBLE)
);
assert_eq!(
- parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 32400),
- Ok(FixedOffset::east(32400).ymd(2014, 12, 31).and_hms(13, 26, 40))
+ parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400),
+ Ok(FixedOffset::east_opt(32400)
+ .unwrap()
+ .with_ymd_and_hms(2014, 12, 31, 13, 26, 40)
+ .unwrap())
);
// TODO test with a variable time zone (for None and Ambiguous cases)
diff --git a/vendor/chrono/src/format/scan.rs b/vendor/chrono/src/format/scan.rs
index 0efb1ee3d..263fec556 100644
--- a/vendor/chrono/src/format/scan.rs
+++ b/vendor/chrono/src/format/scan.rs
@@ -8,13 +8,13 @@
#![allow(deprecated)]
use super::{ParseResult, INVALID, OUT_OF_RANGE, TOO_SHORT};
-use Weekday;
+use crate::Weekday;
/// Returns true when two slices are equal case-insensitively (in ASCII).
/// Assumes that the `pattern` is already converted to lower case.
fn equals(s: &str, pattern: &str) -> bool {
let mut xs = s.as_bytes().iter().map(|&c| match c {
- b'A'...b'Z' => c + 32,
+ b'A'..=b'Z' => c + 32,
_ => c,
});
let mut ys = pattern.as_bytes().iter().cloned();
@@ -34,7 +34,7 @@ fn equals(s: &str, pattern: &str) -> bool {
/// More than `max` digits are consumed up to the first `max` digits.
/// Any number that does not fit in `i64` is an error.
#[inline]
-pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
+pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
assert!(min <= max);
// We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
@@ -48,7 +48,7 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
let mut n = 0i64;
for (i, c) in bytes.iter().take(max).cloned().enumerate() {
// cloned() = copied()
- if c < b'0' || b'9' < c {
+ if !(b'0'..=b'9').contains(&c) {
if i < min {
return Err(INVALID);
} else {
@@ -62,12 +62,12 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
};
}
- Ok((&s[::core::cmp::min(max, bytes.len())..], n))
+ Ok((&s[core::cmp::min(max, bytes.len())..], n))
}
/// Tries to consume at least one digits as a fractional second.
/// Returns the number of whole nanoseconds (0--999,999,999).
-pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
+pub(super) fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
// record the number of digits consumed for later scaling.
let origlen = s.len();
let (s, v) = number(s, 1, 9)?;
@@ -79,14 +79,14 @@ pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?;
// if there are more than 9 digits, skip next digits.
- let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9');
+ let s = s.trim_left_matches(|c: char| ('0'..='9').contains(&c));
Ok((s, v))
}
/// Tries to consume a fixed number of digits as a fractional second.
/// Returns the number of whole nanoseconds (0--999,999,999).
-pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
+pub(super) fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
// record the number of digits consumed for later scaling.
let (s, v) = number(s, digits, digits)?;
@@ -99,7 +99,7 @@ pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
}
/// Tries to parse the month index (0 through 11) with the first three ASCII letters.
-pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
+pub(super) fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
if s.len() < 3 {
return Err(TOO_SHORT);
}
@@ -123,7 +123,7 @@ pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
}
/// Tries to parse the weekday with the first three ASCII letters.
-pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
+pub(super) fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
if s.len() < 3 {
return Err(TOO_SHORT);
}
@@ -143,9 +143,9 @@ pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
/// Tries to parse the month index (0 through 11) with short or long month names.
/// It prefers long month names to short month names when both are possible.
-pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
+pub(super) fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
// lowercased month names, minus first three chars
- static LONG_MONTH_SUFFIXES: [&'static str; 12] =
+ static LONG_MONTH_SUFFIXES: [&str; 12] =
["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];
let (mut s, month0) = short_month0(s)?;
@@ -161,9 +161,9 @@ pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
/// Tries to parse the weekday with short or long weekday names.
/// It prefers long weekday names to short weekday names when both are possible.
-pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
+pub(super) fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
// lowercased weekday names, minus first three chars
- static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] =
+ static LONG_WEEKDAY_SUFFIXES: [&str; 7] =
["day", "sday", "nesday", "rsday", "day", "urday", "day"];
let (mut s, weekday) = short_weekday(s)?;
@@ -178,7 +178,7 @@ pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
}
/// Tries to consume exactly one given character.
-pub fn char(s: &str, c1: u8) -> ParseResult<&str> {
+pub(super) fn char(s: &str, c1: u8) -> ParseResult<&str> {
match s.as_bytes().first() {
Some(&c) if c == c1 => Ok(&s[1..]),
Some(_) => Err(INVALID),
@@ -187,7 +187,7 @@ pub fn char(s: &str, c1: u8) -> ParseResult<&str> {
}
/// Tries to consume one or more whitespace.
-pub fn space(s: &str) -> ParseResult<&str> {
+pub(super) fn space(s: &str) -> ParseResult<&str> {
let s_ = s.trim_left();
if s_.len() < s.len() {
Ok(s_)
@@ -199,7 +199,7 @@ pub fn space(s: &str) -> ParseResult<&str> {
}
/// Consumes any number (including zero) of colon or spaces.
-pub fn colon_or_space(s: &str) -> ParseResult<&str> {
+pub(super) fn colon_or_space(s: &str) -> ParseResult<&str> {
Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace()))
}
@@ -207,7 +207,7 @@ pub fn colon_or_space(s: &str) -> ParseResult<&str> {
///
/// The additional `colon` may be used to parse a mandatory or optional `:`
/// between hours and minutes, and should return either a new suffix or `Err` when parsing fails.
-pub fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)>
+pub(super) fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)>
where
F: FnMut(&str) -> ParseResult<&str>,
{
@@ -240,7 +240,7 @@ where
// hours (00--99)
let hours = match digits(s)? {
- (h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
+ (h1 @ b'0'..=b'9', h2 @ b'0'..=b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
_ => return Err(INVALID),
};
s = &s[2..];
@@ -252,8 +252,8 @@ where
// if the next two items are digits then we have to add minutes
let minutes = if let Ok(ds) = digits(s) {
match ds {
- (m1 @ b'0'...b'5', m2 @ b'0'...b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
- (b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE),
+ (m1 @ b'0'..=b'5', m2 @ b'0'..=b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
+ (b'6'..=b'9', b'0'..=b'9') => return Err(OUT_OF_RANGE),
_ => return Err(INVALID),
}
} else if allow_missing_minutes {
@@ -272,7 +272,7 @@ where
}
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`.
-pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
+pub(super) fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
where
F: FnMut(&str) -> ParseResult<&str>,
{
@@ -296,7 +296,7 @@ where
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as
/// `+00:00`, and allows missing minutes entirely.
-pub fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
+pub(super) fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
where
F: FnMut(&str) -> ParseResult<&str>,
{
@@ -308,16 +308,16 @@ where
/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones.
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
-pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
+pub(super) fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
// tries to parse legacy time zone names
let upto = s
.as_bytes()
.iter()
.position(|&c| match c {
- b'a'...b'z' | b'A'...b'Z' => false,
+ b'a'..=b'z' | b'A'..=b'Z' => false,
_ => true,
})
- .unwrap_or_else(|| s.len());
+ .unwrap_or(s.len());
if upto > 0 {
let name = &s[..upto];
let s = &s[upto..];
@@ -343,8 +343,73 @@ pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
}
}
-/// Tries to consume everyting until next whitespace-like symbol.
+/// Tries to consume everything until next whitespace-like symbol.
/// Does not provide any offset information from the consumed data.
-pub fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> {
+pub(super) fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> {
Ok((s.trim_left_matches(|c: char| !c.is_whitespace()), ()))
}
+
+/// Tries to consume an RFC2822 comment including preceding ` `.
+///
+/// Returns the remaining string after the closing parenthesis.
+pub(super) fn comment_2822(s: &str) -> ParseResult<(&str, ())> {
+ use CommentState::*;
+
+ let s = s.trim_start();
+
+ let mut state = Start;
+ for (i, c) in s.bytes().enumerate() {
+ state = match (state, c) {
+ (Start, b'(') => Next(1),
+ (Next(1), b')') => return Ok((&s[i + 1..], ())),
+ (Next(depth), b'\\') => Escape(depth),
+ (Next(depth), b'(') => Next(depth + 1),
+ (Next(depth), b')') => Next(depth - 1),
+ (Next(depth), _) | (Escape(depth), _) => Next(depth),
+ _ => return Err(INVALID),
+ };
+ }
+
+ Err(TOO_SHORT)
+}
+
+enum CommentState {
+ Start,
+ Next(usize),
+ Escape(usize),
+}
+
+#[cfg(test)]
+#[test]
+fn test_rfc2822_comments() {
+ let testdata = [
+ ("", Err(TOO_SHORT)),
+ (" ", Err(TOO_SHORT)),
+ ("x", Err(INVALID)),
+ ("(", Err(TOO_SHORT)),
+ ("()", Ok("")),
+ (" \r\n\t()", Ok("")),
+ ("() ", Ok(" ")),
+ ("()z", Ok("z")),
+ ("(x)", Ok("")),
+ ("(())", Ok("")),
+ ("((()))", Ok("")),
+ ("(x(x(x)x)x)", Ok("")),
+ ("( x ( x ( x ) x ) x )", Ok("")),
+ (r"(\)", Err(TOO_SHORT)),
+ (r"(\()", Ok("")),
+ (r"(\))", Ok("")),
+ (r"(\\)", Ok("")),
+ ("(()())", Ok("")),
+ ("( x ( x ) x ( x ) x )", Ok("")),
+ ];
+
+ for (test_in, expected) in testdata.iter() {
+ let actual = comment_2822(test_in).map(|(s, _)| s);
+ assert_eq!(
+ *expected, actual,
+ "{:?} expected to produce {:?}, but produced {:?}.",
+ test_in, expected, actual
+ );
+ }
+}
diff --git a/vendor/chrono/src/format/strftime.rs b/vendor/chrono/src/format/strftime.rs
index 93820a232..dcaabe49f 100644
--- a/vendor/chrono/src/format/strftime.rs
+++ b/vendor/chrono/src/format/strftime.rs
@@ -11,9 +11,9 @@ The following specifiers are available both to formatting and parsing.
| Spec. | Example | Description |
|-------|----------|----------------------------------------------------------------------------|
| | | **DATE SPECIFIERS:** |
-| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. [^1] |
-| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^2] |
-| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^2] |
+| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. chrono supports years from -262144 to 262143. Note: years before 1 BCE or after 9999 CE, require an initial sign (+/-).|
+| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^1] |
+| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^1] |
| | | |
| `%m` | `07` | Month number (01--12), zero-padded to 2 digits. |
| `%b` | `Jul` | Abbreviated month name. Always 3 letters. |
@@ -28,12 +28,12 @@ The following specifiers are available both to formatting and parsing.
| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. |
| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) |
| | | |
-| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^3] |
+| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^2] |
| `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.|
| | | |
-| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^4] |
-| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^4] |
-| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^4] |
+| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^3] |
+| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^3] |
+| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^3] |
| | | |
| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. |
| | | |
@@ -52,15 +52,15 @@ The following specifiers are available both to formatting and parsing.
| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. |
| | | |
| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. |
-| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^5] |
-| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^8] |
-| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^8] |
-| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^8] |
-| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^8] |
-| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^8] |
-| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^8] |
-| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^8] |
-| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^8] |
+| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^4] |
+| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^7] |
+| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^7] |
+| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^7] |
+| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^7] |
+| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^7] |
+| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^7] |
+| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^7] |
+| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^7] |
| | | |
| `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. |
| `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. |
@@ -68,16 +68,18 @@ The following specifiers are available both to formatting and parsing.
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. |
| | | |
| | | **TIME ZONE SPECIFIERS:** |
-| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^9] |
+| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^8] |
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
| `%:z` | `+09:30` | Same as `%z` but with a colon. |
+|`%::z`|`+09:30:00`| Offset from the local time to UTC with seconds. |
+|`%:::z`| `+09` | Offset from the local time to UTC without minutes. |
| `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. |
| | | |
| | | **DATE & TIME SPECIFIERS:** |
|`%c`|`Sun Jul 8 00:34:60 2001`|Locale's date and time (e.g., Thu Mar 3 23:05:25 2005). |
-| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^6] |
+| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^5] |
| | | |
-| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^7]|
+| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^6]|
| | | |
| | | **SPECIAL SPECIFIERS:** |
| `%t` | | Literal tab (`\t`). |
@@ -95,38 +97,42 @@ Modifier | Description
Notes:
-[^1]: `%Y`:
- Negative years are allowed in formatting but not in parsing.
-
-[^2]: `%C`, `%y`:
+[^1]: `%C`, `%y`:
This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively.
-[^3]: `%U`:
+[^2]: `%U`:
Week 1 starts with the first Sunday in that year.
It is possible to have week 0 for days before the first Sunday.
-[^4]: `%G`, `%g`, `%V`:
+[^3]: `%G`, `%g`, `%V`:
Week 1 is the first week with at least 4 days in that year.
Week 0 does not exist, so this should be used with `%G` or `%g`.
-[^5]: `%S`:
+[^4]: `%S`:
It accounts for leap seconds, so `60` is possible.
-[^6]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional
+[^5]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional
digits for seconds and colons in the time zone offset.
<br>
<br>
+ This format also supports having a `Z` or `UTC` in place of `%:z`. They
+ are equivalent to `+00:00`.
+ <br>
+ <br>
+ Note that all `T`, `Z`, and `UTC` are parsed case-insensitively.
+ <br>
+ <br>
The typical `strftime` implementations have different (and locale-dependent)
formats for this specifier. While Chrono's format for `%+` is far more
stable, it is best to avoid this specifier if you want to control the exact
output.
-[^7]: `%s`:
+[^6]: `%s`:
This is not padded and can be negative.
For the purpose of Chrono, it only accounts for non-leap seconds
so it slightly differs from ISO C `strftime` behavior.
-[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
+[^7]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
<br>
The default `%f` is right-aligned and always zero-padded to 9 digits
for the compatibility with glibc and others,
@@ -157,7 +163,7 @@ Notes:
and parsing `07`, `070000` etc. will yield the same.
Note that they can read nothing if the fractional part is zero.
-[^9]: `%Z`:
+[^8]: `%Z`:
Offset will not be populated from the parsed data, nor will it be validated.
Timezone is completely ignored. Similar to the glibc `strptime` treatment of
this format code.
@@ -169,6 +175,12 @@ Notes:
*/
#[cfg(feature = "unstable-locales")]
+extern crate alloc;
+
+#[cfg(feature = "unstable-locales")]
+use alloc::vec::Vec;
+
+#[cfg(feature = "unstable-locales")]
use super::{locales, Locale};
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad};
@@ -177,9 +189,9 @@ type Fmt<'a> = Vec<Item<'a>>;
#[cfg(not(feature = "unstable-locales"))]
type Fmt<'a> = &'static [Item<'static>];
-static D_FMT: &'static [Item<'static>] =
+static D_FMT: &[Item<'static>] =
&[num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)];
-static D_T_FMT: &'static [Item<'static>] = &[
+static D_T_FMT: &[Item<'static>] = &[
fix!(ShortWeekdayName),
sp!(" "),
fix!(ShortMonthName),
@@ -194,8 +206,7 @@ static D_T_FMT: &'static [Item<'static>] = &[
sp!(" "),
num0!(Year),
];
-static T_FMT: &'static [Item<'static>] =
- &[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)];
+static T_FMT: &[Item<'static>] = &[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)];
/// Parsing iterator for `strftime`-like format strings.
#[derive(Clone, Debug)]
@@ -222,23 +233,18 @@ impl<'a> StrftimeItems<'a> {
/// Creates a new parsing iterator from the `strftime`-like format string.
#[cfg(feature = "unstable-locales")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
pub fn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> {
let d_fmt = StrftimeItems::new(locales::d_fmt(locale)).collect();
let d_t_fmt = StrftimeItems::new(locales::d_t_fmt(locale)).collect();
let t_fmt = StrftimeItems::new(locales::t_fmt(locale)).collect();
- StrftimeItems {
- remainder: s,
- recons: Vec::new(),
- d_fmt: d_fmt,
- d_t_fmt: d_t_fmt,
- t_fmt: t_fmt,
- }
+ StrftimeItems { remainder: s, recons: Vec::new(), d_fmt, d_t_fmt, t_fmt }
}
#[cfg(not(feature = "unstable-locales"))]
fn with_remainer(s: &'a str) -> StrftimeItems<'a> {
- static FMT_NONE: &'static [Item<'static>; 0] = &[];
+ static FMT_NONE: &[Item<'static>; 0] = &[];
StrftimeItems {
remainder: s,
@@ -261,7 +267,7 @@ impl<'a> StrftimeItems<'a> {
}
}
-const HAVE_ALTERNATES: &'static str = "z";
+const HAVE_ALTERNATES: &str = "z";
impl<'a> Iterator for StrftimeItems<'a> {
type Item = Item<'a>;
@@ -407,10 +413,20 @@ impl<'a> Iterator for StrftimeItems<'a> {
}
}
'+' => fix!(RFC3339),
- ':' => match next!() {
- 'z' => fix!(TimezoneOffsetColon),
- _ => Item::Error,
- },
+ ':' => {
+ if self.remainder.starts_with("::z") {
+ self.remainder = &self.remainder[3..];
+ fix!(TimezoneOffsetTripleColon)
+ } else if self.remainder.starts_with(":z") {
+ self.remainder = &self.remainder[2..];
+ fix!(TimezoneOffsetDoubleColon)
+ } else if self.remainder.starts_with('z') {
+ self.remainder = &self.remainder[1..];
+ fix!(TimezoneOffsetColon)
+ } else {
+ Item::Error
+ }
+ }
'.' => match next!() {
'3' => match next!() {
'f' => fix!(Nanosecond3),
@@ -462,7 +478,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
let nextspec = self
.remainder
.find(|c: char| !c.is_whitespace())
- .unwrap_or_else(|| self.remainder.len());
+ .unwrap_or(self.remainder.len());
assert!(nextspec > 0);
let item = sp!(&self.remainder[..nextspec]);
self.remainder = &self.remainder[nextspec..];
@@ -474,7 +490,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
let nextspec = self
.remainder
.find(|c: char| c.is_whitespace() || c == '%')
- .unwrap_or_else(|| self.remainder.len());
+ .unwrap_or(self.remainder.len());
assert!(nextspec > 0);
let item = lit!(&self.remainder[..nextspec]);
self.remainder = &self.remainder[nextspec..];
@@ -487,11 +503,11 @@ impl<'a> Iterator for StrftimeItems<'a> {
#[cfg(test)]
#[test]
fn test_strftime_items() {
- fn parse_and_collect<'a>(s: &'a str) -> Vec<Item<'a>> {
+ fn parse_and_collect(s: &str) -> Vec<Item<'_>> {
// map any error into `[Item::Error]`. useful for easy testing.
let items = StrftimeItems::new(s);
let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) });
- items.collect::<Option<Vec<_>>>().unwrap_or(vec![Item::Error])
+ items.collect::<Option<Vec<_>>>().unwrap_or_else(|| vec![Item::Error])
}
assert_eq!(parse_and_collect(""), []);
@@ -540,9 +556,18 @@ fn test_strftime_items() {
#[cfg(test)]
#[test]
fn test_strftime_docs() {
- use {FixedOffset, TimeZone, Timelike};
-
- let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708);
+ use crate::NaiveDate;
+ use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc};
+
+ let dt = FixedOffset::east_opt(34200)
+ .unwrap()
+ .from_local_datetime(
+ &NaiveDate::from_ymd_opt(2001, 7, 8)
+ .unwrap()
+ .and_hms_nano_opt(0, 34, 59, 1_026_490_708)
+ .unwrap(),
+ )
+ .unwrap();
// date specifiers
assert_eq!(dt.format("%Y").to_string(), "2001");
@@ -559,7 +584,7 @@ fn test_strftime_docs() {
assert_eq!(dt.format("%A").to_string(), "Sunday");
assert_eq!(dt.format("%w").to_string(), "0");
assert_eq!(dt.format("%u").to_string(), "7");
- assert_eq!(dt.format("%U").to_string(), "28");
+ assert_eq!(dt.format("%U").to_string(), "27");
assert_eq!(dt.format("%W").to_string(), "27");
assert_eq!(dt.format("%G").to_string(), "2001");
assert_eq!(dt.format("%g").to_string(), "01");
@@ -599,10 +624,30 @@ fn test_strftime_docs() {
//assert_eq!(dt.format("%Z").to_string(), "ACST");
assert_eq!(dt.format("%z").to_string(), "+0930");
assert_eq!(dt.format("%:z").to_string(), "+09:30");
+ assert_eq!(dt.format("%::z").to_string(), "+09:30:00");
+ assert_eq!(dt.format("%:::z").to_string(), "+09");
// date & time specifiers
assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001");
assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30");
+
+ assert_eq!(
+ dt.with_timezone(&Utc).format("%+").to_string(),
+ "2001-07-07T15:04:60.026490708+00:00"
+ );
+ assert_eq!(
+ dt.with_timezone(&Utc),
+ DateTime::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+").unwrap()
+ );
+ assert_eq!(
+ dt.with_timezone(&Utc),
+ DateTime::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+").unwrap()
+ );
+ assert_eq!(
+ dt.with_timezone(&Utc),
+ DateTime::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap()
+ );
+
assert_eq!(
dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(),
"2001-07-08T00:34:60.026490+09:30"
@@ -618,9 +663,14 @@ fn test_strftime_docs() {
#[cfg(feature = "unstable-locales")]
#[test]
fn test_strftime_docs_localized() {
- use {FixedOffset, TimeZone};
+ use crate::{FixedOffset, NaiveDate, TimeZone};
- let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708);
+ let dt = FixedOffset::east_opt(34200).unwrap().ymd_opt(2001, 7, 8).unwrap().and_hms_nano(
+ 0,
+ 34,
+ 59,
+ 1_026_490_708,
+ );
// date specifiers
assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui");
@@ -646,4 +696,17 @@ fn test_strftime_docs_localized() {
dt.format_localized("%c", Locale::fr_BE).to_string(),
"dim 08 jui 2001 00:34:60 +09:30"
);
+
+ let nd = NaiveDate::from_ymd_opt(2001, 7, 8).unwrap();
+
+ // date specifiers
+ assert_eq!(nd.format_localized("%b", Locale::de_DE).to_string(), "Jul");
+ assert_eq!(nd.format_localized("%B", Locale::de_DE).to_string(), "Juli");
+ assert_eq!(nd.format_localized("%h", Locale::de_DE).to_string(), "Jul");
+ assert_eq!(nd.format_localized("%a", Locale::de_DE).to_string(), "So");
+ assert_eq!(nd.format_localized("%A", Locale::de_DE).to_string(), "Sonntag");
+ assert_eq!(nd.format_localized("%D", Locale::de_DE).to_string(), "07/08/01");
+ assert_eq!(nd.format_localized("%x", Locale::de_DE).to_string(), "08.07.2001");
+ assert_eq!(nd.format_localized("%F", Locale::de_DE).to_string(), "2001-07-08");
+ assert_eq!(nd.format_localized("%v", Locale::de_DE).to_string(), " 8-Jul-2001");
}