The best explanation I've seen is that they wanted to start the time base on a leap year for some reason, and 1900 was not a leap year. So 1904 is the furthest back you could go without including 1900 in the range of supported dates, and needing to explicitly encode 1900 as an exception to the rule that every 4 years is a leap year. But then they could just have easily chosen 1908, or 1976. There's probably a TODO comment in the Mac 128K ROM source from Bud Tribble, reminding himself to pick a better time base after the next project milestone.
Yes! Exactly. Here is the formula used since the original Mac ROM:
1. clock_seconds / 86400 = days_since_Jan_1_1904, with the remainder being seconds_since_the_start_of_the_day
2. seconds_since_the_start_of_the_day / 3600 =
hours, with the remainder being seconds_since_the_start_of_the_hour
3. seconds since the start_of_the_hour / 60 =
minutes, with the remainder being
seconds
4. (days_since_Jan_1_1904 * 4) / 1461 = years_since_1904, with the remainder being days_since_the_start_of_the_year. They multiply * 4 and divide by 1461 to avoid dividing by floating point 365.25.
5. 1904 + years_since_1904 =
year
6. if year mod 4 = 0 then days_til_March = 60 otherwise days_til_March = 59. Because Jan = 31 + Feb = 28 or 29.
7. if days_since_the_start_of_the_year < days_til_March, then starting_month = 1, otherwise starting_month = 3 and subtract days_til_March from days_since_the_start_of_year.
8. ((days_since_the_start_of_year*128)+71)/3919 = magic_month_number, remainder magic_day_number
9. magic_month_number+starting_month =
month
10. (magic_day_number /128) + 1 =
day
Steps 8, 9, and 10 demonstrate how a little funky math can avoid tables or branches when you need to conserve ROM space and/or have a slow machine.
Step 6 will fail at the year 2100, because it is NOT a leap year. It requires more thought as to whether both Steps 4 and 6 fail thereafter, or if it takes missing four leap days until it is off by enough to affect integer math.
Step 6 also explains why they chose to start at 1904. For the mod 4 = 0 trick to work, you need to start on a leap year and not skip any /4 leap years. 1900 was not a leap year, but year 2000 was (/4=leap,/100=no leap,/400=leap).
In summary, these problems are caused by space-efficiency. This isn't even getting into the lack of leap seconds, time zones, or daylight savings time calculations...
- David