I'm trying to support the Hebrew calendar in my Noda Time project, and need to support an operation of "add a year" - or more generally, answer the question of "Given an original date and a target year number, what's the resulting date?"
Firstly, I'd like to check that I'm correct in saying that Microsoft gets the answer wrong in .NET. For example, consider the date of this post: 8th of Sivan, 5774. Sivan is month 10 in the civil month numbering system in a leap year, and 5774 is a leap year. If we add one year to today's date, .NET keeps the month number (10) leaving us with 8th of Tamuz, 5775 - because 5775 is not a leap year. (Supporting code is at the bottom of this question1.) To me, this feels incorrect - it's the month itself that should be preserved, not the month number.
Assuming that my approach is correct (and that we should more reasonably end up with a month number of 9, which is Sivan in a non-leap year), that still leaves some edge cases:
- What should we do if we add a year to the 30th of Heshvan, and Heshvan only has 29 days in the resulting year?
- Similarly, what should we do if we add a year to the 30th of Kislev, and Kislev only has 29 days in the resulting year?
- What should we do if we add a year to a date in Adar I, resulting in a non-leap year?
- Special case: what should we do if we add a year to the 30th of Adar I?
- What should we do if we add a year to a date in Adar II, resulting in a non-leap year?
- What should we do if we add a year to a date in Adar, resulting in a leap year?
I have a proposal:
- If you start with a date in Adar I or Adar II and the target year is not a leap year, the result should be in Adar
- If you start with a date in Adar and the target year is a leap year, the result should be in Adar II
- If the result would be longer than the month length in the target yet, truncate to the last day of the month (so 30th of Adar I would map to 29th of Adar, and the Heshvan/Kislev examples would both truncate to the 29th of the same month)
I believe this is close to but not quite the same as the birthday rule specified in Calendrical Calculations (documented for Hebcal), where 30th of (Adar I/Kislev/Heshvan) would be mapped to 1st of the next month... but so long as the above proposal is reasonable under Jewish tradition, it would be more consistent with other aspects of my code. (I'm happy to throw consistency out of the window if there's good cause, but I'd prefer to keep it otherwise.)
One problematic aspect of all of this is that it has to be context-free. I'm aware that there may very well be different rules for different contexts, but I need to have a "general purpose" rule, unfortunately.
So, is this a reasonable proposal, or do I still have a lot to learn about how to answer even simple questions about the Hebrew calendar?
1 Reference .NET code:
using System;
using System.Globalization;
class Test
{
static void Main()
{
var calendar = new HebrewCalendar();
DateTime start = new DateTime(5774, 10, 8, calendar);
// Prints 5774-10-08
Console.WriteLine(FormatYearMonthDay(start, calendar));
DateTime end = calendar.AddYears(start, 1);
// Prints 5775-10-08
Console.WriteLine(FormatYearMonthDay(end, calendar));
}
static string FormatYearMonthDay(DateTime date, Calendar calendar)
{
return string.Format("{0}-{1}-{2}",
calendar.GetYear(date),
calendar.GetMonth(date),
calendar.GetDayOfMonth(date));
}
}