It makes sense if you assume the following two rules:
- Store the value starting from 1 if that allows for easiest display without having to add or subtract one in common date formats
- In all other cases (or when the first rule could go either way depending on the format), store the value starting from 0
Applying the rule:
tm_sec
, tm_min
, tm_hour
displayed starting from 0 so store starting from 0. In 12-hour format the first hour is 12, but the rest can be displayed "as-is" by starting from 0.
tm_mday
displayed starting from 1 so store starting from 1
tm_mon
displayed starting from 1 in dates like 24/02/1964, but also makes sense to store starting from 0 for ease of indexing strings in an array for dates like 24th February 1964, so could go either way -> start from 0
tm_year
20th century years can be displayed as-is in 2 year format e.g. 24/02/64, or else add 1900, no case where starting from 1 makes sense
tm_wday
Usually displayed by indexing a string array, start from 0
tm_yday
No clear reason to start from 1 for ease of display, start from 0
So tm_mday
is the only case where there is a clear advantage to storing it starting from 1 for ease of display in all common cases.
The reference implementation of asctime
from the C-89 standard is consistent with this, the only adjustment to any of the values being to add 1900 to tm_year
:
char *asctime(const struct tm *timeptr)
{
static const char wday_name[7][3] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static const char mon_name[12][3] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static char result[26];
sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
wday_name[timeptr->tm_wday],
mon_name[timeptr->tm_mon],
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
1900 + timeptr->tm_year);
return result;
}