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
|
||||
|
||||
// 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.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::parse::parse;
|
||||
|
||||
/// An unhabitated type used for `InternalNumeric` and `InternalFixed` below.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
enum Void {}
|
||||
|
||||
/// Padding characters for numeric items.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Pad {
|
||||
|
@ -41,7 +45,7 @@ pub enum Pad {
|
|||
/// It also trims the preceding whitespaces if any.
|
||||
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
||||
/// parsed with the same formatting items.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Numeric {
|
||||
/// Full Gregorian year (FW=4, PW=∞).
|
||||
/// 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=∞).
|
||||
/// For formatting, it assumes UTC upon the absence of time zone offset.
|
||||
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.
|
||||
///
|
||||
/// They have their own rules of formatting and parsing.
|
||||
/// 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 {
|
||||
/// Abbreviated month names.
|
||||
///
|
||||
|
@ -157,15 +179,37 @@ pub enum Fixed {
|
|||
RFC2822,
|
||||
/// RFC 3339 & ISO 8601 date and time syntax.
|
||||
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.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Item<'a> {
|
||||
/// A literally printed and parsed text.
|
||||
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.
|
||||
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;
|
||||
/// the parser simply ignores any padded whitespace and zeroes.
|
||||
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 {
|
||||
match item {
|
||||
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) => {
|
||||
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()),
|
||||
(_, _, _) => None
|
||||
}),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
if let Some(v) = v {
|
||||
|
@ -420,6 +468,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
|||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
match ret {
|
||||
|
|
|
@ -218,7 +218,13 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
|||
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();
|
||||
}
|
||||
|
||||
|
@ -247,6 +253,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
|||
Second => (2, false, Parsed::set_second),
|
||||
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||
|
||||
// for the future expansion
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
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)),
|
||||
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>> {
|
||||
// we have some reconstructed items to return
|
||||
if !self.recons.is_empty() {
|
||||
let item = self.recons[0];
|
||||
let item = self.recons[0].clone();
|
||||
self.recons = &self.recons[1..];
|
||||
return Some(item);
|
||||
}
|
||||
|
@ -292,8 +292,8 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
|||
// adjust `item` if we have any padding modifier
|
||||
if let Some(new_pad) = pad_override {
|
||||
match item {
|
||||
Item::Numeric(kind, _pad) if self.recons.is_empty() =>
|
||||
Some(Item::Numeric(kind, new_pad)),
|
||||
Item::Numeric(ref kind, _pad) if self.recons.is_empty() =>
|
||||
Some(Item::Numeric(kind.clone(), new_pad)),
|
||||
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
|
||||
}
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue