1 : /**********************************************************************
2 : * $Id: cpl_time.cpp 18063 2009-11-21 21:11:49Z warmerdam $
3 : *
4 : * Name: cpl_time.cpp
5 : * Project: CPL - Common Portability Library
6 : * Purpose: Time functions.
7 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
8 : *
9 : **********************************************************************
10 : *
11 : * CPLUnixTimeToYMDHMS() is derived from timesub() in localtime.c from openbsd/freebsd/netbsd.
12 : * CPLYMDHMSToUnixTime() has been implemented by Even Rouault and is in the public domain
13 : *
14 : * Cf http://svn.freebsd.org/viewvc/base/stable/7/lib/libc/stdtime/localtime.c?revision=178142&view=markup
15 : * localtime.c comes with the following header :
16 : *
17 : * This file is in the public domain, so clarified as of
18 : * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
19 : */
20 :
21 : #include "cpl_time.h"
22 :
23 : #define SECSPERMIN 60L
24 : #define MINSPERHOUR 60L
25 : #define HOURSPERDAY 24L
26 : #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
27 : #define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
28 : #define DAYSPERWEEK 7
29 : #define MONSPERYEAR 12
30 :
31 : #define EPOCH_YEAR 1970
32 : #define EPOCH_WDAY 4
33 : #define TM_YEAR_BASE 1900
34 : #define DAYSPERNYEAR 365
35 : #define DAYSPERLYEAR 366
36 :
37 : #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
38 : #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
39 :
40 : static const int mon_lengths[2][MONSPERYEAR] = {
41 : {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
42 : {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
43 : } ;
44 :
45 :
46 : static const int year_lengths[2] = {
47 : DAYSPERNYEAR, DAYSPERLYEAR
48 : };
49 :
50 : /************************************************************************/
51 : /* CPLUnixTimeToYMDHMS() */
52 : /************************************************************************/
53 :
54 : /** Converts a time value since the Epoch (aka "unix" time) to a broken-down UTC time.
55 : *
56 : * This function is similar to gmtime_r().
57 : * This function will always set tm_isdst to 0.
58 : *
59 : * @param unixTime number of seconds since the Epoch.
60 : * @param pRet address of the return structure.
61 : *
62 : * @return the structure pointed by pRet filled with a broken-down UTC time.
63 : */
64 :
65 116 : struct tm * CPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet)
66 : {
67 116 : GIntBig days = unixTime / SECSPERDAY;
68 116 : GIntBig rem = unixTime % SECSPERDAY;
69 :
70 248 : while (rem < 0) {
71 16 : rem += SECSPERDAY;
72 16 : --days;
73 : }
74 :
75 116 : pRet->tm_hour = (int) (rem / SECSPERHOUR);
76 116 : rem = rem % SECSPERHOUR;
77 116 : pRet->tm_min = (int) (rem / SECSPERMIN);
78 : /*
79 : ** A positive leap second requires a special
80 : ** representation. This uses "... ??:59:60" et seq.
81 : */
82 116 : pRet->tm_sec = (int) (rem % SECSPERMIN);
83 116 : pRet->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
84 116 : if (pRet->tm_wday < 0)
85 16 : pRet->tm_wday += DAYSPERWEEK;
86 116 : GIntBig y = EPOCH_YEAR;
87 : int yleap;
88 350 : while (days < 0 || days >= (GIntBig) year_lengths[yleap = isleap(y)])
89 : {
90 : GIntBig newy;
91 :
92 118 : newy = y + days / DAYSPERNYEAR;
93 118 : if (days < 0)
94 18 : --newy;
95 : days -= (newy - y) * DAYSPERNYEAR +
96 : LEAPS_THRU_END_OF(newy - 1) -
97 118 : LEAPS_THRU_END_OF(y - 1);
98 118 : y = newy;
99 : }
100 116 : pRet->tm_year = (int) (y - TM_YEAR_BASE);
101 116 : pRet->tm_yday = (int) days;
102 116 : const int* ip = mon_lengths[yleap];
103 394 : for (pRet->tm_mon = 0; days >= (GIntBig) ip[pRet->tm_mon]; ++(pRet->tm_mon))
104 278 : days = days - (GIntBig) ip[pRet->tm_mon];
105 116 : pRet->tm_mday = (int) (days + 1);
106 116 : pRet->tm_isdst = 0;
107 :
108 116 : return pRet;
109 : }
110 :
111 : /************************************************************************/
112 : /* CPLYMDHMSToUnixTime() */
113 : /************************************************************************/
114 :
115 : /** Converts a broken-down UTC time into time since the Epoch (aka "unix" time).
116 : *
117 : * This function is similar to mktime(), but the passed structure is not modified.
118 : * This function ignores the tm_wday, tm_yday and tm_isdst fields of the passed value.
119 : * No timezone shift will be applied. This function returns 0 for the 1/1/1970 00:00:00
120 : *
121 : * @param brokendowntime broken-downtime UTC time.
122 : *
123 : * @return a number of seconds since the Epoch encoded as a value of type GIntBig,
124 : * or -1 if the time cannot be represented.
125 : */
126 :
127 5738 : GIntBig CPLYMDHMSToUnixTime(const struct tm *brokendowntime)
128 : {
129 : GIntBig days;
130 : int mon;
131 :
132 5738 : if (brokendowntime->tm_mon < 0 || brokendowntime->tm_mon >= 12)
133 238 : return -1;
134 :
135 : /* Number of days of the current month */
136 5500 : days = brokendowntime->tm_mday - 1;
137 :
138 : /* Add the number of days of the current year */
139 5500 : const int* ip = mon_lengths[isleap(TM_YEAR_BASE + brokendowntime->tm_year)];
140 26622 : for(mon=0;mon<brokendowntime->tm_mon;mon++)
141 21122 : days += ip [mon];
142 :
143 : /* Add the number of days of the other years */
144 : days += (TM_YEAR_BASE + (GIntBig)brokendowntime->tm_year - EPOCH_YEAR) * DAYSPERNYEAR +
145 : LEAPS_THRU_END_OF(TM_YEAR_BASE + (GIntBig)brokendowntime->tm_year - 1) -
146 5500 : LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
147 :
148 : /* Now add the secondes, minutes and hours to the number of days since EPOCH */
149 : return brokendowntime->tm_sec +
150 : brokendowntime->tm_min * SECSPERMIN +
151 : brokendowntime->tm_hour * SECSPERHOUR +
152 5500 : days * SECSPERDAY;
153 : }
|