1 /********************************************************************
2  *       qoftime.c - QofTime, 64bit UTC time handling (seconds).
3  *       Rewritten from scratch for QOF 0.7.0
4  *
5  *  Fri May  5 15:05:24 2006
6  *  Copyright  2006  Neil Williams
7  *  linux@codehelp.co.uk
8  ********************************************************************/
9 /*
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA  02110-1301,  USA
23  */
24 
25 #include "config.h"
26 #include <glib.h>
27 #include <math.h>
28 #include <ctype.h>
29 #include <time.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include "qof.h"
34 #include "qofdate-p.h"
35 
36 static QofLogModule log_module = QOF_MOD_TIME;
37 
38 struct QofTime64
39 {
40 	QofTimeSecs qt_sec;
41 	glong qt_nsec;
42 	gboolean valid;
43 };
44 
45 QofTime *
qof_time_new(void)46 qof_time_new (void)
47 {
48 	QofTime *qt;
49 
50 	qt = g_new0 (QofTime, 1);
51 	qt->valid = FALSE;
52 	return qt;
53 }
54 
55 void
qof_time_free(QofTime * qt)56 qof_time_free (QofTime * qt)
57 {
58 	if (qt == NULL)
59 		return;
60 	g_free (qt);
61 	qt = NULL;
62 }
63 
64 void
qof_time_add_secs(QofTime * qt,QofTimeSecs secs)65 qof_time_add_secs (QofTime * qt, QofTimeSecs secs)
66 {
67 	g_return_if_fail (qt);
68 	g_return_if_fail (qt->valid);
69 	qt->qt_sec += secs;
70 }
71 
72 QofTime *
qof_time_add_secs_copy(QofTime * qt,QofTimeSecs secs)73 qof_time_add_secs_copy (QofTime * qt, QofTimeSecs secs)
74 {
75 	QofTime *copy;
76 
77 	g_return_val_if_fail (qt, NULL);
78 	g_return_val_if_fail (qt->valid, NULL);
79 	copy = qof_time_copy (qt);
80 	copy->qt_sec += secs;
81 	return copy;
82 }
83 
84 static QofTime *
time_normalize(QofTime * qt)85 time_normalize (QofTime * qt)
86 {
87 	g_return_val_if_fail (qt->valid, NULL);
88 	if ((qt->qt_sec < 0) && (qt->qt_nsec > QOF_NSECS))
89 	{
90 		qt->qt_sec -= (qt->qt_nsec / QOF_NSECS);
91 		qt->qt_nsec = qt->qt_nsec % QOF_NSECS;
92 	}
93 	if ((qt->qt_sec >= 0) && (qt->qt_nsec > QOF_NSECS))
94 	{
95 		qt->qt_sec += (qt->qt_nsec / QOF_NSECS);
96 		qt->qt_nsec = qt->qt_nsec % QOF_NSECS;
97 	}
98 	if ((qt->qt_sec < 0) && (qt->qt_nsec < -QOF_NSECS))
99 	{
100 		qt->qt_sec -= -(-qt->qt_nsec / QOF_NSECS);
101 		qt->qt_nsec = -(-qt->qt_nsec % QOF_NSECS);
102 	}
103 	if ((qt->qt_sec >= 0) && (qt->qt_nsec < -QOF_NSECS))
104 	{
105 		qt->qt_sec += -(-qt->qt_nsec / QOF_NSECS);
106 		qt->qt_nsec = -(-qt->qt_nsec % QOF_NSECS);
107 	}
108 	if (qt->qt_sec >= 0 && qt->qt_nsec < 0)
109 	{
110 		qt->qt_sec--;
111 		qt->qt_nsec = QOF_NSECS + qt->qt_nsec;
112 	}
113 	return qt;
114 }
115 
116 void
qof_time_set_secs(QofTime * qt,QofTimeSecs secs)117 qof_time_set_secs (QofTime * qt, QofTimeSecs secs)
118 {
119 	qt->qt_sec = secs;
120 	qt->valid = TRUE;
121 	time_normalize (qt);
122 }
123 
124 void
qof_time_set_nanosecs(QofTime * qt,glong nano)125 qof_time_set_nanosecs (QofTime * qt, glong nano)
126 {
127 	qt->qt_nsec = nano;
128 	qt->valid = TRUE;
129 	time_normalize (qt);
130 }
131 
132 QofTimeSecs
qof_time_get_secs(const QofTime * qt)133 qof_time_get_secs (const QofTime * qt)
134 {
135 	g_return_val_if_fail (qt, 0);
136 	g_return_val_if_fail (qt->valid == TRUE, 0);
137 	return qt->qt_sec;
138 }
139 
140 glong
qof_time_get_nanosecs(const QofTime * qt)141 qof_time_get_nanosecs (const QofTime * qt)
142 {
143 	g_return_val_if_fail (qt->valid == TRUE, 0);
144 	return qt->qt_nsec;
145 }
146 
147 gboolean
qof_time_equal(const QofTime * ta,const QofTime * tb)148 qof_time_equal (const QofTime * ta, const QofTime * tb)
149 {
150 	if (ta == tb)
151 		return TRUE;
152 	if (!ta)
153 		return FALSE;
154 	if (!tb)
155 		return FALSE;
156 	g_return_val_if_fail (ta->valid && tb->valid, FALSE);
157 	if (ta->qt_sec != tb->qt_sec)
158 		return FALSE;
159 	if (ta->qt_nsec != tb->qt_nsec)
160 		return FALSE;
161 	return TRUE;
162 }
163 
164 gint
qof_time_cmp(const QofTime * ta,const QofTime * tb)165 qof_time_cmp (const QofTime * ta, const QofTime * tb)
166 {
167 	g_return_val_if_fail (ta->valid && tb->valid, -1);
168 	if (ta == tb)
169 		return 0;
170 	if (ta->qt_sec < tb->qt_sec)
171 		return -1;
172 	if (ta->qt_sec > tb->qt_sec)
173 		return 1;
174 	if (ta->qt_nsec < tb->qt_nsec)
175 		return -1;
176 	if (ta->qt_nsec > tb->qt_nsec)
177 		return 1;
178 	return 0;
179 }
180 
181 QofTime *
qof_time_diff(const QofTime * ta,const QofTime * tb)182 qof_time_diff (const QofTime * ta, const QofTime * tb)
183 {
184 	QofTime *retval;
185 
186 	g_return_val_if_fail (ta->valid && tb->valid, NULL);
187 	retval = g_new0 (QofTime, 1);
188 	retval->qt_sec = ta->qt_sec - tb->qt_sec;
189 	retval->qt_nsec = ta->qt_nsec - tb->qt_nsec;
190 	retval->valid = TRUE;
191 	time_normalize (retval);
192 	return retval;
193 }
194 
195 QofTime *
qof_time_abs(QofTime * qt)196 qof_time_abs (QofTime * qt)
197 {
198 	g_return_val_if_fail (qt, NULL);
199 	return time_normalize (qt);
200 }
201 
202 gboolean
qof_time_is_valid(const QofTime * qt)203 qof_time_is_valid (const QofTime * qt)
204 {
205 	g_return_val_if_fail (qt, FALSE);
206 	return qt->valid;
207 }
208 
209 QofTime *
qof_time_set(QofTimeSecs t,glong nanosecs)210 qof_time_set (QofTimeSecs t, glong nanosecs)
211 {
212 	QofTime *qt;
213 
214 	qt = qof_time_new ();
215 	qt->qt_sec = t;
216 	qt->qt_nsec = nanosecs;
217 	qt->valid = TRUE;
218 	time_normalize (qt);
219 	return qt;
220 }
221 
222 QofTime *
qof_time_copy(const QofTime * qt)223 qof_time_copy (const QofTime *qt)
224 {
225 	g_return_val_if_fail (qt, NULL);
226 	g_return_val_if_fail (qt->valid, NULL);
227 	return qof_time_set (qt->qt_sec, qt->qt_nsec);
228 }
229 
230 QofTime *
qof_time_from_time_t(time_t t,glong nanosecs)231 qof_time_from_time_t (time_t t, glong nanosecs)
232 {
233 	return qof_time_set (t, nanosecs);
234 }
235 
236 gboolean
qof_time_to_time_t(QofTime * qt,time_t * t,glong * nanosecs)237 qof_time_to_time_t (QofTime * qt, time_t * t, glong * nanosecs)
238 {
239 	if (!qt->valid)
240 		return FALSE;
241 	if (qt->qt_sec < 0)
242 		return FALSE;
243 	if (qt->qt_nsec > 0)
244 	{
245 		*nanosecs = qt->qt_nsec;
246 	}
247 	if ((sizeof (qt->qt_sec) > sizeof (time_t))
248 		&& (qt->qt_sec > G_MAXINT32))
249 	{
250 		PERR (" QofTime too large for time_t on this platform.");
251 		return FALSE;
252 	}
253 	*t = qt->qt_sec;
254 	return TRUE;
255 }
256 
257 QofTime *
qof_time_from_tm(struct tm * qtm,glong nanosecs)258 qof_time_from_tm (struct tm * qtm, glong nanosecs)
259 {
260 	QofDate *qd;
261 	QofTime *qt;
262 
263 	/* avoids use of gmtime_r and therefore time_t */
264 	qd = qof_date_from_struct_tm (qtm);
265 	qd->qd_nanosecs = nanosecs;
266 	qt = qof_date_to_qtime (qd);
267 	qof_date_free (qd);
268 	return qt;
269 }
270 
271 gboolean
qof_time_to_gtimeval(QofTime * qt,GTimeVal * gtv)272 qof_time_to_gtimeval (QofTime * qt, GTimeVal * gtv)
273 {
274 	if (!qt->valid)
275 	{
276 		PERR (" invalid QofTime passed");
277 		return FALSE;
278 	}
279 	if (qt->qt_sec > G_MAXLONG)
280 	{
281 		PERR (" QofTime out of range for GTimeVal");
282 		return FALSE;
283 	}
284 	gtv->tv_sec = (glong) qt->qt_sec;
285 	gtv->tv_usec = qt->qt_nsec;
286 	return TRUE;
287 }
288 
289 void
qof_time_from_gtimeval(QofTime * qt,GTimeVal * gtv)290 qof_time_from_gtimeval (QofTime * qt, GTimeVal * gtv)
291 {
292 	qt->qt_sec = (QofTimeSecs) gtv->tv_sec;
293 	qt->qt_nsec = gtv->tv_usec * 1000;
294 	qt->valid = TRUE;
295 	time_normalize (qt);
296 }
297 
298 GDate *
qof_time_to_gdate(QofTime * qt)299 qof_time_to_gdate (QofTime * qt)
300 {
301 	QofDate *qd;
302 	GDate *d;
303 
304 	qd = qof_date_from_qtime (qt);
305 	d = g_date_new_dmy (qd->qd_mday, qd->qd_mon, qd->qd_year);
306 	if (g_date_valid (d))
307 		return d;
308 	return NULL;
309 }
310 
311 QofTime *
qof_time_from_gdate(GDate * date)312 qof_time_from_gdate (GDate * date)
313 {
314 	struct tm gtm;
315 	QofTime *qt;
316 	QofDate *qd;
317 
318 	g_return_val_if_fail (date, NULL);
319 	g_date_to_struct_tm (date, &gtm);
320 	qd = qof_date_from_struct_tm (&gtm);
321 	qt = qof_date_to_qtime (qd);
322 	qof_date_free (qd);
323 	return qt;
324 }
325 
326 gboolean
qof_time_set_day_end(QofTime * qt)327 qof_time_set_day_end (QofTime * qt)
328 {
329 	if (!qof_time_set_day_start (qt))
330 		return FALSE;
331 	qt->qt_sec += (SECS_PER_DAY - 1);
332 	return TRUE;
333 }
334 
335 gboolean
qof_time_set_day_middle(QofTime * qt)336 qof_time_set_day_middle (QofTime * qt)
337 {
338 	if (!qof_time_set_day_start (qt))
339 		return FALSE;
340 	qt->qt_sec += (SECS_PER_DAY / 2);
341 	return TRUE;
342 }
343 
344 GTimeVal *
qof_time_get_current_start(void)345 qof_time_get_current_start (void)
346 {
347 	GTimeVal *current;
348 	struct tm tm;
349 
350 	/** \todo replace with QofDate */
351 	current = g_new0 (GTimeVal, 1);
352 	g_get_current_time (current);
353 	/* OK to use time_t for current time. */
354 	tm = *gmtime_r (&current->tv_sec, &tm);
355 	current->tv_sec -= tm.tm_sec;
356 	current->tv_sec -= tm.tm_min * 60;
357 	current->tv_sec -= tm.tm_hour * 60 * 60;
358 	return current;
359 }
360 
361 QofTime *
qof_time_get_current(void)362 qof_time_get_current (void)
363 {
364 	QofTime *now;
365 	GTimeVal gnow;
366 
367 	now = qof_time_new ();
368 	g_get_current_time (&gnow);
369 	qof_time_from_gtimeval (now, &gnow);
370 	return now;
371 }
372 
373 gboolean
qof_time_set_day_start(QofTime * qt)374 qof_time_set_day_start (QofTime * qt)
375 {
376 	QofDate *qd;
377 	QofTimeSecs c;
378 
379 	g_return_val_if_fail (qt, FALSE);
380 	qd = qof_date_from_qtime (qt);
381 	if (qd->qd_year < 1970)
382 	{
383 		c = QOF_DAYS_TO_SEC(qd->qd_yday);
384 		c -= QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
385 		c -= qd->qd_gmt_off;
386 		qt->qt_sec = c;
387 		qt->qt_nsec = 0;
388 	}
389 	if (qd->qd_year >= 1970)
390 	{
391 		c = QOF_DAYS_TO_SEC(qd->qd_yday);
392 		c += QOF_DAYS_TO_SEC(days_between (1970, qd->qd_year));
393 		c -= qd->qd_gmt_off;
394 		qt->qt_sec = c;
395 		qt->qt_nsec = 0;
396 	}
397 	qof_date_free (qd);
398 	return TRUE;
399 }
400 
401 QofTime *
qof_time_get_today_start(void)402 qof_time_get_today_start (void)
403 {
404 	QofTime *qt;
405 
406 	qt = qof_time_get_current ();
407 	if (!qof_time_set_day_start (qt))
408 		return NULL;
409 	return qt;
410 }
411 
412 QofTime *
qof_time_get_today_end(void)413 qof_time_get_today_end (void)
414 {
415 	QofTime *qt;
416 
417 	qt = qof_time_get_today_start ();
418 	qt->qt_sec += SECS_PER_DAY - 1;
419 	return qt;
420 }
421 
422 guint8
qof_time_last_mday(QofTime * qt)423 qof_time_last_mday (QofTime * qt)
424 {
425 	GDate *d;
426 	GDateMonth m;
427 	GDateYear y;
428 
429 	g_return_val_if_fail (qt, 0);
430 	d = qof_time_to_gdate (qt);
431 	if (!d)
432 		return 0;
433 	m = g_date_get_month (d);
434 	y = g_date_get_year (d);
435 	return g_date_get_days_in_month (m, y);
436 }
437 
438 gboolean
qof_time_to_dmy(QofTime * qt,guint8 * day,guint8 * month,guint16 * year)439 qof_time_to_dmy (QofTime * qt, guint8 * day, guint8 * month,
440 				 guint16 * year)
441 {
442 	GDate *d;
443 
444 	d = qof_time_to_gdate (qt);
445 	if (!d)
446 		return FALSE;
447 	if (day)
448 		*day = g_date_get_day (d);
449 	if (month)
450 		*month = g_date_get_month (d);
451 	if (year)
452 		*year = g_date_get_year (d);
453 	return TRUE;
454 }
455 
456 QofTime *
qof_time_dmy_to_time(guint8 day,guint8 month,guint16 year)457 qof_time_dmy_to_time (guint8 day, guint8 month, guint16 year)
458 {
459 	GDate *d;
460 	QofTime *qt;
461 
462 	g_return_val_if_fail (g_date_valid_dmy (day, month, year), NULL);
463 	d = g_date_new_dmy (day, month, year);
464 	qt = qof_time_from_gdate (d);
465 	return qt;
466 }
467 
468 gchar *
qof_time_stamp_now(void)469 qof_time_stamp_now (void)
470 {
471 	gint len;
472 	struct tm qtm;
473 	time_t t;
474 	gchar test[MAX_DATE_LENGTH];
475 	const gchar *fmt;
476 
477 	ENTER (" ");
478 	t = time (NULL);
479 	qtm = *gmtime_r (&t, &qtm);
480 	fmt = qof_date_format_get_format (QOF_DATE_FORMAT_UTC);
481 	len = strftime (test, MAX_DATE_LENGTH, fmt, &qtm);
482 	if (len == 0 && test[0] != '\0')
483 	{
484 		LEAVE (" strftime failed.");
485 		return NULL;
486 	}
487 	LEAVE (" ");
488 	return g_strdup (test);
489 }
490