1 /*
2  *  Copyright 2005-2014 Fabrice Colin
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include "config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #ifdef HAVE_TIMEGM
23 #ifndef _XOPEN_SOURCE
24 #define _XOPEN_SOURCE
25 #include <time.h>
26 #undef _XOPEN_SOURCE
27 #else
28 #include <time.h>
29 #endif
30 #else
31 #include <time.h>
32 /* This comment and function are taken from Wget's mktime_from_utc()
33    Converts struct tm to time_t, assuming the data in tm is UTC rather
34    than local timezone.
35 
36    mktime is similar but assumes struct tm, also known as the
37    "broken-down" form of time, is in local time zone.  mktime_from_utc
38    uses mktime to make the conversion understanding that an offset
39    will be introduced by the local time assumption.
40 
41    mktime_from_utc then measures the introduced offset by applying
42    gmtime to the initial result and applying mktime to the resulting
43    "broken-down" form.  The difference between the two mktime results
44    is the measured offset which is then subtracted from the initial
45    mktime result to yield a calendar time which is the value returned.
46 
47    tm_isdst in struct tm is set to 0 to force mktime to introduce a
48    consistent offset (the non DST offset) since tm and tm+o might be
49    on opposite sides of a DST change.
50 
51    Some implementations of mktime return -1 for the nonexistent
52    localtime hour at the beginning of DST.  In this event, use
53    mktime(tm - 1hr) + 3600.
54 
55    Schematically
56      mktime(tm)   --> t+o
57      gmtime(t+o)  --> tm+o
58      mktime(tm+o) --> t+2o
59      t+o - (t+2o - t+o) = t
60 
61    Note that glibc contains a function of the same purpose named
62    `timegm' (reverse of gmtime).  But obviously, it is not universally
63    available, and unfortunately it is not straightforwardly
64    extractable for use here.  Perhaps configure should detect timegm
65    and use it where available.
66 
67    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
68    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
69    Further improved by Roger with assistance from Edward J. Sabol
70    based on input by Jamie Zawinski.  */
mktime_from_utc(struct tm * t)71 static time_t mktime_from_utc (struct tm *t)
72 {
73   time_t tl, tb;
74   struct tm *tg;
75 
76   tl = mktime (t);
77   if (tl == -1)
78     {
79       t->tm_hour--;
80       tl = mktime (t);
81       if (tl == -1)
82 	return -1; /* can't deal with output from strptime */
83       tl += 3600;
84     }
85   tg = gmtime (&tl);
86   tg->tm_isdst = 0;
87   tb = mktime (tg);
88   if (tb == -1)
89     {
90       tg->tm_hour--;
91       tb = mktime (tg);
92       if (tb == -1)
93 	return -1; /* can't deal with output from gmtime */
94       tb += 3600;
95     }
96   return (tl - (tb - tl));
97 }
98 #endif
99 #ifdef USE_NEON
100 #include <neon/ne_dates.h>
101 #else
102 #ifdef USE_CURL
103 #include <curl/curl.h>
104 #endif
105 #endif
106 
107 #include <iostream>
108 
109 #include "TimeConverter.h"
110 
111 using std::clog;
112 using std::endl;
113 using std::string;
114 
TimeConverter()115 TimeConverter::TimeConverter()
116 {
117 }
118 
119 // Inverse of gmtime().
timegm(struct tm * tm)120 time_t TimeConverter::timegm(struct tm *tm)
121 {
122 #ifdef HAVE_TIMEGM
123 	return ::timegm(tm);
124 #else
125 	return mktime_from_utc(tm);
126 #endif
127 }
128 
129 /// Converts into an RFC 822 timestamp.
toTimestamp(time_t aTime,bool inGMTime)130 string TimeConverter::toTimestamp(time_t aTime, bool inGMTime)
131 {
132 	struct tm *pTimeTm = new struct tm;
133 
134 	if (((inGMTime == true) &&
135 #ifdef HAVE_GMTIME_R
136 		(gmtime_r(&aTime, pTimeTm) != NULL)
137 #else
138 		((pTimeTm = gmtime(&aTime)) != NULL)
139 #endif
140 		) ||
141 #ifdef HAVE_LOCALTIME_R
142 		(localtime_r(&aTime, pTimeTm) != NULL)
143 #else
144 		((pTimeTm = localtime(&aTime)) != NULL)
145 #endif
146 		)
147 	{
148 		char timeStr[64];
149 		size_t formattedSize = 0;
150 
151 		if (inGMTime == true)
152 		{
153 			formattedSize = strftime(timeStr, 64, "%a, %d %b %Y %H:%M:%S GMT", pTimeTm);
154 		}
155 		else
156 		{
157 			// FIXME: don't use this extension ?
158 #if defined(__GNU_LIBRARY__)
159 			// %z is a GNU extension
160 			formattedSize = strftime(timeStr, 64, "%a, %d %b %Y %H:%M:%S %z", pTimeTm);
161 #else
162 			formattedSize = strftime(timeStr, 64, "%a, %d %b %Y %H:%M:%S %Z", pTimeTm);
163 #endif
164 		}
165 		if (formattedSize > 0)
166 		{
167 			delete pTimeTm;
168 
169 			return timeStr;
170 		}
171 	}
172 	delete pTimeTm;
173 
174 	return "";
175 }
176 
177 /// Converts from a RFC 822 timestamp.
fromTimestamp(const string & timestamp)178 time_t TimeConverter::fromTimestamp(const string &timestamp)
179 {
180 	if (timestamp.empty() == true)
181 	{
182 		return 0;
183 	}
184 
185 #ifdef USE_NEON
186 	return ne_rfc1123_parse(timestamp.c_str());
187 #else
188 #ifdef USE_CURL
189 	return curl_getdate(timestamp.c_str(), NULL);
190 #else
191 	return time(NULL);
192 #endif
193 #endif
194 }
195 
196 /// Converts to a YYYYMMDD-formatted string.
toYYYYMMDDString(int year,int month,int day)197 string TimeConverter::toYYYYMMDDString(int year, int month, int day)
198 {
199 	char dateStr[64];
200 
201 	if (year < 0)
202 	{
203 		year = 0;
204 	}
205 	else if (year > 9999)
206 	{
207 		year = 9999;
208 	}
209 	if (month < 1)
210 	{
211 		month = 1;
212 	}
213 	else if (month > 12)
214 	{
215 		month = 12;
216 	}
217 	if (day < 1)
218 	{
219 		day = 1;
220 	}
221 	else if (day > 31)
222 	{
223 		day = 31;
224 	}
225 
226 	if (snprintf(dateStr, 63, "%04d%02d%02d", year, month, day) > 0)
227 	{
228 		return dateStr;
229 	}
230 
231 	return "";
232 }
233 
234 /// Converts from a YYYYMMDD-formatted string.
fromYYYYMMDDString(const string & yyyymmdd,bool inGMTime)235 time_t TimeConverter::fromYYYYMMDDString(const string &yyyymmdd, bool inGMTime)
236 {
237 	struct tm timeTm;
238 	time_t gmTime = 0;
239 
240 	// Initialize the structure
241 	timeTm.tm_sec = timeTm.tm_min = timeTm.tm_hour = timeTm.tm_mday = 0;
242 	timeTm.tm_mon = timeTm.tm_year = timeTm.tm_wday = timeTm.tm_yday = timeTm.tm_isdst = 0;
243 
244 #ifdef HAVE_STRPTIME
245 	strptime(yyyymmdd.c_str(), "%Y%m%d", &timeTm);
246 #else
247 	timeTm.tm_year = atoi(yyyymmdd.substr(0, 4).c_str());
248 	timeTm.tm_mon = atoi(yyyymmdd.substr(4, 2).c_str());
249 	timeTm.tm_mday = atoi(yyyymmdd.substr(6, 2).c_str());
250 #endif
251 #ifdef DEBUG
252 	clog << "TimeConverter::fromYYYYMMDDString: " << timeTm.tm_year << " " << timeTm.tm_mon << " " << timeTm.tm_mday << endl;
253 #endif
254 	if (inGMTime == true)
255 	{
256 		gmTime = timegm(&timeTm);
257 	}
258 	else
259 	{
260 		gmTime = mktime(&timeTm);
261 	}
262 
263 	return gmTime;
264 }
265 
266 /// Converts to a HHMMSS-formatted string.
toHHMMSSString(int hours,int minutes,int seconds)267 string TimeConverter::toHHMMSSString(int hours, int minutes, int seconds)
268 {
269 	char timeStr[64];
270 
271 	if (hours < 0)
272 	{
273 		hours = 0;
274 	}
275 	else if (hours > 23)
276 	{
277 		hours = 23;
278 	}
279 	if (minutes < 0)
280 	{
281 		minutes = 0;
282 	}
283 	else if (minutes > 59)
284 	{
285 		minutes = 59;
286 	}
287 	if (seconds < 0)
288 	{
289 		seconds = 0;
290 	}
291 	else if (seconds > 59)
292 	{
293 		seconds = 59;
294 	}
295 
296 	if (snprintf(timeStr, 63, "%02d%02d%02d", hours, minutes, seconds) > 0)
297 	{
298 		return timeStr;
299 	}
300 
301 	return "";
302 }
303 
304 /// Converts from a HHMMSS-formatted string.
fromHHMMSSString(const string & hhmmss,bool inGMTime)305 time_t TimeConverter::fromHHMMSSString(const string &hhmmss, bool inGMTime)
306 {
307 	struct tm timeTm;
308 	time_t gmTime = 0;
309 
310 	// Initialize the structure
311 	timeTm.tm_sec = timeTm.tm_min = timeTm.tm_hour = timeTm.tm_mday = 0;
312 	timeTm.tm_mon = timeTm.tm_year = timeTm.tm_wday = timeTm.tm_yday = timeTm.tm_isdst = 0;
313 
314 #ifdef HAVE_STRPTIME
315 	strptime(hhmmss.c_str(), "%H%M%S", &timeTm);
316 #else
317 	timeTm.tm_hour = atoi(hhmmss.substr(0, 2).c_str());
318 	timeTm.tm_min = atoi(hhmmss.substr(2, 2).c_str());
319 	timeTm.tm_sec = atoi(hhmmss.substr(4, 2).c_str());
320 #endif
321 #ifdef DEBUG
322 	clog << "TimeConverter::fromHHMMSSString: " << timeTm.tm_hour << " " << timeTm.tm_min << " " << timeTm.tm_sec << endl;
323 #endif
324 	if (inGMTime == true)
325 	{
326 		gmTime = timegm(&timeTm);
327 	}
328 	else
329 	{
330 		gmTime = mktime(&timeTm);
331 	}
332 
333 	return gmTime;
334 }
335 
toNormalDate(time_t aTime,DateFormat format)336 string TimeConverter::toNormalDate(time_t aTime, DateFormat format)
337 {
338 	struct tm *pTimeTm = new struct tm;
339 
340 	if (
341 #ifdef HAVE_LOCALTIME_R
342 		(localtime_r(&aTime, pTimeTm) != NULL)
343 #else
344 		((pTimeTm = localtime(&aTime)) != NULL)
345 #endif
346 		)
347 	{
348 		char timeStr[64];
349 		size_t formattedSize = 0;
350 
351 		if (format == DATE_EUROPE)
352 		{
353 			// FIXME: don't use this extension ?
354 #if defined(__GNU_LIBRARY__)
355 			// %z is a GNU extension
356 			formattedSize = strftime(timeStr, 64, "%A, %d %B %Y %H:%M:%S %z", pTimeTm);
357 #else
358 			formattedSize = strftime(timeStr, 64, "%A, %d %B %Y %H:%M:%S %Z", pTimeTm);
359 #endif
360 		}
361 		else
362 		{
363 			// FIXME: don't use this extension ?
364 #if defined(__GNU_LIBRARY__)
365 			// %z is a GNU extension
366 			formattedSize = strftime(timeStr, 64, "%Y-%m-%d %a %H:%M:%S %z", pTimeTm);
367 #else
368 			formattedSize = strftime(timeStr, 64, "%Y-%m-%d %a %H:%M:%S %Z", pTimeTm);
369 #endif
370 		}
371 		if (formattedSize > 0)
372 		{
373 			delete pTimeTm;
374 
375 			return timeStr;
376 		}
377 	}
378 	delete pTimeTm;
379 
380 	return "";
381 }
382 
383