Apply a patch to fix sparse warnings. (#154696, Kjartan Maraas)
[platform/upstream/glib.git] / glib / gdate.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /* 
28  * MT safe
29  */
30
31 #include "config.h"
32
33 #define DEBUG_MSG(x)    /* */
34 #ifdef G_ENABLE_DEBUG
35 /* #define DEBUG_MSG(args)      g_message args ; */
36 #endif
37
38 #include "galias.h"
39 #include "glib.h"
40
41 #include <time.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <locale.h>
45
46 GDate*
47 g_date_new (void)
48 {
49   GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
50   
51   return d;
52 }
53
54 GDate*
55 g_date_new_dmy (GDateDay day, GDateMonth m, GDateYear y)
56 {
57   GDate *d;
58   g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
59   
60   d = g_new (GDate, 1);
61   
62   d->julian = FALSE;
63   d->dmy    = TRUE;
64   
65   d->month = m;
66   d->day   = day;
67   d->year  = y;
68   
69   g_assert (g_date_valid (d));
70   
71   return d;
72 }
73
74 GDate*
75 g_date_new_julian (guint32 j)
76 {
77   GDate *d;
78   g_return_val_if_fail (g_date_valid_julian (j), NULL);
79   
80   d = g_new (GDate, 1);
81   
82   d->julian = TRUE;
83   d->dmy    = FALSE;
84   
85   d->julian_days = j;
86   
87   g_assert (g_date_valid (d));
88   
89   return d;
90 }
91
92 void
93 g_date_free (GDate *d)
94 {
95   g_return_if_fail (d != NULL);
96   
97   g_free (d);
98 }
99
100 gboolean     
101 g_date_valid (const GDate *d)
102 {
103   g_return_val_if_fail (d != NULL, FALSE);
104   
105   return (d->julian || d->dmy);
106 }
107
108 static const guint8 days_in_months[2][13] = 
109 {  /* error, jan feb mar apr may jun jul aug sep oct nov dec */
110   {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
111   {  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
112 };
113
114 static const guint16 days_in_year[2][14] = 
115 {  /* 0, jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec */
116   {  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 
117   {  0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
118 };
119
120 gboolean     
121 g_date_valid_month (GDateMonth   m)
122
123   return ( (m > G_DATE_BAD_MONTH) && (m < 13) );
124 }
125
126 gboolean     
127 g_date_valid_year (GDateYear    y)
128 {
129   return ( y > G_DATE_BAD_YEAR );
130 }
131
132 gboolean     
133 g_date_valid_day (GDateDay     d)
134 {
135   return ( (d > G_DATE_BAD_DAY) && (d < 32) );
136 }
137
138 gboolean     
139 g_date_valid_weekday (GDateWeekday w)
140 {
141   return ( (w > G_DATE_BAD_WEEKDAY) && (w < 8) );
142 }
143
144 gboolean     
145 g_date_valid_julian (guint32      j)
146 {
147   return (j > G_DATE_BAD_JULIAN);
148 }
149
150 gboolean     
151 g_date_valid_dmy (GDateDay     d, 
152                   GDateMonth   m, 
153                   GDateYear    y)
154 {
155   return ( (m > G_DATE_BAD_MONTH) &&
156            (m < 13)               && 
157            (d > G_DATE_BAD_DAY)   && 
158            (y > G_DATE_BAD_YEAR)  &&   /* must check before using g_date_is_leap_year */
159            (d <=  (g_date_is_leap_year (y) ? 
160                    days_in_months[1][m] : days_in_months[0][m])) );
161 }
162
163
164 /* "Julian days" just means an absolute number of days, where Day 1 ==
165  *   Jan 1, Year 1
166  */
167 static void
168 g_date_update_julian (const GDate *const_d)
169 {
170   GDate *d = (GDate *) const_d;
171   GDateYear year;
172   gint index;
173   
174   g_return_if_fail (d != NULL);
175   g_return_if_fail (d->dmy);
176   g_return_if_fail (!d->julian);
177   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
178   
179   /* What we actually do is: multiply years * 365 days in the year,
180    *  add the number of years divided by 4, subtract the number of
181    *  years divided by 100 and add the number of years divided by 400,
182    *  which accounts for leap year stuff. Code from Steffen Beyer's
183    *  DateCalc. 
184    */
185   
186   year = d->year - 1; /* we know d->year > 0 since it's valid */
187   
188   d->julian_days = year * 365U;
189   d->julian_days += (year >>= 2); /* divide by 4 and add */
190   d->julian_days -= (year /= 25); /* divides original # years by 100 */
191   d->julian_days += year >> 2;    /* divides by 4, which divides original by 400 */
192   
193   index = g_date_is_leap_year (d->year) ? 1 : 0;
194   
195   d->julian_days += days_in_year[index][d->month] + d->day;
196   
197   g_return_if_fail (g_date_valid_julian (d->julian_days));
198   
199   d->julian = TRUE;
200 }
201
202 static void 
203 g_date_update_dmy (const GDate *const_d)
204 {
205   GDate *d = (GDate *) const_d;
206   GDateYear y;
207   GDateMonth m;
208   GDateDay day;
209   
210   guint32 A, B, C, D, E, M;
211   
212   g_return_if_fail (d != NULL);
213   g_return_if_fail (d->julian);
214   g_return_if_fail (!d->dmy);
215   g_return_if_fail (g_date_valid_julian (d->julian_days));
216   
217   /* Formula taken from the Calendar FAQ; the formula was for the
218    *  Julian Period which starts on 1 January 4713 BC, so we add
219    *  1,721,425 to the number of days before doing the formula.
220    *
221    * I'm sure this can be simplified for our 1 January 1 AD period
222    * start, but I can't figure out how to unpack the formula.  
223    */
224   
225   A = d->julian_days + 1721425 + 32045;
226   B = ( 4 *(A + 36524) )/ 146097 - 1;
227   C = A - (146097 * B)/4;
228   D = ( 4 * (C + 365) ) / 1461 - 1;
229   E = C - ((1461*D) / 4);
230   M = (5 * (E - 1) + 2)/153;
231   
232   m = M + 3 - (12*(M/10));
233   day = E - (153*M + 2)/5;
234   y = 100 * B + D - 4800 + (M/10);
235   
236 #ifdef G_ENABLE_DEBUG
237   if (!g_date_valid_dmy (day, m, y)) 
238     {
239       g_warning ("\nOOPS julian: %u  computed dmy: %u %u %u\n", 
240                  d->julian_days, day, m, y);
241     }
242 #endif
243   
244   d->month = m;
245   d->day   = day;
246   d->year  = y;
247   
248   d->dmy = TRUE;
249 }
250
251 GDateWeekday 
252 g_date_get_weekday (const GDate *d)
253 {
254   g_return_val_if_fail (d != NULL, G_DATE_BAD_WEEKDAY);
255   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
256   
257   if (!d->julian) 
258     {
259       g_date_update_julian (d);
260     }
261   g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
262   
263   return ((d->julian_days - 1) % 7) + 1;
264 }
265
266 GDateMonth   
267 g_date_get_month (const GDate *d)
268 {
269   g_return_val_if_fail (d != NULL, G_DATE_BAD_MONTH);
270   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
271   
272   if (!d->dmy) 
273     {
274       g_date_update_dmy (d);
275     }
276   g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
277   
278   return d->month;
279 }
280
281 GDateYear    
282 g_date_get_year (const GDate *d)
283 {
284   g_return_val_if_fail (d != NULL, G_DATE_BAD_YEAR);
285   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
286   
287   if (!d->dmy) 
288     {
289       g_date_update_dmy (d);
290     }
291   g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);  
292   
293   return d->year;
294 }
295
296 GDateDay     
297 g_date_get_day (const GDate *d)
298 {
299   g_return_val_if_fail (d != NULL, G_DATE_BAD_DAY);
300   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
301   
302   if (!d->dmy) 
303     {
304       g_date_update_dmy (d);
305     }
306   g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);  
307   
308   return d->day;
309 }
310
311 guint32      
312 g_date_get_julian (const GDate *d)
313 {
314   g_return_val_if_fail (d != NULL, G_DATE_BAD_JULIAN);
315   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
316   
317   if (!d->julian) 
318     {
319       g_date_update_julian (d);
320     }
321   g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN);  
322   
323   return d->julian_days;
324 }
325
326 guint        
327 g_date_get_day_of_year (const GDate *d)
328 {
329   gint index;
330   
331   g_return_val_if_fail (d != NULL, 0);
332   g_return_val_if_fail (g_date_valid (d), 0);
333   
334   if (!d->dmy) 
335     {
336       g_date_update_dmy (d);
337     }
338   g_return_val_if_fail (d->dmy, 0);  
339   
340   index = g_date_is_leap_year (d->year) ? 1 : 0;
341   
342   return (days_in_year[index][d->month] + d->day);
343 }
344
345 guint        
346 g_date_get_monday_week_of_year (const GDate *d)
347 {
348   GDateWeekday wd;
349   guint day;
350   GDate first;
351   
352   g_return_val_if_fail (d != NULL, 0);
353   g_return_val_if_fail (g_date_valid (d), 0);
354   
355   if (!d->dmy) 
356     {
357       g_date_update_dmy (d);
358     }
359   g_return_val_if_fail (d->dmy, 0);  
360   
361   g_date_clear (&first, 1);
362   
363   g_date_set_dmy (&first, 1, 1, d->year);
364   
365   wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
366   day = g_date_get_day_of_year (d) - 1;
367   
368   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
369 }
370
371 guint        
372 g_date_get_sunday_week_of_year (const GDate *d)
373 {
374   GDateWeekday wd;
375   guint day;
376   GDate first;
377   
378   g_return_val_if_fail (d != NULL, 0);
379   g_return_val_if_fail (g_date_valid (d), 0);
380   
381   if (!d->dmy) 
382     {
383       g_date_update_dmy (d);
384     }
385   g_return_val_if_fail (d->dmy, 0);  
386   
387   g_date_clear (&first, 1);
388   
389   g_date_set_dmy (&first, 1, 1, d->year);
390   
391   wd = g_date_get_weekday (&first);
392   if (wd == 7) wd = 0; /* make Sunday day 0 */
393   day = g_date_get_day_of_year (d) - 1;
394   
395   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
396 }
397
398 /**
399  * g_date_get_iso8601_week_of_year:
400  * @date: a valid #GDate
401  *
402  * Returns the week of the year, where weeks are interpreted according
403  * to ISO 8601. 
404  * 
405  * Returns: ISO 8601 week number of the year.
406  *
407  * Since: 2.6
408  **/
409 guint
410 g_date_get_iso8601_week_of_year (const GDate *d)
411 {
412   guint j, d4, L, d1, w;
413
414   g_return_val_if_fail (d != NULL, 0);
415   g_return_val_if_fail (g_date_valid (d), 0);
416   
417   if (!d->julian)
418     g_date_update_julian (d);
419   g_return_val_if_fail (d->julian, 0);
420
421   /* Formula taken from the Calendar FAQ; the formula was for the
422    * Julian Period which starts on 1 January 4713 BC, so we add
423    * 1,721,425 to the number of days before doing the formula. 
424    */
425   j  = d->julian + 1721425;
426   d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461;
427   L  = d4 / 1460;
428   d1 = ((d4 - L) % 365) + L;
429   w  = d1 / 7 + 1;
430
431   return w;
432 }
433
434 gint
435 g_date_days_between (const GDate *d1,
436                      const GDate *d2)
437 {
438   g_return_val_if_fail (d1 != NULL, 0);
439   g_return_val_if_fail (d2 != NULL, 0);
440
441   g_return_val_if_fail (g_date_valid (d1), 0);
442   g_return_val_if_fail (g_date_valid (d2), 0);
443
444   return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
445 }
446
447 void         
448 g_date_clear (GDate       *d, guint ndates)
449 {
450   g_return_if_fail (d != NULL);
451   g_return_if_fail (ndates != 0);
452   
453   memset (d, 0x0, ndates*sizeof (GDate)); 
454 }
455
456 G_LOCK_DEFINE_STATIC (g_date_global);
457
458 /* These are for the parser, output to the user should use *
459  * g_date_strftime () - this creates more never-freed memory to annoy
460  * all those memory debugger users. :-) 
461  */
462
463 static gchar *long_month_names[13] = 
464
465   "Error", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 
466 };
467
468 static gchar *short_month_names[13] = 
469 {
470   "Error", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 
471 };
472
473 /* This tells us if we need to update the parse info */
474 static gchar *current_locale = NULL;
475
476 /* order of these in the current locale */
477 static GDateDMY dmy_order[3] = 
478 {
479    G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
480 };
481
482 /* Where to chop two-digit years: i.e., for the 1930 default, numbers
483  * 29 and below are counted as in the year 2000, numbers 30 and above
484  * are counted as in the year 1900.  
485  */
486
487 static GDateYear twodigit_start_year = 1930;
488
489 /* It is impossible to enter a year between 1 AD and 99 AD with this
490  * in effect.  
491  */
492 static gboolean using_twodigit_years = FALSE;
493
494 /* Adjustment of locale era to AD, non-zero means using locale era
495  */
496 static gint locale_era_adjust = 0;
497
498 struct _GDateParseTokens {
499   gint num_ints;
500   gint n[3];
501   guint month;
502 };
503
504 typedef struct _GDateParseTokens GDateParseTokens;
505
506 #define NUM_LEN 10
507
508 /* HOLDS: g_date_global_lock */
509 static void
510 g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
511 {
512   gchar num[4][NUM_LEN+1];
513   gint i;
514   const guchar *s;
515   
516   /* We count 4, but store 3; so we can give an error
517    * if there are 4.
518    */
519   num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
520   
521   s = (const guchar *) str;
522   pt->num_ints = 0;
523   while (*s && pt->num_ints < 4) 
524     {
525       
526       i = 0;
527       while (*s && g_ascii_isdigit (*s) && i <= NUM_LEN)
528         {
529           num[pt->num_ints][i] = *s;
530           ++s; 
531           ++i;
532         }
533       
534       if (i > 0) 
535         {
536           num[pt->num_ints][i] = '\0';
537           ++(pt->num_ints);
538         }
539       
540       if (*s == '\0') break;
541       
542       ++s;
543     }
544   
545   pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
546   pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
547   pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
548   
549   pt->month = G_DATE_BAD_MONTH;
550   
551   if (pt->num_ints < 3)
552     {
553       gchar *casefold;
554       gchar *normalized;
555       
556       casefold = g_utf8_casefold (str, -1);
557       normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
558       g_free (casefold);
559
560       i = 1;
561       while (i < 13)
562         {
563           if (long_month_names[i] != NULL) 
564             {
565               const gchar *found = strstr (normalized, long_month_names[i]);
566               
567               if (found != NULL)
568                 {
569                   pt->month = i;
570                   break;
571                 }
572             }
573           
574           if (short_month_names[i] != NULL) 
575             {
576               const gchar *found = strstr (normalized, short_month_names[i]);
577               
578               if (found != NULL)
579                 {
580                   pt->month = i;
581                   break;
582                 }
583             }
584
585           ++i;
586         }
587
588       g_free (normalized);
589     }
590 }
591
592 /* HOLDS: g_date_global_lock */
593 static void
594 g_date_prepare_to_parse (const gchar *str, GDateParseTokens *pt)
595 {
596   const gchar *locale = setlocale (LC_TIME, NULL);
597   gboolean recompute_localeinfo = FALSE;
598   GDate d;
599   
600   g_return_if_fail (locale != NULL); /* should not happen */
601   
602   g_date_clear (&d, 1);              /* clear for scratch use */
603   
604   if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) ) 
605     {
606       recompute_localeinfo = TRUE;  /* Uh, there used to be a reason for the temporary */
607     }
608   
609   if (recompute_localeinfo)
610     {
611       int i = 1;
612       GDateParseTokens testpt;
613       gchar buf[128];
614       
615       g_free (current_locale); /* still works if current_locale == NULL */
616       
617       current_locale = g_strdup (locale);
618       
619       while (i < 13) 
620         {
621           gchar *casefold;
622           
623           g_date_set_dmy (&d, 1, i, 1);
624           
625           g_return_if_fail (g_date_valid (&d));
626           
627           g_date_strftime (buf, 127, "%b", &d);
628
629           casefold = g_utf8_casefold (buf, -1);
630           g_free (short_month_names[i]);
631           short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
632           g_free (casefold);
633           
634           g_date_strftime (buf, 127, "%B", &d);
635           casefold = g_utf8_casefold (buf, -1);
636           g_free (long_month_names[i]);
637           long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
638           g_free (casefold);
639           
640           ++i;
641         }
642       
643       /* Determine DMY order */
644       
645       /* had to pick a random day - don't change this, some strftimes
646        * are broken on some days, and this one is good so far. */
647       g_date_set_dmy (&d, 4, 7, 1976);
648       
649       g_date_strftime (buf, 127, "%x", &d);
650       
651       g_date_fill_parse_tokens (buf, &testpt);
652       
653       i = 0;
654       while (i < testpt.num_ints)
655         {
656           switch (testpt.n[i])
657             {
658             case 7:
659               dmy_order[i] = G_DATE_MONTH;
660               break;
661             case 4:
662               dmy_order[i] = G_DATE_DAY;
663               break;
664             case 76:
665               using_twodigit_years = TRUE; /* FALL THRU */
666             case 1976:
667               dmy_order[i] = G_DATE_YEAR;
668               break;
669             default:
670               /* assume locale era */
671               locale_era_adjust = 1976 - testpt.n[i];
672               dmy_order[i] = G_DATE_YEAR;
673               break;
674             }
675           ++i;
676         }
677       
678 #ifdef G_ENABLE_DEBUG
679       DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
680       i = 1;
681       while (i < 13) 
682         {
683           DEBUG_MSG (("  %s   %s", long_month_names[i], short_month_names[i]));
684           ++i;
685         }
686       if (using_twodigit_years)
687         DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
688       { 
689         gchar *strings[3];
690         i = 0;
691         while (i < 3)
692           {
693             switch (dmy_order[i])
694               {
695               case G_DATE_MONTH:
696                 strings[i] = "Month";
697                 break;
698               case G_DATE_YEAR:
699                 strings[i] = "Year";
700                 break;
701               case G_DATE_DAY:
702                 strings[i] = "Day";
703                 break;
704               default:
705                 strings[i] = NULL;
706                 break;
707               }
708             ++i;
709           }
710         DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
711         DEBUG_MSG (("**Sample date in this locale: `%s'", buf));
712       }
713 #endif
714     }
715   
716   g_date_fill_parse_tokens (str, pt);
717 }
718
719 void         
720 g_date_set_parse (GDate       *d, 
721                   const gchar *str)
722 {
723   GDateParseTokens pt;
724   guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
725   
726   g_return_if_fail (d != NULL);
727   
728   /* set invalid */
729   g_date_clear (d, 1);
730   
731   G_LOCK (g_date_global);
732
733   g_date_prepare_to_parse (str, &pt);
734   
735   DEBUG_MSG (("Found %d ints, `%d' `%d' `%d' and written out month %d", 
736               pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
737   
738   
739   if (pt.num_ints == 4) 
740     {
741       G_UNLOCK (g_date_global);
742       return; /* presumably a typo; bail out. */
743     }
744   
745   if (pt.num_ints > 1)
746     {
747       int i = 0;
748       int j = 0;
749       
750       g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
751       
752       while (i < pt.num_ints && j < 3) 
753         {
754           switch (dmy_order[j])
755             {
756             case G_DATE_MONTH:
757             {
758               if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
759                 {
760                   m = pt.month;
761                   ++j;      /* skip months, but don't skip this number */
762                   continue;
763                 }
764               else 
765                 m = pt.n[i];
766             }
767             break;
768             case G_DATE_DAY:
769             {
770               if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
771                 {
772                   day = 1;
773                   ++j;      /* skip days, since we may have month/year */
774                   continue;
775                 }
776               day = pt.n[i];
777             }
778             break;
779             case G_DATE_YEAR:
780             {
781               y  = pt.n[i];
782               
783               if (locale_era_adjust != 0)
784                 {
785                   y += locale_era_adjust;
786                 }
787               else if (using_twodigit_years && y < 100)
788                 {
789                   guint two     =  twodigit_start_year % 100;
790                   guint century = (twodigit_start_year / 100) * 100;
791                   
792                   if (y < two)
793                     century += 100;
794                   
795                   y += century;
796                 }
797             }
798             break;
799             default:
800               break;
801             }
802           
803           ++i;
804           ++j;
805         }
806       
807       
808       if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
809         {
810           /* Try YYYY MM DD */
811           y   = pt.n[0];
812           m   = pt.n[1];
813           day = pt.n[2];
814           
815           if (using_twodigit_years && y < 100) 
816             y = G_DATE_BAD_YEAR; /* avoids ambiguity */
817         }
818       else if (pt.num_ints == 2)
819         {
820           if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
821             {
822               m = pt.month;
823             }
824         }
825     }
826   else if (pt.num_ints == 1) 
827     {
828       if (pt.month != G_DATE_BAD_MONTH)
829         {
830           /* Month name and year? */
831           m    = pt.month;
832           day  = 1;
833           y = pt.n[0];
834         }
835       else
836         {
837           /* Try yyyymmdd and yymmdd */
838           
839           m   = (pt.n[0]/100) % 100;
840           day = pt.n[0] % 100;
841           y   = pt.n[0]/10000;
842           
843           /* FIXME move this into a separate function */
844           if (using_twodigit_years && y < 100)
845             {
846               guint two     =  twodigit_start_year % 100;
847               guint century = (twodigit_start_year / 100) * 100;
848               
849               if (y < two)
850                 century += 100;
851               
852               y += century;
853             }
854         }
855     }
856   
857   /* See if we got anything valid out of all this. */
858   /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
859   if (y < 8000 && g_date_valid_dmy (day, m, y)) 
860     {
861       d->month = m;
862       d->day   = day;
863       d->year  = y;
864       d->dmy   = TRUE;
865     }
866 #ifdef G_ENABLE_DEBUG
867   else 
868     DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
869 #endif
870   G_UNLOCK (g_date_global);
871 }
872
873 void         
874 g_date_set_time (GDate *d,
875                  GTime  time)
876 {
877   time_t t = time;
878   struct tm tm;
879   
880   g_return_if_fail (d != NULL);
881   
882 #ifdef HAVE_LOCALTIME_R
883   localtime_r (&t, &tm);
884 #else
885   {
886     struct tm *ptm = localtime (&t);
887     g_assert (ptm);
888     memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
889   }
890 #endif
891   
892   d->julian = FALSE;
893   
894   d->month = tm.tm_mon + 1;
895   d->day   = tm.tm_mday;
896   d->year  = tm.tm_year + 1900;
897   
898   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
899   
900   d->dmy    = TRUE;
901 }
902
903 void         
904 g_date_set_month (GDate     *d, 
905                   GDateMonth m)
906 {
907   g_return_if_fail (d != NULL);
908   g_return_if_fail (g_date_valid_month (m));
909
910   if (d->julian && !d->dmy) g_date_update_dmy(d);
911   d->julian = FALSE;
912   
913   d->month = m;
914   
915   if (g_date_valid_dmy (d->day, d->month, d->year))
916     d->dmy = TRUE;
917   else 
918     d->dmy = FALSE;
919 }
920
921 void         
922 g_date_set_day (GDate     *d, 
923                 GDateDay day)
924 {
925   g_return_if_fail (d != NULL);
926   g_return_if_fail (g_date_valid_day (day));
927   
928   if (d->julian && !d->dmy) g_date_update_dmy(d);
929   d->julian = FALSE;
930   
931   d->day = day;
932   
933   if (g_date_valid_dmy (d->day, d->month, d->year))
934     d->dmy = TRUE;
935   else 
936     d->dmy = FALSE;
937 }
938
939 void         
940 g_date_set_year (GDate     *d, 
941                  GDateYear  y)
942 {
943   g_return_if_fail (d != NULL);
944   g_return_if_fail (g_date_valid_year (y));
945   
946   if (d->julian && !d->dmy) g_date_update_dmy(d);
947   d->julian = FALSE;
948   
949   d->year = y;
950   
951   if (g_date_valid_dmy (d->day, d->month, d->year))
952     d->dmy = TRUE;
953   else 
954     d->dmy = FALSE;
955 }
956
957 void         
958 g_date_set_dmy (GDate     *d, 
959                 GDateDay   day, 
960                 GDateMonth m, 
961                 GDateYear  y)
962 {
963   g_return_if_fail (d != NULL);
964   g_return_if_fail (g_date_valid_dmy (day, m, y));
965   
966   d->julian = FALSE;
967   
968   d->month = m;
969   d->day   = day;
970   d->year  = y;
971   
972   d->dmy = TRUE;
973 }
974
975 void         
976 g_date_set_julian (GDate *d, guint32 j)
977 {
978   g_return_if_fail (d != NULL);
979   g_return_if_fail (g_date_valid_julian (j));
980   
981   d->julian_days = j;
982   d->julian = TRUE;
983   d->dmy = FALSE;
984 }
985
986
987 gboolean     
988 g_date_is_first_of_month (const GDate *d)
989 {
990   g_return_val_if_fail (d != NULL, FALSE);
991   g_return_val_if_fail (g_date_valid (d), FALSE);
992   
993   if (!d->dmy) 
994     {
995       g_date_update_dmy (d);
996     }
997   g_return_val_if_fail (d->dmy, FALSE);  
998   
999   if (d->day == 1) return TRUE;
1000   else return FALSE;
1001 }
1002
1003 gboolean     
1004 g_date_is_last_of_month (const GDate *d)
1005 {
1006   gint index;
1007   
1008   g_return_val_if_fail (d != NULL, FALSE);
1009   g_return_val_if_fail (g_date_valid (d), FALSE);
1010   
1011   if (!d->dmy) 
1012     {
1013       g_date_update_dmy (d);
1014     }
1015   g_return_val_if_fail (d->dmy, FALSE);  
1016   
1017   index = g_date_is_leap_year (d->year) ? 1 : 0;
1018   
1019   if (d->day == days_in_months[index][d->month]) return TRUE;
1020   else return FALSE;
1021 }
1022
1023 void         
1024 g_date_add_days (GDate *d, guint ndays)
1025 {
1026   g_return_if_fail (d != NULL);
1027   g_return_if_fail (g_date_valid (d));
1028   
1029   if (!d->julian)
1030     {
1031       g_date_update_julian (d);
1032     }
1033   g_return_if_fail (d->julian);
1034   
1035   d->julian_days += ndays;
1036   d->dmy = FALSE;
1037 }
1038
1039 void         
1040 g_date_subtract_days (GDate *d, guint ndays)
1041 {
1042   g_return_if_fail (d != NULL);
1043   g_return_if_fail (g_date_valid (d));
1044   
1045   if (!d->julian)
1046     {
1047       g_date_update_julian (d);
1048     }
1049   g_return_if_fail (d->julian);
1050   g_return_if_fail (d->julian_days > ndays);
1051   
1052   d->julian_days -= ndays;
1053   d->dmy = FALSE;
1054 }
1055
1056 void         
1057 g_date_add_months (GDate       *d, 
1058                    guint        nmonths)
1059 {
1060   guint years, months;
1061   gint index;
1062   
1063   g_return_if_fail (d != NULL);
1064   g_return_if_fail (g_date_valid (d));
1065   
1066   if (!d->dmy) 
1067     {
1068       g_date_update_dmy (d);
1069     }
1070   g_return_if_fail (d->dmy);  
1071   
1072   nmonths += d->month - 1;
1073   
1074   years  = nmonths/12;
1075   months = nmonths%12;
1076   
1077   d->month = months + 1;
1078   d->year  += years;
1079   
1080   index = g_date_is_leap_year (d->year) ? 1 : 0;
1081   
1082   if (d->day > days_in_months[index][d->month])
1083     d->day = days_in_months[index][d->month];
1084   
1085   d->julian = FALSE;
1086   
1087   g_return_if_fail (g_date_valid (d));
1088 }
1089
1090 void         
1091 g_date_subtract_months (GDate       *d, 
1092                         guint        nmonths)
1093 {
1094   guint years, months;
1095   gint index;
1096   
1097   g_return_if_fail (d != NULL);
1098   g_return_if_fail (g_date_valid (d));
1099   
1100   if (!d->dmy) 
1101     {
1102       g_date_update_dmy (d);
1103     }
1104   g_return_if_fail (d->dmy);  
1105   
1106   years  = nmonths/12;
1107   months = nmonths%12;
1108   
1109   g_return_if_fail (d->year > years);
1110   
1111   d->year  -= years;
1112   
1113   if (d->month > months) d->month -= months;
1114   else 
1115     {
1116       months -= d->month;
1117       d->month = 12 - months;
1118       d->year -= 1;
1119     }
1120   
1121   index = g_date_is_leap_year (d->year) ? 1 : 0;
1122   
1123   if (d->day > days_in_months[index][d->month])
1124     d->day = days_in_months[index][d->month];
1125   
1126   d->julian = FALSE;
1127   
1128   g_return_if_fail (g_date_valid (d));
1129 }
1130
1131 void         
1132 g_date_add_years (GDate       *d, 
1133                   guint        nyears)
1134 {
1135   g_return_if_fail (d != NULL);
1136   g_return_if_fail (g_date_valid (d));
1137   
1138   if (!d->dmy) 
1139     {
1140       g_date_update_dmy (d);
1141     }
1142   g_return_if_fail (d->dmy);  
1143   
1144   d->year += nyears;
1145   
1146   if (d->month == 2 && d->day == 29)
1147     {
1148       if (!g_date_is_leap_year (d->year))
1149         {
1150           d->day = 28;
1151         }
1152     }
1153   
1154   d->julian = FALSE;
1155 }
1156
1157 void         
1158 g_date_subtract_years (GDate       *d, 
1159                        guint        nyears)
1160 {
1161   g_return_if_fail (d != NULL);
1162   g_return_if_fail (g_date_valid (d));
1163   
1164   if (!d->dmy) 
1165     {
1166       g_date_update_dmy (d);
1167     }
1168   g_return_if_fail (d->dmy);  
1169   g_return_if_fail (d->year > nyears);
1170   
1171   d->year -= nyears;
1172   
1173   if (d->month == 2 && d->day == 29)
1174     {
1175       if (!g_date_is_leap_year (d->year))
1176         {
1177           d->day = 28;
1178         }
1179     }
1180   
1181   d->julian = FALSE;
1182 }
1183
1184
1185 gboolean     
1186 g_date_is_leap_year (GDateYear  year)
1187 {
1188   g_return_val_if_fail (g_date_valid_year (year), FALSE);
1189   
1190   return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
1191            (year % 400) == 0 );
1192 }
1193
1194 guint8         
1195 g_date_get_days_in_month (GDateMonth month, 
1196                           GDateYear  year)
1197 {
1198   gint index;
1199   
1200   g_return_val_if_fail (g_date_valid_year (year), 0);
1201   g_return_val_if_fail (g_date_valid_month (month), 0);
1202   
1203   index = g_date_is_leap_year (year) ? 1 : 0;
1204   
1205   return days_in_months[index][month];
1206 }
1207
1208 guint8       
1209 g_date_get_monday_weeks_in_year (GDateYear  year)
1210 {
1211   GDate d;
1212   
1213   g_return_val_if_fail (g_date_valid_year (year), 0);
1214   
1215   g_date_clear (&d, 1);
1216   g_date_set_dmy (&d, 1, 1, year);
1217   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1218   g_date_set_dmy (&d, 31, 12, year);
1219   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1220   if (g_date_is_leap_year (year)) 
1221     {
1222       g_date_set_dmy (&d, 2, 1, year);
1223       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1224       g_date_set_dmy (&d, 30, 12, year);
1225       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1226     }
1227   return 52;
1228 }
1229
1230 guint8       
1231 g_date_get_sunday_weeks_in_year (GDateYear  year)
1232 {
1233   GDate d;
1234   
1235   g_return_val_if_fail (g_date_valid_year (year), 0);
1236   
1237   g_date_clear (&d, 1);
1238   g_date_set_dmy (&d, 1, 1, year);
1239   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1240   g_date_set_dmy (&d, 31, 12, year);
1241   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1242   if (g_date_is_leap_year (year)) 
1243     {
1244       g_date_set_dmy (&d, 2, 1, year);
1245       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1246       g_date_set_dmy (&d, 30, 12, year);
1247       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1248     }
1249   return 52;
1250 }
1251
1252 gint         
1253 g_date_compare (const GDate *lhs, 
1254                 const GDate *rhs)
1255 {
1256   g_return_val_if_fail (lhs != NULL, 0);
1257   g_return_val_if_fail (rhs != NULL, 0);
1258   g_return_val_if_fail (g_date_valid (lhs), 0);
1259   g_return_val_if_fail (g_date_valid (rhs), 0);
1260   
1261   /* Remember the self-comparison case! I think it works right now. */
1262   
1263   while (TRUE)
1264     {
1265       
1266       if (lhs->julian && rhs->julian) 
1267         {
1268           if (lhs->julian_days < rhs->julian_days) return -1;
1269           else if (lhs->julian_days > rhs->julian_days) return 1;
1270           else                                          return 0;
1271         }
1272       else if (lhs->dmy && rhs->dmy) 
1273         {
1274           if (lhs->year < rhs->year)               return -1;
1275           else if (lhs->year > rhs->year)               return 1;
1276           else 
1277             {
1278               if (lhs->month < rhs->month)         return -1;
1279               else if (lhs->month > rhs->month)         return 1;
1280               else 
1281                 {
1282                   if (lhs->day < rhs->day)              return -1;
1283                   else if (lhs->day > rhs->day)              return 1;
1284                   else                                       return 0;
1285                 }
1286               
1287             }
1288           
1289         }
1290       else
1291         {
1292           if (!lhs->julian) g_date_update_julian (lhs);
1293           if (!rhs->julian) g_date_update_julian (rhs);
1294           g_return_val_if_fail (lhs->julian, 0);
1295           g_return_val_if_fail (rhs->julian, 0);
1296         }
1297       
1298     }
1299   return 0; /* warnings */
1300 }
1301
1302
1303 void        
1304 g_date_to_struct_tm (const GDate *d, 
1305                      struct tm   *tm)
1306 {
1307   GDateWeekday day;
1308      
1309   g_return_if_fail (d != NULL);
1310   g_return_if_fail (g_date_valid (d));
1311   g_return_if_fail (tm != NULL);
1312   
1313   if (!d->dmy) 
1314     {
1315       g_date_update_dmy (d);
1316     }
1317   g_return_if_fail (d->dmy);
1318   
1319   /* zero all the irrelevant fields to be sure they're valid */
1320   
1321   /* On Linux and maybe other systems, there are weird non-POSIX
1322    * fields on the end of struct tm that choke strftime if they
1323    * contain garbage.  So we need to 0 the entire struct, not just the
1324    * fields we know to exist. 
1325    */
1326   
1327   memset (tm, 0x0, sizeof (struct tm));
1328   
1329   tm->tm_mday = d->day;
1330   tm->tm_mon  = d->month - 1; /* 0-11 goes in tm */
1331   tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
1332   
1333   day = g_date_get_weekday (d);
1334   if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
1335   
1336   tm->tm_wday = (int)day;
1337   
1338   tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
1339   tm->tm_isdst = -1; /* -1 means "information not available" */
1340 }
1341
1342 void
1343 g_date_clamp (GDate *date,
1344               const GDate *min_date,
1345               const GDate *max_date)
1346 {
1347   g_return_if_fail (date);
1348   g_return_if_fail (g_date_valid (date));
1349   if (min_date != NULL)
1350     g_return_if_fail (g_date_valid (min_date));
1351   if (max_date != NULL)
1352     g_return_if_fail (g_date_valid (max_date));
1353   if (min_date != NULL && max_date != NULL)
1354     g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
1355
1356   if (min_date && g_date_compare (date, min_date) < 0)
1357     *date = *min_date;
1358
1359   if (max_date && g_date_compare (max_date, date) < 0)
1360     *date = *max_date;
1361 }
1362
1363 void
1364 g_date_order (GDate *date1,
1365               GDate *date2)
1366 {
1367   g_return_if_fail (date1 != NULL);
1368   g_return_if_fail (date2 != NULL);
1369   g_return_if_fail (g_date_valid (date1));
1370   g_return_if_fail (g_date_valid (date2));
1371
1372   if (g_date_compare (date1, date2) > 0)
1373     {
1374       GDate tmp = *date1;
1375       *date1 = *date2;
1376       *date2 = tmp;
1377     }
1378 }
1379
1380 gsize     
1381 g_date_strftime (gchar       *s, 
1382                  gsize        slen, 
1383                  const gchar *format, 
1384                  const GDate *d)
1385 {
1386   struct tm tm;
1387   gsize locale_format_len = 0;
1388   gchar *locale_format;
1389   gsize tmplen;
1390   gchar *tmpbuf;
1391   gsize tmpbufsize;
1392   gsize convlen = 0;
1393   gchar *convbuf;
1394   GError *error = NULL;
1395   gsize retval;
1396
1397   g_return_val_if_fail (d != NULL, 0);
1398   g_return_val_if_fail (g_date_valid (d), 0);
1399   g_return_val_if_fail (slen > 0, 0); 
1400   g_return_val_if_fail (format != 0, 0);
1401   g_return_val_if_fail (s != 0, 0);
1402
1403   g_date_to_struct_tm (d, &tm);
1404
1405   locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
1406
1407   if (error)
1408     {
1409       g_warning (G_STRLOC "Error converting format to locale encoding: %s\n", error->message);
1410       g_error_free (error);
1411
1412       s[0] = '\0';
1413       return 0;
1414     }
1415
1416   tmpbufsize = MAX (128, locale_format_len * 2);
1417   while (TRUE)
1418     {
1419       tmpbuf = g_malloc (tmpbufsize);
1420
1421       /* Set the first byte to something other than '\0', to be able to
1422        * recognize whether strftime actually failed or just returned "".
1423        */
1424       tmpbuf[0] = '\1';
1425       tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
1426
1427       if (tmplen == 0 && tmpbuf[0] != '\0')
1428         {
1429           g_free (tmpbuf);
1430           tmpbufsize *= 2;
1431
1432           if (tmpbufsize > 65536)
1433             {
1434               g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up\n");
1435               g_free (locale_format);
1436
1437               s[0] = '\0';
1438               return 0;
1439             }
1440         }
1441       else
1442         break;
1443     }
1444   g_free (locale_format);
1445
1446   convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
1447   g_free (tmpbuf);
1448
1449   if (error)
1450     {
1451       g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s\n", error->message);
1452       g_error_free (error);
1453
1454       s[0] = '\0';
1455       return 0;
1456     }
1457
1458   if (slen <= convlen)
1459     {
1460       /* Ensure only whole characters are copied into the buffer.
1461        */
1462       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
1463       g_assert (end != NULL);
1464       convlen = end - convbuf;
1465
1466       /* Return 0 because the buffer isn't large enough.
1467        */
1468       retval = 0;
1469     }
1470   else
1471     retval = convlen;
1472
1473   memcpy (s, convbuf, convlen);
1474   s[convlen] = '\0';
1475   g_free (convbuf);
1476
1477   return retval;
1478 }
1479