import ceylon.time.base { DayOfWeek, Month } import ceylon.time { Date, newDate = date } import ceylon.time.chronology { gregorian } "Alias to represent a specific day." shared alias DayOfMonth => Integer; "Rule describing the day a rule applieds to." shared abstract class OnDay() of OnFixedDay | OnFirstOfMonth | OnLastOfMonth { shared formal Date date(Year year, Month month); } "Represents a fixed day of month. For example, a value `3` on February, 2004, means exactly _February 3. 2004_. " shared class OnFixedDay(fixedDate) extends OnDay() { shared DayOfMonth fixedDate; shared actual Boolean equals(Object other) { if(is OnFixedDay other) { return fixedDate == other.fixedDate; } return false; } shared actual Date date(Year year, Month month) { return newDate(year, month, fixedDate); } } "Represents a day equal to or higher than a day of week. For example, given the rule `Sun>=1` it can mean one of the following: either _June 1. 2014_ or _June 7. 2015_ (or anything in between) depending on the year and month of the overall rule. " shared class OnFirstOfMonth(dayOfWeek, onDateOrAfter) extends OnDay() { shared DayOfWeek dayOfWeek; shared DayOfMonth onDateOrAfter; shared actual Boolean equals(Object other) { if(is OnFirstOfMonth other) { return onDateOrAfter == other.onDateOrAfter && dayOfWeek == other.dayOfWeek; } return false; } shared actual Date date(Year year, Month month) { value initial = newDate(year, month, onDateOrAfter); "onDateOrAfter should always be a valid day for the month" assert(exists result = initial.rangeTo(initial.withDay(month.numberOfDays(initial.leapYear))).find(matchesDayOfWeekAndDay)); return result; } Boolean matchesDayOfWeekAndDay(Date dateTime) { return dateTime.day >= onDateOrAfter && dateTime.dayOfWeek == dayOfWeek; } } "Represents the last day of week, for example: * `lastSun` * `lastSat` For example, `lastSun` of February 2015 is _February 22, 2015_" shared class OnLastOfMonth(dayOfWeek) extends OnDay() { shared DayOfWeek dayOfWeek; shared actual Boolean equals(Object other) { if(is OnLastOfMonth other) { return dayOfWeek == other.dayOfWeek; } return false; } shared actual Date date(Year year, Month month) { value initial = newDate(year, month, month.numberOfDays(gregorian.leapYear(year))); Date? result = initial.rangeTo(initial.withDay(1)) .find((Date element) => element.dayOfWeek == dayOfWeek); assert(exists result); return result; } }