Minor additions to formatting items.
- Formatting item types are no longer `Copy`. - `Numeric` and `Fixed` items now have `Internal` variants reserved for the future expansion. It had been hard to expand the items without totally breaking the backward compatibility (as per the API evolution guideline of RFC 1105). - `Item::Owned{Literal,Space}` for the owned variant of `Item::{Literal,Space}` has been added. Closes #76.
This commit is contained in:
parent
7ea1ce5080
commit
0ac41c70b1
|
@ -185,7 +185,7 @@ assert_eq!(dt.ordinal(), 332); // the day of year
|
||||||
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
||||||
|
|
||||||
// time zone accessor and manipulation
|
// time zone accessor and manipulation
|
||||||
assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
|
assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
|
||||||
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
||||||
assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,10 @@ pub use self::strftime::StrftimeItems;
|
||||||
pub use self::parsed::Parsed;
|
pub use self::parsed::Parsed;
|
||||||
pub use self::parse::parse;
|
pub use self::parse::parse;
|
||||||
|
|
||||||
|
/// An unhabitated type used for `InternalNumeric` and `InternalFixed` below.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
/// Padding characters for numeric items.
|
/// Padding characters for numeric items.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Pad {
|
pub enum Pad {
|
||||||
|
@ -41,7 +45,7 @@ pub enum Pad {
|
||||||
/// It also trims the preceding whitespaces if any.
|
/// It also trims the preceding whitespaces if any.
|
||||||
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
||||||
/// parsed with the same formatting items.
|
/// parsed with the same formatting items.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Numeric {
|
pub enum Numeric {
|
||||||
/// Full Gregorian year (FW=4, PW=∞).
|
/// 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.
|
||||||
|
@ -88,13 +92,31 @@ pub enum Numeric {
|
||||||
/// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
|
/// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
|
||||||
/// For formatting, it assumes UTC upon the absence of time zone offset.
|
/// For formatting, it assumes UTC upon the absence of time zone offset.
|
||||||
Timestamp,
|
Timestamp,
|
||||||
|
|
||||||
|
/// Internal uses only.
|
||||||
|
///
|
||||||
|
/// This item exists so that one can add additional internal-only formatting
|
||||||
|
/// without breaking major compatibility (as enum variants cannot be selectively private).
|
||||||
|
Internal(InternalNumeric),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An opaque type representing numeric item types for internal uses only.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct InternalNumeric {
|
||||||
|
_dummy: Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for InternalNumeric {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "<InternalNumeric>")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fixed-format item types.
|
/// Fixed-format item types.
|
||||||
///
|
///
|
||||||
/// They have their own rules of formatting and parsing.
|
/// They have their own rules of formatting and parsing.
|
||||||
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
|
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Fixed {
|
pub enum Fixed {
|
||||||
/// Abbreviated month names.
|
/// Abbreviated month names.
|
||||||
///
|
///
|
||||||
|
@ -157,15 +179,37 @@ pub enum Fixed {
|
||||||
RFC2822,
|
RFC2822,
|
||||||
/// RFC 3339 & ISO 8601 date and time syntax.
|
/// RFC 3339 & ISO 8601 date and time syntax.
|
||||||
RFC3339,
|
RFC3339,
|
||||||
|
|
||||||
|
/// Internal uses only.
|
||||||
|
///
|
||||||
|
/// This item exists so that one can add additional internal-only formatting
|
||||||
|
/// without breaking major compatibility (as enum variants cannot be selectively private).
|
||||||
|
Internal(InternalFixed),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An opaque type representing fixed-format item types for internal uses only.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct InternalFixed {
|
||||||
|
_dummy: Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for InternalFixed {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "<InternalFixed>")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single formatting item. This is used for both formatting and parsing.
|
/// A single formatting item. This is used for both formatting and parsing.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Item<'a> {
|
pub enum Item<'a> {
|
||||||
/// A literally printed and parsed text.
|
/// A literally printed and parsed text.
|
||||||
Literal(&'a str),
|
Literal(&'a str),
|
||||||
|
/// Same to `Literal` but with the string owned by the item.
|
||||||
|
OwnedLiteral(Box<str>),
|
||||||
/// Whitespace. Prints literally but reads zero or more whitespace.
|
/// Whitespace. Prints literally but reads zero or more whitespace.
|
||||||
Space(&'a str),
|
Space(&'a str),
|
||||||
|
/// Same to `Space` but with the string owned by the item.
|
||||||
|
OwnedSpace(Box<str>),
|
||||||
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
||||||
/// the parser simply ignores any padded whitespace and zeroes.
|
/// the parser simply ignores any padded whitespace and zeroes.
|
||||||
Numeric(Numeric, Pad),
|
Numeric(Numeric, Pad),
|
||||||
|
@ -268,6 +312,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
for item in items {
|
for item in items {
|
||||||
match item {
|
match item {
|
||||||
Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)),
|
Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)),
|
||||||
|
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => try!(write!(w, "{}", s)),
|
||||||
|
|
||||||
Item::Numeric(spec, pad) => {
|
Item::Numeric(spec, pad) => {
|
||||||
use self::Numeric::*;
|
use self::Numeric::*;
|
||||||
|
@ -305,6 +350,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
Some((d.and_time(*t) - off).timestamp()),
|
Some((d.and_time(*t) - off).timestamp()),
|
||||||
(_, _, _) => None
|
(_, _, _) => None
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(v) = v {
|
if let Some(v) = v {
|
||||||
|
@ -420,6 +468,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
match ret {
|
match ret {
|
||||||
|
|
|
@ -218,7 +218,13 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
||||||
s = &s[prefix.len()..];
|
s = &s[prefix.len()..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::Space(_) => {
|
Item::OwnedLiteral(ref prefix) => {
|
||||||
|
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||||
|
if !s.starts_with(&prefix[..]) { return Err(INVALID); }
|
||||||
|
s = &s[prefix.len()..];
|
||||||
|
}
|
||||||
|
|
||||||
|
Item::Space(_) | Item::OwnedSpace(_) => {
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,6 +253,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
||||||
Second => (2, false, Parsed::set_second),
|
Second => (2, false, Parsed::set_second),
|
||||||
Nanosecond => (9, false, Parsed::set_nanosecond),
|
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||||
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
|
@ -324,6 +333,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
||||||
|
|
||||||
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||||
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
||||||
fn next(&mut self) -> Option<Item<'a>> {
|
fn next(&mut self) -> Option<Item<'a>> {
|
||||||
// we have some reconstructed items to return
|
// we have some reconstructed items to return
|
||||||
if !self.recons.is_empty() {
|
if !self.recons.is_empty() {
|
||||||
let item = self.recons[0];
|
let item = self.recons[0].clone();
|
||||||
self.recons = &self.recons[1..];
|
self.recons = &self.recons[1..];
|
||||||
return Some(item);
|
return Some(item);
|
||||||
}
|
}
|
||||||
|
@ -292,8 +292,8 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
||||||
// adjust `item` if we have any padding modifier
|
// adjust `item` if we have any padding modifier
|
||||||
if let Some(new_pad) = pad_override {
|
if let Some(new_pad) = pad_override {
|
||||||
match item {
|
match item {
|
||||||
Item::Numeric(kind, _pad) if self.recons.is_empty() =>
|
Item::Numeric(ref kind, _pad) if self.recons.is_empty() =>
|
||||||
Some(Item::Numeric(kind, new_pad)),
|
Some(Item::Numeric(kind.clone(), new_pad)),
|
||||||
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
|
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue