Don't g_assert that localtime() returns non-NULL. It does return NULL at
[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
888     if (ptm == NULL)
889       {
890         /* Happens at least in Microsoft's C library if you pass a
891          * negative time_t. Use 2000-01-01 as default date.
892          */
893 #ifndef G_DISABLE_CHECKS
894         g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
895 #endif
896
897         tm.tm_mon = 0;
898         tm.tm_mday = 1;
899         tm.tm_year = 100;
900       }
901     else
902       memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
903   }
904 #endif
905   
906   d->julian = FALSE;
907   
908   d->month = tm.tm_mon + 1;
909   d->day   = tm.tm_mday;
910   d->year  = tm.tm_year + 1900;
911   
912   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
913   
914   d->dmy    = TRUE;
915 }
916
917 void         
918 g_date_set_month (GDate     *d, 
919                   GDateMonth m)
920 {
921   g_return_if_fail (d != NULL);
922   g_return_if_fail (g_date_valid_month (m));
923
924   if (d->julian && !d->dmy) g_date_update_dmy(d);
925   d->julian = FALSE;
926   
927   d->month = m;
928   
929   if (g_date_valid_dmy (d->day, d->month, d->year))
930     d->dmy = TRUE;
931   else 
932     d->dmy = FALSE;
933 }
934
935 void         
936 g_date_set_day (GDate     *d, 
937                 GDateDay day)
938 {
939   g_return_if_fail (d != NULL);
940   g_return_if_fail (g_date_valid_day (day));
941   
942   if (d->julian && !d->dmy) g_date_update_dmy(d);
943   d->julian = FALSE;
944   
945   d->day = day;
946   
947   if (g_date_valid_dmy (d->day, d->month, d->year))
948     d->dmy = TRUE;
949   else 
950     d->dmy = FALSE;
951 }
952
953 void         
954 g_date_set_year (GDate     *d, 
955                  GDateYear  y)
956 {
957   g_return_if_fail (d != NULL);
958   g_return_if_fail (g_date_valid_year (y));
959   
960   if (d->julian && !d->dmy) g_date_update_dmy(d);
961   d->julian = FALSE;
962   
963   d->year = y;
964   
965   if (g_date_valid_dmy (d->day, d->month, d->year))
966     d->dmy = TRUE;
967   else 
968     d->dmy = FALSE;
969 }
970
971 void         
972 g_date_set_dmy (GDate     *d, 
973                 GDateDay   day, 
974                 GDateMonth m, 
975                 GDateYear  y)
976 {
977   g_return_if_fail (d != NULL);
978   g_return_if_fail (g_date_valid_dmy (day, m, y));
979   
980   d->julian = FALSE;
981   
982   d->month = m;
983   d->day   = day;
984   d->year  = y;
985   
986   d->dmy = TRUE;
987 }
988
989 void         
990 g_date_set_julian (GDate *d, guint32 j)
991 {
992   g_return_if_fail (d != NULL);
993   g_return_if_fail (g_date_valid_julian (j));
994   
995   d->julian_days = j;
996   d->julian = TRUE;
997   d->dmy = FALSE;
998 }
999
1000
1001 gboolean     
1002 g_date_is_first_of_month (const GDate *d)
1003 {
1004   g_return_val_if_fail (d != NULL, FALSE);
1005   g_return_val_if_fail (g_date_valid (d), FALSE);
1006   
1007   if (!d->dmy) 
1008     {
1009       g_date_update_dmy (d);
1010     }
1011   g_return_val_if_fail (d->dmy, FALSE);  
1012   
1013   if (d->day == 1) return TRUE;
1014   else return FALSE;
1015 }
1016
1017 gboolean     
1018 g_date_is_last_of_month (const GDate *d)
1019 {
1020   gint index;
1021   
1022   g_return_val_if_fail (d != NULL, FALSE);
1023   g_return_val_if_fail (g_date_valid (d), FALSE);
1024   
1025   if (!d->dmy) 
1026     {
1027       g_date_update_dmy (d);
1028     }
1029   g_return_val_if_fail (d->dmy, FALSE);  
1030   
1031   index = g_date_is_leap_year (d->year) ? 1 : 0;
1032   
1033   if (d->day == days_in_months[index][d->month]) return TRUE;
1034   else return FALSE;
1035 }
1036
1037 void         
1038 g_date_add_days (GDate *d, guint ndays)
1039 {
1040   g_return_if_fail (d != NULL);
1041   g_return_if_fail (g_date_valid (d));
1042   
1043   if (!d->julian)
1044     {
1045       g_date_update_julian (d);
1046     }
1047   g_return_if_fail (d->julian);
1048   
1049   d->julian_days += ndays;
1050   d->dmy = FALSE;
1051 }
1052
1053 void         
1054 g_date_subtract_days (GDate *d, guint ndays)
1055 {
1056   g_return_if_fail (d != NULL);
1057   g_return_if_fail (g_date_valid (d));
1058   
1059   if (!d->julian)
1060     {
1061       g_date_update_julian (d);
1062     }
1063   g_return_if_fail (d->julian);
1064   g_return_if_fail (d->julian_days > ndays);
1065   
1066   d->julian_days -= ndays;
1067   d->dmy = FALSE;
1068 }
1069
1070 void         
1071 g_date_add_months (GDate       *d, 
1072                    guint        nmonths)
1073 {
1074   guint years, months;
1075   gint index;
1076   
1077   g_return_if_fail (d != NULL);
1078   g_return_if_fail (g_date_valid (d));
1079   
1080   if (!d->dmy) 
1081     {
1082       g_date_update_dmy (d);
1083     }
1084   g_return_if_fail (d->dmy);  
1085   
1086   nmonths += d->month - 1;
1087   
1088   years  = nmonths/12;
1089   months = nmonths%12;
1090   
1091   d->month = months + 1;
1092   d->year  += years;
1093   
1094   index = g_date_is_leap_year (d->year) ? 1 : 0;
1095   
1096   if (d->day > days_in_months[index][d->month])
1097     d->day = days_in_months[index][d->month];
1098   
1099   d->julian = FALSE;
1100   
1101   g_return_if_fail (g_date_valid (d));
1102 }
1103
1104 void         
1105 g_date_subtract_months (GDate       *d, 
1106                         guint        nmonths)
1107 {
1108   guint years, months;
1109   gint index;
1110   
1111   g_return_if_fail (d != NULL);
1112   g_return_if_fail (g_date_valid (d));
1113   
1114   if (!d->dmy) 
1115     {
1116       g_date_update_dmy (d);
1117     }
1118   g_return_if_fail (d->dmy);  
1119   
1120   years  = nmonths/12;
1121   months = nmonths%12;
1122   
1123   g_return_if_fail (d->year > years);
1124   
1125   d->year  -= years;
1126   
1127   if (d->month > months) d->month -= months;
1128   else 
1129     {
1130       months -= d->month;
1131       d->month = 12 - months;
1132       d->year -= 1;
1133     }
1134   
1135   index = g_date_is_leap_year (d->year) ? 1 : 0;
1136   
1137   if (d->day > days_in_months[index][d->month])
1138     d->day = days_in_months[index][d->month];
1139   
1140   d->julian = FALSE;
1141   
1142   g_return_if_fail (g_date_valid (d));
1143 }
1144
1145 void         
1146 g_date_add_years (GDate       *d, 
1147                   guint        nyears)
1148 {
1149   g_return_if_fail (d != NULL);
1150   g_return_if_fail (g_date_valid (d));
1151   
1152   if (!d->dmy) 
1153     {
1154       g_date_update_dmy (d);
1155     }
1156   g_return_if_fail (d->dmy);  
1157   
1158   d->year += nyears;
1159   
1160   if (d->month == 2 && d->day == 29)
1161     {
1162       if (!g_date_is_leap_year (d->year))
1163         {
1164           d->day = 28;
1165         }
1166     }
1167   
1168   d->julian = FALSE;
1169 }
1170
1171 void         
1172 g_date_subtract_years (GDate       *d, 
1173                        guint        nyears)
1174 {
1175   g_return_if_fail (d != NULL);
1176   g_return_if_fail (g_date_valid (d));
1177   
1178   if (!d->dmy) 
1179     {
1180       g_date_update_dmy (d);
1181     }
1182   g_return_if_fail (d->dmy);  
1183   g_return_if_fail (d->year > nyears);
1184   
1185   d->year -= nyears;
1186   
1187   if (d->month == 2 && d->day == 29)
1188     {
1189       if (!g_date_is_leap_year (d->year))
1190         {
1191           d->day = 28;
1192         }
1193     }
1194   
1195   d->julian = FALSE;
1196 }
1197
1198
1199 gboolean     
1200 g_date_is_leap_year (GDateYear  year)
1201 {
1202   g_return_val_if_fail (g_date_valid_year (year), FALSE);
1203   
1204   return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
1205            (year % 400) == 0 );
1206 }
1207
1208 guint8         
1209 g_date_get_days_in_month (GDateMonth month, 
1210                           GDateYear  year)
1211 {
1212   gint index;
1213   
1214   g_return_val_if_fail (g_date_valid_year (year), 0);
1215   g_return_val_if_fail (g_date_valid_month (month), 0);
1216   
1217   index = g_date_is_leap_year (year) ? 1 : 0;
1218   
1219   return days_in_months[index][month];
1220 }
1221
1222 guint8       
1223 g_date_get_monday_weeks_in_year (GDateYear  year)
1224 {
1225   GDate d;
1226   
1227   g_return_val_if_fail (g_date_valid_year (year), 0);
1228   
1229   g_date_clear (&d, 1);
1230   g_date_set_dmy (&d, 1, 1, year);
1231   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1232   g_date_set_dmy (&d, 31, 12, year);
1233   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1234   if (g_date_is_leap_year (year)) 
1235     {
1236       g_date_set_dmy (&d, 2, 1, year);
1237       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1238       g_date_set_dmy (&d, 30, 12, year);
1239       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1240     }
1241   return 52;
1242 }
1243
1244 guint8       
1245 g_date_get_sunday_weeks_in_year (GDateYear  year)
1246 {
1247   GDate d;
1248   
1249   g_return_val_if_fail (g_date_valid_year (year), 0);
1250   
1251   g_date_clear (&d, 1);
1252   g_date_set_dmy (&d, 1, 1, year);
1253   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1254   g_date_set_dmy (&d, 31, 12, year);
1255   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1256   if (g_date_is_leap_year (year)) 
1257     {
1258       g_date_set_dmy (&d, 2, 1, year);
1259       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1260       g_date_set_dmy (&d, 30, 12, year);
1261       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1262     }
1263   return 52;
1264 }
1265
1266 gint         
1267 g_date_compare (const GDate *lhs, 
1268                 const GDate *rhs)
1269 {
1270   g_return_val_if_fail (lhs != NULL, 0);
1271   g_return_val_if_fail (rhs != NULL, 0);
1272   g_return_val_if_fail (g_date_valid (lhs), 0);
1273   g_return_val_if_fail (g_date_valid (rhs), 0);
1274   
1275   /* Remember the self-comparison case! I think it works right now. */
1276   
1277   while (TRUE)
1278     {
1279       
1280       if (lhs->julian && rhs->julian) 
1281         {
1282           if (lhs->julian_days < rhs->julian_days) return -1;
1283           else if (lhs->julian_days > rhs->julian_days) return 1;
1284           else                                          return 0;
1285         }
1286       else if (lhs->dmy && rhs->dmy) 
1287         {
1288           if (lhs->year < rhs->year)               return -1;
1289           else if (lhs->year > rhs->year)               return 1;
1290           else 
1291             {
1292               if (lhs->month < rhs->month)         return -1;
1293               else if (lhs->month > rhs->month)         return 1;
1294               else 
1295                 {
1296                   if (lhs->day < rhs->day)              return -1;
1297                   else if (lhs->day > rhs->day)              return 1;
1298                   else                                       return 0;
1299                 }
1300               
1301             }
1302           
1303         }
1304       else
1305         {
1306           if (!lhs->julian) g_date_update_julian (lhs);
1307           if (!rhs->julian) g_date_update_julian (rhs);
1308           g_return_val_if_fail (lhs->julian, 0);
1309           g_return_val_if_fail (rhs->julian, 0);
1310         }
1311       
1312     }
1313   return 0; /* warnings */
1314 }
1315
1316
1317 void        
1318 g_date_to_struct_tm (const GDate *d, 
1319                      struct tm   *tm)
1320 {
1321   GDateWeekday day;
1322      
1323   g_return_if_fail (d != NULL);
1324   g_return_if_fail (g_date_valid (d));
1325   g_return_if_fail (tm != NULL);
1326   
1327   if (!d->dmy) 
1328     {
1329       g_date_update_dmy (d);
1330     }
1331   g_return_if_fail (d->dmy);
1332   
1333   /* zero all the irrelevant fields to be sure they're valid */
1334   
1335   /* On Linux and maybe other systems, there are weird non-POSIX
1336    * fields on the end of struct tm that choke strftime if they
1337    * contain garbage.  So we need to 0 the entire struct, not just the
1338    * fields we know to exist. 
1339    */
1340   
1341   memset (tm, 0x0, sizeof (struct tm));
1342   
1343   tm->tm_mday = d->day;
1344   tm->tm_mon  = d->month - 1; /* 0-11 goes in tm */
1345   tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
1346   
1347   day = g_date_get_weekday (d);
1348   if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
1349   
1350   tm->tm_wday = (int)day;
1351   
1352   tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
1353   tm->tm_isdst = -1; /* -1 means "information not available" */
1354 }
1355
1356 void
1357 g_date_clamp (GDate *date,
1358               const GDate *min_date,
1359               const GDate *max_date)
1360 {
1361   g_return_if_fail (date);
1362   g_return_if_fail (g_date_valid (date));
1363   if (min_date != NULL)
1364     g_return_if_fail (g_date_valid (min_date));
1365   if (max_date != NULL)
1366     g_return_if_fail (g_date_valid (max_date));
1367   if (min_date != NULL && max_date != NULL)
1368     g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
1369
1370   if (min_date && g_date_compare (date, min_date) < 0)
1371     *date = *min_date;
1372
1373   if (max_date && g_date_compare (max_date, date) < 0)
1374     *date = *max_date;
1375 }
1376
1377 void
1378 g_date_order (GDate *date1,
1379               GDate *date2)
1380 {
1381   g_return_if_fail (date1 != NULL);
1382   g_return_if_fail (date2 != NULL);
1383   g_return_if_fail (g_date_valid (date1));
1384   g_return_if_fail (g_date_valid (date2));
1385
1386   if (g_date_compare (date1, date2) > 0)
1387     {
1388       GDate tmp = *date1;
1389       *date1 = *date2;
1390       *date2 = tmp;
1391     }
1392 }
1393
1394 gsize     
1395 g_date_strftime (gchar       *s, 
1396                  gsize        slen, 
1397                  const gchar *format, 
1398                  const GDate *d)
1399 {
1400   struct tm tm;
1401   gsize locale_format_len = 0;
1402   gchar *locale_format;
1403   gsize tmplen;
1404   gchar *tmpbuf;
1405   gsize tmpbufsize;
1406   gsize convlen = 0;
1407   gchar *convbuf;
1408   GError *error = NULL;
1409   gsize retval;
1410
1411   g_return_val_if_fail (d != NULL, 0);
1412   g_return_val_if_fail (g_date_valid (d), 0);
1413   g_return_val_if_fail (slen > 0, 0); 
1414   g_return_val_if_fail (format != 0, 0);
1415   g_return_val_if_fail (s != 0, 0);
1416
1417   g_date_to_struct_tm (d, &tm);
1418
1419   locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
1420
1421   if (error)
1422     {
1423       g_warning (G_STRLOC "Error converting format to locale encoding: %s\n", error->message);
1424       g_error_free (error);
1425
1426       s[0] = '\0';
1427       return 0;
1428     }
1429
1430   tmpbufsize = MAX (128, locale_format_len * 2);
1431   while (TRUE)
1432     {
1433       tmpbuf = g_malloc (tmpbufsize);
1434
1435       /* Set the first byte to something other than '\0', to be able to
1436        * recognize whether strftime actually failed or just returned "".
1437        */
1438       tmpbuf[0] = '\1';
1439       tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
1440
1441       if (tmplen == 0 && tmpbuf[0] != '\0')
1442         {
1443           g_free (tmpbuf);
1444           tmpbufsize *= 2;
1445
1446           if (tmpbufsize > 65536)
1447             {
1448               g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up\n");
1449               g_free (locale_format);
1450
1451               s[0] = '\0';
1452               return 0;
1453             }
1454         }
1455       else
1456         break;
1457     }
1458   g_free (locale_format);
1459
1460   convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
1461   g_free (tmpbuf);
1462
1463   if (error)
1464     {
1465       g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s\n", error->message);
1466       g_error_free (error);
1467
1468       s[0] = '\0';
1469       return 0;
1470     }
1471
1472   if (slen <= convlen)
1473     {
1474       /* Ensure only whole characters are copied into the buffer.
1475        */
1476       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
1477       g_assert (end != NULL);
1478       convlen = end - convbuf;
1479
1480       /* Return 0 because the buffer isn't large enough.
1481        */
1482       retval = 0;
1483     }
1484   else
1485     retval = convlen;
1486
1487   memcpy (s, convbuf, convlen);
1488   s[convlen] = '\0';
1489   g_free (convbuf);
1490
1491   return retval;
1492 }
1493