3

I tried using the <time.h> library but it seems like <time.h> doesn't really support times dating back earlier than 1900 and possibly further than some x year.

My questions here are:

  1. Can I use <time.h> to compute time difference (in seconds, hours or days) between, e.g.:

    May 1st 2744y and January 24th 566y

  2. Does <time.h> support leap years? Meaning if I'd calculate the difference above - would that count leap years? I can obviously make something like:

    int toDays(struct tm *date)
    {
        int days = date->tm_yday;
        int year;
        for (year = 1; year < date->tm_year; ++year)
        {
            days += isLeapYear(year) ? 366 : 365;
        }
        return days;
    }
    

But then again - tm_year counts from 1900, am I right?

To be honest, I have no understanding of this structure and I will probably write one on my own, unless someone can help me on this. Is there a point in using <time.h> when I want to compute years like in question 1?

cadaniluk
  • 15,027
  • 2
  • 39
  • 67
Ernio
  • 948
  • 10
  • 25
  • 1
    It depends on your O/S and the library, and the 'bittiness' of your machine. 32-bit systems may have problems outside the range 1902..2037 (32-bit signed integers based on 1970-01-01 00:00:00 +00:00 as the epoch). 64-bit systems often have a 64-bit `time_t` type and the date range on those is humongous (death of the universe type scale). Yes, the functions know about leap years. Whether they understand all the vagaries of when which area of the world changed from Julian to Gregorian calendar (if, indeed, it ever followed either) is a separate discussion. Time zones are complex too! – Jonathan Leffler Nov 02 '15 at 07:56
  • Possible duplicate of [How to print time difference in accuracy of milliseconds and nanoseconds?](http://stackoverflow.com/questions/16275444/how-to-print-time-difference-in-accuracy-of-milliseconds-and-nanoseconds) – Oleksandr Kravchuk Nov 02 '15 at 07:57
  • @OleksandrKravchuk: Why that as a duplicate? This is about whole seconds, not milliseconds or nanoseconds, isn't it? – Jonathan Leffler Nov 02 '15 at 07:59
  • What is the significance of the `y` in `2744y` and `566y`? Do you mean that the years are in the 28th and 6th millennia? Or the years are 2744 CE and 566 CE (where CE is also known as AD; it is the Gregorian calendar)? Or something else? – Jonathan Leffler Nov 02 '15 at 08:02
  • It also depends on the version of the tz database you have installed (probably already coming with your OS). `difftime()` is essentially a trivial substraction (with some quirks). Leaps and such are handled via the `time_t` <-> `struct tm` conversion and that depends on information from the time zone database. – dhke Nov 02 '15 at 08:03
  • 3
    If you are going back in time to dates like 24 Jan 566, you should be aware that early dates don't use the Gregorian calendar. – M Oehm Nov 02 '15 at 08:04
  • @MOehm You don't have to go that far back. Dates are probably wrong for e.g. Alaska before 1867-10-18 (the day before that one is 1867-10-06 Julian). – dhke Nov 02 '15 at 08:11
  • @dhke: Wikipedia says Greece adopted it as late as 1932. And the ouput of `cal 9 1752` in Linux is a nice gimmick, but it has arbitrarily picked Britain as locale. I just wanted to point out that date calculations for the past can have several snags. – M Oehm Nov 02 '15 at 08:19

3 Answers3

1

You can try difftime() function.

First you have to define two structures which can hold the desired data e.g.

struct tm start_date, end_date;

Then fill the structure with the data as per your date.

Then use difftime() as

seconds = difftime(mktime(&end_date),mktime(&start_date))

Following example will help you in understanding the flow.

#include<stdio.h>
#include<time.h>
int main()
{
   time_t now;
   struct tm start_date, end_date;
   start_date = *localtime(&now);
   end_date = *localtime(&now);

   start_date.tm_year = 1013;
   end_date.tm_year = 1015;

   unsigned long int diff = difftime(mktime(&end_date), mktime(&start_date));
   printf("DIFF: [%lu]",diff);
   return(0);
}
Pawan
  • 1,537
  • 1
  • 15
  • 19
1

If you have a 64-bit system such as Mac OS X (10.11 El Capitan used), then this works:

#include <inttypes.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
    int y1 = 27440;
    int m1 = 5;
    int d1 = 1;
    int y2 = 5660;
    int m2 = 1;
    int d2 = 24;
    struct tm tm1 = { .tm_year = y1 - 1900, .tm_mon = m1 - 1, .tm_mday = d1 };
    struct tm tm2 = { .tm_year = y2 - 1900, .tm_mon = m2 - 1, .tm_mday = d2 };
    time_t t1 = mktime(&tm1);
    time_t t2 = mktime(&tm2);
    size_t dt = t1 - t2;       // Dodgy assignment…I get away with it, but…

    char buffer[128];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm1);
    printf("t1 = %20s\n", buffer);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm2);
    printf("t2 = %20s\n", buffer);

    printf("t1 = %" PRIdMAX "\n", (intmax_t)t1);
    printf("t2 = %" PRIdMAX "\n", (intmax_t)t2);
    printf("dt = %zu seconds\n", dt);
    printf("dt = %zu hours\n", dt / 3600);
    printf("dt = %zu days\n", dt / (24 * 3600));
    return 0;
}

