【问题标题】:Why does tm_mday start from 1 while all other elements of struct tm start from 0?为什么 tm_mday 从 1 开始,而 struct tm 的所有其他元素从 0 开始?
【发布时间】:2015-10-01 13:14:54
【问题描述】:

answering another question 时,我告诉 OP 他需要正确初始化他的struct tm 变量,但需要小心,因为他不能简单地使用

struct tm mytime;
memset(&mytime, 0, sizeof(mytime));

因为 并非所有 struct tm 的字段从 0 开始都有效。 A closer look on struct tm 向我展示了 struct tm一个 字段没有 0 作为有效值,即 tm_mday

int    tm_sec   seconds [0,61]
int    tm_min   minutes [0,59]
int    tm_hour  hour [0,23]
int    tm_mday  day of month [1,31]
int    tm_mon   month of year [0,11]
int    tm_year  years since 1900
int    tm_wday  day of week [0,6] (Sunday = 0)
int    tm_yday  day of year [0,365]
int    tm_isdst daylight savings flag

为什么?决定对于这个元素,0 不应该是有效值的决定背后的想法是什么???

【问题讨论】:

  • tm_year 也是从 1 开始(没有年份“零”),只是减少了 1900,这可能掩盖了零的缺失。 ;) 通常所有值都有最适合直接显示(小时..秒)或索引字符串数组(月)的范围。
  • @CiaPan: indexing string arrays 听起来很合理,但是,tm_yday 没有字符串索引,0 是有效的。
  • @CiaPan tm_year 为零很好:这意味着年份 1900
  • 我不知道这是否是基本原理的一部分,但如果tm_mday 从 1 开始,要找到下一个/上一个星期日,你只需要做tm_mday - tm_wday(和±7),等等,所以它允许相对“干净”的公式。
  • 只是一些猜测。秒、分钟、小时、月、年有固定的转换,但天没有。例如。 1m = 60s; 1h = 60m; 1 个月 = ???日; 1y = 12m。所以,你可以使用mod 到 s/m/h/y,比如x mod 60s ,'x mod 60m','x mod 60h',x mod 12m,这可能会产生0s ,0m,0h 结果。但我们不会将mod 应用于当天,例如x mod ??? day(base is 30 ?31 ? or 29 ?)。

标签: c time struct mktime


【解决方案1】:

如果你假设以下两条规则是有意义的:

  • 存储从 1 开始的值,如果这允许最简单的显示,而无需在常用日期格式中加减一个
  • 在所有其他情况下(或者当第一条规则可以根据格式采用任一方式时),存储从 0 开始的值

应用规则:

  • tm_sectm_mintm_hour 从 0 开始显示,因此从 0 开始存储。在 12 小时格式中,第一个小时是 12,但其余的可以从 0 开始“按原样”显示。李>
  • tm_mday 从 1 开始显示,所以从 1 开始存储
  • tm_mon 在 1964 年 2 月 24 日等日期从 1 开始显示,但从 0 开始存储也很有意义,以便于在 1964 年 2 月 24 日等日期的数组中索引字符串,因此可以采用任何一种方式 -> 从 0 开始
  • tm_year 20 世纪年份可以按原样以 2 年格式显示,例如24/02/64,否则添加 1900,从 1 开始没有任何意义
  • tm_wday 通常通过索引字符串数组来显示,从0开始
  • tm_yday 没有明确的理由从1开始为了方便展示,从0开始

因此,tm_mday 是唯一一种从 1 开始存储它有明显优势的情况,以便在所有常见情况下显示。

C-89 standard 中的asctime 的参考实现与此一致,对任何值的唯一调整是在tm_year 中添加1900:

     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;
     }

【讨论】:

  • tm_mon 的推理没有说服力;您也可以将所有其他东西放在一个数组中。而tm_wdaytm_yday 也不遵循“规则1”。
  • @BlueMoon 没有明确的理由说明如果 tm_wday 和 tm_yday 从 1 开始存储会更容易显示,因此规则 1 适合 IMO。
  • @Scooter,一年中的某一天没有用于任何常见的日期显示格式,因此理由不如一个月中的某一天那么引人注目。
  • Hmmm static char result[26]; sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", 对于具有大值字段的漏洞利用已经成熟。与字节成本相比,黑客的担忧并没有那么高。
  • 我觉得这不是很有说服力。大多数情况下,您会将月份显示为数字,例如2018-03-1919.3.20183/19/2018;因此,您每次都必须添加 1。
【解决方案2】:

我猜巴比伦人决定在一个月的第一天使用数字1(原因很明显),因为他们还没有发明数字0

(第 1 年是 AC 第一年的原因相同)

从那时起,没有人改变月份的编号。

【讨论】:

  • 但是为什么tm_mon 是从 0 开始而不是从 1 开始??
  • @MichaelWalz:见上面 CiaPan 的评论 (stackoverflow.com/questions/31380566/…)。索引字符串数组可能是这里的解释...
  • 嗯,第一个月被称为“一月”,而不是“0”或“1”,所以无论如何你都需要一个查找表。在 C 中,这个表最自然地使用从 0 开始的整数进行索引。
  • @eckes 但零tm_year有效。如果tm_mday1开始,因为它“不需要索引字符串数组”,那么为什么tm_yday不从1开始呢?
  • @DrKoch 娱乐:过度简化:第 10 个月 12 月 9 日、11 月 9 日至 ember、10 月 8 日和 9 月 7 日,并开始了一年在三月,所有闰年的垃圾都在二月的最后一个月。然后政客们介入了。朱利叶斯·凯撒(Julius Caesar)解决了这个问题,并于次年被暗杀。这就是修复日历所得到的。
猜你喜欢
  • 1970-01-01
  • 2011-02-16
  • 2023-03-22
  • 2017-04-17
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
相关资源
最近更新 更多