Skip to content

Commit 77e2770

Browse files
authored
Merge pull request #320 from SamokhinIlya/DelayFormatAlign
DelayFormat now works with alignment and width
2 parents d8c68f6 + 5b0818e commit 77e2770

File tree

2 files changed

+127
-54
lines changed

2 files changed

+127
-54
lines changed

src/datetime.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,4 +1721,34 @@ mod tests {
17211721
assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH);
17221722
assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH);
17231723
}
1724+
1725+
#[test]
1726+
fn test_datetime_format_alignment() {
1727+
let datetime = Utc.ymd(2007, 01, 02);
1728+
1729+
// Item::Literal
1730+
let percent = datetime.format("%%");
1731+
assert_eq!(" %", format!("{:>3}", percent));
1732+
assert_eq!("% ", format!("{:<3}", percent));
1733+
assert_eq!(" % ", format!("{:^3}", percent));
1734+
1735+
// Item::Numeric
1736+
let year = datetime.format("%Y");
1737+
assert_eq!(" 2007", format!("{:>6}", year));
1738+
assert_eq!("2007 ", format!("{:<6}", year));
1739+
assert_eq!(" 2007 ", format!("{:^6}", year));
1740+
1741+
// Item::Fixed
1742+
let tz = datetime.format("%Z");
1743+
assert_eq!(" UTC", format!("{:>5}", tz));
1744+
assert_eq!("UTC ", format!("{:<5}", tz));
1745+
assert_eq!(" UTC ", format!("{:^5}", tz));
1746+
1747+
// [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric]
1748+
let ymd = datetime.format("%Y %B %d");
1749+
let ymd_formatted = "2007 January 02";
1750+
assert_eq!(format!(" {}", ymd_formatted), format!("{:>17}", ymd));
1751+
assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd));
1752+
assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd));
1753+
}
17241754
}

src/format/mod.rs

Lines changed: 97 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,15 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
334334

