8 //! Derived from: <https://raw.githubusercontent.com/quickfur/dcal/master/dcal.d>.
10 //! Originally converted to Rust by [Daniel Keep](https://github.com/DanielKeep).
14 /// Date representation.
15 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
16 struct NaiveDate(i32, u32, u32);
19 pub fn from_ymd(y: i32, m: u32, d: u32) -> NaiveDate {
20 assert!(1 <= m && m <= 12, "m = {:?}", m);
21 assert!(1 <= d && d <= NaiveDate(y, m, 1).days_in_month(), "d = {:?}", d);
25 pub fn year(&self) -> i32 {
29 pub fn month(&self) -> u32 {
33 pub fn day(&self) -> u32 {
37 pub fn succ(&self) -> NaiveDate {
38 let (mut y, mut m, mut d, n) = (
39 self.year(), self.month(), self.day()+1, self.days_in_month());
48 NaiveDate::from_ymd(y, m, d)
51 pub fn weekday(&self) -> Weekday {
55 let year = self.year();
56 let dow_jan_1 = (year*365 + ((year-1) / 4) - ((year-1) / 100) + ((year-1) / 400)) % 7;
57 let dow = (dow_jan_1 + (self.day_of_year() as i32 - 1)) % 7;
58 [Sun, Mon, Tue, Wed, Thu, Fri, Sat][dow as usize]
61 pub fn isoweekdate(&self) -> (i32, u32, Weekday) {
62 let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday();
64 // Work out this date's DOtY and week number, not including year adjustment.
65 let doy_0 = self.day_of_year() - 1;
66 let mut week_mon_0: i32 = ((first_dow_mon_0 + doy_0) / 7) as i32;
68 if self.first_week_in_prev_year() {
72 let weeks_in_year = self.last_week_number();
74 // Work out the final result.
75 // If the week is `-1` or `>= weeks_in_year`, we will need to adjust the year.
76 let year = self.year();
77 let wd = self.weekday();
80 (year - 1, NaiveDate::from_ymd(year - 1, 1, 1).last_week_number(), wd)
81 } else if week_mon_0 >= weeks_in_year as i32 {
82 (year + 1, (week_mon_0 + 1 - weeks_in_year as i32) as u32, wd)
84 (year, (week_mon_0 + 1) as u32, wd)
88 fn first_week_in_prev_year(&self) -> bool {
89 let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday();
91 // Any day in the year *before* the first Monday of that year
92 // is considered to be in the last week of the previous year,
93 // assuming the first week has *less* than four days in it.
94 // Adjust the week appropriately.
95 ((7 - first_dow_mon_0) % 7) < 4
98 fn year_first_day_of_week(&self) -> Weekday {
99 NaiveDate::from_ymd(self.year(), 1, 1).weekday()
102 fn weeks_in_year(&self) -> u32 {
103 let days_in_last_week = self.year_first_day_of_week().num_days_from_monday() + 1;
104 if days_in_last_week >= 4 { 53 } else { 52 }
107 fn last_week_number(&self) -> u32 {
108 let wiy = self.weeks_in_year();
109 if self.first_week_in_prev_year() { wiy - 1 } else { wiy }
112 fn day_of_year(&self) -> u32 {
113 (1..self.1).map(|m| NaiveDate::from_ymd(self.year(), m, 1).days_in_month())
114 .fold(0, |a,b| a+b) + self.day()
117 fn is_leap_year(&self) -> bool {
118 let year = self.year();
121 } else if year % 100 != 0 {
123 } else if year % 400 != 0 {
130 fn days_in_month(&self) -> u32 {
133 /* Feb */ 2 => if self.is_leap_year() { 29 } else { 28 },
149 impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate {
150 type Output = NaiveDate;
152 fn add(self, other: &'b NaiveDate) -> NaiveDate {
153 assert_eq!(*other, NaiveDate(0, 0, 1));
158 impl std::iter::Step for NaiveDate {
159 fn steps_between(_: &Self, _: &Self) -> Option<usize> {
163 fn forward_checked(start: Self, n: usize) -> Option<Self> {
164 Some((0..n).fold(start, |x, _| x.succ()))
167 fn backward_checked(_: Self, _: usize) -> Option<Self> {
172 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
184 pub fn num_days_from_monday(&self) -> u32 {
197 pub fn num_days_from_sunday(&self) -> u32 {
211 /// `GroupBy` implementation.
212 struct GroupBy<It: Iterator, F> {
213 it: std::iter::Peekable<It>,
217 impl<It, F> Clone for GroupBy<It, F>
219 It: Iterator + Clone,
223 fn clone(&self) -> Self {
231 impl<'a, G, It: 'a, F: 'a> Iterator for GroupBy<It, F>
232 where It: Iterator + Clone,
234 F: Clone + FnMut(&It::Item) -> G,
237 type Item = (G, InGroup<std::iter::Peekable<It>, F, G>);
239 fn next(&mut self) -> Option<Self::Item> {
240 self.it.peek().map(&mut self.f).map(|key| {
241 let start = self.it.clone();
242 while let Some(k) = self.it.peek().map(&mut self.f) {
249 (key.clone(), InGroup {
258 #[derive(Copy, Clone)]
259 struct InGroup<It, F, G> {
265 impl<It: Iterator, F: FnMut(&It::Item) -> G, G: Eq> Iterator for InGroup<It, F, G> {
266 type Item = It::Item;
268 fn next(&mut self) -> Option<It::Item> {
269 self.it.next().and_then(|x| {
270 if (self.f)(&x) == self.g { Some(x) } else { None }
275 trait IteratorExt: Iterator + Sized {
276 fn group_by<G, F>(self, f: F) -> GroupBy<Self, F>
277 where F: Clone + FnMut(&Self::Item) -> G,
280 GroupBy { it: self.peekable(), f }
283 fn join(mut self, sep: &str) -> String
284 where Self::Item: std::fmt::Display {
285 let mut s = String::new();
286 if let Some(e) = self.next() {
287 write!(s, "{}", e).unwrap();
290 write!(s, "{}", e).unwrap();
296 // HACK(eddyb): only needed because `impl Trait` can't be
297 // used with trait methods: `.foo()` becomes `.__(foo)`.
298 fn __<F, R>(self, f: F) -> R
299 where F: FnOnce(Self) -> R {
304 impl<It> IteratorExt for It where It: Iterator {}
306 /// Generates an iterator that yields exactly `n` spaces.
307 fn spaces(n: usize) -> std::iter::Take<std::iter::Repeat<char>> {
308 std::iter::repeat(' ').take(n)
312 assert_eq!(spaces(0).collect::<String>(), "");
313 assert_eq!(spaces(10).collect::<String>(), " ")
316 /// Returns an iterator of dates in a given year.
317 fn dates_in_year(year: i32) -> impl Iterator<Item=NaiveDate>+Clone {
319 it: NaiveDate::from_ymd(year, 1, 1)..,
320 f: |d: &NaiveDate| d.year(),
325 fn test_dates_in_year() {
327 let mut dates = dates_in_year(2013);
328 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 1)));
331 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 2)));
333 // Check monthly roll-over.
335 assert!(dates.next() != None);
338 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 31)));
339 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 2, 1)));
343 // Check length of year.
344 let mut dates = dates_in_year(2013);
346 assert!(dates.next() != None);
348 assert_eq!(dates.next(), None);
352 // Check length of leap year.
353 let mut dates = dates_in_year(1984);
355 assert!(dates.next() != None);
357 assert_eq!(dates.next(), None);
361 /// Convenience trait for verifying that a given type iterates over
363 trait DateIterator: Iterator<Item=NaiveDate> + Clone {}
364 impl<It> DateIterator for It where It: Iterator<Item=NaiveDate> + Clone {}
377 let by_x = input.iter().cloned().group_by(|a| a[0]);
378 let expected_1: &[&[[i32; 2]]] = &[
379 &[[1, 1], [1, 1], [1, 2]],
380 &[[2, 2], [2, 3], [2, 3]],
383 for ((_, a), b) in by_x.zip(expected_1.iter().cloned()) {
384 assert_eq!(&a.collect::<Vec<_>>()[..], b);
387 let by_y = input.iter().cloned().group_by(|a| a[1]);
388 let expected_2: &[&[[i32; 2]]] = &[
391 &[[2, 3], [2, 3], [3, 3]]
393 for ((_, a), b) in by_y.zip(expected_2.iter().cloned()) {
394 assert_eq!(&a.collect::<Vec<_>>()[..], b);
398 /// Groups an iterator of dates by month.
399 fn by_month(it: impl Iterator<Item=NaiveDate> + Clone)
400 -> impl Iterator<Item=(u32, impl Iterator<Item=NaiveDate> + Clone)> + Clone
402 it.group_by(|d| d.month())
406 let mut months = dates_in_year(2013).__(by_month);
407 for (month, (_, mut date)) in (1..13).zip(&mut months) {
408 assert_eq!(date.nth(0).unwrap(), NaiveDate::from_ymd(2013, month, 1));
410 assert!(months.next().is_none());
413 /// Groups an iterator of dates by week.
414 fn by_week(it: impl DateIterator)
415 -> impl Iterator<Item=(u32, impl DateIterator)> + Clone
417 // We go forward one day because `isoweekdate` considers the week to start on a Monday.
418 it.group_by(|d| d.succ().isoweekdate().1)
421 fn test_isoweekdate() {
422 fn weeks_uniq(year: i32) -> Vec<((i32, u32), u32)> {
423 let mut weeks = dates_in_year(year).map(|d| d.isoweekdate())
424 .map(|(y,w,_)| (y,w));
425 let mut result = vec![];
426 let mut accum = (weeks.next().unwrap(), 1);
439 let wu_1984 = weeks_uniq(1984);
440 assert_eq!(&wu_1984[..2], &[((1983, 52), 1), ((1984, 1), 7)]);
441 assert_eq!(&wu_1984[wu_1984.len()-2..], &[((1984, 52), 7), ((1985, 1), 1)]);
443 let wu_2013 = weeks_uniq(2013);
444 assert_eq!(&wu_2013[..2], &[((2013, 1), 6), ((2013, 2), 7)]);
445 assert_eq!(&wu_2013[wu_2013.len()-2..], &[((2013, 52), 7), ((2014, 1), 2)]);
447 let wu_2015 = weeks_uniq(2015);
448 assert_eq!(&wu_2015[..2], &[((2015, 1), 4), ((2015, 2), 7)]);
449 assert_eq!(&wu_2015[wu_2015.len()-2..], &[((2015, 52), 7), ((2015, 53), 4)]);
453 let mut weeks = dates_in_year(2013).__(by_week);
455 &*weeks.next().unwrap().1.collect::<Vec<_>>(),
457 NaiveDate::from_ymd(2013, 1, 1),
458 NaiveDate::from_ymd(2013, 1, 2),
459 NaiveDate::from_ymd(2013, 1, 3),
460 NaiveDate::from_ymd(2013, 1, 4),
461 NaiveDate::from_ymd(2013, 1, 5),
465 &*weeks.next().unwrap().1.collect::<Vec<_>>(),
467 NaiveDate::from_ymd(2013, 1, 6),
468 NaiveDate::from_ymd(2013, 1, 7),
469 NaiveDate::from_ymd(2013, 1, 8),
470 NaiveDate::from_ymd(2013, 1, 9),
471 NaiveDate::from_ymd(2013, 1, 10),
472 NaiveDate::from_ymd(2013, 1, 11),
473 NaiveDate::from_ymd(2013, 1, 12),
476 assert_eq!(weeks.next().unwrap().1.nth(0).unwrap(), NaiveDate::from_ymd(2013, 1, 13));
479 /// The number of columns per day in the formatted output.
480 const COLS_PER_DAY: u32 = 3;
482 /// The number of columns per week in the formatted output.
483 const COLS_PER_WEEK: u32 = 7 * COLS_PER_DAY;
485 /// Formats an iterator of weeks into an iterator of strings.
486 fn format_weeks(it: impl Iterator<Item = impl DateIterator>) -> impl Iterator<Item=String> {
488 let mut buf = String::with_capacity((COLS_PER_DAY * COLS_PER_WEEK + 2) as usize);
490 // Format each day into its own cell and append to target string.
491 let mut last_day = 0;
492 let mut first = true;
494 last_day = d.weekday().num_days_from_sunday();
496 // Insert enough filler to align the first day with its respective day-of-week.
498 buf.extend(spaces((COLS_PER_DAY * last_day) as usize));
502 write!(buf, " {:>2}", d.day()).unwrap();
505 // Insert more filler at the end to fill up the remainder of the week,
506 // if its a short week (e.g., at the end of the month).
507 buf.extend(spaces((COLS_PER_DAY * (6 - last_day)) as usize));
512 fn test_format_weeks() {
513 let jan_2013 = dates_in_year(2013)
514 .__(by_month).next() // pick January 2013 for testing purposes
515 // NOTE: This `map` is because `next` returns an `Option<_>`.
518 .map(|(_, weeks)| weeks)
523 jan_2013.as_ref().map(|s| &**s),
525 \x20 6 7 8 9 10 11 12\n\
526 \x2013 14 15 16 17 18 19\n\
527 \x2020 21 22 23 24 25 26\n\
528 \x2027 28 29 30 31 ")
532 /// Formats the name of a month, centered on `COLS_PER_WEEK`.
533 fn month_title(month: u32) -> String {
534 const MONTH_NAMES: &'static [&'static str] = &[
535 "January", "February", "March", "April", "May", "June",
536 "July", "August", "September", "October", "November", "December"
538 assert_eq!(MONTH_NAMES.len(), 12);
540 // Determine how many spaces before and after the month name
541 // we need to center it over the formatted weeks in the month.
542 let name = MONTH_NAMES[(month - 1) as usize];
543 assert!(name.len() < COLS_PER_WEEK as usize);
544 let before = (COLS_PER_WEEK as usize - name.len()) / 2;
545 let after = COLS_PER_WEEK as usize - name.len() - before;
547 // Note: being slightly more verbose to avoid extra allocations.
548 let mut result = String::with_capacity(COLS_PER_WEEK as usize);
549 result.extend(spaces(before));
550 result.push_str(name);
551 result.extend(spaces(after));
555 fn test_month_title() {
556 assert_eq!(month_title(1).len(), COLS_PER_WEEK as usize);
560 fn format_month(it: impl DateIterator) -> impl Iterator<Item=String> {
561 let mut month_days = it.peekable();
562 let title = month_title(month_days.peek().unwrap().month());
564 Some(title).into_iter()
565 .chain(month_days.__(by_week)
566 .map(|(_, week)| week)
570 fn test_format_month() {
571 let month_fmt = dates_in_year(2013)
572 .__(by_month).next() // Pick January as a test case
573 .map(|(_, days)| days.into_iter()
578 month_fmt.as_ref().map(|s| &**s),
581 \x20 6 7 8 9 10 11 12\n\
582 \x2013 14 15 16 17 18 19\n\
583 \x2020 21 22 23 24 25 26\n\
584 \x2027 28 29 30 31 ")
588 /// Formats an iterator of months.
589 fn format_months(it: impl Iterator<Item = impl DateIterator>)
590 -> impl Iterator<Item=impl Iterator<Item=String>>
595 /// Takes an iterator of iterators of strings; the sub-iterators are consumed
596 /// in lock-step, with their elements joined together.
597 trait PasteBlocks: Iterator + Sized
598 where Self::Item: Iterator<Item = String> {
599 fn paste_blocks(self, sep_width: usize) -> PasteBlocksIter<Self::Item> {
601 iters: self.collect(),
604 sep_width: sep_width,
609 impl<It> PasteBlocks for It where It: Iterator, It::Item: Iterator<Item=String> {}
611 struct PasteBlocksIter<StrIt>
612 where StrIt: Iterator<Item=String> {
614 cache: Vec<Option<String>>,
615 col_widths: Option<Vec<usize>>,
619 impl<StrIt> Iterator for PasteBlocksIter<StrIt>
620 where StrIt: Iterator<Item=String> {
623 fn next(&mut self) -> Option<String> {
626 // `cache` is now the next line from each iterator.
627 self.cache.extend(self.iters.iter_mut().map(|it| it.next()));
629 // If every line in `cache` is `None`, we have nothing further to do.
630 if self.cache.iter().all(|e| e.is_none()) { return None }
632 // Get the column widths if we haven't already.
633 let col_widths = match self.col_widths {
636 self.col_widths = Some(self.cache.iter()
637 .map(|ms| ms.as_ref().map(|s| s.len()).unwrap_or(0))
639 &**self.col_widths.as_ref().unwrap()
643 // Fill in any `None`s with spaces.
644 let mut parts = col_widths.iter().cloned().zip(self.cache.iter_mut())
645 .map(|(w,ms)| ms.take().unwrap_or_else(|| spaces(w).collect()));
647 // Join them all together.
648 let first = parts.next().unwrap_or(String::new());
649 let sep_width = self.sep_width;
650 Some(parts.fold(first, |mut accum, next| {
651 accum.extend(spaces(sep_width));
652 accum.push_str(&next);
658 fn test_paste_blocks() {
659 let row = dates_in_year(2013)
660 .__(by_month).map(|(_, days)| days)
667 " January February March \n\
668 \x20 1 2 3 4 5 1 2 1 2\n\
669 \x20 6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9\n\
670 \x2013 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16\n\
671 \x2020 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23\n\
672 \x2027 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30\n\
677 /// Produces an iterator that yields `n` elements at a time.
678 trait Chunks: Iterator + Sized {
679 fn chunks(self, n: usize) -> ChunksIter<Self> {
688 impl<It> Chunks for It where It: Iterator {}
690 struct ChunksIter<It>
696 // Note: `chunks` in Rust is more-or-less impossible without overhead of some kind.
697 // Aliasing rules mean you need to add dynamic borrow checking, and the design of
698 // `Iterator` means that you need to have the iterator's state kept in an allocation
699 // that is jointly owned by the iterator itself and the sub-iterator.
700 // As such, I've chosen to cop-out and just heap-allocate each chunk.
702 impl<It> Iterator for ChunksIter<It>
704 type Item = Vec<It::Item>;
706 fn next(&mut self) -> Option<Vec<It::Item>> {
707 let first = self.it.next()?;
709 let mut result = Vec::with_capacity(self.n);
712 Some((&mut self.it).take(self.n-1)
713 .fold(result, |mut acc, next| { acc.push(next); acc }))
718 let r = &[1, 2, 3, 4, 5, 6, 7];
719 let c = r.iter().cloned().chunks(3).collect::<Vec<_>>();
720 assert_eq!(&*c, &[vec![1, 2, 3], vec![4, 5, 6], vec![7]]);
724 fn format_year(year: i32, months_per_row: usize) -> String {
725 const COL_SPACING: usize = 1;
727 // Start by generating all dates for the given year.
730 // Group them by month and throw away month number.
731 .__(by_month).map(|(_, days)| days)
733 // Group the months into horizontal rows.
734 .chunks(months_per_row)
736 // Format each row...
737 .map(|r| r.into_iter()
738 // ... by formatting each month ...
741 // ... and horizontally pasting each respective month's lines together.
742 .paste_blocks(COL_SPACING)
746 // Insert a blank line between each row.
750 fn test_format_year() {
751 const MONTHS_PER_ROW: usize = 3;
753 macro_rules! assert_eq_cal {
754 ($lhs:expr, $rhs:expr) => {
756 println!("got:\n```\n{}\n```\n", $lhs.replace(" ", "."));
757 println!("expected:\n```\n{}\n```", $rhs.replace(" ", "."));
758 panic!("calendars didn't match!");
763 assert_eq_cal!(&format_year(1984, MONTHS_PER_ROW), "\
764 \x20 January February March \n\
765 \x20 1 2 3 4 5 6 7 1 2 3 4 1 2 3\n\
766 \x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 4 5 6 7 8 9 10\n\
767 \x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 11 12 13 14 15 16 17\n\
768 \x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 18 19 20 21 22 23 24\n\
769 \x2029 30 31 26 27 28 29 25 26 27 28 29 30 31\n\
771 \x20 April May June \n\
772 \x20 1 2 3 4 5 6 7 1 2 3 4 5 1 2\n\
773 \x20 8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9\n\
774 \x2015 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16\n\
775 \x2022 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23\n\
776 \x2029 30 27 28 29 30 31 24 25 26 27 28 29 30\n\
778 \x20 July August September \n\
779 \x20 1 2 3 4 5 6 7 1 2 3 4 1\n\
780 \x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8\n\
781 \x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15\n\
782 \x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22\n\
783 \x2029 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29\n\
786 \x20 October November December \n\
787 \x20 1 2 3 4 5 6 1 2 3 1\n\
788 \x20 7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8\n\
789 \x2014 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15\n\
790 \x2021 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22\n\
791 \x2028 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29\n\
794 assert_eq_cal!(&format_year(2015, MONTHS_PER_ROW), "\
795 \x20 January February March \n\
796 \x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7\n\
797 \x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14\n\
798 \x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21\n\
799 \x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28\n\
800 \x2025 26 27 28 29 30 31 29 30 31 \n\
802 \x20 April May June \n\
803 \x20 1 2 3 4 1 2 1 2 3 4 5 6\n\
804 \x20 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13\n\
805 \x2012 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20\n\
806 \x2019 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27\n\
807 \x2026 27 28 29 30 24 25 26 27 28 29 30 28 29 30 \n\
810 \x20 July August September \n\
811 \x20 1 2 3 4 1 1 2 3 4 5\n\
812 \x20 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12\n\
813 \x2012 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19\n\
814 \x2019 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26\n\
815 \x2026 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 \n\
818 \x20 October November December \n\
819 \x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5\n\
820 \x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12\n\
821 \x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19\n\
822 \x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26\n\
823 \x2025 26 27 28 29 30 31 29 30 27 28 29 30 31 ");
829 test_dates_in_year();