New Win32-only function. Use the wide character Win32 API to do the work
[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 "glib.h"
39
40 #include <time.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <locale.h>
44
45 #ifdef G_OS_WIN32
46 #include <windows.h>
47 #endif
48
49 #include "galias.h"
50
51 GDate*
52 g_date_new (void)
53 {
54   GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
55   
56   return d;
57 }
58
59 GDate*
60 g_date_new_dmy (GDateDay day, GDateMonth m, GDateYear y)
61 {
62   GDate *d;
63   g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
64   
65   d = g_new (GDate, 1);
66   
67   d->julian = FALSE;
68   d->dmy    = TRUE;
69   
70   d->month = m;
71   d->day   = day;
72   d->year  = y;
73   
74   g_assert (g_date_valid (d));
75   
76   return d;
77 }
78
79 GDate*
80 g_date_new_julian (guint32 j)
81 {
82   GDate *d;
83   g_return_val_if_fail (g_date_valid_julian (j), NULL);
84   
85   d = g_new (GDate, 1);
86   
87   d->julian = TRUE;
88   d->dmy    = FALSE;
89   
90   d->julian_days = j;
91   
92   g_assert (g_date_valid (d));
93   
94   return d;
95 }
96
97 void
98 g_date_free (GDate *d)
99 {
100   g_return_if_fail (d != NULL);
101   
102   g_free (d);
103 }
104
105 gboolean     
106 g_date_valid (const GDate *d)
107 {
108   g_return_val_if_fail (d != NULL, FALSE);
109   
110   return (d->julian || d->dmy);
111 }
112
113 static const guint8 days_in_months[2][13] = 
114 {  /* error, jan feb mar apr may jun jul aug sep oct nov dec */
115   {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
116   {  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
117 };
118
119 static const guint16 days_in_year[2][14] = 
120 {  /* 0, jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec */
121   {  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 
122   {  0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
123 };
124
125 gboolean     
126 g_date_valid_month (GDateMonth   m)
127
128   return ( (m > G_DATE_BAD_MONTH) && (m < 13) );
129 }
130
131 gboolean     
132 g_date_valid_year (GDateYear    y)
133 {
134   return ( y > G_DATE_BAD_YEAR );
135 }
136
137 gboolean     
138 g_date_valid_day (GDateDay     d)
139 {
140   return ( (d > G_DATE_BAD_DAY) && (d < 32) );
141 }
142
143 gboolean     
144 g_date_valid_weekday (GDateWeekday w)
145 {
146   return ( (w > G_DATE_BAD_WEEKDAY) && (w < 8) );
147 }
148
149 gboolean     
150 g_date_valid_julian (guint32      j)
151 {
152   return (j > G_DATE_BAD_JULIAN);
153 }
154
155 gboolean     
156 g_date_valid_dmy (GDateDay     d, 
157                   GDateMonth   m, 
158                   GDateYear    y)
159 {
160   return ( (m > G_DATE_BAD_MONTH) &&
161            (m < 13)               && 
162            (d > G_DATE_BAD_DAY)   && 
163            (y > G_DATE_BAD_YEAR)  &&   /* must check before using g_date_is_leap_year */
164            (d <=  (g_date_is_leap_year (y) ? 
165                    days_in_months[1][m] : days_in_months[0][m])) );
166 }
167
168
169 /* "Julian days" just means an absolute number of days, where Day 1 ==
170  *   Jan 1, Year 1
171  */
172 static void
173 g_date_update_julian (const GDate *const_d)
174 {
175   GDate *d = (GDate *) const_d;
176   GDateYear year;
177   gint index;
178   
179   g_return_if_fail (d != NULL);
180   g_return_if_fail (d->dmy);
181   g_return_if_fail (!d->julian);
182   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
183   
184   /* What we actually do is: multiply years * 365 days in the year,
185    *  add the number of years divided by 4, subtract the number of
186    *  years divided by 100 and add the number of years divided by 400,
187    *  which accounts for leap year stuff. Code from Steffen Beyer's
188    *  DateCalc. 
189    */
190   
191   year = d->year - 1; /* we know d->year > 0 since it's valid */
192   
193   d->julian_days = year * 365U;
194   d->julian_days += (year >>= 2); /* divide by 4 and add */
195   d->julian_days -= (year /= 25); /* divides original # years by 100 */
196   d->julian_days += year >> 2;    /* divides by 4, which divides original by 400 */
197   
198   index = g_date_is_leap_year (d->year) ? 1 : 0;
199   
200   d->julian_days += days_in_year[index][d->month] + d->day;
201   
202   g_return_if_fail (g_date_valid_julian (d->julian_days));
203   
204   d->julian = TRUE;
205 }
206
207 static void 
208 g_date_update_dmy (const GDate *const_d)
209 {
210   GDate *d = (GDate *) const_d;
211   GDateYear y;
212   GDateMonth m;
213   GDateDay day;
214   
215   guint32 A, B, C, D, E, M;
216   
217   g_return_if_fail (d != NULL);
218   g_return_if_fail (d->julian);
219   g_return_if_fail (!d->dmy);
220   g_return_if_fail (g_date_valid_julian (d->julian_days));
221   
222   /* Formula taken from the Calendar FAQ; the formula was for the
223    *  Julian Period which starts on 1 January 4713 BC, so we add
224    *  1,721,425 to the number of days before doing the formula.
225    *
226    * I'm sure this can be simplified for our 1 January 1 AD period
227    * start, but I can't figure out how to unpack the formula.  
228    */
229   
230   A = d->julian_days + 1721425 + 32045;
231   B = ( 4 *(A + 36524) )/ 146097 - 1;
232   C = A - (146097 * B)/4;
233   D = ( 4 * (C + 365) ) / 1461 - 1;
234   E = C - ((1461*D) / 4);
235   M = (5 * (E - 1) + 2)/153;
236   
237   m = M + 3 - (12*(M/10));
238   day = E - (153*M + 2)/5;
239   y = 100 * B + D - 4800 + (M/10);
240   
241 #ifdef G_ENABLE_DEBUG
242   if (!g_date_valid_dmy (day, m, y)) 
243     {
244       g_warning ("\nOOPS julian: %u  computed dmy: %u %u %u\n", 
245                  d->julian_days, day, m, y);
246     }
247 #endif
248   
249   d->month = m;
250   d->day   = day;
251   d->year  = y;
252   
253   d->dmy = TRUE;
254 }
255
256 GDateWeekday 
257 g_date_get_weekday (const GDate *d)
258 {
259   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
260   
261   if (!d->julian) 
262     {
263       g_date_update_julian (d);
264     }
265   g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
266   
267   return ((d->julian_days - 1) % 7) + 1;
268 }
269
270 GDateMonth   
271 g_date_get_month (const GDate *d)
272 {
273   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
274   
275   if (!d->dmy) 
276     {
277       g_date_update_dmy (d);
278     }
279   g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
280   
281   return d->month;
282 }
283
284 GDateYear    
285 g_date_get_year (const GDate *d)
286 {
287   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
288   
289   if (!d->dmy) 
290     {
291       g_date_update_dmy (d);
292     }
293   g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);  
294   
295   return d->year;
296 }
297
298 GDateDay     
299 g_date_get_day (const GDate *d)
300 {
301   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
302   
303   if (!d->dmy) 
304     {
305       g_date_update_dmy (d);
306     }
307   g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);  
308   
309   return d->day;
310 }
311
312 guint32      
313 g_date_get_julian (const GDate *d)
314 {
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 (g_date_valid (d), 0);
332   
333   if (!d->dmy) 
334     {
335       g_date_update_dmy (d);
336     }
337   g_return_val_if_fail (d->dmy, 0);  
338   
339   index = g_date_is_leap_year (d->year) ? 1 : 0;
340   
341   return (days_in_year[index][d->month] + d->day);
342 }
343
344 guint        
345 g_date_get_monday_week_of_year (const GDate *d)
346 {
347   GDateWeekday wd;
348   guint day;
349   GDate first;
350   
351   g_return_val_if_fail (g_date_valid (d), 0);
352   
353   if (!d->dmy) 
354     {
355       g_date_update_dmy (d);
356     }
357   g_return_val_if_fail (d->dmy, 0);  
358   
359   g_date_clear (&first, 1);
360   
361   g_date_set_dmy (&first, 1, 1, d->year);
362   
363   wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
364   day = g_date_get_day_of_year (d) - 1;
365   
366   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
367 }
368
369 guint        
370 g_date_get_sunday_week_of_year (const GDate *d)
371 {
372   GDateWeekday wd;
373   guint day;
374   GDate first;
375   
376   g_return_val_if_fail (g_date_valid (d), 0);
377   
378   if (!d->dmy) 
379     {
380       g_date_update_dmy (d);
381     }
382   g_return_val_if_fail (d->dmy, 0);  
383   
384   g_date_clear (&first, 1);
385   
386   g_date_set_dmy (&first, 1, 1, d->year);
387   
388   wd = g_date_get_weekday (&first);
389   if (wd == 7) wd = 0; /* make Sunday day 0 */
390   day = g_date_get_day_of_year (d) - 1;
391   
392   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
393 }
394
395 /**
396  * g_date_get_iso8601_week_of_year:
397  * @date: a valid #GDate
398  *
399  * Returns the week of the year, where weeks are interpreted according
400  * to ISO 8601. 
401  * 
402  * Returns: ISO 8601 week number of the year.
403  *
404  * Since: 2.6
405  **/
406 guint
407 g_date_get_iso8601_week_of_year (const GDate *d)
408 {
409   guint j, d4, L, d1, w;
410
411   g_return_val_if_fail (g_date_valid (d), 0);
412   
413   if (!d->julian)
414     g_date_update_julian (d);
415   g_return_val_if_fail (d->julian, 0);
416
417   /* Formula taken from the Calendar FAQ; the formula was for the
418    * Julian Period which starts on 1 January 4713 BC, so we add
419    * 1,721,425 to the number of days before doing the formula. 
420    */
421   j  = d->julian_days + 1721425;
422   d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461;
423   L  = d4 / 1460;
424   d1 = ((d4 - L) % 365) + L;
425   w  = d1 / 7 + 1;
426
427   return w;
428 }
429
430 gint
431 g_date_days_between (const GDate *d1,
432                      const GDate *d2)
433 {
434   g_return_val_if_fail (g_date_valid (d1), 0);
435   g_return_val_if_fail (g_date_valid (d2), 0);
436
437   return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
438 }
439
440 void         
441 g_date_clear (GDate *d, guint ndates)
442 {
443   g_return_if_fail (d != NULL);
444   g_return_if_fail (ndates != 0);
445   
446   memset (d, 0x0, ndates*sizeof (GDate)); 
447 }
448
449 G_LOCK_DEFINE_STATIC (g_date_global);
450
451 /* These are for the parser, output to the user should use *
452  * g_date_strftime () - this creates more never-freed memory to annoy
453  * all those memory debugger users. :-) 
454  */
455
456 static gchar *long_month_names[13] = 
457
458   NULL,
459 };
460
461 static gchar *short_month_names[13] = 
462 {
463   NULL, 
464 };
465
466 /* This tells us if we need to update the parse info */
467 static gchar *current_locale = NULL;
468
469 /* order of these in the current locale */
470 static GDateDMY dmy_order[3] = 
471 {
472    G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
473 };
474
475 /* Where to chop two-digit years: i.e., for the 1930 default, numbers
476  * 29 and below are counted as in the year 2000, numbers 30 and above
477  * are counted as in the year 1900.  
478  */
479
480 static const GDateYear twodigit_start_year = 1930;
481
482 /* It is impossible to enter a year between 1 AD and 99 AD with this
483  * in effect.  
484  */
485 static gboolean using_twodigit_years = FALSE;
486
487 /* Adjustment of locale era to AD, non-zero means using locale era
488  */
489 static gint locale_era_adjust = 0;
490
491 struct _GDateParseTokens {
492   gint num_ints;
493   gint n[3];
494   guint month;
495 };
496
497 typedef struct _GDateParseTokens GDateParseTokens;
498
499 #define NUM_LEN 10
500
501 /* HOLDS: g_date_global_lock */
502 static void
503 g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
504 {
505   gchar num[4][NUM_LEN+1];
506   gint i;
507   const guchar *s;
508   
509   /* We count 4, but store 3; so we can give an error
510    * if there are 4.
511    */
512   num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
513   
514   s = (const guchar *) str;
515   pt->num_ints = 0;
516   while (*s && pt->num_ints < 4) 
517     {
518       
519       i = 0;
520       while (*s && g_ascii_isdigit (*s) && i < NUM_LEN)
521         {
522           num[pt->num_ints][i] = *s;
523           ++s; 
524           ++i;
525         }
526       
527       if (i > 0) 
528         {
529           num[pt->num_ints][i] = '\0';
530           ++(pt->num_ints);
531         }
532       
533       if (*s == '\0') break;
534       
535       ++s;
536     }
537   
538   pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
539   pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
540   pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
541   
542   pt->month = G_DATE_BAD_MONTH;
543   
544   if (pt->num_ints < 3)
545     {
546       gchar *casefold;
547       gchar *normalized;
548       
549       casefold = g_utf8_casefold (str, -1);
550       normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
551       g_free (casefold);
552
553       i = 1;
554       while (i < 13)
555         {
556           if (long_month_names[i] != NULL) 
557             {
558               const gchar *found = strstr (normalized, long_month_names[i]);
559               
560               if (found != NULL)
561                 {
562                   pt->month = i;
563                   break;
564                 }
565             }
566           
567           if (short_month_names[i] != NULL) 
568             {
569               const gchar *found = strstr (normalized, short_month_names[i]);
570               
571               if (found != NULL)
572                 {
573                   pt->month = i;
574                   break;
575                 }
576             }
577
578           ++i;
579         }
580
581       g_free (normalized);
582     }
583 }
584
585 /* HOLDS: g_date_global_lock */
586 static void
587 g_date_prepare_to_parse (const gchar *str, GDateParseTokens *pt)
588 {
589   const gchar *locale = setlocale (LC_TIME, NULL);
590   gboolean recompute_localeinfo = FALSE;
591   GDate d;
592   
593   g_return_if_fail (locale != NULL); /* should not happen */
594   
595   g_date_clear (&d, 1);              /* clear for scratch use */
596   
597   if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) ) 
598     {
599       recompute_localeinfo = TRUE;  /* Uh, there used to be a reason for the temporary */
600     }
601   
602   if (recompute_localeinfo)
603     {
604       int i = 1;
605       GDateParseTokens testpt;
606       gchar buf[128];
607       
608       g_free (current_locale); /* still works if current_locale == NULL */
609       
610       current_locale = g_strdup (locale);
611       
612       short_month_names[0] = "Error";
613       long_month_names[0] = "Error";
614
615       while (i < 13) 
616         {
617           gchar *casefold;
618           
619           g_date_set_dmy (&d, 1, i, 1);
620           
621           g_return_if_fail (g_date_valid (&d));
622           
623           g_date_strftime (buf, 127, "%b", &d);
624
625           casefold = g_utf8_casefold (buf, -1);
626           g_free (short_month_names[i]);
627           short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
628           g_free (casefold);
629           
630           g_date_strftime (buf, 127, "%B", &d);
631           casefold = g_utf8_casefold (buf, -1);
632           g_free (long_month_names[i]);
633           long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
634           g_free (casefold);
635           
636           ++i;
637         }
638       
639       /* Determine DMY order */
640       
641       /* had to pick a random day - don't change this, some strftimes
642        * are broken on some days, and this one is good so far. */
643       g_date_set_dmy (&d, 4, 7, 1976);
644       
645       g_date_strftime (buf, 127, "%x", &d);
646       
647       g_date_fill_parse_tokens (buf, &testpt);
648       
649       i = 0;
650       while (i < testpt.num_ints)
651         {
652           switch (testpt.n[i])
653             {
654             case 7:
655               dmy_order[i] = G_DATE_MONTH;
656               break;
657             case 4:
658               dmy_order[i] = G_DATE_DAY;
659               break;
660             case 76:
661               using_twodigit_years = TRUE; /* FALL THRU */
662             case 1976:
663               dmy_order[i] = G_DATE_YEAR;
664               break;
665             default:
666               /* assume locale era */
667               locale_era_adjust = 1976 - testpt.n[i];
668               dmy_order[i] = G_DATE_YEAR;
669               break;
670             }
671           ++i;
672         }
673       
674 #ifdef G_ENABLE_DEBUG
675       DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
676       i = 1;
677       while (i < 13) 
678         {
679           DEBUG_MSG (("  %s   %s", long_month_names[i], short_month_names[i]));
680           ++i;
681         }
682       if (using_twodigit_years)
683         DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
684       { 
685         gchar *strings[3];
686         i = 0;
687         while (i < 3)
688           {
689             switch (dmy_order[i])
690               {
691               case G_DATE_MONTH:
692                 strings[i] = "Month";
693                 break;
694               case G_DATE_YEAR:
695                 strings[i] = "Year";
696                 break;
697               case G_DATE_DAY:
698                 strings[i] = "Day";
699                 break;
700               default:
701                 strings[i] = NULL;
702                 break;
703               }
704             ++i;
705           }
706         DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
707         DEBUG_MSG (("**Sample date in this locale: `%s'", buf));
708       }
709 #endif
710     }
711   
712   g_date_fill_parse_tokens (str, pt);
713 }
714
715 void         
716 g_date_set_parse (GDate       *d, 
717                   const gchar *str)
718 {
719   GDateParseTokens pt;
720   guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
721   
722   g_return_if_fail (d != NULL);
723   
724   /* set invalid */
725   g_date_clear (d, 1);
726   
727   G_LOCK (g_date_global);
728
729   g_date_prepare_to_parse (str, &pt);
730   
731   DEBUG_MSG (("Found %d ints, `%d' `%d' `%d' and written out month %d", 
732               pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
733   
734   
735   if (pt.num_ints == 4) 
736     {
737       G_UNLOCK (g_date_global);
738       return; /* presumably a typo; bail out. */
739     }
740   
741   if (pt.num_ints > 1)
742     {
743       int i = 0;
744       int j = 0;
745       
746       g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
747       
748       while (i < pt.num_ints && j < 3) 
749         {
750           switch (dmy_order[j])
751             {
752             case G_DATE_MONTH:
753             {
754               if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
755                 {
756                   m = pt.month;
757                   ++j;      /* skip months, but don't skip this number */
758                   continue;
759                 }
760               else 
761                 m = pt.n[i];
762             }
763             break;
764             case G_DATE_DAY:
765             {
766               if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
767                 {
768                   day = 1;
769                   ++j;      /* skip days, since we may have month/year */
770                   continue;
771                 }
772               day = pt.n[i];
773             }
774             break;
775             case G_DATE_YEAR:
776             {
777               y  = pt.n[i];
778               
779               if (locale_era_adjust != 0)
780                 {
781                   y += locale_era_adjust;
782                 }
783               else if (using_twodigit_years && y < 100)
784                 {
785                   guint two     =  twodigit_start_year % 100;
786                   guint century = (twodigit_start_year / 100) * 100;
787                   
788                   if (y < two)
789                     century += 100;
790                   
791                   y += century;
792                 }
793             }
794             break;
795             default:
796               break;
797             }
798           
799           ++i;
800           ++j;
801         }
802       
803       
804       if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
805         {
806           /* Try YYYY MM DD */
807           y   = pt.n[0];
808           m   = pt.n[1];
809           day = pt.n[2];
810           
811           if (using_twodigit_years && y < 100) 
812             y = G_DATE_BAD_YEAR; /* avoids ambiguity */
813         }
814       else if (pt.num_ints == 2)
815         {
816           if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
817             {
818               m = pt.month;
819             }
820         }
821     }
822   else if (pt.num_ints == 1) 
823     {
824       if (pt.month != G_DATE_BAD_MONTH)
825         {
826           /* Month name and year? */
827           m    = pt.month;
828           day  = 1;
829           y = pt.n[0];
830         }
831       else
832         {
833           /* Try yyyymmdd and yymmdd */
834           
835           m   = (pt.n[0]/100) % 100;
836           day = pt.n[0] % 100;
837           y   = pt.n[0]/10000;
838           
839           /* FIXME move this into a separate function */
840           if (using_twodigit_years && y < 100)
841             {
842               guint two     =  twodigit_start_year % 100;
843               guint century = (twodigit_start_year / 100) * 100;
844               
845               if (y < two)
846                 century += 100;
847               
848               y += century;
849             }
850         }
851     }
852   
853   /* See if we got anything valid out of all this. */
854   /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
855   if (y < 8000 && g_date_valid_dmy (day, m, y)) 
856     {
857       d->month = m;
858       d->day   = day;
859       d->year  = y;
860       d->dmy   = TRUE;
861     }
862 #ifdef G_ENABLE_DEBUG
863   else 
864     DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
865 #endif
866   G_UNLOCK (g_date_global);
867 }
868
869 /**
870  * g_date_set_time_t:
871  * @date: a #GDate 
872  * @timet: <type>time_t</type> value to set
873  *
874  * Sets the value of a date from a <type>time_t</type> value. 
875  *
876  * To set the value of a date to the current day, you could write:
877  * <informalexample><programlisting> 
878  *  g_date_set_time_t (date, time (NULL)); 
879  * </programlisting></informalexample>
880  *
881  * Since: 2.10
882  */
883 void         
884 g_date_set_time_t (GDate *date,
885                    time_t timet)
886 {
887   struct tm tm;
888   
889   g_return_if_fail (date != NULL);
890   
891 #ifdef HAVE_LOCALTIME_R
892   localtime_r (&timet, &tm);
893 #else
894   {
895     struct tm *ptm = localtime (&timet);
896
897     if (ptm == NULL)
898       {
899         /* Happens at least in Microsoft's C library if you pass a
900          * negative time_t. Use 2000-01-01 as default date.
901          */
902 #ifndef G_DISABLE_CHECKS
903         g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
904 #endif
905
906         tm.tm_mon = 0;
907         tm.tm_mday = 1;
908         tm.tm_year = 100;
909       }
910     else
911       memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
912   }
913 #endif
914   
915   date->julian = FALSE;
916   
917   date->month = tm.tm_mon + 1;
918   date->day   = tm.tm_mday;
919   date->year  = tm.tm_year + 1900;
920   
921   g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year));
922   
923   date->dmy    = TRUE;
924 }
925
926
927 /**
928  * g_date_set_time:
929  * @date: a #GDate.
930  * @time_: #GTime value to set.
931  *
932  * Sets the value of a date from a #GTime value. 
933  *
934  * @Deprecated:2.10: Use g_date_set_time_t() instead.
935  */
936 void
937 g_date_set_time (GDate    *date,
938                  GTime     time_)
939 {
940   g_date_set_time_t (date, (time_t) time_);
941 }
942
943 /**
944  * g_date_set_time_val:
945  * @date: a #GDate 
946  * @timeval: #GTimeVal value to set
947  *
948  * Sets the value of a date from a #GTimeVal value.  Note that the
949  * @tv_usec member is ignored, because #GDate can't make use of the
950  * additional precision.
951  *
952  * Since: 2.10
953  */
954 void
955 g_date_set_time_val (GDate    *date,
956                      GTimeVal *timeval)
957 {
958   g_date_set_time_t (date, (time_t) timeval->tv_sec);
959 }
960
961 void         
962 g_date_set_month (GDate     *d, 
963                   GDateMonth m)
964 {
965   g_return_if_fail (d != NULL);
966   g_return_if_fail (g_date_valid_month (m));
967
968   if (d->julian && !d->dmy) g_date_update_dmy(d);
969   d->julian = FALSE;
970   
971   d->month = m;
972   
973   if (g_date_valid_dmy (d->day, d->month, d->year))
974     d->dmy = TRUE;
975   else 
976     d->dmy = FALSE;
977 }
978
979 void         
980 g_date_set_day (GDate     *d, 
981                 GDateDay day)
982 {
983   g_return_if_fail (d != NULL);
984   g_return_if_fail (g_date_valid_day (day));
985   
986   if (d->julian && !d->dmy) g_date_update_dmy(d);
987   d->julian = FALSE;
988   
989   d->day = day;
990   
991   if (g_date_valid_dmy (d->day, d->month, d->year))
992     d->dmy = TRUE;
993   else 
994     d->dmy = FALSE;
995 }
996
997 void         
998 g_date_set_year (GDate     *d, 
999                  GDateYear  y)
1000 {
1001   g_return_if_fail (d != NULL);
1002   g_return_if_fail (g_date_valid_year (y));
1003   
1004   if (d->julian && !d->dmy) g_date_update_dmy(d);
1005   d->julian = FALSE;
1006   
1007   d->year = y;
1008   
1009   if (g_date_valid_dmy (d->day, d->month, d->year))
1010     d->dmy = TRUE;
1011   else 
1012     d->dmy = FALSE;
1013 }
1014
1015 void         
1016 g_date_set_dmy (GDate     *d, 
1017                 GDateDay   day, 
1018                 GDateMonth m, 
1019                 GDateYear  y)
1020 {
1021   g_return_if_fail (d != NULL);
1022   g_return_if_fail (g_date_valid_dmy (day, m, y));
1023   
1024   d->julian = FALSE;
1025   
1026   d->month = m;
1027   d->day   = day;
1028   d->year  = y;
1029   
1030   d->dmy = TRUE;
1031 }
1032
1033 void         
1034 g_date_set_julian (GDate *d, guint32 j)
1035 {
1036   g_return_if_fail (d != NULL);
1037   g_return_if_fail (g_date_valid_julian (j));
1038   
1039   d->julian_days = j;
1040   d->julian = TRUE;
1041   d->dmy = FALSE;
1042 }
1043
1044
1045 gboolean     
1046 g_date_is_first_of_month (const GDate *d)
1047 {
1048   g_return_val_if_fail (g_date_valid (d), FALSE);
1049   
1050   if (!d->dmy) 
1051     {
1052       g_date_update_dmy (d);
1053     }
1054   g_return_val_if_fail (d->dmy, FALSE);  
1055   
1056   if (d->day == 1) return TRUE;
1057   else return FALSE;
1058 }
1059
1060 gboolean     
1061 g_date_is_last_of_month (const GDate *d)
1062 {
1063   gint index;
1064   
1065   g_return_val_if_fail (g_date_valid (d), FALSE);
1066   
1067   if (!d->dmy) 
1068     {
1069       g_date_update_dmy (d);
1070     }
1071   g_return_val_if_fail (d->dmy, FALSE);  
1072   
1073   index = g_date_is_leap_year (d->year) ? 1 : 0;
1074   
1075   if (d->day == days_in_months[index][d->month]) return TRUE;
1076   else return FALSE;
1077 }
1078
1079 void         
1080 g_date_add_days (GDate *d, guint ndays)
1081 {
1082   g_return_if_fail (g_date_valid (d));
1083   
1084   if (!d->julian)
1085     {
1086       g_date_update_julian (d);
1087     }
1088   g_return_if_fail (d->julian);
1089   
1090   d->julian_days += ndays;
1091   d->dmy = FALSE;
1092 }
1093
1094 void         
1095 g_date_subtract_days (GDate *d, guint ndays)
1096 {
1097   g_return_if_fail (g_date_valid (d));
1098   
1099   if (!d->julian)
1100     {
1101       g_date_update_julian (d);
1102     }
1103   g_return_if_fail (d->julian);
1104   g_return_if_fail (d->julian_days > ndays);
1105   
1106   d->julian_days -= ndays;
1107   d->dmy = FALSE;
1108 }
1109
1110 void         
1111 g_date_add_months (GDate       *d, 
1112                    guint        nmonths)
1113 {
1114   guint years, months;
1115   gint index;
1116   
1117   g_return_if_fail (g_date_valid (d));
1118   
1119   if (!d->dmy) 
1120     {
1121       g_date_update_dmy (d);
1122     }
1123   g_return_if_fail (d->dmy);  
1124   
1125   nmonths += d->month - 1;
1126   
1127   years  = nmonths/12;
1128   months = nmonths%12;
1129   
1130   d->month = months + 1;
1131   d->year  += years;
1132   
1133   index = g_date_is_leap_year (d->year) ? 1 : 0;
1134   
1135   if (d->day > days_in_months[index][d->month])
1136     d->day = days_in_months[index][d->month];
1137   
1138   d->julian = FALSE;
1139   
1140   g_return_if_fail (g_date_valid (d));
1141 }
1142
1143 void         
1144 g_date_subtract_months (GDate       *d, 
1145                         guint        nmonths)
1146 {
1147   guint years, months;
1148   gint index;
1149   
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   years  = nmonths/12;
1159   months = nmonths%12;
1160   
1161   g_return_if_fail (d->year > years);
1162   
1163   d->year  -= years;
1164   
1165   if (d->month > months) d->month -= months;
1166   else 
1167     {
1168       months -= d->month;
1169       d->month = 12 - months;
1170       d->year -= 1;
1171     }
1172   
1173   index = g_date_is_leap_year (d->year) ? 1 : 0;
1174   
1175   if (d->day > days_in_months[index][d->month])
1176     d->day = days_in_months[index][d->month];
1177   
1178   d->julian = FALSE;
1179   
1180   g_return_if_fail (g_date_valid (d));
1181 }
1182
1183 void         
1184 g_date_add_years (GDate       *d, 
1185                   guint        nyears)
1186 {
1187   g_return_if_fail (g_date_valid (d));
1188   
1189   if (!d->dmy) 
1190     {
1191       g_date_update_dmy (d);
1192     }
1193   g_return_if_fail (d->dmy);  
1194   
1195   d->year += nyears;
1196   
1197   if (d->month == 2 && d->day == 29)
1198     {
1199       if (!g_date_is_leap_year (d->year))
1200         {
1201           d->day = 28;
1202         }
1203     }
1204   
1205   d->julian = FALSE;
1206 }
1207
1208 void         
1209 g_date_subtract_years (GDate       *d, 
1210                        guint        nyears)
1211 {
1212   g_return_if_fail (g_date_valid (d));
1213   
1214   if (!d->dmy) 
1215     {
1216       g_date_update_dmy (d);
1217     }
1218   g_return_if_fail (d->dmy);  
1219   g_return_if_fail (d->year > nyears);
1220   
1221   d->year -= nyears;
1222   
1223   if (d->month == 2 && d->day == 29)
1224     {
1225       if (!g_date_is_leap_year (d->year))
1226         {
1227           d->day = 28;
1228         }
1229     }
1230   
1231   d->julian = FALSE;
1232 }
1233
1234
1235 gboolean     
1236 g_date_is_leap_year (GDateYear  year)
1237 {
1238   g_return_val_if_fail (g_date_valid_year (year), FALSE);
1239   
1240   return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
1241            (year % 400) == 0 );
1242 }
1243
1244 guint8         
1245 g_date_get_days_in_month (GDateMonth month, 
1246                           GDateYear  year)
1247 {
1248   gint index;
1249   
1250   g_return_val_if_fail (g_date_valid_year (year), 0);
1251   g_return_val_if_fail (g_date_valid_month (month), 0);
1252   
1253   index = g_date_is_leap_year (year) ? 1 : 0;
1254   
1255   return days_in_months[index][month];
1256 }
1257
1258 guint8       
1259 g_date_get_monday_weeks_in_year (GDateYear  year)
1260 {
1261   GDate d;
1262   
1263   g_return_val_if_fail (g_date_valid_year (year), 0);
1264   
1265   g_date_clear (&d, 1);
1266   g_date_set_dmy (&d, 1, 1, year);
1267   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1268   g_date_set_dmy (&d, 31, 12, year);
1269   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1270   if (g_date_is_leap_year (year)) 
1271     {
1272       g_date_set_dmy (&d, 2, 1, year);
1273       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1274       g_date_set_dmy (&d, 30, 12, year);
1275       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1276     }
1277   return 52;
1278 }
1279
1280 guint8       
1281 g_date_get_sunday_weeks_in_year (GDateYear  year)
1282 {
1283   GDate d;
1284   
1285   g_return_val_if_fail (g_date_valid_year (year), 0);
1286   
1287   g_date_clear (&d, 1);
1288   g_date_set_dmy (&d, 1, 1, year);
1289   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1290   g_date_set_dmy (&d, 31, 12, year);
1291   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1292   if (g_date_is_leap_year (year)) 
1293     {
1294       g_date_set_dmy (&d, 2, 1, year);
1295       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1296       g_date_set_dmy (&d, 30, 12, year);
1297       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1298     }
1299   return 52;
1300 }
1301
1302 gint         
1303 g_date_compare (const GDate *lhs, 
1304                 const GDate *rhs)
1305 {
1306   g_return_val_if_fail (lhs != NULL, 0);
1307   g_return_val_if_fail (rhs != NULL, 0);
1308   g_return_val_if_fail (g_date_valid (lhs), 0);
1309   g_return_val_if_fail (g_date_valid (rhs), 0);
1310   
1311   /* Remember the self-comparison case! I think it works right now. */
1312   
1313   while (TRUE)
1314     {
1315       
1316       if (lhs->julian && rhs->julian) 
1317         {
1318           if (lhs->julian_days < rhs->julian_days) return -1;
1319           else if (lhs->julian_days > rhs->julian_days) return 1;
1320           else                                          return 0;
1321         }
1322       else if (lhs->dmy && rhs->dmy) 
1323         {
1324           if (lhs->year < rhs->year)               return -1;
1325           else if (lhs->year > rhs->year)               return 1;
1326           else 
1327             {
1328               if (lhs->month < rhs->month)         return -1;
1329               else if (lhs->month > rhs->month)         return 1;
1330               else 
1331                 {
1332                   if (lhs->day < rhs->day)              return -1;
1333                   else if (lhs->day > rhs->day)              return 1;
1334                   else                                       return 0;
1335                 }
1336               
1337             }
1338           
1339         }
1340       else
1341         {
1342           if (!lhs->julian) g_date_update_julian (lhs);
1343           if (!rhs->julian) g_date_update_julian (rhs);
1344           g_return_val_if_fail (lhs->julian, 0);
1345           g_return_val_if_fail (rhs->julian, 0);
1346         }
1347       
1348     }
1349   return 0; /* warnings */
1350 }
1351
1352
1353 void        
1354 g_date_to_struct_tm (const GDate *d, 
1355                      struct tm   *tm)
1356 {
1357   GDateWeekday day;
1358      
1359   g_return_if_fail (g_date_valid (d));
1360   g_return_if_fail (tm != NULL);
1361   
1362   if (!d->dmy) 
1363     {
1364       g_date_update_dmy (d);
1365     }
1366   g_return_if_fail (d->dmy);
1367   
1368   /* zero all the irrelevant fields to be sure they're valid */
1369   
1370   /* On Linux and maybe other systems, there are weird non-POSIX
1371    * fields on the end of struct tm that choke strftime if they
1372    * contain garbage.  So we need to 0 the entire struct, not just the
1373    * fields we know to exist. 
1374    */
1375   
1376   memset (tm, 0x0, sizeof (struct tm));
1377   
1378   tm->tm_mday = d->day;
1379   tm->tm_mon  = d->month - 1; /* 0-11 goes in tm */
1380   tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
1381   
1382   day = g_date_get_weekday (d);
1383   if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
1384   
1385   tm->tm_wday = (int)day;
1386   
1387   tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
1388   tm->tm_isdst = -1; /* -1 means "information not available" */
1389 }
1390
1391 void
1392 g_date_clamp (GDate *date,
1393               const GDate *min_date,
1394               const GDate *max_date)
1395 {
1396   g_return_if_fail (g_date_valid (date));
1397
1398   if (min_date != NULL)
1399     g_return_if_fail (g_date_valid (min_date));
1400   if (max_date != NULL)
1401     g_return_if_fail (g_date_valid (max_date));
1402   if (min_date != NULL && max_date != NULL)
1403     g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
1404
1405   if (min_date && g_date_compare (date, min_date) < 0)
1406     *date = *min_date;
1407
1408   if (max_date && g_date_compare (max_date, date) < 0)
1409     *date = *max_date;
1410 }
1411
1412 void
1413 g_date_order (GDate *date1,
1414               GDate *date2)
1415 {
1416   g_return_if_fail (g_date_valid (date1));
1417   g_return_if_fail (g_date_valid (date2));
1418
1419   if (g_date_compare (date1, date2) > 0)
1420     {
1421       GDate tmp = *date1;
1422       *date1 = *date2;
1423       *date2 = tmp;
1424     }
1425 }
1426
1427 #ifdef G_OS_WIN32
1428 static gsize
1429 win32_strftime_helper (const GDate     *d,
1430                        const gchar     *format,
1431                        const struct tm *tm,
1432                        gchar           *s,
1433                        gsize            slen)
1434 {
1435   SYSTEMTIME systemtime;
1436   TIME_ZONE_INFORMATION tzinfo;
1437   LCID lcid;
1438   int n, k;
1439   GArray *result;
1440   const gchar *p;
1441   gunichar c;
1442   const wchar_t digits[] = L"0123456789";
1443   gchar *convbuf;
1444   glong convlen = 0;
1445   gsize retval;
1446
1447   systemtime.wYear = tm->tm_year + 1900;
1448   systemtime.wMonth = tm->tm_mon + 1;
1449   systemtime.wDayOfWeek = tm->tm_wday;
1450   systemtime.wDay = tm->tm_mday;
1451   systemtime.wHour = tm->tm_hour;
1452   systemtime.wMinute = tm->tm_min;
1453   systemtime.wSecond = tm->tm_sec;
1454   systemtime.wMilliseconds = 0;
1455   
1456   lcid = GetThreadLocale ();
1457   result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2));
1458
1459   p = format;
1460   while (*p)
1461     {
1462       c = g_utf8_get_char (p);
1463       if (c == '%')
1464         {
1465           p = g_utf8_next_char (p);
1466           if (!*p)
1467             {
1468               s[0] = '\0';
1469               g_array_free (result, TRUE);
1470
1471               return 0;
1472             }
1473           
1474           c = g_utf8_get_char (p);
1475           if (c == 'E' || c == 'O')
1476             {
1477               /* Ignore modified conversion specifiers for now. */
1478               p = g_utf8_next_char (p);
1479               if (!*p)
1480                 {
1481                   s[0] = '\0';
1482                   g_array_free (result, TRUE);
1483                   
1484                   return 0;
1485                 }
1486
1487               c = g_utf8_get_char (p);
1488             }
1489
1490           switch (c)
1491             {
1492             case 'a':
1493               if (systemtime.wDayOfWeek == 0)
1494                 k = 6;
1495               else
1496                 k = systemtime.wDayOfWeek - 1;
1497               n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0);
1498               g_array_set_size (result, result->len + n);
1499               GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
1500               g_array_set_size (result, result->len - 1);
1501               break;
1502             case 'A':
1503               if (systemtime.wDayOfWeek == 0)
1504                 k = 6;
1505               else
1506                 k = systemtime.wDayOfWeek - 1;
1507               n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0);
1508               g_array_set_size (result, result->len + n);
1509               GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
1510               g_array_set_size (result, result->len - 1);
1511               break;
1512             case 'b':
1513             case 'h':
1514               n = GetLocaleInfoW (lcid, LOCALE_SABBREVMONTHNAME1+systemtime.wMonth-1, NULL, 0);
1515               g_array_set_size (result, result->len + n);
1516               GetLocaleInfoW (lcid, LOCALE_SABBREVMONTHNAME1+systemtime.wMonth-1, ((wchar_t *) result->data) + result->len - n, n);
1517               g_array_set_size (result, result->len - 1);
1518               break;
1519             case 'B':
1520               n = GetLocaleInfoW (lcid, LOCALE_SMONTHNAME1+systemtime.wMonth-1, NULL, 0);
1521               g_array_set_size (result, result->len + n);
1522               GetLocaleInfoW (lcid, LOCALE_SMONTHNAME1+systemtime.wMonth-1, ((wchar_t *) result->data) + result->len - n, n);
1523               g_array_set_size (result, result->len - 1);
1524               break;
1525             case 'c':
1526               n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
1527               if (n > 0)
1528                 {
1529                   g_array_set_size (result, result->len + n);
1530                   GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
1531                   g_array_set_size (result, result->len - 1);
1532                 }
1533               g_array_append_vals (result, L" ", 1);
1534               n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
1535               if (n > 0)
1536                 {
1537                   g_array_set_size (result, result->len + n);
1538                   GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
1539                   g_array_set_size (result, result->len - 1);
1540                 }
1541               break;
1542             case 'C':
1543               g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
1544               g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1);
1545               break;
1546             case 'd':
1547               g_array_append_vals (result, digits + systemtime.wDay/10, 1);
1548               g_array_append_vals (result, digits + systemtime.wDay%10, 1);
1549               break;
1550             case 'D':
1551               g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
1552               g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
1553               g_array_append_vals (result, L"/", 1);
1554               g_array_append_vals (result, digits + systemtime.wDay/10, 1);
1555               g_array_append_vals (result, digits + systemtime.wDay%10, 1);
1556               g_array_append_vals (result, L"/", 1);
1557               g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
1558               g_array_append_vals (result, digits + systemtime.wYear%10, 1);
1559               break;
1560             case 'e':
1561               if (systemtime.wDay >= 10)
1562                 g_array_append_vals (result, digits + systemtime.wDay/10, 1);
1563               else
1564                 g_array_append_vals (result, L" ", 1);
1565               g_array_append_vals (result, digits + systemtime.wDay%10, 1);
1566               break;
1567
1568               /* A GDate has no time fields, so for now we can
1569                * hardcode all time conversions into zeros (or 12 for
1570                * %I). The alternative code snippets in the #else
1571                * branches are here ready to be taken into use when
1572                * needed by a g_strftime() or g_date_and_time_format()
1573                * or whatever.
1574                */
1575             case 'H':
1576 #if 1
1577               g_array_append_vals (result, L"00", 2);
1578 #else
1579               g_array_append_vals (result, digits + systemtime.wHour/10, 1);
1580               g_array_append_vals (result, digits + systemtime.wHour%10, 1);
1581 #endif
1582               break;
1583             case 'I':
1584 #if 1
1585               g_array_append_vals (result, L"12", 2);
1586 #else
1587               if (systemtime.wHour == 0)
1588                 g_array_append_vals (result, L"12", 2);
1589               else
1590                 {
1591                   g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
1592                   g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
1593                 }
1594 #endif
1595               break;
1596             case  'j':
1597               g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1);
1598               g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1);
1599               g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1);
1600               break;
1601             case 'm':
1602               g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
1603               g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
1604               break;
1605             case 'M':
1606 #if 1
1607               g_array_append_vals (result, L"00", 2);
1608 #else
1609               g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
1610               g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
1611 #endif
1612               break;
1613             case 'n':
1614               g_array_append_vals (result, L"\n", 1);
1615               break;
1616             case 'p':
1617               n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
1618               if (n > 0)
1619                 {
1620                   g_array_set_size (result, result->len + n);
1621                   GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
1622                   g_array_set_size (result, result->len - 1);
1623                 }
1624               break;
1625             case 'r':
1626               /* This is a rather odd format. Hard to say what to do.
1627                * Let's always use the POSIX %I:%M:%S %p
1628                */
1629 #if 1
1630               g_array_append_vals (result, L"12:00:00", 8);
1631 #else
1632               if (systemtime.wHour == 0)
1633                 g_array_append_vals (result, L"12", 2);
1634               else
1635                 {
1636                   g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
1637                   g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
1638                 }
1639               g_array_append_vals (result, L":", 1);
1640               g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
1641               g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
1642               g_array_append_vals (result, L":", 1);
1643               g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
1644               g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
1645               g_array_append_vals (result, L" ", 1);
1646 #endif
1647               n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
1648               if (n > 0)
1649                 {
1650                   g_array_set_size (result, result->len + n);
1651                   GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
1652                   g_array_set_size (result, result->len - 1);
1653                 }
1654               break;
1655             case 'R':
1656 #if 1
1657               g_array_append_vals (result, L"00:00", 5);
1658 #else
1659               g_array_append_vals (result, digits + systemtime.wHour/10, 1);
1660               g_array_append_vals (result, digits + systemtime.wHour%10, 1);
1661               g_array_append_vals (result, L":", 1);
1662               g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
1663               g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
1664 #endif
1665               break;
1666             case 'S':
1667 #if 1
1668               g_array_append_vals (result, L"00", 2);
1669 #else
1670               g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
1671               g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
1672 #endif
1673               break;
1674             case 't':
1675               g_array_append_vals (result, L"\t", 1);
1676               break;
1677             case 'T':
1678 #if 1
1679               g_array_append_vals (result, L"00:00:00", 8);
1680 #else
1681               g_array_append_vals (result, digits + systemtime.wHour/10, 1);
1682               g_array_append_vals (result, digits + systemtime.wHour%10, 1);
1683               g_array_append_vals (result, L":", 1);
1684               g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
1685               g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
1686               g_array_append_vals (result, L":", 1);
1687               g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
1688               g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
1689 #endif
1690               break;
1691             case 'u':
1692               if (systemtime.wDayOfWeek == 0)
1693                 g_array_append_vals (result, L"7", 1);
1694               else
1695                 g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
1696               break;
1697             case 'U':
1698               n = g_date_get_sunday_week_of_year (d);
1699               g_array_append_vals (result, digits + n/10, 1);
1700               g_array_append_vals (result, digits + n%10, 1);
1701               break;
1702             case 'V':
1703               n = g_date_get_iso8601_week_of_year (d);
1704               g_array_append_vals (result, digits + n/10, 1);
1705               g_array_append_vals (result, digits + n%10, 1);
1706               break;
1707             case 'w':
1708               g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
1709               break;
1710             case 'W':
1711               n = g_date_get_monday_week_of_year (d);
1712               g_array_append_vals (result, digits + n/10, 1);
1713               g_array_append_vals (result, digits + n%10, 1);
1714               break;
1715             case 'x':
1716               n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
1717               if (n > 0)
1718                 {
1719                   g_array_set_size (result, result->len + n);
1720                   GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
1721                   g_array_set_size (result, result->len - 1);
1722                 }
1723               break;
1724             case 'X':
1725               n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
1726               if (n > 0)
1727                 {
1728                   g_array_set_size (result, result->len + n);
1729                   GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
1730                   g_array_set_size (result, result->len - 1);
1731                 }
1732               break;
1733             case 'y':
1734               g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
1735               g_array_append_vals (result, digits + systemtime.wYear%10, 1);
1736               break;
1737             case 'Y':
1738               g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
1739               g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1);
1740               g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
1741               g_array_append_vals (result, digits + systemtime.wYear%10, 1);
1742               break;
1743             case 'Z':
1744               n = GetTimeZoneInformation (&tzinfo);
1745               if (n == TIME_ZONE_ID_UNKNOWN)
1746                 ;
1747               else if (n == TIME_ZONE_ID_STANDARD)
1748                 g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName));
1749               else if (n == TIME_ZONE_ID_DAYLIGHT)
1750                 g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName));
1751               break;
1752             case '%':
1753               g_array_append_vals (result, L"%", 1);
1754               break;
1755             }      
1756         } 
1757       else if (c <= 0xFFFF)
1758         {
1759           wchar_t wc = c;
1760           g_array_append_vals (result, &wc, 1);
1761         }
1762       else
1763         {
1764           glong nwc;
1765           wchar_t *ws;
1766
1767           ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL);
1768           g_array_append_vals (result, ws, nwc);
1769           g_free (ws);
1770         }
1771       p = g_utf8_next_char (p);
1772     }
1773   
1774   convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL);
1775   g_array_free (result, TRUE);
1776
1777   if (!convbuf)
1778     {
1779       s[0] = '\0';
1780       return 0;
1781     }
1782   
1783   if (slen <= convlen)
1784     {
1785       /* Ensure only whole characters are copied into the buffer. */
1786       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
1787       g_assert (end != NULL);
1788       convlen = end - convbuf;
1789
1790       /* Return 0 because the buffer isn't large enough. */
1791       retval = 0;
1792     }
1793   else
1794     retval = convlen;
1795
1796   memcpy (s, convbuf, convlen);
1797   s[convlen] = '\0';
1798   g_free (convbuf);
1799
1800   return retval;
1801 }
1802
1803 #endif
1804
1805 gsize     
1806 g_date_strftime (gchar       *s, 
1807                  gsize        slen, 
1808                  const gchar *format, 
1809                  const GDate *d)
1810 {
1811   struct tm tm;
1812 #ifndef G_OS_WIN32
1813   gsize locale_format_len = 0;
1814   gchar *locale_format;
1815   gsize tmplen;
1816   gchar *tmpbuf;
1817   gsize tmpbufsize;
1818   gsize convlen = 0;
1819   gchar *convbuf;
1820   GError *error = NULL;
1821   gsize retval;
1822 #endif
1823
1824   g_return_val_if_fail (g_date_valid (d), 0);
1825   g_return_val_if_fail (slen > 0, 0); 
1826   g_return_val_if_fail (format != 0, 0);
1827   g_return_val_if_fail (s != 0, 0);
1828
1829   g_date_to_struct_tm (d, &tm);
1830
1831 #ifdef G_OS_WIN32
1832   if (!g_utf8_validate (format, -1, NULL))
1833     {
1834       s[0] = '\0';
1835       return 0;
1836     }
1837   return win32_strftime_helper (d, format, &tm, s, slen);
1838 #else
1839
1840   locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
1841
1842   if (error)
1843     {
1844       g_warning (G_STRLOC "Error converting format to locale encoding: %s\n", error->message);
1845       g_error_free (error);
1846
1847       s[0] = '\0';
1848       return 0;
1849     }
1850
1851   tmpbufsize = MAX (128, locale_format_len * 2);
1852   while (TRUE)
1853     {
1854       tmpbuf = g_malloc (tmpbufsize);
1855
1856       /* Set the first byte to something other than '\0', to be able to
1857        * recognize whether strftime actually failed or just returned "".
1858        */
1859       tmpbuf[0] = '\1';
1860       tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
1861
1862       if (tmplen == 0 && tmpbuf[0] != '\0')
1863         {
1864           g_free (tmpbuf);
1865           tmpbufsize *= 2;
1866
1867           if (tmpbufsize > 65536)
1868             {
1869               g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up\n");
1870               g_free (locale_format);
1871
1872               s[0] = '\0';
1873               return 0;
1874             }
1875         }
1876       else
1877         break;
1878     }
1879   g_free (locale_format);
1880
1881   convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
1882   g_free (tmpbuf);
1883
1884   if (error)
1885     {
1886       g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s\n", error->message);
1887       g_error_free (error);
1888
1889       s[0] = '\0';
1890       return 0;
1891     }
1892
1893   if (slen <= convlen)
1894     {
1895       /* Ensure only whole characters are copied into the buffer.
1896        */
1897       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
1898       g_assert (end != NULL);
1899       convlen = end - convbuf;
1900
1901       /* Return 0 because the buffer isn't large enough.
1902        */
1903       retval = 0;
1904     }
1905   else
1906     retval = convlen;
1907
1908   memcpy (s, convbuf, convlen);
1909   s[convlen] = '\0';
1910   g_free (convbuf);
1911
1912   return retval;
1913 #endif
1914 }
1915
1916 #define __G_DATE_C__
1917 #include "galiasdef.c"
1918