[california/wip/725785-create-recurring: 16/16] Fix dealing with day-of-week position in month for DTSTART
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/725785-create-recurring: 16/16] Fix dealing with day-of-week position in month for DTSTART
- Date: Wed, 25 Jun 2014 02:13:13 +0000 (UTC)
commit 509c8fb81756483a83b0b6d3ed048ec7e25f8f7f
Author: Jim Nelson <jim yorba org>
Date: Tue Jun 24 19:12:41 2014 -0700
Fix dealing with day-of-week position in month for DTSTART
src/calendar/calendar-date.vala | 26 ++++++++++++++++++
src/calendar/calendar-week.vala | 13 +++++++++
src/component/component-details-parser.vala | 21 ++++++++------
src/tests/tests-calendar-date.vala | 38 +++++++++++++++++++++++++++
src/tests/tests-quick-add-recurring.vala | 37 +++++++++++++++++++++++++-
5 files changed, 125 insertions(+), 10 deletions(-)
---
diff --git a/src/calendar/calendar-date.vala b/src/calendar/calendar-date.vala
index 9f0e71f..7c800b4 100644
--- a/src/calendar/calendar-date.vala
+++ b/src/calendar/calendar-date.vala
@@ -196,6 +196,32 @@ public class Date : Unit<Date>, Gee.Comparable<Date>, Gee.Hashable<Date> {
}
/**
+ * Returns the 1-based position of the current { link day_of_week} in the { link month}.
+ *
+ * For example, if this is the first Monday of the month, returns 1.
+ */
+ public int day_of_week_position_in_month() {
+ MonthOfYear moy = month_of_year();
+
+ // Walk every week of this month ... first day of week doesn't matter here, first Monday
+ // is first Monday whether or not the week starts with Sunday or Monday
+ // TODO: Would be better to find a non-iterative algorithm for this
+ int position = 1;
+ foreach (Week week in moy.to_week_span(FirstOfWeek.SUNDAY)) {
+ // if this date is in the week, found it
+ if (week.contains(this))
+ break;
+
+ // only increase position counter if this day of week is present in the week that is
+ // also in this month (weeks can span months)
+ if (week.date_at(day_of_week).month_of_year().equal_to(moy))
+ position++;
+ }
+
+ return position;
+ }
+
+ /**
* Returns the { link MonthOfYear} the { link Date} falls in.
*/
public MonthOfYear month_of_year() {
diff --git a/src/calendar/calendar-week.vala b/src/calendar/calendar-week.vala
index 65f3eaa..f33292c 100644
--- a/src/calendar/calendar-week.vala
+++ b/src/calendar/calendar-week.vala
@@ -64,6 +64,19 @@ public class Week : Unit<Week>, Gee.Comparable<Week>, Gee.Hashable<Week> {
}
/**
+ * Returns the { link Date} for the { link DayOfWeek}.
+ */
+ public Date date_at(DayOfWeek dow) {
+ // although mixing FirstOfWeek is dangerous, don't trust simple math here because of this issue
+ foreach (Date date in to_date_span()) {
+ if (date.day_of_week.equal_to(dow))
+ return date;
+ }
+
+ assert_not_reached();
+ }
+
+ /**
* @inheritDoc
*/
public override Week adjust(int quantity) {
diff --git a/src/component/component-details-parser.vala b/src/component/component-details-parser.vala
index 0c067a6..4be57a2 100644
--- a/src/component/component-details-parser.vala
+++ b/src/component/component-details-parser.vala
@@ -544,7 +544,8 @@ public class DetailsParser : BaseObject {
if (dow != null) {
Calendar.DayOfWeek[] by_days = iterate<Calendar.DayOfWeek>(dow).to_array();
- // if interval is an ordinal, the rule is for "nth day of the month", so it's a week number
+ // if interval is an ordinal, the rule is for "nth day of the month", so it's a position
+ // (i.e. "1st tuesday")
if (!is_ordinal)
return set_rrule_weekly(by_days, interval);
else
@@ -590,14 +591,16 @@ public class DetailsParser : BaseObject {
}
// Using the supplied by days, find the first upcoming start_date that matches one of them
- // that is also the week number (unless zero, which means "any")
- private void set_byday_start_date(Calendar.DayOfWeek[]? by_days, int week_no) {
+ // that is also the position (unless zero, which means "any")
+ private void set_byday_start_date(Calendar.DayOfWeek[]? by_days, int position) {
+ assert(position >= 0);
+
// find the earliest date in the by_days; if it's earlier than the start_date or the
// start_date isn't defined, use the earliest
if (by_days != null) {
Gee.Set<Calendar.DayOfWeek> dows = from_array<Calendar.DayOfWeek>(by_days).to_hash_set();
Calendar.Date earliest = Calendar.System.today.upcoming(true, (date) => {
- if (week_no != 0 && date.week_of(Calendar.System.first_of_week).week_of_month != week_no)
+ if (position != 0 && date.day_of_week_position_in_month() != position)
return false;
return dows.contains(date.day_of_week);
@@ -641,11 +644,11 @@ public class DetailsParser : BaseObject {
return true;
}
- // "every first tuesday"
- private bool set_rrule_nth_day_of_week(Calendar.DayOfWeek[]? by_days, int week_no) {
+ // "every 1st tuesday"
+ private bool set_rrule_nth_day_of_week(Calendar.DayOfWeek[]? by_days, int position) {
// Although a month can span 6 calendar weeks, a day of a week never appears in more than
// five of them
- if (week_no < 1 || week_no > 5)
+ if (position < 1 || position > 5)
return false;
if (rrule == null)
@@ -654,10 +657,10 @@ public class DetailsParser : BaseObject {
return false;
Gee.Map<Calendar.DayOfWeek?, int> map = from_array<Calendar.DayOfWeek>(by_days)
- .to_hash_map_as_keys<int>(dow => week_no);
+ .to_hash_map_as_keys<int>(dow => position);
rrule.add_by_rule(RecurrenceRule.ByRule.DAY, RecurrenceRule.encode_days(map));
- set_byday_start_date(by_days, week_no);
+ set_byday_start_date(by_days, position);
return true;
}
diff --git a/src/tests/tests-calendar-date.vala b/src/tests/tests-calendar-date.vala
index 2d3b085..22c5205 100644
--- a/src/tests/tests-calendar-date.vala
+++ b/src/tests/tests-calendar-date.vala
@@ -20,6 +20,11 @@ private class CalendarDate : UnitTest.Harness {
add_case("prior-exclusive", prior_exclusive);
add_case("upcoming-today", upcoming_today);
add_case("upcoming-next-week", upcoming_next_week);
+ add_case("day-of-week-position-1", day_of_week_position_1);
+ add_case("day-of-week-position-2", day_of_week_position_2);
+ add_case("day-of-week-position-3", day_of_week_position_3);
+ add_case("day-of-week-position-4", day_of_week_position_4);
+ add_case("day-of-week-position-5", day_of_week_position_5);
}
protected override void setup() throws Error {
@@ -161,6 +166,39 @@ private class CalendarDate : UnitTest.Harness {
return diff == 7;
}
+
+ private bool test_dow_position(Calendar.Date date, int expected, out string? dump) throws Error {
+ int position = date.day_of_week_position_in_month();
+
+ dump = "%s position=%d, expected=%d".printf(date.to_string(), position, expected);
+
+ return position == expected;
+ }
+
+ private Calendar.Date jun2014(int dom) throws Error {
+ return new Calendar.Date(Calendar.DayOfMonth.for(dom), Calendar.Month.JUN,
+ new Calendar.Year(2014));
+ }
+
+ private bool day_of_week_position_1(out string? dump) throws Error {
+ return test_dow_position(jun2014(1), 1, out dump);
+ }
+
+ private bool day_of_week_position_2(out string? dump) throws Error {
+ return test_dow_position(jun2014(9), 2, out dump);
+ }
+
+ private bool day_of_week_position_3(out string? dump) throws Error {
+ return test_dow_position(jun2014(20), 3, out dump);
+ }
+
+ private bool day_of_week_position_4(out string? dump) throws Error {
+ return test_dow_position(jun2014(23), 4, out dump);
+ }
+
+ private bool day_of_week_position_5(out string? dump) throws Error {
+ return test_dow_position(jun2014(30), 5, out dump);
+ }
}
}
diff --git a/src/tests/tests-quick-add-recurring.vala b/src/tests/tests-quick-add-recurring.vala
index 3c94ee9..2db35d4 100644
--- a/src/tests/tests-quick-add-recurring.vala
+++ b/src/tests/tests-quick-add-recurring.vala
@@ -50,6 +50,7 @@ private class QuickAddRecurring : UnitTest.Harness {
// MONTHLY
add_case("every-first-tuesday", every_first_tuesday);
add_case("every-first-tuesday-for-3-weeks", every_first_tuesday_for_3_weeks);
+ add_case("every-second-sunday-until", every_second_sunday_until);
add_case("every-sixth-tuesday", every_sixth_tuesday);
// YEARLY
@@ -65,6 +66,7 @@ private class QuickAddRecurring : UnitTest.Harness {
add_case("yearly-meeting-july-15th", yearly_meeting_july_15th);
add_case("meeting-every-july-4th-15th", meeting_every_july_4th_15th);
add_case("every-july-4th-3-years", every_july_4th_3_years);
+ add_case("every-aug-1st-until", every_aug_1st_until);
}
protected override void setup() throws Error {
@@ -514,7 +516,7 @@ private class QuickAddRecurring : UnitTest.Harness {
Calendar.DayOfWeek.TUE).to_hash_map_as_keys<int>(dow => 1);
Component.Event event;
- return basic("meeting at work at 10am every 1st tuesday for 3 weeks", out event, out dump)
+ return basic("meeting at work at 10am every 1st tuesday for 3 months", out event, out dump)
&& event.rrule.is_monthly
&& event.rrule.interval == 1
&& event.rrule.count == 3
@@ -523,6 +525,24 @@ private class QuickAddRecurring : UnitTest.Harness {
&& check_byrule_day(event, by_days);
}
+ private bool every_second_sunday_until(out string? dump) throws Error {
+ Gee.Map<Calendar.DayOfWeek?, int> by_days = iterate<Calendar.DayOfWeek?>(
+ Calendar.DayOfWeek.SUN).to_hash_map_as_keys<int>(dow => 2);
+
+ Component.Event event;
+ return basic("meeting at work at 10am every 2nd sunday until august 1st", out event, out dump)
+ && event.rrule.is_monthly
+ && event.rrule.interval == 1
+ && event.rrule.until_date != null
+ && event.rrule.until_date.month == Calendar.Month.AUG
+ && event.rrule.until_date.day_of_month.value == 1
+ && event.rrule.until_date.year.compare_to(Calendar.System.today.year) >= 0
+ && event.exact_time_span.start_date.day_of_week.equal_to(Calendar.DayOfWeek.SUN)
+ && event.exact_time_span.start_date.day_of_month.value >= 7
+ && event.exact_time_span.start_date.day_of_month.value <= 14
+ && check_byrule_day(event, by_days);
+ }
+
// bad input
private bool every_sixth_tuesday(out string? dump) throws Error {
Component.DetailsParser parser = new Component.DetailsParser(
@@ -674,6 +694,21 @@ private class QuickAddRecurring : UnitTest.Harness {
&& event.exact_time_span.start_date.month == Calendar.Month.JUL
&& event.exact_time_span.start_date.day_of_month.value == 4;
}
+
+ private bool every_aug_1st_until(out string? dump) throws Error {
+ Component.Event event;
+ return multiday("meeting at work aug 15 yearly until sep 1", out event, out dump)
+ && event.rrule.is_yearly
+ && event.rrule.interval == 1
+ && event.rrule.until_date != null
+ && event.rrule.until_date.month == Calendar.Month.SEP
+ && event.rrule.until_date.day_of_month.value == 1
+ && event.rrule.until_date.year.compare_to(Calendar.System.today.year) >= 0
+ && event.date_span.start_date.month == Calendar.Month.AUG
+ && event.date_span.start_date.day_of_month.value == 15
+ && event.date_span.start_date.year.compare_to(Calendar.System.today.year) >= 0
+ && event.date_span.end_date.equal_to(event.date_span.start_date);
+ }
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]