335335
/// Tries to format given arguments with given formatting items.
336336
/// Internally used by `DelayedFormat`.
337-
pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>,
338-
off: Option<&(String, FixedOffset)>, items: I) -> fmt::Result
339-
where I: Iterator<Item=Item<'a>> {
337+
pub fn format<'a, I>(
338+
w: &mut fmt::Formatter,
339+
date: Option<&NaiveDate>,
340+
time: Option<&NaiveTime>,
341+
off: Option<&(String, FixedOffset)>,
342+
items: I,
343+
) -> fmt::Result
344+
where I: Iterator<Item=Item<'a>>
345+
{
340346
// full and abbreviated month and weekday names
341347
static SHORT_MONTHS: [&'static str; 12] =
342348
["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
@@ -348,10 +354,13 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
348354
static LONG_WEEKDAYS: [&'static str; 7] =
349355
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
350356

357+
use std::fmt::Write;
358+
let mut result = String::new();
359+
351360
for item in items {
352361
match item {
353-
Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)),
354-
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => try!(write!(w, "{}", s)),
362+
Item::Literal(s) | Item::Space(s) => result.push_str(s),
363+
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
355364

356365
Item::Numeric(spec, pad) => {
357366
use self::Numeric::*;
@@ -398,23 +407,26 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
398407
Internal(ref int) => match int._dummy {},
399408
};
400409

410+
401411
if let Some(v) = v {
402-
if (spec == Year || spec == IsoYear) && !(0 <= v && v < 10_000) {
403-
// non-four-digit years require an explicit sign as per ISO 8601
404-
match pad {
405-
Pad::None => try!(write!(w, "{:+}", v)),
406-
Pad::Zero => try!(write!(w, "{:+01$}", v, width + 1)),
407-
Pad::Space => try!(write!(w, "{:+1$}", v, width + 1)),
408-
}
409-
} else {
410-
match pad {
411-
Pad::None => try!(write!(w, "{}", v)),
412-
Pad::Zero => try!(write!(w, "{:01$}", v, width)),
413-
Pad::Space => try!(write!(w, "{:1$}", v, width)),
412+
try!(
413+
if (spec == Year || spec == IsoYear) && !(0 <= v && v < 10_000) {
414+
// non-four-digit years require an explicit sign as per ISO 8601
415+
match pad {
416+
Pad::None => write!(result, "{:+}", v),
417+
Pad::Zero => write!(result, "{:+01$}", v, width + 1),
418+
Pad::Space => write!(result, "{:+1$}", v, width + 1),
419+
}
420+
} else {
421+
match pad {
422+
Pad::None => write!(result, "{}", v),
423+
Pad::Zero => write!(result, "{:01$}", v, width),
424+
Pad::Space => write!(result, "{:1$}", v, width),
425+
}
414426
}
415-
}
427+
)
416428
} else {
417-
return Err(fmt::Error); // insufficient arguments for given format
429+
return Err(fmt::Error) // insufficient arguments for given format
418430
}
419431
},
420432

@@ -423,108 +435,139 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
423435

424436
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
425437
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
426-
fn write_local_minus_utc(w: &mut fmt::Formatter, off: FixedOffset,
427-
allow_zulu: bool, use_colon: bool) -> fmt::Result {
438+
fn write_local_minus_utc(
439+
result: &mut String,
440+
off: FixedOffset,
441+
allow_zulu: bool,
442+
use_colon: bool,
443+
) -> fmt::Result {
428444
let off = off.local_minus_utc();
429445
if !allow_zulu || off != 0 {
430446
let (sign, off) = if off < 0 {('-', -off)} else {('+', off)};
431447
if use_colon {
432-
write!(w, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
448+
write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
433449
} else {
434-
write!(w, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
450+
write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
435451
}
436452
} else {
437-
write!(w, "Z")
453+
result.push_str("Z");
454+
Ok(())
438455
}
439456
}
440457

441458
let ret = match spec {
442459
ShortMonthName =>
443-
date.map(|d| write!(w, "{}", SHORT_MONTHS[d.month0() as usize])),
460+
date.map(|d| {
461+
result.push_str(SHORT_MONTHS[d.month0() as usize]);
462+
Ok(())
463+
}),
444464
LongMonthName =>
445-
date.map(|d| write!(w, "{}", LONG_MONTHS[d.month0() as usize])),
465+
date.map(|d| {
466+
result.push_str(LONG_MONTHS[d.month0() as usize]);
467+
Ok(())
468+
}),
446469
ShortWeekdayName =>
447-
date.map(|d| write!(w, "{}",
448-
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize])),
470+
date.map(|d| {
471+
result.push_str(
472+
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
473+
);
474+
Ok(())
475+
}),
449476
LongWeekdayName =>
450-
date.map(|d| write!(w, "{}",
451-
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize])),
477+
date.map(|d| {
478+
result.push_str(
479+
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
480+
);
481+
Ok(())
482+
}),
452483
LowerAmPm =>
453-
time.map(|t| write!(w, "{}", if t.hour12().0 {"pm"} else {"am"})),
484+
time.map(|t| {
485+
result.push_str(if t.hour12().0 {"pm"} else {"am"});
486+
Ok(())
487+
}),
454488
UpperAmPm =>
455-
time.map(|t| write!(w, "{}", if t.hour12().0 {"PM"} else {"AM"})),
489+
time.map(|t| {
490+
result.push_str(if t.hour12().0 {"PM"} else {"AM"});
491+
Ok(())
492+
}),
456493
Nanosecond =>
457494
time.map(|t| {
458495
let nano = t.nanosecond() % 1_000_000_000;
459496
if nano == 0 {
460497
Ok(())
461498
} else if nano % 1_000_000 == 0 {
462-
write!(w, ".{:03}", nano / 1_000_000)
499+
write!(result, ".{:03}", nano / 1_000_000)
463500
} else if nano % 1_000 == 0 {
464-
write!(w, ".{:06}", nano / 1_000)
501+
write!(result, ".{:06}", nano / 1_000)
465502
} else {
466-
write!(w, ".{:09}", nano)
503+
write!(result, ".{:09}", nano)
467504
}
468505
}),
469506
Nanosecond3 =>
470507
time.map(|t| {
471508
let nano = t.nanosecond() % 1_000_000_000;
472-
write!(w, ".{:03}", nano / 1_000_000)
509+
write!(result, ".{:03}", nano / 1_000_000)
473510
}),
474511
Nanosecond6 =>
475512
time.map(|t| {
476513
let nano = t.nanosecond() % 1_000_000_000;
477-
write!(w, ".{:06}", nano / 1_000)
514+
write!(result, ".{:06}", nano / 1_000)
478515
}),
479516
Nanosecond9 =>
480517
time.map(|t| {
481518
let nano = t.nanosecond() % 1_000_000_000;
482-
write!(w, ".{:09}", nano)
519+
write!(result, ".{:09}", nano)
483520
}),
484521
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
485522
time.map(|t| {
486523
let nano = t.nanosecond() % 1_000_000_000;
487-
write!(w, "{:03}", nano / 1_000_000)
524+
write!(result, "{:03}", nano / 1_000_000)
488525
}),
489526
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
490527
time.map(|t| {
491528
let nano = t.nanosecond() % 1_000_000_000;
492-
write!(w, "{:06}", nano / 1_000)
529+
write!(result, "{:06}", nano / 1_000)
493530
}),
494531
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
495532
time.map(|t| {
496533
let nano = t.nanosecond() % 1_000_000_000;
497-
write!(w, "{:09}", nano)
534+
write!(result, "{:09}", nano)
498535
}),
499536
TimezoneName =>
500-
off.map(|&(ref name, _)| write!(w, "{}", *name)),
537+
off.map(|&(ref name, _)| {
538+
result.push_str(name);
539+
Ok(())
540+
}),
501541
TimezoneOffsetColon =>
502-
off.map(|&(_, off)| write_local_minus_utc(w, off, false, true)),
542+
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, true)),
503543
TimezoneOffsetColonZ =>
504-
off.map(|&(_, off)| write_local_minus_utc(w, off, true, true)),
544+
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, true)),
505545
TimezoneOffset =>
506-
off.map(|&(_, off)| write_local_minus_utc(w, off, false, false)),
546+
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, false)),
507547
TimezoneOffsetZ =>
508-
off.map(|&(_, off)| write_local_minus_utc(w, off, true, false)),
548+
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, false)),
509549
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
510550
panic!("Do not try to write %#z it is undefined"),
511551
RFC2822 => // same to `%a, %e %b %Y %H:%M:%S %z`
512552
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
513553
let sec = t.second() + t.nanosecond() / 1_000_000_000;
514-
try!(write!(w, "{}, {:2} {} {:04} {:02}:{:02}:{:02} ",
515-
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize],
516-
d.day(), SHORT_MONTHS[d.month0() as usize], d.year(),
517-
t.hour(), t.minute(), sec));
518-
Some(write_local_minus_utc(w, off, false, false))
554+
try!(write!(
555+
result,
556+
"{}, {:2} {} {:04} {:02}:{:02}:{:02} ",
557+
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize],
558+
d.day(), SHORT_MONTHS[d.month0() as usize], d.year(),
559+
t.hour(), t.minute(), sec
560+
));
561+
Some(write_local_minus_utc(&mut result, off, false, false))
519562
} else {
520563
None
521564
},
522565
RFC3339 => // same to `%Y-%m-%dT%H:%M:%S%.f%:z`
523566
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
524567
// reuse `Debug` impls which already print ISO 8601 format.
525568
// this is faster in this way.
526-
try!(write!(w, "{:?}T{:?}", d, t));
527-
Some(write_local_minus_utc(w, off, false, true))
569+
try!(write!(result, "{:?}T{:?}", d, t));
570+
Some(write_local_minus_utc(&mut result, off, false, true))
528571
} else {
529572
None
530573
},
@@ -540,7 +583,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
540583
}
541584
}
542585

543-
Ok(())
586+
w.pad(&result)
544587
}
545588

546589
mod parsed;

0 commit comments

Comments
 (0)