QOF  0.7.5
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 }