source: trunk/third/glib2/glib/gdate.c @ 20721

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