Addition some interval to tm structs

北城以北 提交于 2019-11-30 18:24:08

There are two functions which convert time formats:

  1. mktime() which converts struct tm (representing local time) to time_t.
  2. localtime() which converts time_t to local time in struct tm.

Interesing is the first one, which accepts out-of-range struct member values and as a side-product of conversion set them (and all others) appropriately. This may be used to correct field data values after arithmetic operations. However type of fields is int, so there may be an overflow (on 16 bit system), if e. g. you add number of seconds in the year.

So if you want to have actual dates this code would help (modified copy of answer from @pmg):

struct tm addinterval(struct tm x, int y, int m, int d) {
    x.tm_year += y;
    x.tm_mon += m;
    x.tm_mday += d;
    mktime(&x);
    return x;
}

Also note about tm_isdst member, care about it. Its value may cause time to shift forth and back when you jump over daylight time switch dates.

The standard addition operator works.

struct tm x;
/* add 2 years and 3 days to x */
x.tm_year += 2;
x.tm_mday += 3;

Edit: you can easily make a function

struct tm addinterval(struct tm x, int y, int m, int d) {
    x.tm_year += y;
    x.tm_mon += m;
    x.tm_mday += d;
    mktime(&x); /* normalize result */
    return x;
}

EDIT: added mktime to normalize result

I suggest to convert the date at hand to a number of days first. Adding an interval is trivial then. Afterwards, convert the number back to a date.

You can find algorithms for convernig a date to a number of days and back at e.g. http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html

The other answers lead to highly unstable results depending on how your system initializes struct tm and whether mid-day time values were initialized properly.

If all you are interested in is changes in the date, while the time remains the same, then set tm_isdst, tm_hour, tm_sec all to 0 before passing to mktime. Better yet, grab their values before and reset them after for consistency (and if they were inconsistent before, they'll consistently remain so). Reusing code from other answers:

tm addinterval(tm t, int y, int m, int d)
{
    auto hour = t.tm_hour;
    auto min = t.tm_min;
    auto sec = t.tm_sec;

    // First we discover the DST Flag. By setting hour to 12
    //   we can assure the mktime does not shift the date
    //   because it will shift the hour!
    t.tm_isdst = 0;
    t.tm_hour = 12;
    t.tm_min = 0;
    t.tm_sec = 0;
    mktime(&t);

    // Now we can add the interval
    t.tm_year += y;
    t.tm_mon += m;
    t.tm_mday += d;
    mktime(&t);

    // Now reset the mid-day time values
    t.tm_hour = hour;
    t.tm_min = min;
    t.tm_sec = sec;

    // Return struct tm while keeping mid-day time the same
    //   while the only values that changed are the date and perhaps isdst.
    return t;
}

I wish it was simpler, but that's just how it has to be.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!