mktime

Discussion in 'C Programming' started by Anders Wegge Keller, Dec 6, 2012.

  1. According to the C2011 draft I have available, the values in the
    struct tm, passed to mktime(), are not restricted to the normalized
    ranges. (2.27.2.3 §2).

    Is this to be taken literally, such that any combination of values in
    the tm struct, that corresponds to something that can be expressed in
    a time_t is a valid input?

    As I read the following paragrahp, that seem to be the case. Which
    lead me to think of exxtreme cases like tm_sec, tm_min, tm_hour,
    tm_mday and tm_mon all at INT_MAX, and tm_year set to a sufficiently
    large negative value.

    Am I missing something, or would a conforming implementation have to
    handle this?

    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
     
    Anders Wegge Keller, Dec 6, 2012
    #1
    1. Advertising

  2. Anders Wegge Keller

    James Kuyper Guest

    On 12/06/2012 09:25 AM, Anders Wegge Keller wrote:
    >
    > According to the C2011 draft I have available, the values in the
    > struct tm, passed to mktime(), are not restricted to the normalized
    > ranges. (2.27.2.3 §2).
    >
    > Is this to be taken literally, such that any combination of values in
    > the tm struct, that corresponds to something that can be expressed in
    > a time_t is a valid input?


    Yes. This feature, allows performance of date arithmetic. For instance,
    take a struct tm that represents 2012-12-06 09:41, subtract 7 from
    tm_mday, and pass it to mktime(), which should normalize that struct to
    describe 2012-11-29 09:41. This is, in fact, the main thing I've ever
    used mktime() for. mktime() has to know how many days there are in each
    month, which years are leap years, what day of the week each date is; so
    it saves me from having to write all that logic in my own code.

    > As I read the following paragrahp, that seem to be the case. Which
    > lead me to think of exxtreme cases like tm_sec, tm_min, tm_hour,
    > tm_mday and tm_mon all at INT_MAX, and tm_year set to a sufficiently
    > large negative value.
    >
    > Am I missing something, or would a conforming implementation have to
    > handle this?


    Yes, it would.
    --
    James Kuyper
     
    James Kuyper, Dec 6, 2012
    #2
    1. Advertising

  3. Anders Wegge Keller

    Eric Sosman Guest

    On 12/6/2012 9:25 AM, Anders Wegge Keller wrote:
    >
    > According to the C2011 draft I have available, the values in the
    > struct tm, passed to mktime(), are not restricted to the normalized
    > ranges. (2.27.2.3 §2).


    ITYM 7.27.2.3.

    > Is this to be taken literally,


    That's the usual way to take normative text ...

    > such that any combination of values in
    > the tm struct, that corresponds to something that can be expressed in
    > a time_t is a valid input?


    Yes, keeping in mind that time_t's range is implementation-
    defined (7.27.1p4). If the tm_xxx values correspond to a time_t
    value in that implementation-defined range, 7.27.2.3p3 requires
    mktime() to return that time_t value.

    Clearly, the intent is to relieve the programmer of the burden
    of all that date arithmetic. If you've got a struct tm representing
    some time T and you want to determine the time at T plus 01:23:45,
    you can blithely add 1*3600+23*60+45 to the tm_sec field and call
    mktime(), and mktime() will figure things out.

    > As I read the following paragrahp, that seem to be the case. Which
    > lead me to think of exxtreme cases like tm_sec, tm_min, tm_hour,
    > tm_mday and tm_mon all at INT_MAX, and tm_year set to a sufficiently
    > large negative value.
    >
    > Am I missing something, or would a conforming implementation have to
    > handle this?


    I think so. The only out I can imagine is that if mktime()
    cannot determine whether Standard or Daylight time prevails, it
    may be unable to find "the" corresponding time_t value and could
    then return (time_t)-1 to indicate failure. But if the question
    boils down to "Must mktime() be careful about overflow in its
    internal computations?" I think the answer must be "Yes."

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 6, 2012
    #3
  4. Eric Sosman <> writes:

    > On 12/6/2012 9:25 AM, Anders Wegge Keller wrote:


    >> According to the C2011 draft I have available, the values in the
    >> struct tm, passed to mktime(), are not restricted to the normalized
    >> ranges. (2.27.2.3 §2).


    > ITYM 7.27.2.3.


    yes.

    >> Is this to be taken literally,


    > That's the usual way to take normative text ...


    As for legislative works, the wording of normative standards doesn't
    always mean what the un-initiated takes them to mean.

    ...

    > then return (time_t)-1 to indicate failure. But if the question
    > boils down to "Must mktime() be careful about overflow in its
    > internal computations?" I think the answer must be "Yes."


    I guess my question can be reduced to exactly that.

    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
     
    Anders Wegge Keller, Dec 6, 2012
    #4
  5. Anders Wegge Keller

    Eric Sosman Guest

    On 12/6/2012 11:40 AM, Anders Wegge Keller wrote:
    > Eric Sosman <> writes:
    >> [...]
    >> then return (time_t)-1 to indicate failure. But if the question
    >> boils down to "Must mktime() be careful about overflow in its
    >> internal computations?" I think the answer must be "Yes."

    >
    > I guess my question can be reduced to exactly that.


    Okay: I'm sticking with "Yes."

    FWIW, PJ Plauger seems to agree (he's non-normative, but since
    he served on the library subcommittee for the original ANSI Standard,
    he's a pretty good indicator of intent). In "The Standard C Library"
    he exhibits a sample implementation of mktime(), and in describing
    its underpinnings he writes

    "You can start with the year 2000, back up 2,000 months,
    and advance 2 billion seconds, for example. [...] The
    logic is carefully crafted to avoid integer overflow
    regardless of argument values."

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 6, 2012
    #5
  6. Eric Sosman <> writes:
    [...]
    > Clearly, the intent is to relieve the programmer of the burden
    > of all that date arithmetic. If you've got a struct tm representing
    > some time T and you want to determine the time at T plus 01:23:45,
    > you can blithely add 1*3600+23*60+45 to the tm_sec field and call
    > mktime(), and mktime() will figure things out.

    [...]

    Or you can add 1 to tm_hour, 23 to tm_min, and 45 to tm_sec, and let
    mktime() figure *everything* out.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Dec 6, 2012
    #6
  7. Eric Sosman <> writes:

    > On 12/6/2012 11:40 AM, Anders Wegge Keller wrote:
    >> Eric Sosman <> writes:
    >>> [...]
    >>> then return (time_t)-1 to indicate failure. But if the question
    >>> boils down to "Must mktime() be careful about overflow in its
    >>> internal computations?" I think the answer must be "Yes."

    >>
    >> I guess my question can be reduced to exactly that.

    >
    > Okay: I'm sticking with "Yes."
    >
    > FWIW, PJ Plauger seems to agree (he's non-normative, but since
    > he served on the library subcommittee for the original ANSI Standard,
    > he's a pretty good indicator of intent). In "The Standard C Library"
    > he exhibits a sample implementation of mktime(), and in describing
    > its underpinnings he writes
    >
    > "You can start with the year 2000, back up 2,000 months,
    > and advance 2 billion seconds, for example. [...] The
    > logic is carefully crafted to avoid integer overflow
    > regardless of argument values."


    You can advance 2 billion seconds *if* int is at least 32 bit (tm_sec is
    of type int).

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Dec 6, 2012
    #7
  8. Keith Thompson <> writes:

    > Eric Sosman <> writes:
    > [...]
    > > Clearly, the intent is to relieve the programmer of the burden
    > > of all that date arithmetic. If you've got a struct tm representing
    > > some time T and you want to determine the time at T plus 01:23:45,
    > > you can blithely add 1*3600+23*60+45 to the tm_sec field and call
    > > mktime(), and mktime() will figure things out.

    > [...]
    >
    > Or you can add 1 to tm_hour, 23 to tm_min, and 45 to tm_sec, and let
    > mktime() figure *everything* out.


    Yep :)

    I should have thought about this possibility 10 years ago, and spared
    myself a lot of trouble.

    --
    /Wegge

    Leder efter redundant peering af dk.*,linux.debian.*
     
    Anders Wegge Keller, Dec 6, 2012
    #8
  9. Anders Wegge Keller

    Eric Sosman Guest

    On 12/6/2012 3:28 PM, Keith Thompson wrote:
    > Eric Sosman <> writes:
    >
    >> FWIW, PJ Plauger seems to agree (he's non-normative, but since
    >> he served on the library subcommittee for the original ANSI Standard,
    >> he's a pretty good indicator of intent). In "The Standard C Library"
    >> he exhibits a sample implementation of mktime(), and in describing
    >> its underpinnings he writes
    >>
    >> "You can start with the year 2000, back up 2,000 months,
    >> and advance 2 billion seconds, for example. [...] The
    >> logic is carefully crafted to avoid integer overflow
    >> regardless of argument values."

    >
    > You can advance 2 billion seconds *if* int is at least 32 bit (tm_sec is
    > of type int).


    Don't complain to me; complain to the quoted author. Besides:

    for (int count = 0; count < 100; ++count) {
    mktime(tm);
    tm->tm_sec += 20000;
    }
    mktime(tm);
    printf("So there, nyaaah!\n");

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 6, 2012
    #9
  10. Anders Wegge Keller

    Eric Sosman Guest

    On 12/6/2012 5:36 PM, Eric Sosman wrote:
    > On 12/6/2012 3:28 PM, Keith Thompson wrote:
    >> Eric Sosman <> writes:
    >>
    >>> FWIW, PJ Plauger seems to agree (he's non-normative, but since
    >>> he served on the library subcommittee for the original ANSI Standard,
    >>> he's a pretty good indicator of intent). In "The Standard C Library"
    >>> he exhibits a sample implementation of mktime(), and in describing
    >>> its underpinnings he writes
    >>>
    >>> "You can start with the year 2000, back up 2,000 months,
    >>> and advance 2 billion seconds, for example. [...] The
    >>> logic is carefully crafted to avoid integer overflow
    >>> regardless of argument values."

    >>
    >> You can advance 2 billion seconds *if* int is at least 32 bit (tm_sec is
    >> of type int).

    >
    > Don't complain to me; complain to the quoted author. Besides:
    >
    > for (int count = 0; count < 100; ++count) {
    > mktime(tm);
    > tm->tm_sec += 20000;
    > }
    > mktime(tm);
    > printf("So there, nyaaah!\n");


    Oh, ratz.

    for (int a = 0; a < 1000; ++a) {
    for (int b = 0; b < 1000; ++b) {
    mktime(tm);
    tm->tm_sec += 2000;
    }
    }
    mktime(tm);
    printf("So there, this time for sure!\n");

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 6, 2012
    #10
  11. "Keith Thompson" <> schrieb im Newsbeitrag
    news:...
    > Eric Sosman <> writes:
    > [...]
    >> Clearly, the intent is to relieve the programmer of the burden
    >> of all that date arithmetic. If you've got a struct tm representing
    >> some time T and you want to determine the time at T plus 01:23:45,
    >> you can blithely add 1*3600+23*60+45 to the tm_sec field and call
    >> mktime(), and mktime() will figure things out.

    > [...]
    >
    > Or you can add 1 to tm_hour, 23 to tm_min, and 45 to tm_sec, and let
    > mktime() figure *everything* out.


    These may result in different values around a leap second (at the end of
    some years) or around the transition from normal time to daylight savings
    time or vice versa.
     
    Heinrich Wolf, Dec 7, 2012
    #11
  12. Anders Wegge Keller

    James Kuyper Guest

    On 12/07/2012 03:48 AM, Heinrich Wolf wrote:
    >
    > "Keith Thompson" <> schrieb im Newsbeitrag
    > news:...
    >> Eric Sosman <> writes:
    >> [...]
    >>> Clearly, the intent is to relieve the programmer of the burden
    >>> of all that date arithmetic. If you've got a struct tm representing
    >>> some time T and you want to determine the time at T plus 01:23:45,
    >>> you can blithely add 1*3600+23*60+45 to the tm_sec field and call
    >>> mktime(), and mktime() will figure things out.

    >> [...]
    >>
    >> Or you can add 1 to tm_hour, 23 to tm_min, and 45 to tm_sec, and let
    >> mktime() figure *everything* out.

    >
    > These may result in different values around a leap second (at the end of
    > some years) or around the transition from normal time to daylight savings
    > time or vice versa.


    Yes - and I think the different result you would get in those cases may
    often be the one that was actually intended.


    --
    James Kuyper
     
    James Kuyper, Dec 7, 2012
    #12
  13. Anders Wegge Keller

    James Kuyper Guest

    On 12/07/2012 12:23 PM, christian.bau wrote:
    > Question: If you start with 28th February 2012, then adding one year,
    > followed by adding two days, will get you 28th Feb 2013, then 2nd Mar
    > 2013. But adding two days, followed by adding one year, will get you
    > 1st Mar 2012, followed by 1st Mar 2013. Not the same.
    >
    > Are there rules for this kind of thing? Is it unspecified or
    > undefined?
    >
    > What's supposed to happen if you add one month to March 31st 2012? The
    > result can't be April 31st; is it April 30th, May 1st, or something
    > else?


    No, the rules for such things aren't specified, and that lack of
    specification is a problem.

    given:
    #include <time.h>
    // Two different ways to describe 2012-03-31:
    struct tm a = {.tm_year=2012-1900, .tm_month = 3-1, .tm_day = 31};
    struct tm b = {.tm_year=2012-1900, .tm_month = 4-1, .tm_day = 0};
    a.tm_month += 1;
    mktime(&a);
    b.tm_month += 1;
    mktime(&b);

    My preference would be that this point, 'a' represents 2012-05-01, while
    'b' represents 2012-04-30. But the standard doesn't address such questions.
     
    James Kuyper, Dec 7, 2012
    #13
  14. Anders Wegge Keller

    Eric Sosman Guest

    On 12/7/2012 12:23 PM, christian.bau wrote:
    > Question: If you start with 28th February 2012, then adding one year,
    > followed by adding two days, will get you 28th Feb 2013, then 2nd Mar
    > 2013. But adding two days, followed by adding one year, will get you
    > 1st Mar 2012, followed by 1st Mar 2013. Not the same.


    mktime() converts dates, which is not the same as doing
    arithmetic on them. You may imagine that you are "adding one
    year" to 2012-02-28, and you may even use ...tm_year++ to do
    it, but when you pass the struct to mktime() it doesn't know
    the history of the tm_year value. All it sees is tm_year = 113
    (2013 - 1900), and it says "Here I am in 2013; now let's look
    at the other fields." If tm_mday is 30, mktime() will make an
    adjustment based on the length of 2013's February; it has no
    way of knowing that the struct once held a different tm_year.

    > Are there rules for this kind of thing? Is it unspecified or
    > undefined?


    The only rule I see is 7.27.2.2p2: "[...] the final value
    of tm_mday is not set until tm_mon and tm_year are determined,"
    which I understand as meaning that day-of-month yields to
    month and year rather than the other way around.

    > What's supposed to happen if you add one month to March 31st 2012? The
    > result can't be April 31st; is it April 30th, May 1st, or something
    > else?


    Again, mktime() can't know what values formerly occupied the
    struct fields before the current ones were stored. All it sees
    is that tm_mon = 3 (April) and tm_mday = 31; it knows that 31 is
    out of range for April and adjusts to May 1.

    You could certainly get strange results by doing "the same"
    set of adjustments in sequence, with intervening mktime() calls:

    struct tm x = ...; // 2012-02-29
    x.tm_mon++; // 2012-03-29
    mktime(&x); // 2012-03-29
    x.tm_mday++; // 2012-03-30
    mktime(&x); // 2012-03-30

    struct tm y = ...; // 2012-02-29
    y.tm_mday++; // 2012-02-30
    mktime(&y); // 2012-03-01 corrected
    y.tm_mon++; // 2012-04-01
    mktime(&y); // 2012-04-01, April Fool!

    Each mktime() call has a well-defined outcome, yet the two
    sequences lead to different results -- Well, that's just the
    way our creaky old calendar crumbles. Still, the creakiness
    shouldn't be too terribly surprising to a C programmer, who's
    already sensitive to the fact that Order Matters:

    int x = 12 / 5 * 3; // 6
    int y = 12 * 3 / 5; // 7

    If you're comfortable with the fact that doing "the same"
    multiplications and divisions in different orders yields
    different outcomes, maybe you can reconcile yourself to the
    similar situation with date adjustments.

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 7, 2012
    #14
  15. "christian.bau" <> schrieb im Newsbeitrag
    news:...
    > Question: If you start with 28th February 2012, then adding one year,
    > followed by adding two days, will get you 28th Feb 2013, then 2nd Mar
    > 2013. But adding two days, followed by adding one year, will get you
    > 1st Mar 2012, followed by 1st Mar 2013. Not the same.
    >
    > Are there rules for this kind of thing? Is it unspecified or
    > undefined?
    >
    > What's supposed to happen if you add one month to March 31st 2012? The
    > result can't be April 31st; is it April 30th, May 1st, or something
    > else?


    I tried with my Borland C++Builder 5 on Windows XP. But that is only one of
    many implementations.

    2012-2-28 + 1-0-0 = 2013-2-28
    2013-2-28 + 0-0-2 = mktime(2013-2-30) = 2013-3-2

    2012-2-28 + 0-0-2 = mktime(2012-2-30) = 2012-3-1
    2012-3-1 + 1-0-0 = 2013-3-1

    2012-2-28 + 1-0-2 = mktime(2013-2-30) = 2013-3-2

    2012-3-31 + 0-1-0 = mktime(2012-4-31) = 2012-5-1
     
    Heinrich Wolf, Dec 7, 2012
    #15
  16. "Heinrich Wolf" <> schrieb im Newsbeitrag
    news:k9saf7$jn$-online.net...
    ....
    > These may result in different values around a leap second (at the end of
    > some years) or around the transition from normal time to daylight savings
    > time or vice versa.

    ....
    My Fedora 14 man page for mktime tells me that leap seconds exist, but is
    there anybody out there who has seen them on a system?

    My Fedora 14 man page for mktime in German:
    ....
    Die Elemente der Struktur tm sind:

    tm_sec Die Anzahl der Sekunden nach der vollen Minute,
    normalerweise im Bereich 0 bis 59, jedoch in Ausnahmefällen bis 61 um
    Schaltsekunden zu
    erlauben.
    ....
    Translated into English:
    ....
    The elements of struct tm are:

    tm_sec The number of seconds after the full minute, normally in the
    range 0 to 59, but in exceptional cases up to 61 in order to allow leap
    seconds.
    ....

    I searched for leap seconds on 2 different systems: Fedora 14 Linux, and
    Windows XP with Borland C++Builder 5.
    I expected the calculation localtime(mktime(1970-1-2 00-00-00) + 10000 * 24
    * 60 * 60 seconds) to result in a tm struct with some odd seconds, but on
    both systems I got 1997-05-20 01-00-00
     
    Heinrich Wolf, Dec 7, 2012
    #16
  17. Anders Wegge Keller

    Eric Sosman Guest

    On 12/7/2012 3:39 PM, Heinrich Wolf wrote:
    >
    > "Heinrich Wolf" <> schrieb im Newsbeitrag
    > news:k9saf7$jn$-online.net...
    > ...
    >> These may result in different values around a leap second (at the end
    >> of some years) or around the transition from normal time to daylight
    >> savings time or vice versa.

    > ...
    > My Fedora 14 man page for mktime tells me that leap seconds exist, but
    > is there anybody out there who has seen them on a system?
    >
    > My Fedora 14 man page for mktime in German:
    > ...
    > Die Elemente der Struktur tm sind:
    >
    > tm_sec Die Anzahl der Sekunden nach der vollen Minute,
    > normalerweise im Bereich 0 bis 59, jedoch in Ausnahmefällen bis 61 um
    > Schaltsekunden zu
    > erlauben.
    > ...
    > Translated into English:
    > ...
    > The elements of struct tm are:
    >
    > tm_sec The number of seconds after the full minute, normally in
    > the range 0 to 59, but in exceptional cases up to 61 in order to allow
    > leap seconds.
    > ...
    >
    > I searched for leap seconds on 2 different systems: Fedora 14 Linux, and
    > Windows XP with Borland C++Builder 5.
    > I expected the calculation localtime(mktime(1970-1-2 00-00-00) + 10000 *
    > 24 * 60 * 60 seconds) to result in a tm struct with some odd seconds,
    > but on both systems I got 1997-05-20 01-00-00


    I remember seeing the [0..61] range for tm_sec long ago, and
    perhaps it still persists in some non-C documents. Even in the
    original 1989 ANSI C Standard, though, the range was [0..60] in
    keeping with the fact that leap seconds are added (or removed)
    one at a time, never two at once.

    As to the calculation -- well, timekeeping is complicated.
    POSIX *defines* the day as 86400 seconds, intentionally ignoring
    leap seconds (see <http://en.wikipedia.org/wiki/Unix_time>), so
    we should not expect a C implementation based on POSIX time to
    generate tm_sec==60. The C Standard does not require the library
    to overcome this problem:

    7.27.1p4: "The range and precision of times representable
    in clock_t and time_t are implementation-defined."

    7.27.2.4p3: "The time function returns the implementation’s
    *best approximation* [emphasis mine] to the current calendar
    time."

    Timekeeping and time calculation on POSIX and POSIX-like systems
    is "close enough for jazz," sufficiently accurate for many purposes
    but not astonishingly good. C implementations don't have to do any
    better (and on a strictly-conforming POSIX system, I guess they're
    actually forbidden to do better).

    --
    Eric Sosman
    d
     
    Eric Sosman, Dec 7, 2012
    #17
  18. Anders Wegge Keller

    Alan Curry Guest

    In article <k9tk5a$caq$-online.net>,
    Heinrich Wolf <> wrote:
    >
    >I searched for leap seconds on 2 different systems: Fedora 14 Linux, and
    >Windows XP with Borland C++Builder 5.
    >I expected the calculation localtime(mktime(1970-1-2 00-00-00) + 10000 * 24
    >* 60 * 60 seconds) to result in a tm struct with some odd seconds, but on
    >both systems I got 1997-05-20 01-00-00
    >


    On unix-like systems the time_t has two interpretations, "posix" and "right".
    The posix time_t leaps with the leap seconds, making them mostly invisible
    unless your program is running during a leap. Adding 86400 to a posix time_t
    always gets you the same time the next day (plus or minus an hour for
    daylight savings).

    With the posix time_t, mktime covers up evidence of leap seconds especially
    well. You can set a struct tm to out-of-range 23:59:60 on a non-leap-second
    day and mktime will normalize it to 0:00:00 the next day and return the exact
    same time_t it would have returned for the legitimate 23:59:60 if the day had
    been a leap second day.

    If you indicate with the TZ environment variable that you want the "right"
    time, then mktime and localtime will adjust their behavior and the expected
    odd seconds will show up.

    Here's a program demonstrating the differences:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <stdint.h>

    static void showtm(int year, int mon, int day, int hour, int min, int sec)
    {
    time_t t;
    struct tm tm = {
    .tm_year = year-1900,
    .tm_mon = mon-1,
    .tm_mday = day,
    .tm_hour = hour,
    .tm_min = min,
    .tm_sec = sec
    };
    t = mktime(&tm);
    printf("%d-%d-%d %d:%02d:%02d = %ju\n",
    year, mon, day, hour, min, sec, (uintmax_t)t);
    }

    int main(void)
    {
    time_t now;
    struct tm *nowtm;
    time_t tenyearsago;
    struct tm tenyearsagotm;
    const char *tzlist[] = { "posix/Etc/GMT", "right/Etc/GMT" };
    int i;

    for(i=0;i<2;++i) {
    printf("%sTZ=%s\n", i?"\n":"", tzlist);
    setenv("TZ", tzlist, 1);
    tzset();

    now = time(0);
    nowtm = localtime(&now);

    tenyearsagotm = *nowtm;
    tenyearsagotm.tm_year -= 12;
    tenyearsago = mktime(&tenyearsagotm);

    printf("Days between now and this time 12 years ago: %.10f\n",
    (now-tenyearsago)/86400.);

    printf("This is a leap second:\n");
    showtm(2012, 6, 30, 23, 59, 59);
    showtm(2012, 6, 30, 23, 59, 60);
    showtm(2012, 7, 1, 0, 0, 0);
    printf("This is not a leap second:\n");
    showtm(2011, 6, 30, 23, 59, 59);
    showtm(2011, 6, 30, 23, 59, 60);
    showtm(2011, 7, 1, 0, 0, 0);
    }

    return 0;
    }

    --
    Alan Curry
     
    Alan Curry, Dec 7, 2012
    #18
  19. Eric Sosman <> writes:
    > On 12/7/2012 3:39 PM, Heinrich Wolf wrote:

    [...]
    >> My Fedora 14 man page for mktime in German:
    >> ...
    >> Die Elemente der Struktur tm sind:
    >>
    >> tm_sec Die Anzahl der Sekunden nach der vollen Minute,
    >> normalerweise im Bereich 0 bis 59, jedoch in Ausnahmefällen bis 61 um
    >> Schaltsekunden zu
    >> erlauben.
    >> ...
    >> Translated into English:
    >> ...
    >> The elements of struct tm are:
    >>
    >> tm_sec The number of seconds after the full minute, normally in
    >> the range 0 to 59, but in exceptional cases up to 61 in order to allow
    >> leap seconds.
    >> ...
    >>
    >> I searched for leap seconds on 2 different systems: Fedora 14 Linux, and
    >> Windows XP with Borland C++Builder 5.
    >> I expected the calculation localtime(mktime(1970-1-2 00-00-00) + 10000 *
    >> 24 * 60 * 60 seconds) to result in a tm struct with some odd seconds,
    >> but on both systems I got 1997-05-20 01-00-00

    >
    > I remember seeing the [0..61] range for tm_sec long ago, and
    > perhaps it still persists in some non-C documents. Even in the
    > original 1989 ANSI C Standard, though, the range was [0..60] in
    > keeping with the fact that leap seconds are added (or removed)
    > one at a time, never two at once.


    The 1990 ISO C standard specifies a range of [0, 61] for tm_sec:

    int tm_sec; /* seconds after the minute -- [0, 61] */

    with a footnote:

    The range [0,61] for tm_sec allows for as many as two leap seconds.

    C99 changes the range to [0, 60] and changes the footnote to:

    The range [0, 60] for tm_sec allows for a positive leap second.

    There's no mention of the change in the C99 rationale.

    I've heard (not sure where) that the [0, 61] range in C90 was
    the result of a misunderstanding; if it were necessary to have
    two leap seconds in a year (which hasn't happened so far) they'd
    be added at the end of June and the end of December. I *guess*
    that if more than two were required, they'd be spread more or less
    evenly through the year.

    Are you *sure* that the 1989 ANSI standard specifies [0, 60]?
    If so, that would be an inconsistency between ANSI C89 and ISO C90,
    and I didn't think there were any.

    Interestingly, a 1988 ANSI C draft ("ansi.c.txt", I'm not sure
    where I got it) shows a range of [0, 60].

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Dec 8, 2012
    #19
  20. Thanks a lot for the code.

    With posix TZ the days between 12 years is an integer.
    With right TZ the days between 12 years have a fractional part.

    showtm gives different time_t values for posix TZ and right TZ.

    However the time_t is always the same
    for 6-30 23:59:60 and 7-1 0:00:00
    regardless of the year (leap second or not).
    Maybe my Fedora 14 is too old and does not know about the leap second
    on 2012-6-30?
     
    Heinrich Wolf, Dec 8, 2012
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Florian Quetting

    mktime segfault

    Florian Quetting, Dec 31, 2004, in forum: C++
    Replies:
    2
    Views:
    524
    Florian Quetting
    Dec 31, 2004
  2. Rajmohan

    GMT equivalent of mktime

    Rajmohan, Jul 14, 2003, in forum: C Programming
    Replies:
    1
    Views:
    894
    Mark McIntyre
    Jul 14, 2003
  3. Gore

    mktime and DST.

    Gore, Oct 7, 2003, in forum: C Programming
    Replies:
    1
    Views:
    4,288
    Richard Bos
    Oct 7, 2003
  4. Bengt Richter

    time.mktime memory access violation bug

    Bengt Richter, Nov 18, 2003, in forum: Python
    Replies:
    6
    Views:
    704
    Bengt Richter
    Nov 21, 2003
  5. McBooCzech

    time.mktime problem

    McBooCzech, Aug 30, 2005, in forum: Python
    Replies:
    4
    Views:
    882
    Edvard Majakari
    Sep 5, 2005
Loading...

Share This Page