New module 'timegm'.
[gnulib.git] / lib / timegm.c
1 /* Convert calendar time to simple time, inverse of mktime.
2    Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2003
3    Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #if HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification.  Get mktime and gmtime declarations.  */
24 #include "timegm.h"
25
26 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
27    than local timezone.
28
29    mktime is similar but assumes struct tm, also known as the
30    "broken-down" form of time, is in local time zone.  mktime_from_utc
31    uses mktime to make the conversion understanding that an offset
32    will be introduced by the local time assumption.
33
34    mktime_from_utc then measures the introduced offset by applying
35    gmtime to the initial result and applying mktime to the resulting
36    "broken-down" form.  The difference between the two mktime results
37    is the measured offset which is then subtracted from the initial
38    mktime result to yield a calendar time which is the value returned.
39
40    tm_isdst in struct tm is set to 0 to force mktime to introduce a
41    consistent offset (the non DST offset) since tm and tm+o might be
42    on opposite sides of a DST change.
43
44    Some implementations of mktime return -1 for the nonexistent
45    localtime hour at the beginning of DST.  In this event, use
46    mktime(tm - 1hr) + 3600.
47
48    Schematically
49    mktime(tm)   --> t+o
50    gmtime(t+o)  --> tm+o
51    mktime(tm+o) --> t+2o
52    t+o - (t+2o - t+o) = t
53
54    Note that glibc contains a function of the same purpose named
55    `timegm' (reverse of gmtime).  But obviously, it is not universally
56    available, and unfortunately it is not straightforwardly
57    extractable for use here.  Perhaps configure should detect timegm
58    and use it where available.
59
60    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
61    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
62    Further improved by Roger with assistance from Edward J. Sabol
63    based on input by Jamie Zawinski.  */
64
65 time_t
66 timegm (struct tm *tm)
67 {
68   time_t tl, tb;
69   struct tm *tg;
70
71   tl = mktime (tm);
72   if (tl == (time_t) -1)
73     {
74       tm->tm_hour--;
75       tl = mktime (tm);
76       if (tl == (time_t) -1)
77         return (time_t) -1;
78       tl += 3600;
79     }
80 #if HAVE_GMTIME_R && HAVE_DECL_GMTIME_R
81   tg = gmtime_r (&tl, tg);
82 #else
83   tg = gmtime (&tl);
84 #endif
85   tg->tm_isdst = 0;
86   tb = mktime (tg);
87   if (tb == (time_t) -1)
88     {
89       tg->tm_hour--;
90       tb = mktime (tg);
91       if (tb == (time_t) -1)
92         return (time_t) -1;
93       tb += 3600;
94     }
95   return (tl - (tb - tl));
96 }