QOF: qofstrftime.c Source File

QOF 0.7.5

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

qofstrftime.c
1 /***************************************************************************
2 * qofstrftime.c
3 *
4 * Sun May 21 15:59:32 2006
5 * Copyright (C) 1991-1999, 2000, 2001, 2003, 2004, 2005, 2006
6 * Free Software Foundation, Inc.
7 *
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 /*
26 Modified version of strftime from Debian coreutils package.
27
28 (note that the GNU date command includes only strftime,
29 the QOF strptime code comes direct from the GNU glibc.)
30
31 1. Removed preprocessor directives that are always true or always false within QOF
32 2. Extended variables to full 64bit ranges, even on 32bit platforms.
33 3. Replaced time_t with qt_time to prevent overflow in 2038.
34 4. Replaced struct tm with QofDate to prevent overflow.
35 Neil Williams <linux@codehelp.co.uk>
36 */
37
38 #include "config.h"
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <sys/time.h>
42 #include <time.h>
43 #include <wchar.h>
44 #include <limits.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <glib.h>
48 #include "qof.h"
49 #include "qofdate-p.h"
50
51 static QofLogModule log_module = QOF_MOD_DATE;
52
53 #define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
54 #define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
55
56 #define FPRINTFTIME 0
57
58 /* Shift A right by B bits portably, by dividing A by 2**B and
59 truncating towards minus infinity. A and B should be free of side
60 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
61 INT_BITS is the number of useful bits in an int. GNU code can
62 assume that INT_BITS is at least 32.
63
64 ISO C99 says that A >> B is implementation-defined if A < 0. Some
65 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
66 right in the usual way when A < 0, so SHR falls back on division if
67 ordinary A >> B doesn't seem to be the usual signed shift. */
68 #define SHR(a, b) \
69 (-1 >> 1 == -1 \
70 ? (a) >> (b) \
71 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
72
73 /* Bound on length of the string representing an integer
74 type or expression T. Subtract 1 for the sign bit if t is signed;
75 log10 (2.0) < 146/485; add 1 for integer division truncation;
76 add 1 more for a minus sign if needed. */
77 #define INT_strlen _BOUND(t) \
78 ((sizeof (t) * CHAR_BIT - 1) * 146 / 485 + 2)
79
80 /* IMPORTANT: QofDate does not use 1900 or 1970 as a base, all
81 years in QofDate are true values. */
82 #define TM_YEAR_BASE 0
83
84 #define add(n, f) \
85 do \
86 { \
87 gint _n = (n); \
88 gint _delta = width - _n; \
89 gint _incr = _n + (_delta > 0 ? _delta : 0); \
90 if ((size_t) _incr >= maxsize - i) \
91 return 0; \
92 if (p) \
93 { \
94 if (digits == 0 && _delta > 0) \
95 { \
96 if (pad == ('0')) \
97 memset_zero (p, _delta); \
98 else \
99 memset_space (p, _delta); \
100 } \
101 f; \
102 p += FPRINTFTIME ? 0 : _n; \
103 } \
104 i += _incr; \
105 } while (0)
106
107 # define add1(C) add (1, *p = C)
108
109 # define cpy(n, s) \
110 add ((n), \
111 if (to_lowcase) \
112 memcpy_lowcase (p, (s), _n); \
113 else if (to_uppcase) \
114 memcpy_uppcase (p, (s), _n); \
115 else \
116 memcpy ((void *) p, (void const *) (s), _n))
117
118 #define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
119 #define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
120
121 /* We don't use `isdigit' here since the locale dependent
122 interpretation is not what we want here. We only need to accept
123 the arabic digits in the ASCII range. One day there is perhaps a
124 more reliable way to accept other sets of digits. */
125 #define ISDIGIT(Ch) ((guint) (Ch) - ('0') <= 9)
126 /* The number of days from the first day of the first ISO week of this
127 year to the year day YDAY with week day WDAY. ISO weeks start on
128 Monday; the first ISO week has the year's first Thursday. YDAY may
129 be as small as YDAY_MINIMUM. */
130 #define ISO_WEEK_START_WDAY 1 /* Monday */
131 #define ISO_WEEK1_WDAY 4 /* Thursday */
132 #define YDAY_MINIMUM (-366)
133
134 static const mbstate_t mbstate_zero;
135 const gchar *format_end = NULL;
136
137 static gchar *
138 memcpy_lowcase (gchar * dest, const gchar * src, size_t len)
139 {
140 while (len-- > 0)
141 dest[len] = TOLOWER ((guchar) src[len], loc);
142 return dest;
143 }
144
145 static gchar *
146 memcpy_uppcase (gchar * dest, const gchar * src, size_t len)
147 {
148 while (len-- > 0)
149 dest[len] = TOUPPER ((guchar) src[len], loc);
150 return dest;
151 }
152
153 static gint
154 iso_week_days (gint yday, gint wday)
155 {
156 /* Add enough to the first operand of % to make it nonnegative. */
157 gint big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
158 return (yday
159 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
160 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
161 }
162
163 size_t
164 strftime_case (gboolean upcase, gchar * s,
165 size_t maxsize, const gchar *format, const QofDate *qd,
166 gint ut, glong ns)
167 {
168 const gchar *zone;
169 gint hour12 = qd->qd_hour;
170 size_t i = 0;
171 gchar *p = s;
172 const gchar *f;
173 QofDate copy = *qd;
174 qd = &copy;
175 zone = (const gchar *) qd->qd_zone;
176 if (ut)
177 {
178 if (!(zone && *zone))
179 {
180 setenv ("TZ", "GMT", 1);
181 zone = "GMT";
182 }
183 }
184 else
185 {
186 /* POSIX.1 requires that local time zone information be
187 used as though strftime called tzset. */
188 tzset ();
189 }
190
191 if (hour12 > 12)
192 hour12 -= 12;
193 else if (hour12 == 0)
194 hour12 = 12;
195
196 for (f = format; *f != '\0'; ++f)
197 {
198 gint pad = 0; /* Padding for number ('-', '_', or 0). */
199 gint modifier; /* Field modifier ('E', 'O', or 0). */
200 gint digits = 0; /* Max digits for numeric format. */
201 glong number_value; /* Numeric value to be printed. */
202 guint u_number_value; /* (unsigned int) number_value. */
203 gboolean negative_number; /* The number is negative. */
204 gboolean always_output_a_sign; /* +/- should always be output. */
205 gint tz_colon_mask; /* Bitmask of where ':' should appear. */
206 const gchar *subfmt;
207 gchar sign_char;
208 gchar *bufp;
209 gchar buf[MAX_DATE_BUFFER];
210 gint width = -1;
211 gboolean to_lowcase = FALSE;
212 gboolean to_uppcase = upcase;
213 size_t colons;
214 gboolean change_case = FALSE;
215 gint format_char;
216
217 switch (*f)
218 {
219 case ('%'):
220 break;
221
222 case ('\b'):
223 case ('\t'):
224 case ('\n'):
225 case ('\v'):
226 case ('\f'):
227 case ('\r'):
228 case (' '):
229 case ('!'):
230 case ('"'):
231 case ('#'):
232 case ('&'):
233 case ('\''):
234 case ('('):
235 case (')'):
236 case ('*'):
237 case ('+'):
238 case (','):
239 case ('-'):
240 case ('.'):
241 case ('/'):
242 case ('0'):
243 case ('1'):
244 case ('2'):
245 case ('3'):
246 case ('4'):
247 case ('5'):
248 case ('6'):
249 case ('7'):
250 case ('8'):
251 case ('9'):
252 case (':'):
253 case (';'):
254 case ('<'):
255 case ('='):
256 case ('>'):
257 case ('?'):
258 case ('A'):
259 case ('B'):
260 case ('C'):
261 case ('D'):
262 case ('E'):
263 case ('F'):
264 case ('G'):
265 case ('H'):
266 case ('I'):
267 case ('J'):
268 case ('K'):
269 case ('L'):
270 case ('M'):
271 case ('N'):
272 case ('O'):
273 case ('P'):
274 case ('Q'):
275 case ('R'):
276 case ('S'):
277 case ('T'):
278 case ('U'):
279 case ('V'):
280 case ('W'):
281 case ('X'):
282 case ('Y'):
283 case ('Z'):
284 case ('['):
285 case ('\\'):
286 case (']'):
287 case ('^'):
288 case ('_'):
289 case ('a'):
290 case ('b'):
291 case ('c'):
292 case ('d'):
293 case ('e'):
294 case ('f'):
295 case ('g'):
296 case ('h'):
297 case ('i'):
298 case ('j'):
299 case ('k'):
300 case ('l'):
301 case ('m'):
302 case ('n'):
303 case ('o'):
304 case ('p'):
305 case ('q'):
306 case ('r'):
307 case ('s'):
308 case ('t'):
309 case ('u'):
310 case ('v'):
311 case ('w'):
312 case ('x'):
313 case ('y'):
314 case ('z'):
315 case ('{'):
316 case ('|'):
317 case ('}'):
318 case ('~'):
319 /* The C Standard requires these 98 characters (plus '%')
320 to be in the basic execution character set. None of
321 these characters can start a multibyte sequence, so they
322 need not be analyzed further. */
323 add1 (*f);
324 continue;
325
326 default:
327 /* Copy this multibyte sequence until we reach its end,
328 find an error, or come back to the initial shift state.
329 */
330 {
331 mbstate_t mbstate = mbstate_zero;
332 size_t len = 0;
333 size_t fsize;
334
335 if (!format_end)
336 format_end = f + strlen (f) + 1;
337 fsize = format_end - f;
338
339 do
340 {
341 size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
342
343 if (bytes == 0)
344 break;
345
346 if (bytes == (size_t) - 2)
347 {
348 len += strlen (f + len);
349 break;
350 }
351
352 if (bytes == (size_t) - 1)
353 {
354 len++;
355 break;
356 }
357
358 len += bytes;
359 }
360 while (!mbsinit (&mbstate));
361
362 cpy (len, f);
363 f += len - 1;
364 continue;
365 }
366 }
367
368 /* Check for flags that can modify a format. */
369 while (1)
370 {
371 switch (*++f)
372 {
373 /* This influences the number formats. */
374 case ('_'):
375 case ('-'):
376 case ('0'):
377 pad = *f;
378 continue;
379
380 /* This changes textual output. */
381 case ('^'):
382 to_uppcase = TRUE;
383 continue;
384 case ('#'):
385 change_case = TRUE;
386 continue;
387
388 default:
389 break;
390 }
391 break;
392 }
393
394 /* As a GNU extension we allow to specify the field width. */
395 if (ISDIGIT (*f))
396 {
397 width = 0;
398 do
399 {
400 if (width > INT_MAX / 10
401 || (width == INT_MAX / 10
402 && *f - ('0') > INT_MAX % 10))
403 /* Avoid overflow. */
404 width = INT_MAX;
405 else
406 {
407 width *= 10;
408 width += *f - ('0');
409 }
410 ++f;
411 }
412 while (ISDIGIT (*f));
413 }
414
415 /* Check for modifiers. */
416 switch (*f)
417 {
418 case ('E'):
419 case ('O'):
420 modifier = *f++;
421 break;
422
423 default:
424 modifier = 0;
425 break;
426 }
427
428 /* Now do the specified format. */
429 format_char = *f;
430 switch (format_char)
431 {
432 #define DO_NUMBER(d, v) \
433 digits = d; \
434 number_value = v; goto do_number
435 #define DO_SIGNED_NUMBER(d, negative, v) \
436 digits = d; \
437 negative_number = negative; \
438 u_number_value = v; goto do_signed_number
439
440 /* The mask is not what you might think.
441 When the ordinal i'th bit is set, insert a colon
442 before the i'th digit of the time zone representation. */
443 #define DO_TZ_OFFSET(d, negative, mask, v) \
444 digits = d; \
445 negative_number = negative; \
446 tz_colon_mask = mask; \
447 u_number_value = v; goto do_tz_offset
448 #define DO_NUMBER_SPACEPAD(d, v) \
449 digits = d; \
450 number_value = v; goto do_number_spacepad
451
452 case ('%'):
453 if (modifier != 0)
454 goto bad_format;
455 add1 (*f);
456 break;
457
458 case ('a'):
459 if (modifier != 0)
460 goto bad_format;
461 if (change_case)
462 {
463 to_uppcase = TRUE;
464 to_lowcase = FALSE;
465 }
466 goto underlying_strftime;
467
468 case 'A':
469 if (modifier != 0)
470 goto bad_format;
471 if (change_case)
472 {
473 to_uppcase = TRUE;
474 to_lowcase = FALSE;
475 }
476 goto underlying_strftime;
477
478 case ('b'):
479 case ('h'):
480 if (change_case)
481 {
482 to_uppcase = TRUE;
483 to_lowcase = FALSE;
484 }
485 if (modifier != 0)
486 goto bad_format;
487 goto underlying_strftime;
488
489 case ('B'):
490 if (modifier != 0)
491 goto bad_format;
492 if (change_case)
493 {
494 to_uppcase = TRUE;
495 to_lowcase = FALSE;
496 }
497 goto underlying_strftime;
498
499 case ('c'):
500 if (modifier == ('O'))
501 goto bad_format;
502 goto underlying_strftime;
503
504 subformat:
505 {
506 size_t len = strftime_case (to_uppcase,
507 NULL, ((size_t) - 1),
508 subfmt, qd, ut, ns);
509 add (len, strftime_case (to_uppcase, p,
510 (maxsize - i), subfmt, qd, ut, ns));
511 }
512 break;
513
514 underlying_strftime:
515 {
516 /* try to handle locale-specific formats */
517 gchar ufmt[5];
518 gchar *u = ufmt;
519 gchar ubuf[1024]; /* enough for any single format in practice */
520 size_t len;
521 /* Make sure we're calling the actual underlying strftime.
522 In some cases, config.h contains something like
523 "#define strftime rpl_strftime". */
524 # ifdef strftime
525 # undef strftime
526 size_t strftime ();
527 #endif
528
529 /* The space helps distinguish strftime failure from
530 empty output. */
531 *u++ = ' ';
532 *u++ = '%';
533 if (modifier != 0)
534 *u++ = modifier;
535 *u++ = format_char;
536 *u = '\0';
537 {
538 glong nanosecs;
539 struct tm bad;
540 if(!qof_date_to_struct_tm ((QofDate*)qd, &bad, &nanosecs))
541 {
542 PERR (" locale format out of range.");
543 break;
544 }
545 len = strftime (ubuf, sizeof ubuf, ufmt, &bad);
546 }
547 if (len != 0)
548 cpy (len - 1, ubuf + 1);
549 }
550 break;
551
552 case ('C'):
553 if (modifier == ('O'))
554 goto bad_format;
555 if (modifier == ('E'))
556 {
557 goto underlying_strftime;
558 }
559
560 {
561 /* convert to use QofDate->qd_year which is 64bit */
562 gint century = qd->qd_year / 100 + TM_YEAR_BASE / 100;
563 century -= qd->qd_year % 100 < 0 && 0 < century;
564 DO_SIGNED_NUMBER (2,
565 qd->qd_year < -TM_YEAR_BASE, century);
566 }
567
568 case ('x'):
569 if (modifier == ('O'))
570 goto bad_format;
571 goto underlying_strftime;
572 case ('D'):
573 if (modifier != 0)
574 goto bad_format;
575 subfmt = ("%m/%d/%y");
576 goto subformat;
577
578 case ('d'):
579 if (modifier == ('E'))
580 goto bad_format;
581
582 DO_NUMBER (2, qd->qd_mday);
583
584 case ('e'):
585 if (modifier == ('E'))
586 goto bad_format;
587
588 DO_NUMBER_SPACEPAD (2, qd->qd_mday);
589
590 /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
591 and then jump to one of these labels. */
592 do_tz_offset:
593 always_output_a_sign = TRUE;
594 goto do_number_body;
595
596 do_number_spacepad:
597 /* Force `_' flag unless overridden by `0' or `-' flag. */
598 if (pad != ('0') && pad != ('-'))
599 pad = ('_');
600
601 do_number:
602 /* Format NUMBER_VALUE according to the MODIFIER flag. */
603 negative_number = number_value < 0;
604 u_number_value = number_value;
605
606 do_signed_number:
607 always_output_a_sign = FALSE;
608 tz_colon_mask = 0;
609
610 do_number_body:
611 /* Format U_NUMBER_VALUE according to the MODIFIER flag.
612 NEGATIVE_NUMBER is nonzero if the original number was
613 negative; in this case it was converted directly to
614 unsigned int (i.e., modulo (UINT_MAX + 1)) without
615 negating it. */
616 if (modifier == ('O') && !negative_number)
617 {
618 goto underlying_strftime;
619 }
620
621 bufp = buf + sizeof (buf) / sizeof (buf[0]);
622
623 if (negative_number)
624 u_number_value = -u_number_value;
625
626 do
627 {
628 if (tz_colon_mask & 1)
629 *--bufp = ':';
630 tz_colon_mask >>= 1;
631 *--bufp = u_number_value % 10 + ('0');
632 u_number_value /= 10;
633 }
634 while (u_number_value != 0 || tz_colon_mask != 0);
635
636 do_number_sign_and_padding:
637 if (digits < width)
638 digits = width;
639
640 sign_char = (negative_number ? ('-')
641 : always_output_a_sign ? ('+') : 0);
642
643 if (pad == ('-'))
644 {
645 if (sign_char)
646 add1 (sign_char);
647 }
648 else
649 {
650 gint padding =
651 digits - (buf + (sizeof (buf) / sizeof (buf[0])) -
652 bufp) - !!sign_char;
653
654 if (padding > 0)
655 {
656 if (pad == ('_'))
657 {
658 if ((size_t) padding >= maxsize - i)
659 return 0;
660
661 if (p)
662 memset_space (p, padding);
663 i += padding;
664 width = width > padding ? width - padding : 0;
665 if (sign_char)
666 add1 (sign_char);
667 }
668 else
669 {
670 if ((size_t) digits >= maxsize - i)
671 return 0;
672
673 if (sign_char)
674 add1 (sign_char);
675
676 if (p)
677 memset_zero (p, padding);
678 i += padding;
679 width = 0;
680 }
681 }
682 else
683 {
684 if (sign_char)
685 add1 (sign_char);
686 }
687 }
688
689 cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
690 break;
691
692 case ('F'):
693 if (modifier != 0)
694 goto bad_format;
695 subfmt = ("%Y-%m-%d");
696 goto subformat;
697
698 case ('H'):
699 if (modifier == ('E'))
700 goto bad_format;
701
702 DO_NUMBER (2, qd->qd_hour);
703
704 case ('I'):
705 if (modifier == ('E'))
706 goto bad_format;
707
708 DO_NUMBER (2, hour12);
709
710 case ('k'): /* GNU extension. */
711 if (modifier == ('E'))
712 goto bad_format;
713
714 DO_NUMBER_SPACEPAD (2, qd->qd_hour);
715
716 case ('l'): /* GNU extension. */
717 if (modifier == ('E'))
718 goto bad_format;
719
720 DO_NUMBER_SPACEPAD (2, hour12);
721
722 case ('j'):
723 if (modifier == ('E'))
724 goto bad_format;
725
726 DO_SIGNED_NUMBER (3, qd->qd_yday < -1, qd->qd_yday + 1U);
727
728 case ('M'):
729 if (modifier == ('E'))
730 goto bad_format;
731
732 DO_NUMBER (2, qd->qd_min);
733
734 case ('m'):
735 if (modifier == ('E'))
736 goto bad_format;
737
738 DO_SIGNED_NUMBER (2, qd->qd_mon < -1, qd->qd_mon);
739
740 case ('N'): /* GNU extension. */
741 if (modifier == ('E'))
742 goto bad_format;
743
744 number_value = ns;
745 if (width == -1)
746 width = 9;
747 else
748 {
749 /* Take an explicit width less than 9 as a precision. */
750 gint j;
751 for (j = width; j < 9; j++)
752 number_value /= 10;
753 }
754
755 DO_NUMBER (width, number_value);
756
757 case ('n'):
758 add1 (('\n'));
759 break;
760
761 case ('P'):
762 to_lowcase = TRUE;
763 format_char = ('p');
764
765 case ('p'):
766 if (change_case)
767 {
768 to_uppcase = FALSE;
769 to_lowcase = TRUE;
770 }
771 goto underlying_strftime;
772
773 case ('R'):
774 subfmt = ("%H:%M");
775 goto subformat;
776
777 case ('r'):
778 goto underlying_strftime;
779
780 case ('S'):
781 if (modifier == ('E'))
782 goto bad_format;
783
784 DO_NUMBER (2, qd->qd_sec);
785
786 case ('s'): /* GNU extension.
787 number of seconds since the epoch.
788 basically QofTimeSecs as a string.
789 */
790 {
791 glong nanosecs;
792 QofTime *time;
793 QofTimeSecs t;
794
795 time = qof_date_to_qtime ((QofDate*)qd);
796 t = qof_time_get_secs (time);
797 nanosecs = qof_time_get_nanosecs (time);
798
799 /* Generate string value for T using time_t arithmetic;
800 this works even if sizeof (long) < sizeof (time_t). */
801
802 bufp = buf + sizeof (buf) / sizeof (buf[0]);
803 negative_number = t < 0;
804
805 do
806 {
807 gint d = t % 10;
808 t /= 10;
809 *--bufp = (negative_number ? -d : d) + ('0');
810 }
811 while (t != 0);
812
813 digits = 1;
814 always_output_a_sign = FALSE;
815 goto do_number_sign_and_padding;
816 }
817
818 case ('X'):
819 if (modifier == ('O'))
820 goto bad_format;
821 goto underlying_strftime;
822 case ('T'):
823 subfmt = ("%H:%M:%S");
824 goto subformat;
825
826 case ('t'):
827 add1 (('\t'));
828 break;
829
830 case ('u'):
831 DO_NUMBER (1, (qd->qd_wday - 1 + 7) % 7 + 1);
832
833 case ('U'):
834 if (modifier == ('E'))
835 goto bad_format;
836
837 DO_NUMBER (2, (qd->qd_yday - qd->qd_wday + 7) / 7);
838
839 case ('V'):
840 case ('g'):
841 case ('G'):
842 if (modifier == ('E'))
843 goto bad_format;
844 {
845 gint year_adjust = 0;
846 gint days = iso_week_days (qd->qd_yday, qd->qd_wday);
847
848 if (days < 0)
849 {
850 /* This ISO week belongs to the previous year. */
851 year_adjust = -1;
852 days =
853 iso_week_days (qd->qd_yday +
854 (365 + qof_date_isleap (qd->qd_year - 1)),
855 qd->qd_wday);
856 }
857 else
858 {
859 gint d =
860 iso_week_days (qd->qd_yday - (365 +
861 qof_date_isleap (qd->qd_year)),
862 qd->qd_wday);
863 if (0 <= d)
864 {
865 /* This ISO week belongs to the next year. */
866 year_adjust = 1;
867 days = d;
868 }
869 }
870
871 switch (*f)
872 {
873 case ('g'):
874 {
875 /* use QofDate->qd_year */
876 gint yy = (qd->qd_year % 100 + year_adjust) % 100;
877 DO_NUMBER (2, (0 <= yy
878 ? yy : qd->qd_year <
879 -TM_YEAR_BASE -
880 year_adjust ? -yy : yy + 100));
881 }
882
883 case ('G'):
884 /* use QofDate->qd_year */
885 DO_SIGNED_NUMBER (4,
886 qd->qd_year <
887 -TM_YEAR_BASE - year_adjust,
888 (qd->qd_year + (guint) TM_YEAR_BASE +
889 year_adjust));
890
891 default:
892 DO_NUMBER (2, days / 7 + 1);
893 }
894 }
895
896 case ('W'):
897 if (modifier == ('E'))
898 goto bad_format;
899
900 DO_NUMBER (2,
901 (qd->qd_yday - (qd->qd_wday - 1 + 7) % 7 + 7) / 7);
902
903 case ('w'):
904 if (modifier == ('E'))
905 goto bad_format;
906
907 DO_NUMBER (1, qd->qd_wday);
908
909 case ('Y'):
910 if (modifier == 'E')
911 {
912 goto underlying_strftime;
913 }
914 if (modifier == ('O'))
915 goto bad_format;
916 else
917 /* use QofDate->qd_year */
918 DO_SIGNED_NUMBER (4, qd->qd_year < -TM_YEAR_BASE,
919 qd->qd_year + TM_YEAR_BASE);
920
921 case ('y'):
922 if (modifier == ('E'))
923 {
924 goto underlying_strftime;
925 }
926
927 {
928 gint64 yy = qd->qd_year % 100;
929 if (yy < 0)
930 yy = qd->qd_year < -TM_YEAR_BASE ? -yy : yy + 100;
931 DO_NUMBER (2, yy);
932 }
933
934 case ('Z'):
935 if (change_case)
936 {
937 to_uppcase = FALSE;
938 to_lowcase = TRUE;
939 }
940
941 /* The tzset() call might have changed the value. */
942 if (!(zone && *zone) && qd->qd_is_dst >= 0)
943 zone = tzname[qd->qd_is_dst != 0];
944 if (!zone)
945 zone = "";
946
947 cpy (strlen (zone), zone);
948 break;
949
950 case (':'):
951 /* :, ::, and ::: are valid only just before 'z'.
952 :::: etc. are rejected later. */
953 for (colons = 1; f[colons] == (':'); colons++)
954 continue;
955 if (f[colons] != ('z'))
956 goto bad_format;
957 f += colons;
958 goto do_z_conversion;
959
960 case ('z'):
961 colons = 0;
962
963 do_z_conversion:
964 if (qd->qd_is_dst < 0)
965 break;
966
967 {
968 gint diff;
969 gint hour_diff;
970 gint min_diff;
971 gint sec_diff;
972 diff = qd->qd_gmt_off;
973 hour_diff = diff / 60 / 60;
974 min_diff = diff / 60 % 60;
975 sec_diff = diff % 60;
976
977 switch (colons)
978 {
979 case 0: /* +hhmm */
980 DO_TZ_OFFSET (5, diff < 0, 0,
981 hour_diff * 100 + min_diff);
982
983 case 1:
984 tz_hh_mm: /* +hh:mm */
985 DO_TZ_OFFSET (6, diff < 0, 04,
986 hour_diff * 100 + min_diff);
987
988 case 2:
989 tz_hh_mm_ss: /* +hh:mm:ss */
990 DO_TZ_OFFSET (9, diff < 0, 024,
991 hour_diff * 10000 + min_diff * 100 + sec_diff);
992
993 case 3: /* +hh if possible, else +hh:mm, else +hh:mm:ss */
994 if (sec_diff != 0)
995 goto tz_hh_mm_ss;
996 if (min_diff != 0)
997 goto tz_hh_mm;
998 DO_TZ_OFFSET (3, diff < 0, 0, hour_diff);
999
1000 default:
1001 goto bad_format;
1002 }
1003 }
1004
1005 case ('\0'): /* GNU extension: % at end of format. */
1006 --f;
1007 /* Fall through. */
1008 default:
1009 /* Unknown format; output the format, including the '%',
1010 since this is most likely the right thing to do if a
1011 multibyte string has been misparsed. */
1012 bad_format:
1013 {
1014 gint flen;
1015 for (flen = 1; f[1 - flen] != ('%'); flen++)
1016 continue;
1017 cpy (flen, &f[1 - flen]);
1018 }
1019 break;
1020 }
1021 }
1022 return i;
1023 }
QofDate_s::qd_yday
gshort qd_yday
Definition: qofdate.h:189
QofDate_s::qd_year
gint64 qd_year
Extended version to cope with full range of dates.
Definition: qofdate.h:181
MAX_DATE_BUFFER
#define MAX_DATE_BUFFER
The maximum length of a QofDate buffer.
Definition: qofdate.h:92
PERR
#define PERR(format, args...)
Definition: qoflog.h:183
QofDate_s::qd_zone
const gchar * qd_zone
Calculated value based on struct tm.tm_zone.
Definition: qofdate.h:208
qof_time_get_secs
QofTimeSecs qof_time_get_secs(const QofTime *qt)
Get the number of seconds.
Definition: qoftime.c:133
QOF_MOD_DATE
#define QOF_MOD_DATE
Definition: qofdate.h:98
QofDate_s::qd_is_dst
gshort qd_is_dst
Definition: qofdate.h:193
qof_time_get_nanosecs
glong qof_time_get_nanosecs(const QofTime *qt)
Get the number of seconds.
Definition: qoftime.c:141
qof_date_to_qtime
QofTime * qof_date_to_qtime(const QofDate *qd)
Definition: qofdate.c:892
QofDate_s
Full range replacement for struct tm.
Definition: qofdate.h:138
QofDate_s::qd_hour
glong qd_hour
Signed replacement of struct tm.tm_hour.
Definition: qofdate.h:157
QofDate_s::qd_min
glong qd_min
Signed replacement of struct tm.tm_min.
Definition: qofdate.h:150
QofDate_s::qd_mon
glong qd_mon
Signed replacement of struct tm.tm_mon.
Definition: qofdate.h:171
qof_date_isleap
#define qof_date_isleap(year)
Definition: qofdate.h:222
qof_date_to_struct_tm
gboolean qof_date_to_struct_tm(const QofDate *qd, struct tm *stm, glong *nanosecs)
Convert a QofDate to a struct tm.
Definition: qofdate.c:702
QofDate_s::qd_sec
gint64 qd_sec
Definition: qofdate.h:143
QofDate_s::qd_gmt_off
glong qd_gmt_off
Calculated value based on struct tm.tm_gmtoff.
Definition: qofdate.h:201
QofTimeSecs
gint64 QofTimeSecs
Replacement for time_t.
Definition: qoftime.h:129
QofTime
struct QofTime64 QofTime
Use a 64-bit signed int QofTime.
Definition: qoftime.h:120
QofDate_s::qd_wday
gshort qd_wday
Definition: qofdate.h:185
QofDate_s::qd_mday
glong qd_mday
Signed replacement of struct tm.tm_mday.
Definition: qofdate.h:164
QofLogModule
const gchar * QofLogModule
Definition: qofid.h:85