.NET Library to generate a perpetual calendar based on rules.
The main purpose of this library is to simplify the generation of calendar days tables.
More in details with this libary you can:
- define calendars by a succint syntax
- combine calendars in a hierarchy
- check if a date match a calendar and fetch day info
- generate calendar days tables in JSON or XML formats
- serialize and deserialize calendar definition in JSON or XML, useful to persist a calendar on an external system (DB or filesystem)
The definition of a calendar is achieved by specifing a set of rules.
Each rule is made up of the following parts:
- An evaluation policy
- A date matcher expression
- A description of the day (optional)
- A list of working day periods (optional)
Policy | Description |
---|---|
Fallthrough | The rule is accepted but the evaluation continues with the following rules |
Accept | The rule is accepted and the evaluation stops |
Date matcher pattern is inspired by CRON expressions. They are made up of four parts separated by blanks:
- Year Matcher Expression
- Month Matcher Expression
- Day Of Month Matcher Expression
- Day Of Week Matcher Expression
Each part can be a compination of multiple expression seprated by a comma.
A year matcher expression is made up by a range specification and, optionally, a year predicate. Year predicates are appended to the range expression.
Expression | Description |
---|---|
* | Any year |
2000.. | Any year from 2000 included |
..2000 | Any year until 2000 included |
2000..2020 | Any year between 2000 and 2020 included |
Expression | Predicate | Description |
---|---|---|
*%2 | Modulo | Any year which modulo 2 is zero |
2000../2 | Periodic | Every 2 years starting from 2000. ATTENTION: this predicate requires that the range is left closed. |
*/Leap | Leap | Every leap year |
*/NotLeap | NotLeap | Every not leap year |
A month matcher expression is made up by a range specification and, optionally, a month predicate. Month predicates are appended to the range expression.
Months are represented by the numbers 1 (January) to 12 (December).
Expression | Description |
---|---|
* | Any month |
2.. | Any month from february included |
..11 | Any month until november included |
2..11 | Any month between february and november |
Expression | Predicate | Description |
---|---|---|
*%2 | Modulo | Any month which modulo 2 is zero |
1../2 | Periodic | January, March, May, July, September, November. ATTENTION: this predicate requires that the range is left closed. |
A day of month matcher expression is made up by a range specification and, optionally, a day of month predicate. Day of month predicates are appended to the range expression.
Day of months are represented by the numbers 1 (the 1st) to 31 (the 31st).
Expression | Description |
---|---|
* | Any day of month |
2.. | Any day of month from the 2nd included |
..11 | Any day of month until the 11th included |
2..11 | Any day of month between the 2nd and the 11th |
Expression | Predicate | Description |
---|---|---|
*%2 | Modulo | Any day of month wich modulo 2 is zero |
1../2 | Periodic | The day of months that are odd. ATTENTION: this predicate requires that the range is left closed. |
A day of week matcher expression is made up by a range specification and.
Day of weeks are represented by the numbers 0 (Sunday), 1 (Monday), … to 7 (Sunday again).
The double representation of Sunday simplifies writing rules for different cultural contexts.
Expression | Description |
---|---|
* | Any day of week |
2.. | Any day of week from Tuesday to Sunday (7) |
..2 | Any day of week from Sunday (0) to Tuesday |
1..5 | Any weekday |
6..7 | Any weekend |
You can optionally provide a description of the day enclosing it in a pair of double brackets.
The following day rule matches for every day.
* * * * [[every day]]
You can optionally provide a comma separated list of working day periods. A time period is specified by providing a begin and an end time.
A calendar rule without a working day period is interpreted as a non-working day rule.
ATTENTION: hours must be provided with two digits.
00:00-24:00
For a working example please refer to the Sample App.
var rules = new CalendarRules
{
{DayRulePolicy.Fallthrough, DayRuleParser.Parse("* * * * [[working day]] 08:30-13:30,14:30-17:30")},
{DayRulePolicy.Fallthrough, DayRuleParser.Parse("* * * 6..7 [[non working day]]")}
};
var calendar = new Calendar("Standard working week", rules);
var rules = new CalendarRules
{
{DayRulePolicy.Fallthrough, DayRuleParser.Parse("*/Leap 3 1 * [[leap year compensation day]]")},
};
var calendar = new Calendar("Compensation day on leap years", rules);
Once you have defined a Calendar you can generate a calendar day table.
The calendar days table generated by the Sample App are:
<?xml version="1.0" encoding="utf-16"?>
<CalendarDays>
<Day date="2020-12-01" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-02" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-03" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-04" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-05" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-06" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-07" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-08" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-09" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-10" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-11" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-12" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-13" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-14" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-15" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-16" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-17" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-18" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-19" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-20" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-21" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-22" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-23" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-24" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-25" isWorkingDay="false">
<Description>xmas</Description>
</Day>
<Day date="2020-12-26" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-27" isWorkingDay="false">
<Description>non working day</Description>
</Day>
<Day date="2020-12-28" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-29" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-30" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
<Day date="2020-12-31" isWorkingDay="true">
<Description>working day</Description>
<WorkingPeriods>
<TimePeriod begin="08:30" end="13:30"/>
<TimePeriod begin="14:30" end="17:30"/>
</WorkingPeriods>
</Day>
</CalendarDays>
[
{
"date": "2020-12-01",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-02",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-03",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-04",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-05",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-06",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-07",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-08",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-09",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-10",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-11",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-12",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-13",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-14",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-15",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-16",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-17",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-18",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-19",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-20",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-21",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-22",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-23",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-24",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-25",
"description": "xmas",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-26",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-27",
"description": "non working day",
"isWorkingDay": false,
"workingPeriods": null
},
{
"date": "2020-12-28",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-29",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-30",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
},
{
"date": "2020-12-31",
"description": "working day",
"isWorkingDay": true,
"workingPeriods": [
{
"begin": "08:30",
"end": "13:30"
},
{
"begin": "14:30",
"end": "17:30"
}
]
}
]