Most of the time, dates are represented as a year, month and day in a particular calendar system - occasionally with an era as well. An alternative approach is to represent a date as a year, week, and day-of-week.
For example, using the ISO-8601 rule, the year/month/day combination "2016, February, 4" is equivalently year 2016, week 5, Thursday. All of this is performed relative to a specific calendar system.
However, under the same rule the year/month/day combination "2014, December, 29th" is equivalently year 2015, week 1, Monday: the "year" part of a year/month/day representation isn't always the same as the year part of the year/week/day-of-week representation for the same date. To avoid ambiguity, I refer to the year part of a year/week/day-of-week representation as a week-year.
The two representations are exactly equivalent: every day can be represented in either form. Week-years are more widely used in some countries than in others, typically in business or educational settings.
There are different ways of mapping between year/month/day and week-year/week/day-of-week representations, which we refer to as week-year rules.
As of Noda Time 2.0, week-year rules are entirely separate from calendar
systems. A rule is represented by the
IWeekYearRule
interface, and built-in implementations are obtained via factory
members in the
WeekYearRules
static class.
Although the GetWeeksInWeekYear
and GetLocalDate
methods require
a CalendarSystem
, we also provide extension methods in
WeekYearRuleExtensions
that default to the ISO calendar system, delegating to the existing
IWeekYearRule
methods. This reduces the burden of implementing
IWeekYearRule
, but allows callers who are only using the ISO
calendar system to omit it.
There are two methods to convert from year-month-day representation
to week-based representation: GetWeekYear
and GetWeekOfWeekYear
,
both accepting a LocalDate
. There is no equivalent to obtain the
day of week, as that does not change based on the rule or even on
the calendar system.
Noda Time 2.0 comes with two kinds of rule: ISO-like rules, and BCL rules.
Let's start with the most common rule: WeekYearRules.Iso
, which
implements the rule specified in ISO-8601. Under this rule:
To put the "four days or more" rule in a different way: if the first day of the calendar year is Monday, Tuesday, Wednesday or Thursday, then the week containing that day is week 1 of the same year; if the calendar year starts on a Friday, Saturday or Sunday, then that week is in the previous week-year.
Examples:
Although this is the canonical ISO-8601 rule, we can construct rules which behave similarly, which I call "ISO-like" rules. These provide two other axes of flexibility:
Use the WeekYearRules.ForMinDaysInFirstWeek(int)
and
WeekYearRules.ForMinDaysInFirstWeek(int, IsoDayOfWeek)
methods to
construct ISO-like rules. The first overload defaults to using Monday
as the first day of the week.
The rule returned by WeekYearRules.Iso
property is equivalent to
the rule returned by
WeekYearRules.ForMinDaysInFirstWeek(4, IsoDayOfWeek.Monday)
.
BCL rules are obtained via WeekYearRules.FromBclRule
, which
accepts two parameters:
These mirror the second and third parameters to the
Calendar.GetWeekOfYear
method in the BCL.
It's easiest to think of the BCL rules as being equivalent to the
ISO-like rules in most respects, with a mapping from
CalendarWeekRule
to "minimum number of days in first week" of:
FirstDay
: 1FirstFourDayWeek
: 4FirstFullWeek
: 7There's then one subtle difference between ISO-like rules and BCL-like rules:
In an ISO-like rule, every week has exactly seven days. In a BCL-like rule, if the start of the first week of a week-year is in the previous calendar year, that week is split into two, so that the week-year of a date is never more than the calendar year of the same date.
It's easiest to see this in action with a couple of examples. The
BCL rule using CalendarWeekRule.FirstFourDayWeek
and Monday as a
first day of the week is equivalent to WeekYearRules.Iso
. Let's
see what happens around the turn of the year for two years. First
2014/2015:
Date | Day of week | ISO week | BCL week |
2014-12-28 | Sunday | 2014 week 52 | 2014 week 52 |
2014-12-29 | Monday | 2015 week 1 | 2014 week 53 |
2014-12-30 | Tuesday | 2015 week 1 | 2014 week 53 |
2014-12-31 | Wednesday | 2015 week 1 | 2014 week 53 |
2015-01-01 | Thursday | 2015 week 1 | 2015 week 1 |
2015-01-02 | Friday | 2015 week 1 | 2015 week 1 |
2015-01-03 | Saturday | 2015 week 1 | 2015 week 1 |
2015-01-04 | Sunday | 2015 week 1 | 2015 week 1 |
2015-01-05 | Monday | 2015 week 2 | 2015 week 2 |
As you can see, 2015 started on a Thursday. In the ISO rule, that means that the whole Monday-to-Sunday week is 2015 week 1. In the BCL rule, Monday to Wednesday are in 2014 week 53, and Thursday to Sunday are in 2015 week 1. For the BCL rule, 2014 week 53 and 2015 week 1 are "short" weeks.
Compare that with the 2015/2016 boundary:
Date | Day of week | ISO week | BCL week |
2015-12-27 | Sunday | 2015 week 52 | 2015 week 52 |
2015-12-28 | Monday | 2015 week 53 | 2015 week 53 |
2015-12-29 | Tuesday | 2015 week 53 | 2015 week 53 |
2015-12-30 | Wednesday | 2015 week 53 | 2015 week 53 |
2015-12-31 | Thursday | 2015 week 53 | 2015 week 53 |
2016-01-01 | Friday | 2015 week 53 | 2015 week 53 |
2016-01-02 | Saturday | 2015 week 53 | 2015 week 53 |
2016-01-03 | Sunday | 2015 week 53 | 2015 week 53 |
2016-01-04 | Monday | 2016 week 1 | 2016 week 1 |
2016 starts on a Friday, which means there are only three days of that Monday-to-Sunday week in 2016... which means that in both the BCL and ISO rules, those days are in the final week of 2015. The first week of 2016 starts on Monday January 4th, and the BCL and ISO rules behave identically, with all weeks being a regular seven days.