The output I get is:

t1 = 27440-05-01 00:00:00
t2 =  5660-01-24 00:00:00
t1 = 803766009600
t2 = 116447184000
dt = 687318825600 seconds
dt = 190921896 hours
dt = 7955079 days

Things are not quite so good trying to go retrospective. Dividing both year values by 10 yields:

t1 =  2744-05-01 00:00:00
t2 =  0566-01-24 00:00:00
t1 = 24435504000
t2 = -1
dt = 24435504001 seconds
dt = 6787640 hours
dt = 282818 days

Note that the -1 indicates an error; the system is not willing to play with dates in the first millennium (which renders the dt calculations inaccurate). AFAICT, on a Mac, mktime() does not go further back in time than 32-bit signed values could go — it accepts 1902-01-01 but rejects 1901-01-01. The 32-bit limit is:

-2147483647 = Fri Dec 13 12:45:53 1901  (US/Pacific)

Test code:

static int test_year(int year)
{
    struct tm tm1 = { .tm_year = year - 1900, .tm_mon = 0, .tm_mday = 1 };
    time_t t1 = mktime(&tm1);
    return (t1 != -1);
}

static void early_year(void)
{
    int y_lo =  566;
    int y_hi = 1902;
    assert(test_year(y_lo) == 0);
    assert(test_year(y_hi) == 1);

    while (y_lo != y_hi)
    {
        int y_md = (y_lo + y_hi) / 2;
        printf("lo = %4d; hi = %4d; md = %4d\n", y_lo, y_hi, y_md);
        if (test_year(y_md) == 0)
            y_lo = y_md + 1;
        else
            y_hi = y_md - 1;
    }
    printf("Valid back to %4d\n", y_lo);
}

Result of calling that code:

lo =  566; hi = 1902; md = 1234
lo = 1235; hi = 1902; md = 1568
lo = 1569; hi = 1902; md = 1735
lo = 1736; hi = 1902; md = 1819
lo = 1820; hi = 1902; md = 1861
lo = 1862; hi = 1902; md = 1882
lo = 1883; hi = 1902; md = 1892
lo = 1893; hi = 1902; md = 1897
lo = 1898; hi = 1902; md = 1900
lo = 1901; hi = 1902; md = 1901
Valid back to 1902

YMMV, as the saying goes; it will depend on the system you're working on. Note that the further back you go in time, the less dependable clocks and calendars become. Ignoring such niceties as 30th February 1712 (in Sweden), you get a wide variety of dates between 1584 and the 20th Century for dates when countries switched from the Julian calendar to the Gregorian calendar (1752 was the switch date for Britain and her colonies, for example). People typically apply the 'proleptic Gregorian' calendar backwards.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0
  1. The data range time.h functions work varies amongst systems. It is reasonable to assume they work for 1970-2037. Many systems work over a wider range. OP's seems to work as early as 1900.

  2. The dominant calender in use today: the Gregorian begin 1582. Its adoption through the world varies. So a date like January 24th 566y needs qualification.

Let us assume OP's time works over 2000 to 2400 and all dates are Gregorian, projected backwards and forwards as needed:

Use the "trick" that 400 Gregorian years is 400*365+97 days (this is a multiple of 7).

So to get the # of days using OP's function, which hopefully works over the 400 year range 2000-2400:

long long day_number(int year, int, month, int day) {
  #define DaysPer400Year (400LL*365 + 97)
  long long yearll = year - 2000LL;
  year = yearll % 400;
  int century400 =  yearll/400;
  if (year < 0) {
    year += 400;
    century400--;
  }
  long long number = century400 * DaysPer400Year;
  number += OP_day_number(year + 2000, month, day);
  return number;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256