Alexis Wilke
Alexis Wilke

Reputation: 20818

Do you know of a C macro to compute Unix time and date?

I'm wondering if someone knows/has a C macro to compute a static Unix time from a hard coded date and time as in:

time_t t = UNIX_TIMESTAMP(2012, 5, 10, 9, 26, 13);

I'm looking into that because I want to have a numeric static timestamp. This will be done hundred of times throughout the software, each time with a different date, and I want to make sure it is fast because it will run hundreds of times every second. Converting dates that many times would definitively slow down things (i.e. calling mktime() is slower than having a static number compiled in place, right?)

[made an update to try to render this paragraph clearer, Nov 23, 2012]

Update

I want to clarify the question with more information about the process being used. As my server receives requests, for each request, it starts a new process. That process is constantly updated with new plugins and quite often such updates require a database update. Those must be run only once. To know whether an update is necessary, I want to use a Unix date (which is better than using a counter because a counter is much more likely to break once in a while.)

The plugins will thus receive an update signal and have their on_update() function called. There I want to do something like this:

void some_plugin::on_update(time_t last_update)
{
  if(last_update < UNIX_TIMESTAMP(2010, 3, 22, 20, 9, 26)) {
    ...run update...
  }
  if(last_update < UNIX_TIMESTAMP(2012, 5, 10, 9, 26, 13)) {
    ...run update...
  }
  // as many test as required...
}

As you can see, if I have to compute the unix timestamp each time, this could represent thousands of calls per process and if you receive 100 hits a second x 1000 calls, you wasted 100,000 calls when you could have had the compiler compute those numbers once at compile time.

Putting the value in a static variable is of no interest because this code will run once per process run.

Note that the last_update variable changes depending on the website being hit (it comes from the database.)

Code

Okay, I got the code now:

// helper (Days in February)
#define _SNAP_UNIX_TIMESTAMP_FDAY(year) \
    (((year) % 400) == 0 ? 29LL : \
        (((year) % 100) == 0 ? 28LL : \
            (((year) % 4) == 0 ? 29LL : \
                28LL)))

// helper (Days in the year)
#define _SNAP_UNIX_TIMESTAMP_YDAY(year, month, day) \
    ( \
        /* January */    static_cast<qint64>(day) \
        /* February */ + ((month) >=  2 ? 31LL : 0LL) \
        /* March */    + ((month) >=  3 ? _SNAP_UNIX_TIMESTAMP_FDAY(year) : 0LL) \
        /* April */    + ((month) >=  4 ? 31LL : 0LL) \
        /* May */      + ((month) >=  5 ? 30LL : 0LL) \
        /* June */     + ((month) >=  6 ? 31LL : 0LL) \
        /* July */     + ((month) >=  7 ? 30LL : 0LL) \
        /* August */   + ((month) >=  8 ? 31LL : 0LL) \
        /* September */+ ((month) >=  9 ? 31LL : 0LL) \
        /* October */  + ((month) >= 10 ? 30LL : 0LL) \
        /* November */ + ((month) >= 11 ? 31LL : 0LL) \
        /* December */ + ((month) >= 12 ? 30LL : 0LL) \
    )

#define SNAP_UNIX_TIMESTAMP(year, month, day, hour, minute, second) \
    ( /* time */ static_cast<qint64>(second) \
                + static_cast<qint64>(minute) * 60LL \
                + static_cast<qint64>(hour) * 3600LL \
    + /* year day (month + day) */ (_SNAP_UNIX_TIMESTAMP_YDAY(year, month, day) - 1) * 86400LL \
    + /* year */ (static_cast<qint64>(year) - 1970LL) * 31536000LL \
                + ((static_cast<qint64>(year) - 1969LL) / 4LL) * 86400LL \
                - ((static_cast<qint64>(year) - 1901LL) / 100LL) * 86400LL \
                + ((static_cast<qint64>(year) - 1601LL) / 400LL) * 86400LL )

WARNING: Do not use these macros to dynamically compute a date. It is SLOWER than mktime(). This being said, if you have a hard coded date, then the compiler will compute the time_t value at compile time. Slower to compile, but faster to execute over and over again.

Upvotes: 3

Views: 1535

Answers (3)

Karoly Horvath
Karoly Horvath

Reputation: 96316

If you measured that it's actually too slow, then here is a practical simple workaround solution:

void myfun() {
  static time_t t = 0;
  if (t == 0)
    t = slow_unix_timestamp(2012, 5, 10, 9, 26, 13);
}

Now it's calculated only once.

Upvotes: 2

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215597

The formula is in POSIX:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

Source: XBD 4.15 Seconds Since the Epoch http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15

Upvotes: 4

Chad
Chad

Reputation: 19052

Not a macro, but the timestamp will only be initialized once, subsequent calls to get_timestamp() will simple be a memory access. You pay the initialization penalty at runtime, but only the first time get_timestamp() is called, knowing that you can initialize it early in your program and allow subsequent calls to be effectively "free".

time_t initialize_timestamp(int y, int m, int d, int h, int min, s)
{
   tm t;
   t.tm_year = y - 1900;
   t.tm_mon = m;
   t.tm.mday = d;
   t.tm_hour = h;
   t.tm_min = min;
   t.tm_sec = s;

   return mktime(&t);
}

time_t get_static_timestamp()
{
   static time_t ts = initialize_timestamp(2012, 5, 10, 9, 26, 13);
   return ts;
}

Upvotes: 1

Related Questions