Main Page | Class List | Directories | File List | Class Members | File Members

gdate.c

Go to the documentation of this file.
00001 /* GLIB - Library of useful routines for C programming
00002  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public
00015  * License along with this library; if not, write to the
00016  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017  * Boston, MA 02111-1307, USA.
00018  */
00019 
00020 /*
00021  * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
00022  * file for a list of people on the GLib Team.  See the ChangeLog
00023  * files for a list of changes.  These files are distributed with
00024  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
00025  */
00026 
00027 /* 
00028  * MT safe
00029  */
00030 
00031 #ifdef HAVE_CONFIG_H
00032 #include <config.h>
00033 #endif
00034 
00035 #include "glib.h"
00036 
00037 #include <time.h>
00038 #include <string.h>
00039 #include <ctype.h>
00040 #include <stdlib.h>
00041 #include <locale.h>
00042 
00043 GDate*
00044 g_date_new ()
00045 {
00046   GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
00047   
00048   return d;
00049 }
00050 
00051 GDate*
00052 g_date_new_dmy (GDateDay day, GDateMonth m, GDateYear y)
00053 {
00054   GDate *d;
00055   g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
00056   
00057   d = g_new (GDate, 1);
00058   
00059   d->julian = FALSE;
00060   d->dmy    = TRUE;
00061   
00062   d->month = m;
00063   d->day   = day;
00064   d->year  = y;
00065   
00066   g_assert (g_date_valid (d));
00067   
00068   return d;
00069 }
00070 
00071 GDate*
00072 g_date_new_julian (guint32 j)
00073 {
00074   GDate *d;
00075   g_return_val_if_fail (g_date_valid_julian (j), NULL);
00076   
00077   d = g_new (GDate, 1);
00078   
00079   d->julian = TRUE;
00080   d->dmy    = FALSE;
00081   
00082   d->julian_days = j;
00083   
00084   g_assert (g_date_valid (d));
00085   
00086   return d;
00087 }
00088 
00089 void
00090 g_date_free (GDate *d)
00091 {
00092   g_return_if_fail (d != NULL);
00093   
00094   g_free (d);
00095 }
00096 
00097 gboolean     
00098 g_date_valid (GDate       *d)
00099 {
00100   g_return_val_if_fail (d != NULL, FALSE);
00101   
00102   return (d->julian || d->dmy);
00103 }
00104 
00105 static const guint8 days_in_months[2][13] = 
00106 {  /* error, jan feb mar apr may jun jul aug sep oct nov dec */
00107   {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
00108   {  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
00109 };
00110 
00111 static const guint16 days_in_year[2][14] = 
00112 {  /* 0, jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec */
00113   {  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 
00114   {  0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
00115 };
00116 
00117 gboolean     
00118 g_date_valid_month (GDateMonth   m)
00119 { 
00120   return ( (m > G_DATE_BAD_MONTH) && (m < 13) );
00121 }
00122 
00123 gboolean     
00124 g_date_valid_year (GDateYear    y)
00125 {
00126   return ( y > G_DATE_BAD_YEAR );
00127 }
00128 
00129 gboolean     
00130 g_date_valid_day (GDateDay     d)
00131 {
00132   return ( (d > G_DATE_BAD_DAY) && (d < 32) );
00133 }
00134 
00135 gboolean     
00136 g_date_valid_weekday (GDateWeekday w)
00137 {
00138   return ( (w > G_DATE_BAD_WEEKDAY) && (w < 8) );
00139 }
00140 
00141 gboolean     
00142 g_date_valid_julian (guint32      j)
00143 {
00144   return (j > G_DATE_BAD_JULIAN);
00145 }
00146 
00147 gboolean     
00148 g_date_valid_dmy (GDateDay     d, 
00149                   GDateMonth   m, 
00150                   GDateYear    y)
00151 {
00152   return ( (m > G_DATE_BAD_MONTH) &&
00153            (m < 13)               && 
00154            (d > G_DATE_BAD_DAY)   && 
00155            (y > G_DATE_BAD_YEAR)  &&   /* must check before using g_date_is_leap_year */
00156            (d <=  (g_date_is_leap_year (y) ? 
00157                    days_in_months[1][m] : days_in_months[0][m])) );
00158 }
00159 
00160 
00161 /* "Julian days" just means an absolute number of days, where Day 1 ==
00162  *   Jan 1, Year 1
00163  */
00164 static void
00165 g_date_update_julian (GDate *d)
00166 {
00167   GDateYear year;
00168   gint index;
00169   
00170   g_return_if_fail (d != NULL);
00171   g_return_if_fail (d->dmy);
00172   g_return_if_fail (!d->julian);
00173   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
00174   
00175   /* What we actually do is: multiply years * 365 days in the year,
00176    *  add the number of years divided by 4, subtract the number of
00177    *  years divided by 100 and add the number of years divided by 400,
00178    *  which accounts for leap year stuff. Code from Steffen Beyer's
00179    *  DateCalc. 
00180    */
00181   
00182   year = d->year - 1; /* we know d->year > 0 since it's valid */
00183   
00184   d->julian_days = year * 365U;
00185   d->julian_days += (year >>= 2); /* divide by 4 and add */
00186   d->julian_days -= (year /= 25); /* divides original # years by 100 */
00187   d->julian_days += year >> 2;    /* divides by 4, which divides original by 400 */
00188   
00189   index = g_date_is_leap_year (d->year) ? 1 : 0;
00190   
00191   d->julian_days += days_in_year[index][d->month] + d->day;
00192   
00193   g_return_if_fail (g_date_valid_julian (d->julian_days));
00194   
00195   d->julian = TRUE;
00196 }
00197 
00198 static void 
00199 g_date_update_dmy (GDate *d)
00200 {
00201   GDateYear y;
00202   GDateMonth m;
00203   GDateDay day;
00204   
00205   guint32 A, B, C, D, E, M;
00206   
00207   g_return_if_fail (d != NULL);
00208   g_return_if_fail (d->julian);
00209   g_return_if_fail (!d->dmy);
00210   g_return_if_fail (g_date_valid_julian (d->julian_days));
00211   
00212   /* Formula taken from the Calendar FAQ; the formula was for the
00213    *  Julian Period which starts on 1 January 4713 BC, so we add
00214    *  1,721,425 to the number of days before doing the formula.
00215    *
00216    * I'm sure this can be simplified for our 1 January 1 AD period
00217    * start, but I can't figure out how to unpack the formula.  
00218    */
00219   
00220   A = d->julian_days + 1721425 + 32045;
00221   B = ( 4 *(A + 36524) )/ 146097 - 1;
00222   C = A - (146097 * B)/4;
00223   D = ( 4 * (C + 365) ) / 1461 - 1;
00224   E = C - ((1461*D) / 4);
00225   M = (5 * (E - 1) + 2)/153;
00226   
00227   m = M + 3 - (12*(M/10));
00228   day = E - (153*M + 2)/5;
00229   y = 100 * B + D - 4800 + (M/10);
00230   
00231 #ifdef G_ENABLE_DEBUG
00232   if (!g_date_valid_dmy (day, m, y)) 
00233     {
00234       g_warning ("\nOOPS julian: %u  computed dmy: %u %u %u\n", 
00235                  d->julian_days, day, m, y);
00236     }
00237 #endif
00238   
00239   d->month = m;
00240   d->day   = day;
00241   d->year  = y;
00242   
00243   d->dmy = TRUE;
00244 }
00245 
00246 GDateWeekday 
00247 g_date_weekday (GDate *d)
00248 {
00249   g_return_val_if_fail (d != NULL, G_DATE_BAD_WEEKDAY);
00250   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
00251   
00252   if (!d->julian) 
00253     {
00254       g_date_update_julian (d);
00255     }
00256   g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
00257   
00258   return ((d->julian_days - 1) % 7) + 1;
00259 }
00260 
00261 GDateMonth   
00262 g_date_month (GDate *d)
00263 {
00264   g_return_val_if_fail (d != NULL, G_DATE_BAD_MONTH);
00265   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
00266   
00267   if (!d->dmy) 
00268     {
00269       g_date_update_dmy (d);
00270     }
00271   g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
00272   
00273   return d->month;
00274 }
00275 
00276 GDateYear    
00277 g_date_year (GDate *d)
00278 {
00279   g_return_val_if_fail (d != NULL, G_DATE_BAD_YEAR);
00280   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
00281   
00282   if (!d->dmy) 
00283     {
00284       g_date_update_dmy (d);
00285     }
00286   g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);  
00287   
00288   return d->year;
00289 }
00290 
00291 GDateDay     
00292 g_date_day (GDate *d)
00293 {
00294   g_return_val_if_fail (d != NULL, G_DATE_BAD_DAY);
00295   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
00296   
00297   if (!d->dmy) 
00298     {
00299       g_date_update_dmy (d);
00300     }
00301   g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);  
00302   
00303   return d->day;
00304 }
00305 
00306 guint32      
00307 g_date_julian (GDate *d)
00308 {
00309   g_return_val_if_fail (d != NULL, G_DATE_BAD_JULIAN);
00310   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
00311   
00312   if (!d->julian) 
00313     {
00314       g_date_update_julian (d);
00315     }
00316   g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN);  
00317   
00318   return d->julian_days;
00319 }
00320 
00321 guint        
00322 g_date_day_of_year (GDate *d)
00323 {
00324   gint index;
00325   
00326   g_return_val_if_fail (d != NULL, 0);
00327   g_return_val_if_fail (g_date_valid (d), 0);
00328   
00329   if (!d->dmy) 
00330     {
00331       g_date_update_dmy (d);
00332     }
00333   g_return_val_if_fail (d->dmy, 0);  
00334   
00335   index = g_date_is_leap_year (d->year) ? 1 : 0;
00336   
00337   return (days_in_year[index][d->month] + d->day);
00338 }
00339 
00340 guint        
00341 g_date_monday_week_of_year (GDate *d)
00342 {
00343   GDateWeekday wd;
00344   guint day;
00345   GDate first;
00346   
00347   g_return_val_if_fail (d != NULL, 0);
00348   g_return_val_if_fail (g_date_valid (d), 0);
00349   
00350   if (!d->dmy) 
00351     {
00352       g_date_update_dmy (d);
00353     }
00354   g_return_val_if_fail (d->dmy, 0);  
00355   
00356   g_date_clear (&first, 1);
00357   
00358   g_date_set_dmy (&first, 1, 1, d->year);
00359   
00360   wd = g_date_weekday (&first) - 1; /* make Monday day 0 */
00361   day = g_date_day_of_year (d) - 1;
00362   
00363   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
00364 }
00365 
00366 guint        
00367 g_date_sunday_week_of_year (GDate *d)
00368 {
00369   GDateWeekday wd;
00370   guint day;
00371   GDate first;
00372   
00373   g_return_val_if_fail (d != NULL, 0);
00374   g_return_val_if_fail (g_date_valid (d), 0);
00375   
00376   if (!d->dmy) 
00377     {
00378       g_date_update_dmy (d);
00379     }
00380   g_return_val_if_fail (d->dmy, 0);  
00381   
00382   g_date_clear (&first, 1);
00383   
00384   g_date_set_dmy (&first, 1, 1, d->year);
00385   
00386   wd = g_date_weekday (&first);
00387   if (wd == 7) wd = 0; /* make Sunday day 0 */
00388   day = g_date_day_of_year (d) - 1;
00389   
00390   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
00391 }
00392 
00393 void         
00394 g_date_clear (GDate       *d, guint ndates)
00395 {
00396   g_return_if_fail (d != NULL);
00397   g_return_if_fail (ndates != 0);
00398   
00399   memset (d, 0x0, ndates*sizeof (GDate)); 
00400 }
00401 
00402 G_LOCK_DEFINE_STATIC (g_date_global);
00403 
00404 /* These are for the parser, output to the user should use *
00405  * g_date_strftime () - this creates more never-freed memory to annoy
00406  * all those memory debugger users. :-) 
00407  */
00408 
00409 static gchar *long_month_names[13] = 
00410 { 
00411   "Error", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 
00412 };
00413 
00414 static gchar *short_month_names[13] = 
00415 {
00416   "Error", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 
00417 };
00418 
00419 /* This tells us if we need to update the parse info */
00420 static gchar *current_locale = NULL;
00421 
00422 /* order of these in the current locale */
00423 static GDateDMY dmy_order[3] = 
00424 {
00425    G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
00426 };
00427 
00428 /* Where to chop two-digit years: i.e., for the 1930 default, numbers
00429  * 29 and below are counted as in the year 2000, numbers 30 and above
00430  * are counted as in the year 1900.  
00431  */
00432 
00433 static GDateYear twodigit_start_year = 1930;
00434 
00435 /* It is impossible to enter a year between 1 AD and 99 AD with this
00436  * in effect.  
00437  */
00438 static gboolean using_twodigit_years = FALSE;
00439 
00440 struct _GDateParseTokens {
00441   gint num_ints;
00442   gint n[3];
00443   guint month;
00444 };
00445 
00446 typedef struct _GDateParseTokens GDateParseTokens;
00447 
00448 #define NUM_LEN 10
00449 
00450 /* HOLDS: g_date_global_lock */
00451 static void
00452 g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
00453 {
00454   gchar num[4][NUM_LEN+1];
00455   gint i;
00456   const guchar *s;
00457   
00458   /* We count 4, but store 3; so we can give an error
00459    * if there are 4.
00460    */
00461   num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
00462   
00463   s = str;
00464   pt->num_ints = 0;
00465   while (*s && pt->num_ints < 4) 
00466     {
00467       
00468       i = 0;
00469       while (*s && isdigit (*s) && i <= NUM_LEN)
00470         {
00471           num[pt->num_ints][i] = *s;
00472           ++s; 
00473           ++i;
00474         }
00475       
00476       if (i > 0) 
00477         {
00478           num[pt->num_ints][i] = '\0';
00479           ++(pt->num_ints);
00480         }
00481       
00482       if (*s == '\0') break;
00483       
00484       ++s;
00485     }
00486   
00487   pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
00488   pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
00489   pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
00490   
00491   pt->month = G_DATE_BAD_MONTH;
00492   
00493   if (pt->num_ints < 3)
00494     {
00495       gchar lcstr[128];
00496       int i = 1;
00497       
00498       strncpy (lcstr, str, 127);
00499       g_strdown (lcstr);
00500       
00501       while (i < 13)
00502         {
00503           if (long_month_names[i] != NULL) 
00504             {
00505               const gchar *found = strstr (lcstr, long_month_names[i]);
00506               
00507               if (found != NULL)
00508                 {
00509                   pt->month = i;
00510                   return;
00511                 }
00512             }
00513           
00514           if (short_month_names[i] != NULL) 
00515             {
00516               const gchar *found = strstr (lcstr, short_month_names[i]);
00517               
00518               if (found != NULL)
00519                 {
00520                   pt->month = i;
00521                   return;
00522                 }
00523             }
00524 
00525           ++i;
00526         }      
00527     }
00528 }
00529 
00530 /* HOLDS: g_date_global_lock */
00531 static void
00532 g_date_prepare_to_parse (const gchar *str, GDateParseTokens *pt)
00533 {
00534   const gchar *locale = setlocale (LC_TIME, NULL);
00535   gboolean recompute_localeinfo = FALSE;
00536   GDate d;
00537   
00538   g_return_if_fail (locale != NULL); /* should not happen */
00539   
00540   g_date_clear (&d, 1);              /* clear for scratch use */
00541   
00542   if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) ) 
00543     {
00544       recompute_localeinfo = TRUE;  /* Uh, there used to be a reason for the temporary */
00545     }
00546   
00547   if (recompute_localeinfo)
00548     {
00549       int i = 1;
00550       GDateParseTokens testpt;
00551       gchar buf[128];
00552       
00553       g_free (current_locale); /* still works if current_locale == NULL */
00554       
00555       current_locale = g_strdup (locale);
00556       
00557       while (i < 13) 
00558         {
00559           g_date_set_dmy (&d, 1, i, 1);
00560           
00561           g_return_if_fail (g_date_valid (&d));
00562           
00563           g_date_strftime (buf, 127, "%b", &d);
00564           g_free (short_month_names[i]);
00565           g_strdown (buf);
00566           short_month_names[i] = g_strdup (buf);
00567           
00568           
00569           
00570           g_date_strftime (buf, 127, "%B", &d);
00571           g_free (long_month_names[i]);
00572           g_strdown (buf);
00573           long_month_names[i] = g_strdup (buf);
00574           
00575           ++i;
00576         }
00577       
00578       /* Determine DMY order */
00579       
00580       /* had to pick a random day - don't change this, some strftimes
00581        * are broken on some days, and this one is good so far. */
00582       g_date_set_dmy (&d, 4, 7, 1976);
00583       
00584       g_date_strftime (buf, 127, "%x", &d);
00585       
00586       g_date_fill_parse_tokens (buf, &testpt);
00587       
00588       i = 0;
00589       while (i < testpt.num_ints)
00590         {
00591           switch (testpt.n[i])
00592             {
00593             case 7:
00594               dmy_order[i] = G_DATE_MONTH;
00595               break;
00596             case 4:
00597               dmy_order[i] = G_DATE_DAY;
00598               break;
00599             case 76:
00600               using_twodigit_years = TRUE; /* FALL THRU */
00601             case 1976:
00602               dmy_order[i] = G_DATE_YEAR;
00603               break;
00604             default:
00605               /* leave it unchanged */
00606               break;
00607             }
00608           ++i;
00609         }
00610       
00611 #ifdef G_ENABLE_DEBUG
00612       g_message ("**GDate prepared a new set of locale-specific parse rules.");
00613       i = 1;
00614       while (i < 13) 
00615         {
00616           g_message ("  %s   %s", long_month_names[i], short_month_names[i]);
00617           ++i;
00618         }
00619       if (using_twodigit_years)
00620         {
00621           g_message ("**Using twodigit years with cutoff year: %u", twodigit_start_year);
00622         }
00623       { 
00624         gchar *strings[3];
00625         i = 0;
00626         while (i < 3)
00627           {
00628             switch (dmy_order[i])
00629               {
00630               case G_DATE_MONTH:
00631                 strings[i] = "Month";
00632                 break;
00633               case G_DATE_YEAR:
00634                 strings[i] = "Year";
00635                 break;
00636               case G_DATE_DAY:
00637                 strings[i] = "Day";
00638                 break;
00639               default:
00640                 strings[i] = NULL;
00641                 break;
00642               }
00643             ++i;
00644           }
00645         g_message ("**Order: %s, %s, %s", strings[0], strings[1], strings[2]);
00646         g_message ("**Sample date in this locale: `%s'", buf);
00647       }
00648 #endif
00649     }
00650   
00651   g_date_fill_parse_tokens (str, pt);
00652 }
00653 
00654 void         
00655 g_date_set_parse (GDate       *d, 
00656                   const gchar *str)
00657 {
00658   GDateParseTokens pt;
00659   guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
00660   
00661   g_return_if_fail (d != NULL);
00662   
00663   /* set invalid */
00664   g_date_clear (d, 1);
00665   
00666   G_LOCK (g_date_global);
00667 
00668   g_date_prepare_to_parse (str, &pt);
00669   
00670 #ifdef G_ENABLE_DEBUG
00671   g_message ("Found %d ints, `%d' `%d' `%d' and written out month %d", 
00672              pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month);
00673 #endif
00674   
00675   
00676   if (pt.num_ints == 4) 
00677     {
00678       G_UNLOCK (g_date_global);
00679       return; /* presumably a typo; bail out. */
00680     }
00681   
00682   if (pt.num_ints > 1)
00683     {
00684       int i = 0;
00685       int j = 0;
00686       
00687       g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
00688       
00689       while (i < pt.num_ints && j < 3) 
00690         {
00691           switch (dmy_order[j])
00692             {
00693             case G_DATE_MONTH:
00694             {
00695               if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
00696                 {
00697                   m = pt.month;
00698                   ++j;      /* skip months, but don't skip this number */
00699                   continue;
00700                 }
00701               else 
00702                 m = pt.n[i];
00703             }
00704             break;
00705             case G_DATE_DAY:
00706             {
00707               if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
00708                 {
00709                   day = 1;
00710                   ++j;      /* skip days, since we may have month/year */
00711                   continue;
00712                 }
00713               day = pt.n[i];
00714             }
00715             break;
00716             case G_DATE_YEAR:
00717             {
00718               y  = pt.n[i];
00719               
00720               if (using_twodigit_years && y < 100)
00721                 {
00722                   guint two     =  twodigit_start_year % 100;
00723                   guint century = (twodigit_start_year / 100) * 100;
00724                   
00725                   if (y < two)
00726                     century += 100;
00727                   
00728                   y += century;
00729                 }
00730             }
00731             break;
00732             default:
00733               break;
00734             }
00735           
00736           ++i;
00737           ++j;
00738         }
00739       
00740       
00741       if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
00742         {
00743           /* Try YYYY MM DD */
00744           y   = pt.n[0];
00745           m   = pt.n[1];
00746           day = pt.n[2];
00747           
00748           if (using_twodigit_years && y < 100) 
00749             y = G_DATE_BAD_YEAR; /* avoids ambiguity */
00750         }
00751     }
00752   else if (pt.num_ints == 1) 
00753     {
00754       if (pt.month != G_DATE_BAD_MONTH)
00755         {
00756           /* Month name and year? */
00757           m    = pt.month;
00758           day  = 1;
00759           y = pt.n[0];
00760         }
00761       else
00762         {
00763           /* Try yyyymmdd and yymmdd */
00764           
00765           m   = (pt.n[0]/100) % 100;
00766           day = pt.n[0] % 100;
00767           y   = pt.n[0]/10000;
00768           
00769           /* FIXME move this into a separate function */
00770           if (using_twodigit_years && y < 100)
00771             {
00772               guint two     =  twodigit_start_year % 100;
00773               guint century = (twodigit_start_year / 100) * 100;
00774               
00775               if (y < two)
00776                 century += 100;
00777               
00778               y += century;
00779             }
00780         }
00781     }
00782   
00783   /* See if we got anything valid out of all this. */
00784   /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
00785   if (y < 8000 && g_date_valid_dmy (day, m, y)) 
00786     {
00787       d->month = m;
00788       d->day   = day;
00789       d->year  = y;
00790       d->dmy   = TRUE;
00791     }
00792 #ifdef G_ENABLE_DEBUG
00793   else 
00794     g_message ("Rejected DMY %u %u %u", day, m, y);
00795 #endif
00796   G_UNLOCK (g_date_global);
00797 }
00798 
00799 void         
00800 g_date_set_time (GDate *d,
00801                  GTime  time)
00802 {
00803   time_t t = time;
00804   struct tm tm;
00805   
00806   g_return_if_fail (d != NULL);
00807   
00808 #ifdef HAVE_LOCALTIME_R
00809   localtime_r (&t, &tm);
00810 #else
00811   {
00812     struct tm *ptm = localtime (&t);
00813     g_assert (ptm);
00814     memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
00815   }
00816 #endif
00817   
00818   d->julian = FALSE;
00819   
00820   d->month = tm.tm_mon + 1;
00821   d->day   = tm.tm_mday;
00822   d->year  = tm.tm_year + 1900;
00823   
00824   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
00825   
00826   d->dmy    = TRUE;
00827 }
00828 
00829 void         
00830 g_date_set_month (GDate     *d, 
00831                   GDateMonth m)
00832 {
00833   g_return_if_fail (d != NULL);
00834   g_return_if_fail (g_date_valid_month (m));
00835 
00836   if (d->julian && !d->dmy) g_date_update_dmy(d);
00837   d->julian = FALSE;
00838   
00839   d->month = m;
00840   
00841   if (g_date_valid_dmy (d->day, d->month, d->year))
00842     d->dmy = TRUE;
00843   else 
00844     d->dmy = FALSE;
00845 }
00846 
00847 void         
00848 g_date_set_day (GDate     *d, 
00849                 GDateDay day)
00850 {
00851   g_return_if_fail (d != NULL);
00852   g_return_if_fail (g_date_valid_day (day));
00853   
00854   if (d->julian && !d->dmy) g_date_update_dmy(d);
00855   d->julian = FALSE;
00856   
00857   d->day = day;
00858   
00859   if (g_date_valid_dmy (d->day, d->month, d->year))
00860     d->dmy = TRUE;
00861   else 
00862     d->dmy = FALSE;
00863 }
00864 
00865 void         
00866 g_date_set_year (GDate     *d, 
00867                  GDateYear  y)
00868 {
00869   g_return_if_fail (d != NULL);
00870   g_return_if_fail (g_date_valid_year (y));
00871   
00872   if (d->julian && !d->dmy) g_date_update_dmy(d);
00873   d->julian = FALSE;
00874   
00875   d->year = y;
00876   
00877   if (g_date_valid_dmy (d->day, d->month, d->year))
00878     d->dmy = TRUE;
00879   else 
00880     d->dmy = FALSE;
00881 }
00882 
00883 void         
00884 g_date_set_dmy (GDate     *d, 
00885                 GDateDay   day, 
00886                 GDateMonth m, 
00887                 GDateYear  y)
00888 {
00889   g_return_if_fail (d != NULL);
00890   g_return_if_fail (g_date_valid_dmy (day, m, y));
00891   
00892   d->julian = FALSE;
00893   
00894   d->month = m;
00895   d->day   = day;
00896   d->year  = y;
00897   
00898   d->dmy = TRUE;
00899 }
00900 
00901 void         
00902 g_date_set_julian (GDate *d, guint32 j)
00903 {
00904   g_return_if_fail (d != NULL);
00905   g_return_if_fail (g_date_valid_julian (j));
00906   
00907   d->julian_days = j;
00908   d->julian = TRUE;
00909   d->dmy = FALSE;
00910 }
00911 
00912 
00913 gboolean     
00914 g_date_is_first_of_month (GDate *d)
00915 {
00916   g_return_val_if_fail (d != NULL, FALSE);
00917   g_return_val_if_fail (g_date_valid (d), FALSE);
00918   
00919   if (!d->dmy) 
00920     {
00921       g_date_update_dmy (d);
00922     }
00923   g_return_val_if_fail (d->dmy, FALSE);  
00924   
00925   if (d->day == 1) return TRUE;
00926   else return FALSE;
00927 }
00928 
00929 gboolean     
00930 g_date_is_last_of_month (GDate *d)
00931 {
00932   gint index;
00933   
00934   g_return_val_if_fail (d != NULL, FALSE);
00935   g_return_val_if_fail (g_date_valid (d), FALSE);
00936   
00937   if (!d->dmy) 
00938     {
00939       g_date_update_dmy (d);
00940     }
00941   g_return_val_if_fail (d->dmy, FALSE);  
00942   
00943   index = g_date_is_leap_year (d->year) ? 1 : 0;
00944   
00945   if (d->day == days_in_months[index][d->month]) return TRUE;
00946   else return FALSE;
00947 }
00948 
00949 void         
00950 g_date_add_days (GDate *d, guint ndays)
00951 {
00952   g_return_if_fail (d != NULL);
00953   g_return_if_fail (g_date_valid (d));
00954   
00955   if (!d->julian)
00956     {
00957       g_date_update_julian (d);
00958     }
00959   g_return_if_fail (d->julian);
00960   
00961   d->julian_days += ndays;
00962   d->dmy = FALSE;
00963 }
00964 
00965 void         
00966 g_date_subtract_days (GDate *d, guint ndays)
00967 {
00968   g_return_if_fail (d != NULL);
00969   g_return_if_fail (g_date_valid (d));
00970   
00971   if (!d->julian)
00972     {
00973       g_date_update_julian (d);
00974     }
00975   g_return_if_fail (d->julian);
00976   g_return_if_fail (d->julian_days > ndays);
00977   
00978   d->julian_days -= ndays;
00979   d->dmy = FALSE;
00980 }
00981 
00982 void         
00983 g_date_add_months (GDate       *d, 
00984                    guint        nmonths)
00985 {
00986   guint years, months;
00987   gint index;
00988   
00989   g_return_if_fail (d != NULL);
00990   g_return_if_fail (g_date_valid (d));
00991   
00992   if (!d->dmy) 
00993     {
00994       g_date_update_dmy (d);
00995     }
00996   g_return_if_fail (d->dmy);  
00997   
00998   nmonths += d->month - 1;
00999   
01000   years  = nmonths/12;
01001   months = nmonths%12;
01002   
01003   d->month = months + 1;
01004   d->year  += years;
01005   
01006   index = g_date_is_leap_year (d->year) ? 1 : 0;
01007   
01008   if (d->day > days_in_months[index][d->month])
01009     d->day = days_in_months[index][d->month];
01010   
01011   d->julian = FALSE;
01012   
01013   g_return_if_fail (g_date_valid (d));
01014 }
01015 
01016 void         
01017 g_date_subtract_months (GDate       *d, 
01018                         guint        nmonths)
01019 {
01020   guint years, months;
01021   gint index;
01022   
01023   g_return_if_fail (d != NULL);
01024   g_return_if_fail (g_date_valid (d));
01025   
01026   if (!d->dmy) 
01027     {
01028       g_date_update_dmy (d);
01029     }
01030   g_return_if_fail (d->dmy);  
01031   
01032   years  = nmonths/12;
01033   months = nmonths%12;
01034   
01035   g_return_if_fail (d->year > years);
01036   
01037   d->year  -= years;
01038   
01039   if (d->month > months) d->month -= months;
01040   else 
01041     {
01042       months -= d->month;
01043       d->month = 12 - months;
01044       d->year -= 1;
01045     }
01046   
01047   index = g_date_is_leap_year (d->year) ? 1 : 0;
01048   
01049   if (d->day > days_in_months[index][d->month])
01050     d->day = days_in_months[index][d->month];
01051   
01052   d->julian = FALSE;
01053   
01054   g_return_if_fail (g_date_valid (d));
01055 }
01056 
01057 void         
01058 g_date_add_years (GDate       *d, 
01059                   guint        nyears)
01060 {
01061   g_return_if_fail (d != NULL);
01062   g_return_if_fail (g_date_valid (d));
01063   
01064   if (!d->dmy) 
01065     {
01066       g_date_update_dmy (d);
01067     }
01068   g_return_if_fail (d->dmy);  
01069   
01070   d->year += nyears;
01071   
01072   if (d->month == 2 && d->day == 29)
01073     {
01074       if (!g_date_is_leap_year (d->year))
01075         {
01076           d->day = 28;
01077         }
01078     }
01079   
01080   d->julian = FALSE;
01081 }
01082 
01083 void         
01084 g_date_subtract_years (GDate       *d, 
01085                        guint        nyears)
01086 {
01087   g_return_if_fail (d != NULL);
01088   g_return_if_fail (g_date_valid (d));
01089   
01090   if (!d->dmy) 
01091     {
01092       g_date_update_dmy (d);
01093     }
01094   g_return_if_fail (d->dmy);  
01095   g_return_if_fail (d->year > nyears);
01096   
01097   d->year -= nyears;
01098   
01099   if (d->month == 2 && d->day == 29)
01100     {
01101       if (!g_date_is_leap_year (d->year))
01102         {
01103           d->day = 28;
01104         }
01105     }
01106   
01107   d->julian = FALSE;
01108 }
01109 
01110 
01111 gboolean     
01112 g_date_is_leap_year (GDateYear  year)
01113 {
01114   g_return_val_if_fail (g_date_valid_year (year), FALSE);
01115   
01116   return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
01117            (year % 400) == 0 );
01118 }
01119 
01120 guint8         
01121 g_date_days_in_month (GDateMonth month, 
01122                       GDateYear  year)
01123 {
01124   gint index;
01125   
01126   g_return_val_if_fail (g_date_valid_year (year), 0);
01127   g_return_val_if_fail (g_date_valid_month (month), 0);
01128   
01129   index = g_date_is_leap_year (year) ? 1 : 0;
01130   
01131   return days_in_months[index][month];
01132 }
01133 
01134 guint8       
01135 g_date_monday_weeks_in_year (GDateYear  year)
01136 {
01137   GDate d;
01138   
01139   g_return_val_if_fail (g_date_valid_year (year), 0);
01140   
01141   g_date_clear (&d, 1);
01142   g_date_set_dmy (&d, 1, 1, year);
01143   if (g_date_weekday (&d) == G_DATE_MONDAY) return 53;
01144   g_date_set_dmy (&d, 31, 12, year);
01145   if (g_date_weekday (&d) == G_DATE_MONDAY) return 53;
01146   if (g_date_is_leap_year (year)) 
01147     {
01148       g_date_set_dmy (&d, 2, 1, year);
01149       if (g_date_weekday (&d) == G_DATE_MONDAY) return 53;
01150       g_date_set_dmy (&d, 30, 12, year);
01151       if (g_date_weekday (&d) == G_DATE_MONDAY) return 53;
01152     }
01153   return 52;
01154 }
01155 
01156 guint8       
01157 g_date_sunday_weeks_in_year (GDateYear  year)
01158 {
01159   GDate d;
01160   
01161   g_return_val_if_fail (g_date_valid_year (year), 0);
01162   
01163   g_date_clear (&d, 1);
01164   g_date_set_dmy (&d, 1, 1, year);
01165   if (g_date_weekday (&d) == G_DATE_SUNDAY) return 53;
01166   g_date_set_dmy (&d, 31, 12, year);
01167   if (g_date_weekday (&d) == G_DATE_SUNDAY) return 53;
01168   if (g_date_is_leap_year (year)) 
01169     {
01170       g_date_set_dmy (&d, 2, 1, year);
01171       if (g_date_weekday (&d) == G_DATE_SUNDAY) return 53;
01172       g_date_set_dmy (&d, 30, 12, year);
01173       if (g_date_weekday (&d) == G_DATE_SUNDAY) return 53;
01174     }
01175   return 52;
01176 }
01177 
01178 gint         
01179 g_date_compare (GDate     *lhs, 
01180                 GDate     *rhs)
01181 {
01182   g_return_val_if_fail (lhs != NULL, 0);
01183   g_return_val_if_fail (rhs != NULL, 0);
01184   g_return_val_if_fail (g_date_valid (lhs), 0);
01185   g_return_val_if_fail (g_date_valid (rhs), 0);
01186   
01187   /* Remember the self-comparison case! I think it works right now. */
01188   
01189   while (TRUE)
01190     {
01191       
01192       if (lhs->julian && rhs->julian) 
01193         {
01194           if (lhs->julian_days < rhs->julian_days) return -1;
01195           else if (lhs->julian_days > rhs->julian_days) return 1;
01196           else                                          return 0;
01197         }
01198       else if (lhs->dmy && rhs->dmy) 
01199         {
01200           if (lhs->year < rhs->year)               return -1;
01201           else if (lhs->year > rhs->year)               return 1;
01202           else 
01203             {
01204               if (lhs->month < rhs->month)         return -1;
01205               else if (lhs->month > rhs->month)         return 1;
01206               else 
01207                 {
01208                   if (lhs->day < rhs->day)              return -1;
01209                   else if (lhs->day > rhs->day)              return 1;
01210                   else                                       return 0;
01211                 }
01212               
01213             }
01214           
01215         }
01216       else
01217         {
01218           if (!lhs->julian) g_date_update_julian (lhs);
01219           if (!rhs->julian) g_date_update_julian (rhs);
01220           g_return_val_if_fail (lhs->julian, 0);
01221           g_return_val_if_fail (rhs->julian, 0);
01222         }
01223       
01224     }
01225   return 0; /* warnings */
01226 }
01227 
01228 
01229 void        
01230 g_date_to_struct_tm (GDate      *d, 
01231                      struct tm   *tm)
01232 {
01233   GDateWeekday day;
01234      
01235   g_return_if_fail (d != NULL);
01236   g_return_if_fail (g_date_valid (d));
01237   g_return_if_fail (tm != NULL);
01238   
01239   if (!d->dmy) 
01240     {
01241       g_date_update_dmy (d);
01242     }
01243   g_return_if_fail (d->dmy);
01244   
01245   /* zero all the irrelevant fields to be sure they're valid */
01246   
01247   /* On Linux and maybe other systems, there are weird non-POSIX
01248    * fields on the end of struct tm that choke strftime if they
01249    * contain garbage.  So we need to 0 the entire struct, not just the
01250    * fields we know to exist. 
01251    */
01252   
01253   memset (tm, 0x0, sizeof (struct tm));
01254   
01255   tm->tm_mday = d->day;
01256   tm->tm_mon  = d->month - 1; /* 0-11 goes in tm */
01257   tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
01258   
01259   day = g_date_weekday (d);
01260   if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
01261   
01262   tm->tm_wday = (int)day;
01263   
01264   tm->tm_yday = g_date_day_of_year (d) - 1; /* 0 to 365 */
01265   tm->tm_isdst = -1; /* -1 means "information not available" */
01266 }
01267 
01268 gsize     
01269 g_date_strftime (gchar       *s, 
01270                  gsize        slen, 
01271                  const gchar *format, 
01272                  GDate       *d)
01273 {
01274   struct tm tm;
01275   gsize retval;
01276   
01277   g_return_val_if_fail (d != NULL, 0);
01278   g_return_val_if_fail (g_date_valid (d), 0);
01279   g_return_val_if_fail (slen > 0, 0); 
01280   g_return_val_if_fail (format != 0, 0);
01281   g_return_val_if_fail (s != 0, 0);
01282   
01283   g_date_to_struct_tm (d, &tm);
01284   
01285   retval = strftime (s, slen, format, &tm);
01286   if (retval == 0)
01287     {
01288       /* If retval == 0, the contents of s are undefined.  We define
01289        *  them. 
01290        */
01291       s[0] = '\0';
01292     }
01293   return retval;
01294 }

© sourcejam.com 2005-2008