我使用tm/mktime是否错误,是否没有解决方法?

我使用tm/mktime是否错误,是否没有解决方法?

问题描述:

我认为以下程序应将每年的第一天从1AD到1970的秒数输出到1970,在其编译的系统上,time_t的大小在其前面(CHAR_BIT是宏,所以我认为您不能只是复制编译后的可执行文件并假设它是正确的,尽管实际上在今天,所有东西都使用8位char.

I think the following program should output the seconds to 1970 for the first day of every year from 1AD to 1970, preceded by the size of time_t on the system it's compiled on (CHAR_BIT is a macro so I think you can't just copy the compiled executable around and assume it's correct though in practice everything uses 8 bit chars these days).

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void do_time(int year)
{
  time_t utc;
  struct tm tp;

  memset(&tp, 0, sizeof(tp));

  tp.tm_sec = 0;
  tp.tm_min = 0;
  tp.tm_hour = 0;
  tp.tm_mday = 1;
  tp.tm_mon = 0;
  tp.tm_year = year - 1900;
  tp.tm_wday = 1;
  tp.tm_yday = 0;
  tp.tm_isdst = -1;

  printf("%d %ld\n",year, mktime(&tp));
}

int main(){
  printf("time_t is %lu bits\n",sizeof(time_t)*CHAR_BIT);
  for (int i = 1; i<1971; i++)
    do_time(i);
  exit(0);
}

但是,在OS X(10.11.3 15D21)上,即使time_t被64位签名,它也只能在> = 1902年使用.我可能理解苹果的程序员是否懒惰并且在1970年之前没有任何支持,但是正确的行为可以追溯到1902年,然后停止,这更像是我的错误.

However on OS X (10.11.3 15D21) this only works for years >= 1902, despite time_t being 64 bit signed. I could potentially understand if the programmers at Apple were lazy and didn't support any years before 1970, but correct behaviour going back to 1902 and then stopping looks more like an error on my part.

咨询C标准:

clock_ttime_t中可以表示的范围和时间精度为 实现定义的. [..]

The range and precision of times representable in clock_t and time_t are implementation-defined. [..]

[N1570§7.27.1/4](强调我的意思)

[N1570 §7.27.1/4] (emphasis mine)

再往下讲,关于mktime:

mktime函数返回指定的日历时间,该时间编码为type的值 time_t.如果无法表示日历时间,则该函数返回值 (time_t)(-1).

The mktime function returns the specified calendar time encoded as a value of type time_t. If the calendar time cannot be represented, the function returns the value (time_t)(-1).

[N1570§7.27.2.3/3]

[N1570 §7.27.2.3/3]

这样,只要mktime的返回值在不起作用的年份中为(time_t)(-1),您就可以靠自己了.

As such, as long as the return value of mktime is (time_t)(-1) for the years where it's not working ... you're on your own.

实际上,IMO,所有这些标准都比较安静:

Actually, IMO, the standard is a bit quiet about all of this:

[..] int tm_year; // years since 1900 [..]

[N1570§7.27.1/4]

[N1570 §7.27.1/4]

这可能意味着自1900年以来的(正)年,但是为什么要使用带符号整数.

This could mean (positive) years since 1900, but then why use a signed integer.

作为旁注:在我的系统(Linux 3.14.40 x86_64 glibc-2.21)上,我得到...

As a side note: On my system (Linux 3.14.40 x86_64 glibc-2.21), I get ...

time_t is 64 bits
1 -62135600008
...
1969 -31539600
1970 -3600


考虑到周围的工作:当然,您可以查看正在执行所需操作的libc实现,并尝试使用其代码(如果您需要遵守的任何许可,如果可以的话).


Considering the work around part: You can of course look at libc implementations that are doing what you want and try to use their code (if that's possible with respect to any licences you need to obey). Here's the one my system uses.