Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Am I using tm/mktime wrong, and if not is there a workaround?

Tags:

c

time

libc

mktime

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

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.

like image 218
Camden Narzt Avatar asked Sep 01 '25 11:09

Camden Narzt


1 Answers

Consulting the C standard:

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

[N1570 §7.27.1/4] (emphasis mine)

And further down, regarding mktime:

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]

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.


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

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

[N1570 §7.27.1/4]

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


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

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.

like image 80
Daniel Jour Avatar answered Sep 04 '25 03:09

Daniel Jour