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, >m);
320 qd = qof_date_from_struct_tm (>m);
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 (¤t->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