• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.1 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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GLib Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GLib at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 /*
26  * MT safe
27  */
28 
29 #include "config.h"
30 #include "glibconfig.h"
31 
32 #define DEBUG_MSG(x)	/* */
33 #ifdef G_ENABLE_DEBUG
34 /* #define DEBUG_MSG(args)	g_message args ; */
35 #endif
36 
37 #include <time.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <locale.h>
41 
42 #ifdef G_OS_WIN32
43 #include <windows.h>
44 #endif
45 
46 #include "gdate.h"
47 
48 #include "gconvert.h"
49 #include "gmem.h"
50 #include "gstrfuncs.h"
51 #include "gtestutils.h"
52 #include "gthread.h"
53 #include "gunicode.h"
54 
55 #ifdef G_OS_WIN32
56 #include "garray.h"
57 #endif
58 
59 /**
60  * SECTION:date
61  * @title: Date and Time Functions
62  * @short_description: calendrical calculations and miscellaneous time stuff
63  *
64  * The #GDate data structure represents a day between January 1, Year 1,
65  * and sometime a few thousand years in the future (right now it will go
66  * to the year 65535 or so, but g_date_set_parse() only parses up to the
67  * year 8000 or so - just count on "a few thousand"). #GDate is meant to
68  * represent everyday dates, not astronomical dates or historical dates
69  * or ISO timestamps or the like. It extrapolates the current Gregorian
70  * calendar forward and backward in time; there is no attempt to change
71  * the calendar to match time periods or locations. #GDate does not store
72  * time information; it represents a day.
73  *
74  * The #GDate implementation has several nice features; it is only a
75  * 64-bit struct, so storing large numbers of dates is very efficient. It
76  * can keep both a Julian and day-month-year representation of the date,
77  * since some calculations are much easier with one representation or the
78  * other. A Julian representation is simply a count of days since some
79  * fixed day in the past; for #GDate the fixed day is January 1, 1 AD.
80  * ("Julian" dates in the #GDate API aren't really Julian dates in the
81  * technical sense; technically, Julian dates count from the start of the
82  * Julian period, Jan 1, 4713 BC).
83  *
84  * #GDate is simple to use. First you need a "blank" date; you can get a
85  * dynamically allocated date from g_date_new(), or you can declare an
86  * automatic variable or array and initialize it to a sane state by
87  * calling g_date_clear(). A cleared date is sane; it's safe to call
88  * g_date_set_dmy() and the other mutator functions to initialize the
89  * value of a cleared date. However, a cleared date is initially
90  * invalid, meaning that it doesn't represent a day that exists.
91  * It is undefined to call any of the date calculation routines on an
92  * invalid date. If you obtain a date from a user or other
93  * unpredictable source, you should check its validity with the
94  * g_date_valid() predicate. g_date_valid() is also used to check for
95  * errors with g_date_set_parse() and other functions that can
96  * fail. Dates can be invalidated by calling g_date_clear() again.
97  *
98  * It is very important to use the API to access the #GDate
99  * struct. Often only the day-month-year or only the Julian
100  * representation is valid. Sometimes neither is valid. Use the API.
101  *
102  * GLib also features #GDateTime which represents a precise time.
103  */
104 
105 /**
106  * G_USEC_PER_SEC:
107  *
108  * Number of microseconds in one second (1 million).
109  * This macro is provided for code readability.
110  */
111 
112 /**
113  * GTimeVal:
114  * @tv_sec: seconds
115  * @tv_usec: microseconds
116  *
117  * Represents a precise time, with seconds and microseconds.
118  * Similar to the struct timeval returned by the gettimeofday()
119  * UNIX system call.
120  *
121  * GLib is attempting to unify around the use of 64-bit integers to
122  * represent microsecond-precision time. As such, this type will be
123  * removed from a future version of GLib. A consequence of using `glong` for
124  * `tv_sec` is that on 32-bit systems `GTimeVal` is subject to the year 2038
125  * problem.
126  *
127  * Deprecated: 2.62: Use #GDateTime or #guint64 instead.
128  */
129 
130 /**
131  * GDate:
132  * @julian_days: the Julian representation of the date
133  * @julian: this bit is set if @julian_days is valid
134  * @dmy: this is set if @day, @month and @year are valid
135  * @day: the day of the day-month-year representation of the date,
136  *     as a number between 1 and 31
137  * @month: the day of the day-month-year representation of the date,
138  *     as a number between 1 and 12
139  * @year: the day of the day-month-year representation of the date
140  *
141  * Represents a day between January 1, Year 1 and a few thousand years in
142  * the future. None of its members should be accessed directly.
143  *
144  * If the #GDate-struct is obtained from g_date_new(), it will be safe
145  * to mutate but invalid and thus not safe for calendrical computations.
146  *
147  * If it's declared on the stack, it will contain garbage so must be
148  * initialized with g_date_clear(). g_date_clear() makes the date invalid
149  * but sane. An invalid date doesn't represent a day, it's "empty." A date
150  * becomes valid after you set it to a Julian day or you set a day, month,
151  * and year.
152  */
153 
154 /**
155  * GTime:
156  *
157  * Simply a replacement for `time_t`. It has been deprecated
158  * since it is not equivalent to `time_t` on 64-bit platforms
159  * with a 64-bit `time_t`. Unrelated to #GTimer.
160  *
161  * Note that #GTime is defined to always be a 32-bit integer,
162  * unlike `time_t` which may be 64-bit on some systems. Therefore,
163  * #GTime will overflow in the year 2038, and you cannot use the
164  * address of a #GTime variable as argument to the UNIX time()
165  * function.
166  *
167  * Instead, do the following:
168  * |[<!-- language="C" -->
169  * time_t ttime;
170  * GTime gtime;
171  *
172  * time (&ttime);
173  * gtime = (GTime)ttime;
174  * ]|
175  *
176  * Deprecated: 2.62: This is not [Y2038-safe](https://en.wikipedia.org/wiki/Year_2038_problem).
177  *    Use #GDateTime or #time_t instead.
178  */
179 
180 /**
181  * GDateDMY:
182  * @G_DATE_DAY: a day
183  * @G_DATE_MONTH: a month
184  * @G_DATE_YEAR: a year
185  *
186  * This enumeration isn't used in the API, but may be useful if you need
187  * to mark a number as a day, month, or year.
188  */
189 
190 /**
191  * GDateDay:
192  *
193  * Integer representing a day of the month; between 1 and 31.
194  * #G_DATE_BAD_DAY represents an invalid day of the month.
195  */
196 
197 /**
198  * GDateMonth:
199  * @G_DATE_BAD_MONTH: invalid value
200  * @G_DATE_JANUARY: January
201  * @G_DATE_FEBRUARY: February
202  * @G_DATE_MARCH: March
203  * @G_DATE_APRIL: April
204  * @G_DATE_MAY: May
205  * @G_DATE_JUNE: June
206  * @G_DATE_JULY: July
207  * @G_DATE_AUGUST: August
208  * @G_DATE_SEPTEMBER: September
209  * @G_DATE_OCTOBER: October
210  * @G_DATE_NOVEMBER: November
211  * @G_DATE_DECEMBER: December
212  *
213  * Enumeration representing a month; values are #G_DATE_JANUARY,
214  * #G_DATE_FEBRUARY, etc. #G_DATE_BAD_MONTH is the invalid value.
215  */
216 
217 /**
218  * GDateYear:
219  *
220  * Integer representing a year; #G_DATE_BAD_YEAR is the invalid
221  * value. The year must be 1 or higher; negative (BC) years are not
222  * allowed. The year is represented with four digits.
223  */
224 
225 /**
226  * GDateWeekday:
227  * @G_DATE_BAD_WEEKDAY: invalid value
228  * @G_DATE_MONDAY: Monday
229  * @G_DATE_TUESDAY: Tuesday
230  * @G_DATE_WEDNESDAY: Wednesday
231  * @G_DATE_THURSDAY: Thursday
232  * @G_DATE_FRIDAY: Friday
233  * @G_DATE_SATURDAY: Saturday
234  * @G_DATE_SUNDAY: Sunday
235  *
236  * Enumeration representing a day of the week; #G_DATE_MONDAY,
237  * #G_DATE_TUESDAY, etc. #G_DATE_BAD_WEEKDAY is an invalid weekday.
238  */
239 
240 /**
241  * G_DATE_BAD_DAY:
242  *
243  * Represents an invalid #GDateDay.
244  */
245 
246 /**
247  * G_DATE_BAD_JULIAN:
248  *
249  * Represents an invalid Julian day number.
250  */
251 
252 /**
253  * G_DATE_BAD_YEAR:
254  *
255  * Represents an invalid year.
256  */
257 
258 /**
259  * g_date_new:
260  *
261  * Allocates a #GDate and initializes
262  * it to a sane state. The new date will
263  * be cleared (as if you'd called g_date_clear()) but invalid (it won't
264  * represent an existing day). Free the return value with g_date_free().
265  *
266  * Returns: a newly-allocated #GDate
267  */
268 GDate*
g_date_new(void)269 g_date_new (void)
270 {
271   GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
272 
273   return d;
274 }
275 
276 /**
277  * g_date_new_dmy:
278  * @day: day of the month
279  * @month: month of the year
280  * @year: year
281  *
282  * Like g_date_new(), but also sets the value of the date. Assuming the
283  * day-month-year triplet you pass in represents an existing day, the
284  * returned date will be valid.
285  *
286  * Returns: a newly-allocated #GDate initialized with @day, @month, and @year
287  */
288 GDate*
g_date_new_dmy(GDateDay day,GDateMonth m,GDateYear y)289 g_date_new_dmy (GDateDay   day,
290                 GDateMonth m,
291                 GDateYear  y)
292 {
293   GDate *d;
294   g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
295 
296   d = g_new (GDate, 1);
297 
298   d->julian = FALSE;
299   d->dmy    = TRUE;
300 
301   d->month = m;
302   d->day   = day;
303   d->year  = y;
304 
305   g_assert (g_date_valid (d));
306 
307   return d;
308 }
309 
310 /**
311  * g_date_new_julian:
312  * @julian_day: days since January 1, Year 1
313  *
314  * Like g_date_new(), but also sets the value of the date. Assuming the
315  * Julian day number you pass in is valid (greater than 0, less than an
316  * unreasonably large number), the returned date will be valid.
317  *
318  * Returns: a newly-allocated #GDate initialized with @julian_day
319  */
320 GDate*
g_date_new_julian(guint32 julian_day)321 g_date_new_julian (guint32 julian_day)
322 {
323   GDate *d;
324   g_return_val_if_fail (g_date_valid_julian (julian_day), NULL);
325 
326   d = g_new (GDate, 1);
327 
328   d->julian = TRUE;
329   d->dmy    = FALSE;
330 
331   d->julian_days = julian_day;
332 
333   g_assert (g_date_valid (d));
334 
335   return d;
336 }
337 
338 /**
339  * g_date_free:
340  * @date: a #GDate to free
341  *
342  * Frees a #GDate returned from g_date_new().
343  */
344 void
g_date_free(GDate * date)345 g_date_free (GDate *date)
346 {
347   g_return_if_fail (date != NULL);
348 
349   g_free (date);
350 }
351 
352 /**
353  * g_date_copy:
354  * @date: a #GDate to copy
355  *
356  * Copies a GDate to a newly-allocated GDate. If the input was invalid
357  * (as determined by g_date_valid()), the invalid state will be copied
358  * as is into the new object.
359  *
360  * Returns: (transfer full): a newly-allocated #GDate initialized from @date
361  *
362  * Since: 2.56
363  */
364 GDate *
g_date_copy(const GDate * date)365 g_date_copy (const GDate *date)
366 {
367   GDate *res;
368   g_return_val_if_fail (date != NULL, NULL);
369 
370   if (g_date_valid (date))
371     res = g_date_new_julian (g_date_get_julian (date));
372   else
373     {
374       res = g_date_new ();
375       *res = *date;
376     }
377 
378   return res;
379 }
380 
381 /**
382  * g_date_valid:
383  * @date: a #GDate to check
384  *
385  * Returns %TRUE if the #GDate represents an existing day. The date must not
386  * contain garbage; it should have been initialized with g_date_clear()
387  * if it wasn't allocated by one of the g_date_new() variants.
388  *
389  * Returns: Whether the date is valid
390  */
391 gboolean
g_date_valid(const GDate * d)392 g_date_valid (const GDate *d)
393 {
394   g_return_val_if_fail (d != NULL, FALSE);
395 
396   return (d->julian || d->dmy);
397 }
398 
399 static const guint8 days_in_months[2][13] =
400 {  /* error, jan feb mar apr may jun jul aug sep oct nov dec */
401   {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
402   {  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
403 };
404 
405 static const guint16 days_in_year[2][14] =
406 {  /* 0, jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec */
407   {  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
408   {  0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
409 };
410 
411 /**
412  * g_date_valid_month:
413  * @month: month
414  *
415  * Returns %TRUE if the month value is valid. The 12 #GDateMonth
416  * enumeration values are the only valid months.
417  *
418  * Returns: %TRUE if the month is valid
419  */
420 gboolean
g_date_valid_month(GDateMonth m)421 g_date_valid_month (GDateMonth m)
422 {
423   return ( (m > G_DATE_BAD_MONTH) && (m < 13) );
424 }
425 
426 /**
427  * g_date_valid_year:
428  * @year: year
429  *
430  * Returns %TRUE if the year is valid. Any year greater than 0 is valid,
431  * though there is a 16-bit limit to what #GDate will understand.
432  *
433  * Returns: %TRUE if the year is valid
434  */
435 gboolean
g_date_valid_year(GDateYear y)436 g_date_valid_year (GDateYear y)
437 {
438   return ( y > G_DATE_BAD_YEAR );
439 }
440 
441 /**
442  * g_date_valid_day:
443  * @day: day to check
444  *
445  * Returns %TRUE if the day of the month is valid (a day is valid if it's
446  * between 1 and 31 inclusive).
447  *
448  * Returns: %TRUE if the day is valid
449  */
450 
451 gboolean
g_date_valid_day(GDateDay d)452 g_date_valid_day (GDateDay d)
453 {
454   return ( (d > G_DATE_BAD_DAY) && (d < 32) );
455 }
456 
457 /**
458  * g_date_valid_weekday:
459  * @weekday: weekday
460  *
461  * Returns %TRUE if the weekday is valid. The seven #GDateWeekday enumeration
462  * values are the only valid weekdays.
463  *
464  * Returns: %TRUE if the weekday is valid
465  */
466 gboolean
g_date_valid_weekday(GDateWeekday w)467 g_date_valid_weekday (GDateWeekday w)
468 {
469   return ( (w > G_DATE_BAD_WEEKDAY) && (w < 8) );
470 }
471 
472 /**
473  * g_date_valid_julian:
474  * @julian_date: Julian day to check
475  *
476  * Returns %TRUE if the Julian day is valid. Anything greater than zero
477  * is basically a valid Julian, though there is a 32-bit limit.
478  *
479  * Returns: %TRUE if the Julian day is valid
480  */
481 gboolean
g_date_valid_julian(guint32 j)482 g_date_valid_julian (guint32 j)
483 {
484   return (j > G_DATE_BAD_JULIAN);
485 }
486 
487 /**
488  * g_date_valid_dmy:
489  * @day: day
490  * @month: month
491  * @year: year
492  *
493  * Returns %TRUE if the day-month-year triplet forms a valid, existing day
494  * in the range of days #GDate understands (Year 1 or later, no more than
495  * a few thousand years in the future).
496  *
497  * Returns: %TRUE if the date is a valid one
498  */
499 gboolean
g_date_valid_dmy(GDateDay d,GDateMonth m,GDateYear y)500 g_date_valid_dmy (GDateDay   d,
501                   GDateMonth m,
502 		  GDateYear  y)
503 {
504   /* No need to check the upper bound of @y, because #GDateYear is 16 bits wide,
505    * just like #GDate.year. */
506   return ( (m > G_DATE_BAD_MONTH) &&
507            (m < 13)               &&
508            (d > G_DATE_BAD_DAY)   &&
509            (y > G_DATE_BAD_YEAR)  &&   /* must check before using g_date_is_leap_year */
510            (d <=  (g_date_is_leap_year (y) ?
511 		   days_in_months[1][m] : days_in_months[0][m])) );
512 }
513 
514 
515 /* "Julian days" just means an absolute number of days, where Day 1 ==
516  *   Jan 1, Year 1
517  */
518 static void
g_date_update_julian(const GDate * const_d)519 g_date_update_julian (const GDate *const_d)
520 {
521   GDate *d = (GDate *) const_d;
522   GDateYear year;
523   gint idx;
524 
525   g_return_if_fail (d != NULL);
526   g_return_if_fail (d->dmy != 0);
527   g_return_if_fail (!d->julian);
528   g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
529 
530   /* What we actually do is: multiply years * 365 days in the year,
531    * add the number of years divided by 4, subtract the number of
532    * years divided by 100 and add the number of years divided by 400,
533    * which accounts for leap year stuff. Code from Steffen Beyer's
534    * DateCalc.
535    */
536 
537   year = d->year - 1; /* we know d->year > 0 since it's valid */
538 
539   d->julian_days = year * 365U;
540   d->julian_days += (year >>= 2); /* divide by 4 and add */
541   d->julian_days -= (year /= 25); /* divides original # years by 100 */
542   d->julian_days += year >> 2;    /* divides by 4, which divides original by 400 */
543 
544   idx = g_date_is_leap_year (d->year) ? 1 : 0;
545 
546   d->julian_days += days_in_year[idx][d->month] + d->day;
547 
548   g_return_if_fail (g_date_valid_julian (d->julian_days));
549 
550   d->julian = TRUE;
551 }
552 
553 static void
g_date_update_dmy(const GDate * const_d)554 g_date_update_dmy (const GDate *const_d)
555 {
556   GDate *d = (GDate *) const_d;
557   GDateYear y;
558   GDateMonth m;
559   GDateDay day;
560 
561   guint32 A, B, C, D, E, M;
562 
563   g_return_if_fail (d != NULL);
564   g_return_if_fail (d->julian);
565   g_return_if_fail (!d->dmy);
566   g_return_if_fail (g_date_valid_julian (d->julian_days));
567 
568   /* Formula taken from the Calendar FAQ; the formula was for the
569    *  Julian Period which starts on 1 January 4713 BC, so we add
570    *  1,721,425 to the number of days before doing the formula.
571    *
572    * I'm sure this can be simplified for our 1 January 1 AD period
573    * start, but I can't figure out how to unpack the formula.
574    */
575 
576   A = d->julian_days + 1721425 + 32045;
577   B = ( 4 *(A + 36524) )/ 146097 - 1;
578   C = A - (146097 * B)/4;
579   D = ( 4 * (C + 365) ) / 1461 - 1;
580   E = C - ((1461*D) / 4);
581   M = (5 * (E - 1) + 2)/153;
582 
583   m = M + 3 - (12*(M/10));
584   day = E - (153*M + 2)/5;
585   y = 100 * B + D - 4800 + (M/10);
586 
587 #ifdef G_ENABLE_DEBUG
588   if (!g_date_valid_dmy (day, m, y))
589     g_warning ("OOPS julian: %u  computed dmy: %u %u %u",
590 	       d->julian_days, day, m, y);
591 #endif
592 
593   d->month = m;
594   d->day   = day;
595   d->year  = y;
596 
597   d->dmy = TRUE;
598 }
599 
600 /**
601  * g_date_get_weekday:
602  * @date: a #GDate
603  *
604  * Returns the day of the week for a #GDate. The date must be valid.
605  *
606  * Returns: day of the week as a #GDateWeekday.
607  */
608 GDateWeekday
g_date_get_weekday(const GDate * d)609 g_date_get_weekday (const GDate *d)
610 {
611   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
612 
613   if (!d->julian)
614     g_date_update_julian (d);
615 
616   g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
617 
618   return ((d->julian_days - 1) % 7) + 1;
619 }
620 
621 /**
622  * g_date_get_month:
623  * @date: a #GDate to get the month from
624  *
625  * Returns the month of the year. The date must be valid.
626  *
627  * Returns: month of the year as a #GDateMonth
628  */
629 GDateMonth
g_date_get_month(const GDate * d)630 g_date_get_month (const GDate *d)
631 {
632   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
633 
634   if (!d->dmy)
635     g_date_update_dmy (d);
636 
637   g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
638 
639   return d->month;
640 }
641 
642 /**
643  * g_date_get_year:
644  * @date: a #GDate
645  *
646  * Returns the year of a #GDate. The date must be valid.
647  *
648  * Returns: year in which the date falls
649  */
650 GDateYear
g_date_get_year(const GDate * d)651 g_date_get_year (const GDate *d)
652 {
653   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
654 
655   if (!d->dmy)
656     g_date_update_dmy (d);
657 
658   g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);
659 
660   return d->year;
661 }
662 
663 /**
664  * g_date_get_day:
665  * @date: a #GDate to extract the day of the month from
666  *
667  * Returns the day of the month. The date must be valid.
668  *
669  * Returns: day of the month
670  */
671 GDateDay
g_date_get_day(const GDate * d)672 g_date_get_day (const GDate *d)
673 {
674   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
675 
676   if (!d->dmy)
677     g_date_update_dmy (d);
678 
679   g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);
680 
681   return d->day;
682 }
683 
684 /**
685  * g_date_get_julian:
686  * @date: a #GDate to extract the Julian day from
687  *
688  * Returns the Julian day or "serial number" of the #GDate. The
689  * Julian day is simply the number of days since January 1, Year 1; i.e.,
690  * January 1, Year 1 is Julian day 1; January 2, Year 1 is Julian day 2,
691  * etc. The date must be valid.
692  *
693  * Returns: Julian day
694  */
695 guint32
g_date_get_julian(const GDate * d)696 g_date_get_julian (const GDate *d)
697 {
698   g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
699 
700   if (!d->julian)
701     g_date_update_julian (d);
702 
703   g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN);
704 
705   return d->julian_days;
706 }
707 
708 /**
709  * g_date_get_day_of_year:
710  * @date: a #GDate to extract day of year from
711  *
712  * Returns the day of the year, where Jan 1 is the first day of the
713  * year. The date must be valid.
714  *
715  * Returns: day of the year
716  */
717 guint
g_date_get_day_of_year(const GDate * d)718 g_date_get_day_of_year (const GDate *d)
719 {
720   gint idx;
721 
722   g_return_val_if_fail (g_date_valid (d), 0);
723 
724   if (!d->dmy)
725     g_date_update_dmy (d);
726 
727   g_return_val_if_fail (d->dmy, 0);
728 
729   idx = g_date_is_leap_year (d->year) ? 1 : 0;
730 
731   return (days_in_year[idx][d->month] + d->day);
732 }
733 
734 /**
735  * g_date_get_monday_week_of_year:
736  * @date: a #GDate
737  *
738  * Returns the week of the year, where weeks are understood to start on
739  * Monday. If the date is before the first Monday of the year, return 0.
740  * The date must be valid.
741  *
742  * Returns: week of the year
743  */
744 guint
g_date_get_monday_week_of_year(const GDate * d)745 g_date_get_monday_week_of_year (const GDate *d)
746 {
747   GDateWeekday wd;
748   guint day;
749   GDate first;
750 
751   g_return_val_if_fail (g_date_valid (d), 0);
752 
753   if (!d->dmy)
754     g_date_update_dmy (d);
755 
756   g_return_val_if_fail (d->dmy, 0);
757 
758   g_date_clear (&first, 1);
759 
760   g_date_set_dmy (&first, 1, 1, d->year);
761 
762   wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
763   day = g_date_get_day_of_year (d) - 1;
764 
765   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
766 }
767 
768 /**
769  * g_date_get_sunday_week_of_year:
770  * @date: a #GDate
771  *
772  * Returns the week of the year during which this date falls, if
773  * weeks are understood to begin on Sunday. The date must be valid.
774  * Can return 0 if the day is before the first Sunday of the year.
775  *
776  * Returns: week number
777  */
778 guint
g_date_get_sunday_week_of_year(const GDate * d)779 g_date_get_sunday_week_of_year (const GDate *d)
780 {
781   GDateWeekday wd;
782   guint day;
783   GDate first;
784 
785   g_return_val_if_fail (g_date_valid (d), 0);
786 
787   if (!d->dmy)
788     g_date_update_dmy (d);
789 
790   g_return_val_if_fail (d->dmy, 0);
791 
792   g_date_clear (&first, 1);
793 
794   g_date_set_dmy (&first, 1, 1, d->year);
795 
796   wd = g_date_get_weekday (&first);
797   if (wd == 7) wd = 0; /* make Sunday day 0 */
798   day = g_date_get_day_of_year (d) - 1;
799 
800   return ((day + wd)/7U + (wd == 0 ? 1 : 0));
801 }
802 
803 /**
804  * g_date_get_iso8601_week_of_year:
805  * @date: a valid #GDate
806  *
807  * Returns the week of the year, where weeks are interpreted according
808  * to ISO 8601.
809  *
810  * Returns: ISO 8601 week number of the year.
811  *
812  * Since: 2.6
813  **/
814 guint
g_date_get_iso8601_week_of_year(const GDate * d)815 g_date_get_iso8601_week_of_year (const GDate *d)
816 {
817   guint j, d4, L, d1, w;
818 
819   g_return_val_if_fail (g_date_valid (d), 0);
820 
821   if (!d->julian)
822     g_date_update_julian (d);
823 
824   g_return_val_if_fail (d->julian, 0);
825 
826   /* Formula taken from the Calendar FAQ; the formula was for the
827    * Julian Period which starts on 1 January 4713 BC, so we add
828    * 1,721,425 to the number of days before doing the formula.
829    */
830   j  = d->julian_days + 1721425;
831   d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461;
832   L  = d4 / 1460;
833   d1 = ((d4 - L) % 365) + L;
834   w  = d1 / 7 + 1;
835 
836   return w;
837 }
838 
839 /**
840  * g_date_days_between:
841  * @date1: the first date
842  * @date2: the second date
843  *
844  * Computes the number of days between two dates.
845  * If @date2 is prior to @date1, the returned value is negative.
846  * Both dates must be valid.
847  *
848  * Returns: the number of days between @date1 and @date2
849  */
850 gint
g_date_days_between(const GDate * d1,const GDate * d2)851 g_date_days_between (const GDate *d1,
852 		     const GDate *d2)
853 {
854   g_return_val_if_fail (g_date_valid (d1), 0);
855   g_return_val_if_fail (g_date_valid (d2), 0);
856 
857   return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
858 }
859 
860 /**
861  * g_date_clear:
862  * @date: pointer to one or more dates to clear
863  * @n_dates: number of dates to clear
864  *
865  * Initializes one or more #GDate structs to a sane but invalid
866  * state. The cleared dates will not represent an existing date, but will
867  * not contain garbage. Useful to init a date declared on the stack.
868  * Validity can be tested with g_date_valid().
869  */
870 void
g_date_clear(GDate * d,guint ndates)871 g_date_clear (GDate *d, guint ndates)
872 {
873   g_return_if_fail (d != NULL);
874   g_return_if_fail (ndates != 0);
875 
876   memset (d, 0x0, ndates*sizeof (GDate));
877 }
878 
879 G_LOCK_DEFINE_STATIC (g_date_global);
880 
881 /* These are for the parser, output to the user should use *
882  * g_date_strftime () - this creates more never-freed memory to annoy
883  * all those memory debugger users. :-)
884  */
885 
886 static gchar *long_month_names[13] =
887 {
888   NULL,
889 };
890 
891 static gchar *long_month_names_alternative[13] =
892 {
893   NULL,
894 };
895 
896 static gchar *short_month_names[13] =
897 {
898   NULL,
899 };
900 
901 static gchar *short_month_names_alternative[13] =
902 {
903   NULL,
904 };
905 
906 /* This tells us if we need to update the parse info */
907 static gchar *current_locale = NULL;
908 
909 /* order of these in the current locale */
910 static GDateDMY dmy_order[3] =
911 {
912    G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
913 };
914 
915 /* Where to chop two-digit years: i.e., for the 1930 default, numbers
916  * 29 and below are counted as in the year 2000, numbers 30 and above
917  * are counted as in the year 1900.
918  */
919 
920 static const GDateYear twodigit_start_year = 1930;
921 
922 /* It is impossible to enter a year between 1 AD and 99 AD with this
923  * in effect.
924  */
925 static gboolean using_twodigit_years = FALSE;
926 
927 /* Adjustment of locale era to AD, non-zero means using locale era
928  */
929 static gint locale_era_adjust = 0;
930 
931 struct _GDateParseTokens {
932   gint num_ints;
933   gint n[3];
934   guint month;
935 };
936 
937 typedef struct _GDateParseTokens GDateParseTokens;
938 
939 static inline gboolean
update_month_match(gsize * longest,const gchar * haystack,const gchar * needle)940 update_month_match (gsize *longest,
941                     const gchar *haystack,
942                     const gchar *needle)
943 {
944   gsize length;
945 
946   if (needle == NULL)
947     return FALSE;
948 
949   length = strlen (needle);
950   if (*longest >= length)
951     return FALSE;
952 
953   if (strstr (haystack, needle) == NULL)
954     return FALSE;
955 
956   *longest = length;
957   return TRUE;
958 }
959 
960 #define NUM_LEN 10
961 
962 /* HOLDS: g_date_global_lock */
963 static void
g_date_fill_parse_tokens(const gchar * str,GDateParseTokens * pt)964 g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
965 {
966   gchar num[4][NUM_LEN+1];
967   gint i;
968   const guchar *s;
969 
970   /* We count 4, but store 3; so we can give an error
971    * if there are 4.
972    */
973   num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
974 
975   s = (const guchar *) str;
976   pt->num_ints = 0;
977   while (*s && pt->num_ints < 4)
978     {
979 
980       i = 0;
981       while (*s && g_ascii_isdigit (*s) && i < NUM_LEN)
982         {
983           num[pt->num_ints][i] = *s;
984           ++s;
985           ++i;
986         }
987 
988       if (i > 0)
989         {
990           num[pt->num_ints][i] = '\0';
991           ++(pt->num_ints);
992         }
993 
994       if (*s == '\0') break;
995 
996       ++s;
997     }
998 
999   pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
1000   pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
1001   pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
1002 
1003   pt->month = G_DATE_BAD_MONTH;
1004 
1005   if (pt->num_ints < 3)
1006     {
1007       gsize longest = 0;
1008       gchar *casefold;
1009       gchar *normalized;
1010 
1011       casefold = g_utf8_casefold (str, -1);
1012       normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1013       g_free (casefold);
1014 
1015       for (i = 1; i < 13; ++i)
1016         {
1017           /* Here month names may be in a genitive case if the language
1018            * grammatical rules require it.
1019            * Examples of how January may look in some languages:
1020            * Catalan: "de gener", Croatian: "siječnja", Polish: "stycznia",
1021            * Upper Sorbian: "januara".
1022            * Note that most of the languages can't or don't use the the
1023            * genitive case here so they use nominative everywhere.
1024            * For example, English always uses "January".
1025            */
1026           if (update_month_match (&longest, normalized, long_month_names[i]))
1027             pt->month = i;
1028 
1029           /* Here month names will be in a nominative case.
1030            * Examples of how January may look in some languages:
1031            * Catalan: "gener", Croatian: "Siječanj", Polish: "styczeń",
1032            * Upper Sorbian: "Januar".
1033            */
1034           if (update_month_match (&longest, normalized, long_month_names_alternative[i]))
1035             pt->month = i;
1036 
1037           /* Differences between abbreviated nominative and abbreviated
1038            * genitive month names are visible in very few languages but
1039            * let's handle them.
1040            */
1041           if (update_month_match (&longest, normalized, short_month_names[i]))
1042             pt->month = i;
1043 
1044           if (update_month_match (&longest, normalized, short_month_names_alternative[i]))
1045             pt->month = i;
1046         }
1047 
1048       g_free (normalized);
1049     }
1050 }
1051 
1052 /* HOLDS: g_date_global_lock */
1053 static void
g_date_prepare_to_parse(const gchar * str,GDateParseTokens * pt)1054 g_date_prepare_to_parse (const gchar      *str,
1055                          GDateParseTokens *pt)
1056 {
1057   const gchar *locale = setlocale (LC_TIME, NULL);
1058   gboolean recompute_localeinfo = FALSE;
1059   GDate d;
1060 
1061   g_return_if_fail (locale != NULL); /* should not happen */
1062 
1063   g_date_clear (&d, 1);              /* clear for scratch use */
1064 
1065   if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) )
1066     recompute_localeinfo = TRUE;  /* Uh, there used to be a reason for the temporary */
1067 
1068   if (recompute_localeinfo)
1069     {
1070       int i = 1;
1071       GDateParseTokens testpt;
1072       gchar buf[128];
1073 
1074       g_free (current_locale); /* still works if current_locale == NULL */
1075 
1076       current_locale = g_strdup (locale);
1077 
1078       short_month_names[0] = "Error";
1079       long_month_names[0] = "Error";
1080 
1081       while (i < 13)
1082         {
1083 	  gchar *casefold;
1084 
1085           g_date_set_dmy (&d, 1, i, 1976);
1086 
1087           g_return_if_fail (g_date_valid (&d));
1088 
1089           g_date_strftime (buf, 127, "%b", &d);
1090 
1091 	  casefold = g_utf8_casefold (buf, -1);
1092           g_free (short_month_names[i]);
1093           short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1094 	  g_free (casefold);
1095 
1096           g_date_strftime (buf, 127, "%B", &d);
1097 	  casefold = g_utf8_casefold (buf, -1);
1098           g_free (long_month_names[i]);
1099           long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1100 	  g_free (casefold);
1101 
1102           g_date_strftime (buf, 127, "%Ob", &d);
1103           casefold = g_utf8_casefold (buf, -1);
1104           g_free (short_month_names_alternative[i]);
1105           short_month_names_alternative[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1106           g_free (casefold);
1107 
1108           g_date_strftime (buf, 127, "%OB", &d);
1109           casefold = g_utf8_casefold (buf, -1);
1110           g_free (long_month_names_alternative[i]);
1111           long_month_names_alternative[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1112           g_free (casefold);
1113 
1114           ++i;
1115         }
1116 
1117       /* Determine DMY order */
1118 
1119       /* had to pick a random day - don't change this, some strftimes
1120        * are broken on some days, and this one is good so far. */
1121       g_date_set_dmy (&d, 4, 7, 1976);
1122 
1123       g_date_strftime (buf, 127, "%x", &d);
1124 
1125       g_date_fill_parse_tokens (buf, &testpt);
1126 
1127       using_twodigit_years = FALSE;
1128       locale_era_adjust = 0;
1129       dmy_order[0] = G_DATE_DAY;
1130       dmy_order[1] = G_DATE_MONTH;
1131       dmy_order[2] = G_DATE_YEAR;
1132 
1133       i = 0;
1134       while (i < testpt.num_ints)
1135         {
1136           switch (testpt.n[i])
1137             {
1138             case 7:
1139               dmy_order[i] = G_DATE_MONTH;
1140               break;
1141             case 4:
1142               dmy_order[i] = G_DATE_DAY;
1143               break;
1144             case 76:
1145               using_twodigit_years = TRUE; /* FALL THRU */
1146             case 1976:
1147               dmy_order[i] = G_DATE_YEAR;
1148               break;
1149             default:
1150               /* assume locale era */
1151               locale_era_adjust = 1976 - testpt.n[i];
1152               dmy_order[i] = G_DATE_YEAR;
1153               break;
1154             }
1155           ++i;
1156         }
1157 
1158 #if defined(G_ENABLE_DEBUG) && 0
1159       DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
1160       i = 1;
1161       while (i < 13)
1162         {
1163           DEBUG_MSG (("  %s   %s", long_month_names[i], short_month_names[i]));
1164           ++i;
1165         }
1166       DEBUG_MSG (("Alternative month names:"));
1167       i = 1;
1168       while (i < 13)
1169         {
1170           DEBUG_MSG (("  %s   %s", long_month_names_alternative[i], short_month_names_alternative[i]));
1171           ++i;
1172         }
1173       if (using_twodigit_years)
1174         {
1175 	  DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
1176         }
1177       {
1178         gchar *strings[3];
1179         i = 0;
1180         while (i < 3)
1181           {
1182             switch (dmy_order[i])
1183               {
1184               case G_DATE_MONTH:
1185                 strings[i] = "Month";
1186                 break;
1187               case G_DATE_YEAR:
1188                 strings[i] = "Year";
1189                 break;
1190               case G_DATE_DAY:
1191                 strings[i] = "Day";
1192                 break;
1193               default:
1194                 strings[i] = NULL;
1195                 break;
1196               }
1197             ++i;
1198           }
1199         DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
1200         DEBUG_MSG (("**Sample date in this locale: '%s'", buf));
1201       }
1202 #endif
1203     }
1204 
1205   g_date_fill_parse_tokens (str, pt);
1206 }
1207 
1208 /**
1209  * g_date_set_parse:
1210  * @date: a #GDate to fill in
1211  * @str: string to parse
1212  *
1213  * Parses a user-inputted string @str, and try to figure out what date it
1214  * represents, taking the [current locale][setlocale] into account. If the
1215  * string is successfully parsed, the date will be valid after the call.
1216  * Otherwise, it will be invalid. You should check using g_date_valid()
1217  * to see whether the parsing succeeded.
1218  *
1219  * This function is not appropriate for file formats and the like; it
1220  * isn't very precise, and its exact behavior varies with the locale.
1221  * It's intended to be a heuristic routine that guesses what the user
1222  * means by a given string (and it does work pretty well in that
1223  * capacity).
1224  */
1225 void
g_date_set_parse(GDate * d,const gchar * str)1226 g_date_set_parse (GDate       *d,
1227                   const gchar *str)
1228 {
1229   GDateParseTokens pt;
1230   guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
1231 
1232   g_return_if_fail (d != NULL);
1233 
1234   /* set invalid */
1235   g_date_clear (d, 1);
1236 
1237   G_LOCK (g_date_global);
1238 
1239   g_date_prepare_to_parse (str, &pt);
1240 
1241   DEBUG_MSG (("Found %d ints, '%d' '%d' '%d' and written out month %d",
1242 	      pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
1243 
1244 
1245   if (pt.num_ints == 4)
1246     {
1247       G_UNLOCK (g_date_global);
1248       return; /* presumably a typo; bail out. */
1249     }
1250 
1251   if (pt.num_ints > 1)
1252     {
1253       int i = 0;
1254       int j = 0;
1255 
1256       g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
1257 
1258       while (i < pt.num_ints && j < 3)
1259         {
1260           switch (dmy_order[j])
1261             {
1262             case G_DATE_MONTH:
1263 	    {
1264 	      if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
1265 		{
1266 		  m = pt.month;
1267 		  ++j;      /* skip months, but don't skip this number */
1268 		  continue;
1269 		}
1270 	      else
1271 		m = pt.n[i];
1272 	    }
1273 	    break;
1274             case G_DATE_DAY:
1275 	    {
1276 	      if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
1277 		{
1278 		  day = 1;
1279 		  ++j;      /* skip days, since we may have month/year */
1280 		  continue;
1281 		}
1282 	      day = pt.n[i];
1283 	    }
1284 	    break;
1285             case G_DATE_YEAR:
1286 	    {
1287 	      y  = pt.n[i];
1288 
1289 	      if (locale_era_adjust != 0)
1290 	        {
1291 		  y += locale_era_adjust;
1292 	        }
1293 	      else if (using_twodigit_years && y < 100)
1294 		{
1295 		  guint two     =  twodigit_start_year % 100;
1296 		  guint century = (twodigit_start_year / 100) * 100;
1297 
1298 		  if (y < two)
1299 		    century += 100;
1300 
1301 		  y += century;
1302 		}
1303 	    }
1304 	    break;
1305             default:
1306               break;
1307             }
1308 
1309           ++i;
1310           ++j;
1311         }
1312 
1313 
1314       if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
1315         {
1316           /* Try YYYY MM DD */
1317           y   = pt.n[0];
1318           m   = pt.n[1];
1319           day = pt.n[2];
1320 
1321           if (using_twodigit_years && y < 100)
1322             y = G_DATE_BAD_YEAR; /* avoids ambiguity */
1323         }
1324       else if (pt.num_ints == 2)
1325 	{
1326 	  if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
1327 	    m = pt.month;
1328 	}
1329     }
1330   else if (pt.num_ints == 1)
1331     {
1332       if (pt.month != G_DATE_BAD_MONTH)
1333         {
1334           /* Month name and year? */
1335           m    = pt.month;
1336           day  = 1;
1337           y = pt.n[0];
1338         }
1339       else
1340         {
1341           /* Try yyyymmdd and yymmdd */
1342 
1343           m   = (pt.n[0]/100) % 100;
1344           day = pt.n[0] % 100;
1345           y   = pt.n[0]/10000;
1346 
1347           /* FIXME move this into a separate function */
1348           if (using_twodigit_years && y < 100)
1349             {
1350               guint two     =  twodigit_start_year % 100;
1351               guint century = (twodigit_start_year / 100) * 100;
1352 
1353               if (y < two)
1354                 century += 100;
1355 
1356               y += century;
1357             }
1358         }
1359     }
1360 
1361   /* See if we got anything valid out of all this. */
1362   /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
1363   if (y < 8000 && g_date_valid_dmy (day, m, y))
1364     {
1365       d->month = m;
1366       d->day   = day;
1367       d->year  = y;
1368       d->dmy   = TRUE;
1369     }
1370 #ifdef G_ENABLE_DEBUG
1371   else
1372     {
1373       DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
1374     }
1375 #endif
1376   G_UNLOCK (g_date_global);
1377 }
1378 
1379 /**
1380  * g_date_set_time_t:
1381  * @date: a #GDate
1382  * @timet: time_t value to set
1383  *
1384  * Sets the value of a date to the date corresponding to a time
1385  * specified as a time_t. The time to date conversion is done using
1386  * the user's current timezone.
1387  *
1388  * To set the value of a date to the current day, you could write:
1389  * |[<!-- language="C" -->
1390  *  time_t now = time (NULL);
1391  *  if (now == (time_t) -1)
1392  *    // handle the error
1393  *  g_date_set_time_t (date, now);
1394  * ]|
1395  *
1396  * Since: 2.10
1397  */
1398 void
g_date_set_time_t(GDate * date,time_t timet)1399 g_date_set_time_t (GDate *date,
1400 		   time_t timet)
1401 {
1402   struct tm tm;
1403 
1404   g_return_if_fail (date != NULL);
1405 
1406 #ifdef HAVE_LOCALTIME_R
1407   localtime_r (&timet, &tm);
1408 #else
1409   {
1410     struct tm *ptm = localtime (&timet);
1411 
1412     if (ptm == NULL)
1413       {
1414 	/* Happens at least in Microsoft's C library if you pass a
1415 	 * negative time_t. Use 2000-01-01 as default date.
1416 	 */
1417 #ifndef G_DISABLE_CHECKS
1418 	g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
1419 #endif
1420 
1421 	tm.tm_mon = 0;
1422 	tm.tm_mday = 1;
1423 	tm.tm_year = 100;
1424       }
1425     else
1426       memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
1427   }
1428 #endif
1429 
1430   date->julian = FALSE;
1431 
1432   date->month = tm.tm_mon + 1;
1433   date->day   = tm.tm_mday;
1434   date->year  = tm.tm_year + 1900;
1435 
1436   g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year));
1437 
1438   date->dmy    = TRUE;
1439 }
1440 
1441 
1442 /**
1443  * g_date_set_time:
1444  * @date: a #GDate.
1445  * @time_: #GTime value to set.
1446  *
1447  * Sets the value of a date from a #GTime value.
1448  * The time to date conversion is done using the user's current timezone.
1449  *
1450  * Deprecated: 2.10: Use g_date_set_time_t() instead.
1451  */
1452 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1453 void
g_date_set_time(GDate * date,GTime time_)1454 g_date_set_time (GDate *date,
1455 		 GTime  time_)
1456 {
1457   g_date_set_time_t (date, (time_t) time_);
1458 }
1459 G_GNUC_END_IGNORE_DEPRECATIONS
1460 
1461 /**
1462  * g_date_set_time_val:
1463  * @date: a #GDate
1464  * @timeval: #GTimeVal value to set
1465  *
1466  * Sets the value of a date from a #GTimeVal value.  Note that the
1467  * @tv_usec member is ignored, because #GDate can't make use of the
1468  * additional precision.
1469  *
1470  * The time to date conversion is done using the user's current timezone.
1471  *
1472  * Since: 2.10
1473  * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use g_date_set_time_t()
1474  *    instead.
1475  */
1476 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1477 void
g_date_set_time_val(GDate * date,GTimeVal * timeval)1478 g_date_set_time_val (GDate    *date,
1479 		     GTimeVal *timeval)
1480 {
1481   g_date_set_time_t (date, (time_t) timeval->tv_sec);
1482 }
1483 G_GNUC_END_IGNORE_DEPRECATIONS
1484 
1485 /**
1486  * g_date_set_month:
1487  * @date: a #GDate
1488  * @month: month to set
1489  *
1490  * Sets the month of the year for a #GDate.  If the resulting
1491  * day-month-year triplet is invalid, the date will be invalid.
1492  */
1493 void
g_date_set_month(GDate * d,GDateMonth m)1494 g_date_set_month (GDate     *d,
1495                   GDateMonth m)
1496 {
1497   g_return_if_fail (d != NULL);
1498   g_return_if_fail (g_date_valid_month (m));
1499 
1500   if (d->julian && !d->dmy) g_date_update_dmy(d);
1501   d->julian = FALSE;
1502 
1503   d->month = m;
1504 
1505   if (g_date_valid_dmy (d->day, d->month, d->year))
1506     d->dmy = TRUE;
1507   else
1508     d->dmy = FALSE;
1509 }
1510 
1511 /**
1512  * g_date_set_day:
1513  * @date: a #GDate
1514  * @day: day to set
1515  *
1516  * Sets the day of the month for a #GDate. If the resulting
1517  * day-month-year triplet is invalid, the date will be invalid.
1518  */
1519 void
g_date_set_day(GDate * d,GDateDay day)1520 g_date_set_day (GDate    *d,
1521                 GDateDay  day)
1522 {
1523   g_return_if_fail (d != NULL);
1524   g_return_if_fail (g_date_valid_day (day));
1525 
1526   if (d->julian && !d->dmy) g_date_update_dmy(d);
1527   d->julian = FALSE;
1528 
1529   d->day = day;
1530 
1531   if (g_date_valid_dmy (d->day, d->month, d->year))
1532     d->dmy = TRUE;
1533   else
1534     d->dmy = FALSE;
1535 }
1536 
1537 /**
1538  * g_date_set_year:
1539  * @date: a #GDate
1540  * @year: year to set
1541  *
1542  * Sets the year for a #GDate. If the resulting day-month-year
1543  * triplet is invalid, the date will be invalid.
1544  */
1545 void
g_date_set_year(GDate * d,GDateYear y)1546 g_date_set_year (GDate     *d,
1547                  GDateYear  y)
1548 {
1549   g_return_if_fail (d != NULL);
1550   g_return_if_fail (g_date_valid_year (y));
1551 
1552   if (d->julian && !d->dmy) g_date_update_dmy(d);
1553   d->julian = FALSE;
1554 
1555   d->year = y;
1556 
1557   if (g_date_valid_dmy (d->day, d->month, d->year))
1558     d->dmy = TRUE;
1559   else
1560     d->dmy = FALSE;
1561 }
1562 
1563 /**
1564  * g_date_set_dmy:
1565  * @date: a #GDate
1566  * @day: day
1567  * @month: month
1568  * @y: year
1569  *
1570  * Sets the value of a #GDate from a day, month, and year.
1571  * The day-month-year triplet must be valid; if you aren't
1572  * sure it is, call g_date_valid_dmy() to check before you
1573  * set it.
1574  */
1575 void
g_date_set_dmy(GDate * d,GDateDay day,GDateMonth m,GDateYear y)1576 g_date_set_dmy (GDate      *d,
1577                 GDateDay    day,
1578                 GDateMonth  m,
1579                 GDateYear   y)
1580 {
1581   g_return_if_fail (d != NULL);
1582   g_return_if_fail (g_date_valid_dmy (day, m, y));
1583 
1584   d->julian = FALSE;
1585 
1586   d->month = m;
1587   d->day   = day;
1588   d->year  = y;
1589 
1590   d->dmy = TRUE;
1591 }
1592 
1593 /**
1594  * g_date_set_julian:
1595  * @date: a #GDate
1596  * @julian_date: Julian day number (days since January 1, Year 1)
1597  *
1598  * Sets the value of a #GDate from a Julian day number.
1599  */
1600 void
g_date_set_julian(GDate * d,guint32 j)1601 g_date_set_julian (GDate   *d,
1602                    guint32  j)
1603 {
1604   g_return_if_fail (d != NULL);
1605   g_return_if_fail (g_date_valid_julian (j));
1606 
1607   d->julian_days = j;
1608   d->julian = TRUE;
1609   d->dmy = FALSE;
1610 }
1611 
1612 /**
1613  * g_date_is_first_of_month:
1614  * @date: a #GDate to check
1615  *
1616  * Returns %TRUE if the date is on the first of a month.
1617  * The date must be valid.
1618  *
1619  * Returns: %TRUE if the date is the first of the month
1620  */
1621 gboolean
g_date_is_first_of_month(const GDate * d)1622 g_date_is_first_of_month (const GDate *d)
1623 {
1624   g_return_val_if_fail (g_date_valid (d), FALSE);
1625 
1626   if (!d->dmy)
1627     g_date_update_dmy (d);
1628 
1629   g_return_val_if_fail (d->dmy, FALSE);
1630 
1631   if (d->day == 1) return TRUE;
1632   else return FALSE;
1633 }
1634 
1635 /**
1636  * g_date_is_last_of_month:
1637  * @date: a #GDate to check
1638  *
1639  * Returns %TRUE if the date is the last day of the month.
1640  * The date must be valid.
1641  *
1642  * Returns: %TRUE if the date is the last day of the month
1643  */
1644 gboolean
g_date_is_last_of_month(const GDate * d)1645 g_date_is_last_of_month (const GDate *d)
1646 {
1647   gint idx;
1648 
1649   g_return_val_if_fail (g_date_valid (d), FALSE);
1650 
1651   if (!d->dmy)
1652     g_date_update_dmy (d);
1653 
1654   g_return_val_if_fail (d->dmy, FALSE);
1655 
1656   idx = g_date_is_leap_year (d->year) ? 1 : 0;
1657 
1658   if (d->day == days_in_months[idx][d->month]) return TRUE;
1659   else return FALSE;
1660 }
1661 
1662 /**
1663  * g_date_add_days:
1664  * @date: a #GDate to increment
1665  * @n_days: number of days to move the date forward
1666  *
1667  * Increments a date some number of days.
1668  * To move forward by weeks, add weeks*7 days.
1669  * The date must be valid.
1670  */
1671 void
g_date_add_days(GDate * d,guint ndays)1672 g_date_add_days (GDate *d,
1673                  guint  ndays)
1674 {
1675   g_return_if_fail (g_date_valid (d));
1676 
1677   if (!d->julian)
1678     g_date_update_julian (d);
1679 
1680   g_return_if_fail (d->julian);
1681   g_return_if_fail (ndays <= G_MAXUINT32 - d->julian_days);
1682 
1683   d->julian_days += ndays;
1684   d->dmy = FALSE;
1685 }
1686 
1687 /**
1688  * g_date_subtract_days:
1689  * @date: a #GDate to decrement
1690  * @n_days: number of days to move
1691  *
1692  * Moves a date some number of days into the past.
1693  * To move by weeks, just move by weeks*7 days.
1694  * The date must be valid.
1695  */
1696 void
g_date_subtract_days(GDate * d,guint ndays)1697 g_date_subtract_days (GDate *d,
1698                       guint  ndays)
1699 {
1700   g_return_if_fail (g_date_valid (d));
1701 
1702   if (!d->julian)
1703     g_date_update_julian (d);
1704 
1705   g_return_if_fail (d->julian);
1706   g_return_if_fail (d->julian_days > ndays);
1707 
1708   d->julian_days -= ndays;
1709   d->dmy = FALSE;
1710 }
1711 
1712 /**
1713  * g_date_add_months:
1714  * @date: a #GDate to increment
1715  * @n_months: number of months to move forward
1716  *
1717  * Increments a date by some number of months.
1718  * If the day of the month is greater than 28,
1719  * this routine may change the day of the month
1720  * (because the destination month may not have
1721  * the current day in it). The date must be valid.
1722  */
1723 void
g_date_add_months(GDate * d,guint nmonths)1724 g_date_add_months (GDate *d,
1725                    guint  nmonths)
1726 {
1727   guint years, months;
1728   gint idx;
1729 
1730   g_return_if_fail (g_date_valid (d));
1731 
1732   if (!d->dmy)
1733     g_date_update_dmy (d);
1734 
1735   g_return_if_fail (d->dmy != 0);
1736   g_return_if_fail (nmonths <= G_MAXUINT - (d->month - 1));
1737 
1738   nmonths += d->month - 1;
1739 
1740   years  = nmonths/12;
1741   months = nmonths%12;
1742 
1743   g_return_if_fail (years <= (guint) (G_MAXUINT16 - d->year));
1744 
1745   d->month = months + 1;
1746   d->year  += years;
1747 
1748   idx = g_date_is_leap_year (d->year) ? 1 : 0;
1749 
1750   if (d->day > days_in_months[idx][d->month])
1751     d->day = days_in_months[idx][d->month];
1752 
1753   d->julian = FALSE;
1754 
1755   g_return_if_fail (g_date_valid (d));
1756 }
1757 
1758 /**
1759  * g_date_subtract_months:
1760  * @date: a #GDate to decrement
1761  * @n_months: number of months to move
1762  *
1763  * Moves a date some number of months into the past.
1764  * If the current day of the month doesn't exist in
1765  * the destination month, the day of the month
1766  * may change. The date must be valid.
1767  */
1768 void
g_date_subtract_months(GDate * d,guint nmonths)1769 g_date_subtract_months (GDate *d,
1770                         guint  nmonths)
1771 {
1772   guint years, months;
1773   gint idx;
1774 
1775   g_return_if_fail (g_date_valid (d));
1776 
1777   if (!d->dmy)
1778     g_date_update_dmy (d);
1779 
1780   g_return_if_fail (d->dmy != 0);
1781 
1782   years  = nmonths/12;
1783   months = nmonths%12;
1784 
1785   g_return_if_fail (d->year > years);
1786 
1787   d->year  -= years;
1788 
1789   if (d->month > months) d->month -= months;
1790   else
1791     {
1792       months -= d->month;
1793       d->month = 12 - months;
1794       d->year -= 1;
1795     }
1796 
1797   idx = g_date_is_leap_year (d->year) ? 1 : 0;
1798 
1799   if (d->day > days_in_months[idx][d->month])
1800     d->day = days_in_months[idx][d->month];
1801 
1802   d->julian = FALSE;
1803 
1804   g_return_if_fail (g_date_valid (d));
1805 }
1806 
1807 /**
1808  * g_date_add_years:
1809  * @date: a #GDate to increment
1810  * @n_years: number of years to move forward
1811  *
1812  * Increments a date by some number of years.
1813  * If the date is February 29, and the destination
1814  * year is not a leap year, the date will be changed
1815  * to February 28. The date must be valid.
1816  */
1817 void
g_date_add_years(GDate * d,guint nyears)1818 g_date_add_years (GDate *d,
1819                   guint  nyears)
1820 {
1821   g_return_if_fail (g_date_valid (d));
1822 
1823   if (!d->dmy)
1824     g_date_update_dmy (d);
1825 
1826   g_return_if_fail (d->dmy != 0);
1827   g_return_if_fail (nyears <= (guint) (G_MAXUINT16 - d->year));
1828 
1829   d->year += nyears;
1830 
1831   if (d->month == 2 && d->day == 29)
1832     {
1833       if (!g_date_is_leap_year (d->year))
1834         d->day = 28;
1835     }
1836 
1837   d->julian = FALSE;
1838 }
1839 
1840 /**
1841  * g_date_subtract_years:
1842  * @date: a #GDate to decrement
1843  * @n_years: number of years to move
1844  *
1845  * Moves a date some number of years into the past.
1846  * If the current day doesn't exist in the destination
1847  * year (i.e. it's February 29 and you move to a non-leap-year)
1848  * then the day is changed to February 29. The date
1849  * must be valid.
1850  */
1851 void
g_date_subtract_years(GDate * d,guint nyears)1852 g_date_subtract_years (GDate *d,
1853                        guint  nyears)
1854 {
1855   g_return_if_fail (g_date_valid (d));
1856 
1857   if (!d->dmy)
1858     g_date_update_dmy (d);
1859 
1860   g_return_if_fail (d->dmy != 0);
1861   g_return_if_fail (d->year > nyears);
1862 
1863   d->year -= nyears;
1864 
1865   if (d->month == 2 && d->day == 29)
1866     {
1867       if (!g_date_is_leap_year (d->year))
1868         d->day = 28;
1869     }
1870 
1871   d->julian = FALSE;
1872 }
1873 
1874 /**
1875  * g_date_is_leap_year:
1876  * @year: year to check
1877  *
1878  * Returns %TRUE if the year is a leap year.
1879  *
1880  * For the purposes of this function, leap year is every year
1881  * divisible by 4 unless that year is divisible by 100. If it
1882  * is divisible by 100 it would be a leap year only if that year
1883  * is also divisible by 400.
1884  *
1885  * Returns: %TRUE if the year is a leap year
1886  */
1887 gboolean
g_date_is_leap_year(GDateYear year)1888 g_date_is_leap_year (GDateYear year)
1889 {
1890   g_return_val_if_fail (g_date_valid_year (year), FALSE);
1891 
1892   return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
1893            (year % 400) == 0 );
1894 }
1895 
1896 /**
1897  * g_date_get_days_in_month:
1898  * @month: month
1899  * @year: year
1900  *
1901  * Returns the number of days in a month, taking leap
1902  * years into account.
1903  *
1904  * Returns: number of days in @month during the @year
1905  */
1906 guint8
g_date_get_days_in_month(GDateMonth month,GDateYear year)1907 g_date_get_days_in_month (GDateMonth month,
1908                           GDateYear  year)
1909 {
1910   gint idx;
1911 
1912   g_return_val_if_fail (g_date_valid_year (year), 0);
1913   g_return_val_if_fail (g_date_valid_month (month), 0);
1914 
1915   idx = g_date_is_leap_year (year) ? 1 : 0;
1916 
1917   return days_in_months[idx][month];
1918 }
1919 
1920 /**
1921  * g_date_get_monday_weeks_in_year:
1922  * @year: a year
1923  *
1924  * Returns the number of weeks in the year, where weeks
1925  * are taken to start on Monday. Will be 52 or 53. The
1926  * date must be valid. (Years always have 52 7-day periods,
1927  * plus 1 or 2 extra days depending on whether it's a leap
1928  * year. This function is basically telling you how many
1929  * Mondays are in the year, i.e. there are 53 Mondays if
1930  * one of the extra days happens to be a Monday.)
1931  *
1932  * Returns: number of Mondays in the year
1933  */
1934 guint8
g_date_get_monday_weeks_in_year(GDateYear year)1935 g_date_get_monday_weeks_in_year (GDateYear year)
1936 {
1937   GDate d;
1938 
1939   g_return_val_if_fail (g_date_valid_year (year), 0);
1940 
1941   g_date_clear (&d, 1);
1942   g_date_set_dmy (&d, 1, 1, year);
1943   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1944   g_date_set_dmy (&d, 31, 12, year);
1945   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1946   if (g_date_is_leap_year (year))
1947     {
1948       g_date_set_dmy (&d, 2, 1, year);
1949       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1950       g_date_set_dmy (&d, 30, 12, year);
1951       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1952     }
1953   return 52;
1954 }
1955 
1956 /**
1957  * g_date_get_sunday_weeks_in_year:
1958  * @year: year to count weeks in
1959  *
1960  * Returns the number of weeks in the year, where weeks
1961  * are taken to start on Sunday. Will be 52 or 53. The
1962  * date must be valid. (Years always have 52 7-day periods,
1963  * plus 1 or 2 extra days depending on whether it's a leap
1964  * year. This function is basically telling you how many
1965  * Sundays are in the year, i.e. there are 53 Sundays if
1966  * one of the extra days happens to be a Sunday.)
1967  *
1968  * Returns: the number of weeks in @year
1969  */
1970 guint8
g_date_get_sunday_weeks_in_year(GDateYear year)1971 g_date_get_sunday_weeks_in_year (GDateYear year)
1972 {
1973   GDate d;
1974 
1975   g_return_val_if_fail (g_date_valid_year (year), 0);
1976 
1977   g_date_clear (&d, 1);
1978   g_date_set_dmy (&d, 1, 1, year);
1979   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1980   g_date_set_dmy (&d, 31, 12, year);
1981   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1982   if (g_date_is_leap_year (year))
1983     {
1984       g_date_set_dmy (&d, 2, 1, year);
1985       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1986       g_date_set_dmy (&d, 30, 12, year);
1987       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1988     }
1989   return 52;
1990 }
1991 
1992 /**
1993  * g_date_compare:
1994  * @lhs: first date to compare
1995  * @rhs: second date to compare
1996  *
1997  * qsort()-style comparison function for dates.
1998  * Both dates must be valid.
1999  *
2000  * Returns: 0 for equal, less than zero if @lhs is less than @rhs,
2001  *     greater than zero if @lhs is greater than @rhs
2002  */
2003 gint
g_date_compare(const GDate * lhs,const GDate * rhs)2004 g_date_compare (const GDate *lhs,
2005                 const GDate *rhs)
2006 {
2007   g_return_val_if_fail (lhs != NULL, 0);
2008   g_return_val_if_fail (rhs != NULL, 0);
2009   g_return_val_if_fail (g_date_valid (lhs), 0);
2010   g_return_val_if_fail (g_date_valid (rhs), 0);
2011 
2012   /* Remember the self-comparison case! I think it works right now. */
2013 
2014   while (TRUE)
2015     {
2016       if (lhs->julian && rhs->julian)
2017         {
2018           if (lhs->julian_days < rhs->julian_days) return -1;
2019           else if (lhs->julian_days > rhs->julian_days) return 1;
2020           else                                          return 0;
2021         }
2022       else if (lhs->dmy && rhs->dmy)
2023         {
2024           if (lhs->year < rhs->year)               return -1;
2025           else if (lhs->year > rhs->year)               return 1;
2026           else
2027             {
2028               if (lhs->month < rhs->month)         return -1;
2029               else if (lhs->month > rhs->month)         return 1;
2030               else
2031                 {
2032                   if (lhs->day < rhs->day)              return -1;
2033                   else if (lhs->day > rhs->day)              return 1;
2034                   else                                       return 0;
2035                 }
2036 
2037             }
2038 
2039         }
2040       else
2041         {
2042           if (!lhs->julian) g_date_update_julian (lhs);
2043           if (!rhs->julian) g_date_update_julian (rhs);
2044           g_return_val_if_fail (lhs->julian, 0);
2045           g_return_val_if_fail (rhs->julian, 0);
2046         }
2047 
2048     }
2049   return 0; /* warnings */
2050 }
2051 
2052 /**
2053  * g_date_to_struct_tm:
2054  * @date: a #GDate to set the struct tm from
2055  * @tm: (not nullable): struct tm to fill
2056  *
2057  * Fills in the date-related bits of a struct tm using the @date value.
2058  * Initializes the non-date parts with something sane but meaningless.
2059  */
2060 void
g_date_to_struct_tm(const GDate * d,struct tm * tm)2061 g_date_to_struct_tm (const GDate *d,
2062                      struct tm   *tm)
2063 {
2064   GDateWeekday day;
2065 
2066   g_return_if_fail (g_date_valid (d));
2067   g_return_if_fail (tm != NULL);
2068 
2069   if (!d->dmy)
2070     g_date_update_dmy (d);
2071 
2072   g_return_if_fail (d->dmy != 0);
2073 
2074   /* zero all the irrelevant fields to be sure they're valid */
2075 
2076   /* On Linux and maybe other systems, there are weird non-POSIX
2077    * fields on the end of struct tm that choke strftime if they
2078    * contain garbage.  So we need to 0 the entire struct, not just the
2079    * fields we know to exist.
2080    */
2081 
2082   memset (tm, 0x0, sizeof (struct tm));
2083 
2084   tm->tm_mday = d->day;
2085   tm->tm_mon  = d->month - 1; /* 0-11 goes in tm */
2086   tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
2087 
2088   day = g_date_get_weekday (d);
2089   if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
2090 
2091   tm->tm_wday = (int)day;
2092 
2093   tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
2094   tm->tm_isdst = -1; /* -1 means "information not available" */
2095 }
2096 
2097 /**
2098  * g_date_clamp:
2099  * @date: a #GDate to clamp
2100  * @min_date: minimum accepted value for @date
2101  * @max_date: maximum accepted value for @date
2102  *
2103  * If @date is prior to @min_date, sets @date equal to @min_date.
2104  * If @date falls after @max_date, sets @date equal to @max_date.
2105  * Otherwise, @date is unchanged.
2106  * Either of @min_date and @max_date may be %NULL.
2107  * All non-%NULL dates must be valid.
2108  */
2109 void
g_date_clamp(GDate * date,const GDate * min_date,const GDate * max_date)2110 g_date_clamp (GDate       *date,
2111 	      const GDate *min_date,
2112 	      const GDate *max_date)
2113 {
2114   g_return_if_fail (g_date_valid (date));
2115 
2116   if (min_date != NULL)
2117     g_return_if_fail (g_date_valid (min_date));
2118 
2119   if (max_date != NULL)
2120     g_return_if_fail (g_date_valid (max_date));
2121 
2122   if (min_date != NULL && max_date != NULL)
2123     g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
2124 
2125   if (min_date && g_date_compare (date, min_date) < 0)
2126     *date = *min_date;
2127 
2128   if (max_date && g_date_compare (max_date, date) < 0)
2129     *date = *max_date;
2130 }
2131 
2132 /**
2133  * g_date_order:
2134  * @date1: the first date
2135  * @date2: the second date
2136  *
2137  * Checks if @date1 is less than or equal to @date2,
2138  * and swap the values if this is not the case.
2139  */
2140 void
g_date_order(GDate * date1,GDate * date2)2141 g_date_order (GDate *date1,
2142               GDate *date2)
2143 {
2144   g_return_if_fail (g_date_valid (date1));
2145   g_return_if_fail (g_date_valid (date2));
2146 
2147   if (g_date_compare (date1, date2) > 0)
2148     {
2149       GDate tmp = *date1;
2150       *date1 = *date2;
2151       *date2 = tmp;
2152     }
2153 }
2154 
2155 #ifdef G_OS_WIN32
2156 static void
append_month_name(GArray * result,LCID lcid,SYSTEMTIME * systemtime,gboolean abbreviated,gboolean alternative)2157 append_month_name (GArray     *result,
2158 		   LCID        lcid,
2159 		   SYSTEMTIME *systemtime,
2160 		   gboolean    abbreviated,
2161 		   gboolean    alternative)
2162 {
2163   int n;
2164   WORD base;
2165   LPCWSTR lpFormat;
2166 
2167   if (alternative)
2168     {
2169       base = abbreviated ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1;
2170       n = GetLocaleInfoW (lcid, base + systemtime->wMonth - 1, NULL, 0);
2171       g_array_set_size (result, result->len + n);
2172       GetLocaleInfoW (lcid, base + systemtime->wMonth - 1,
2173 		      ((wchar_t *) result->data) + result->len - n, n);
2174       g_array_set_size (result, result->len - 1);
2175     }
2176   else
2177     {
2178       /* According to MSDN, this is the correct method to obtain
2179        * the form of the month name used when formatting a full
2180        * date; it must be a genitive case in some languages.
2181        */
2182       lpFormat = abbreviated ? L"ddMMM" : L"ddMMMM";
2183       n = GetDateFormatW (lcid, 0, systemtime, lpFormat, NULL, 0);
2184       g_array_set_size (result, result->len + n);
2185       GetDateFormatW (lcid, 0, systemtime, lpFormat,
2186 		      ((wchar_t *) result->data) + result->len - n, n);
2187       /* We have obtained a day number as two digits and the month name.
2188        * Now let's get rid of those two digits: overwrite them with the
2189        * month name.
2190        */
2191       memmove (((wchar_t *) result->data) + result->len - n,
2192 	       ((wchar_t *) result->data) + result->len - n + 2,
2193 	       (n - 2) * sizeof (wchar_t));
2194       g_array_set_size (result, result->len - 3);
2195     }
2196 }
2197 
2198 static gsize
win32_strftime_helper(const GDate * d,const gchar * format,const struct tm * tm,gchar * s,gsize slen)2199 win32_strftime_helper (const GDate     *d,
2200 		       const gchar     *format,
2201 		       const struct tm *tm,
2202 		       gchar           *s,
2203 		       gsize	        slen)
2204 {
2205   SYSTEMTIME systemtime;
2206   TIME_ZONE_INFORMATION tzinfo;
2207   LCID lcid;
2208   int n, k;
2209   GArray *result;
2210   const gchar *p;
2211   gunichar c, modifier;
2212   const wchar_t digits[] = L"0123456789";
2213   gchar *convbuf;
2214   glong convlen = 0;
2215   gsize retval;
2216 
2217   systemtime.wYear = tm->tm_year + 1900;
2218   systemtime.wMonth = tm->tm_mon + 1;
2219   systemtime.wDayOfWeek = tm->tm_wday;
2220   systemtime.wDay = tm->tm_mday;
2221   systemtime.wHour = tm->tm_hour;
2222   systemtime.wMinute = tm->tm_min;
2223   systemtime.wSecond = tm->tm_sec;
2224   systemtime.wMilliseconds = 0;
2225 
2226   lcid = GetThreadLocale ();
2227   result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2));
2228 
2229   p = format;
2230   while (*p)
2231     {
2232       c = g_utf8_get_char (p);
2233       if (c == '%')
2234 	{
2235 	  p = g_utf8_next_char (p);
2236 	  if (!*p)
2237 	    {
2238 	      s[0] = '\0';
2239 	      g_array_free (result, TRUE);
2240 
2241 	      return 0;
2242 	    }
2243 
2244 	  modifier = '\0';
2245 	  c = g_utf8_get_char (p);
2246 	  if (c == 'E' || c == 'O')
2247 	    {
2248 	      /* "%OB", "%Ob", and "%Oh" are supported, ignore other modified
2249 	       * conversion specifiers for now.
2250 	       */
2251 	      modifier = c;
2252 	      p = g_utf8_next_char (p);
2253 	      if (!*p)
2254 		{
2255 		  s[0] = '\0';
2256 		  g_array_free (result, TRUE);
2257 
2258 		  return 0;
2259 		}
2260 
2261 	      c = g_utf8_get_char (p);
2262 	    }
2263 
2264 	  switch (c)
2265 	    {
2266 	    case 'a':
2267 	      if (systemtime.wDayOfWeek == 0)
2268 		k = 6;
2269 	      else
2270 		k = systemtime.wDayOfWeek - 1;
2271 	      n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0);
2272 	      g_array_set_size (result, result->len + n);
2273 	      GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
2274 	      g_array_set_size (result, result->len - 1);
2275 	      break;
2276 	    case 'A':
2277 	      if (systemtime.wDayOfWeek == 0)
2278 		k = 6;
2279 	      else
2280 		k = systemtime.wDayOfWeek - 1;
2281 	      n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0);
2282 	      g_array_set_size (result, result->len + n);
2283 	      GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
2284 	      g_array_set_size (result, result->len - 1);
2285 	      break;
2286 	    case 'b':
2287 	    case 'h':
2288 	      append_month_name (result, lcid, &systemtime, TRUE,
2289 				 modifier == 'O');
2290 	      break;
2291 	    case 'B':
2292 	      append_month_name (result, lcid, &systemtime, FALSE,
2293 				 modifier == 'O');
2294 	      break;
2295 	    case 'c':
2296 	      n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2297 	      if (n > 0)
2298 		{
2299 		  g_array_set_size (result, result->len + n);
2300 		  GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2301 		  g_array_set_size (result, result->len - 1);
2302 		}
2303 	      g_array_append_vals (result, L" ", 1);
2304 	      n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2305 	      if (n > 0)
2306 		{
2307 		  g_array_set_size (result, result->len + n);
2308 		  GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2309 		  g_array_set_size (result, result->len - 1);
2310 		}
2311 	      break;
2312 	    case 'C':
2313 	      g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
2314 	      g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1);
2315 	      break;
2316 	    case 'd':
2317 	      g_array_append_vals (result, digits + systemtime.wDay/10, 1);
2318 	      g_array_append_vals (result, digits + systemtime.wDay%10, 1);
2319 	      break;
2320 	    case 'D':
2321 	      g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
2322 	      g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
2323 	      g_array_append_vals (result, L"/", 1);
2324 	      g_array_append_vals (result, digits + systemtime.wDay/10, 1);
2325 	      g_array_append_vals (result, digits + systemtime.wDay%10, 1);
2326 	      g_array_append_vals (result, L"/", 1);
2327 	      g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
2328 	      g_array_append_vals (result, digits + systemtime.wYear%10, 1);
2329 	      break;
2330 	    case 'e':
2331 	      if (systemtime.wDay >= 10)
2332 		g_array_append_vals (result, digits + systemtime.wDay/10, 1);
2333 	      else
2334 		g_array_append_vals (result, L" ", 1);
2335 	      g_array_append_vals (result, digits + systemtime.wDay%10, 1);
2336 	      break;
2337 
2338 	      /* A GDate has no time fields, so for now we can
2339 	       * hardcode all time conversions into zeros (or 12 for
2340 	       * %I). The alternative code snippets in the #else
2341 	       * branches are here ready to be taken into use when
2342 	       * needed by a g_strftime() or g_date_and_time_format()
2343 	       * or whatever.
2344 	       */
2345 	    case 'H':
2346 #if 1
2347 	      g_array_append_vals (result, L"00", 2);
2348 #else
2349 	      g_array_append_vals (result, digits + systemtime.wHour/10, 1);
2350 	      g_array_append_vals (result, digits + systemtime.wHour%10, 1);
2351 #endif
2352 	      break;
2353 	    case 'I':
2354 #if 1
2355 	      g_array_append_vals (result, L"12", 2);
2356 #else
2357 	      if (systemtime.wHour == 0)
2358 		g_array_append_vals (result, L"12", 2);
2359 	      else
2360 		{
2361 		  g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
2362 		  g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
2363 		}
2364 #endif
2365 	      break;
2366 	    case  'j':
2367 	      g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1);
2368 	      g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1);
2369 	      g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1);
2370 	      break;
2371 	    case 'm':
2372 	      g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
2373 	      g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
2374 	      break;
2375 	    case 'M':
2376 #if 1
2377 	      g_array_append_vals (result, L"00", 2);
2378 #else
2379 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2380 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2381 #endif
2382 	      break;
2383 	    case 'n':
2384 	      g_array_append_vals (result, L"\n", 1);
2385 	      break;
2386 	    case 'p':
2387 	      n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
2388 	      if (n > 0)
2389 		{
2390 		  g_array_set_size (result, result->len + n);
2391 		  GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
2392 		  g_array_set_size (result, result->len - 1);
2393 		}
2394 	      break;
2395 	    case 'r':
2396 	      /* This is a rather odd format. Hard to say what to do.
2397 	       * Let's always use the POSIX %I:%M:%S %p
2398 	       */
2399 #if 1
2400 	      g_array_append_vals (result, L"12:00:00", 8);
2401 #else
2402 	      if (systemtime.wHour == 0)
2403 		g_array_append_vals (result, L"12", 2);
2404 	      else
2405 		{
2406 		  g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
2407 		  g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
2408 		}
2409 	      g_array_append_vals (result, L":", 1);
2410 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2411 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2412 	      g_array_append_vals (result, L":", 1);
2413 	      g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
2414 	      g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
2415 	      g_array_append_vals (result, L" ", 1);
2416 #endif
2417 	      n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
2418 	      if (n > 0)
2419 		{
2420 		  g_array_set_size (result, result->len + n);
2421 		  GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
2422 		  g_array_set_size (result, result->len - 1);
2423 		}
2424 	      break;
2425 	    case 'R':
2426 #if 1
2427 	      g_array_append_vals (result, L"00:00", 5);
2428 #else
2429 	      g_array_append_vals (result, digits + systemtime.wHour/10, 1);
2430 	      g_array_append_vals (result, digits + systemtime.wHour%10, 1);
2431 	      g_array_append_vals (result, L":", 1);
2432 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2433 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2434 #endif
2435 	      break;
2436 	    case 'S':
2437 #if 1
2438 	      g_array_append_vals (result, L"00", 2);
2439 #else
2440 	      g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
2441 	      g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
2442 #endif
2443 	      break;
2444 	    case 't':
2445 	      g_array_append_vals (result, L"\t", 1);
2446 	      break;
2447 	    case 'T':
2448 #if 1
2449 	      g_array_append_vals (result, L"00:00:00", 8);
2450 #else
2451 	      g_array_append_vals (result, digits + systemtime.wHour/10, 1);
2452 	      g_array_append_vals (result, digits + systemtime.wHour%10, 1);
2453 	      g_array_append_vals (result, L":", 1);
2454 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2455 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2456 	      g_array_append_vals (result, L":", 1);
2457 	      g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
2458 	      g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
2459 #endif
2460 	      break;
2461 	    case 'u':
2462 	      if (systemtime.wDayOfWeek == 0)
2463 		g_array_append_vals (result, L"7", 1);
2464 	      else
2465 		g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
2466 	      break;
2467 	    case 'U':
2468 	      n = g_date_get_sunday_week_of_year (d);
2469 	      g_array_append_vals (result, digits + n/10, 1);
2470 	      g_array_append_vals (result, digits + n%10, 1);
2471 	      break;
2472 	    case 'V':
2473 	      n = g_date_get_iso8601_week_of_year (d);
2474 	      g_array_append_vals (result, digits + n/10, 1);
2475 	      g_array_append_vals (result, digits + n%10, 1);
2476 	      break;
2477 	    case 'w':
2478 	      g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
2479 	      break;
2480 	    case 'W':
2481 	      n = g_date_get_monday_week_of_year (d);
2482 	      g_array_append_vals (result, digits + n/10, 1);
2483 	      g_array_append_vals (result, digits + n%10, 1);
2484 	      break;
2485 	    case 'x':
2486 	      n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2487 	      if (n > 0)
2488 		{
2489 		  g_array_set_size (result, result->len + n);
2490 		  GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2491 		  g_array_set_size (result, result->len - 1);
2492 		}
2493 	      break;
2494 	    case 'X':
2495 	      n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2496 	      if (n > 0)
2497 		{
2498 		  g_array_set_size (result, result->len + n);
2499 		  GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2500 		  g_array_set_size (result, result->len - 1);
2501 		}
2502 	      break;
2503 	    case 'y':
2504 	      g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
2505 	      g_array_append_vals (result, digits + systemtime.wYear%10, 1);
2506 	      break;
2507 	    case 'Y':
2508 	      g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
2509 	      g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1);
2510 	      g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
2511 	      g_array_append_vals (result, digits + systemtime.wYear%10, 1);
2512 	      break;
2513 	    case 'Z':
2514 	      n = GetTimeZoneInformation (&tzinfo);
2515 	      if (n == TIME_ZONE_ID_UNKNOWN)
2516 		;
2517 	      else if (n == TIME_ZONE_ID_STANDARD)
2518 		g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName));
2519 	      else if (n == TIME_ZONE_ID_DAYLIGHT)
2520 		g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName));
2521 	      break;
2522 	    case '%':
2523 	      g_array_append_vals (result, L"%", 1);
2524 	      break;
2525 	    }
2526 	}
2527       else if (c <= 0xFFFF)
2528 	{
2529 	  wchar_t wc = c;
2530 	  g_array_append_vals (result, &wc, 1);
2531 	}
2532       else
2533 	{
2534 	  glong nwc;
2535 	  wchar_t *ws;
2536 
2537 	  ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL);
2538 	  g_array_append_vals (result, ws, nwc);
2539 	  g_free (ws);
2540 	}
2541       p = g_utf8_next_char (p);
2542     }
2543 
2544   convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL);
2545   g_array_free (result, TRUE);
2546 
2547   if (!convbuf)
2548     {
2549       s[0] = '\0';
2550       return 0;
2551     }
2552 
2553   if (slen <= convlen)
2554     {
2555       /* Ensure only whole characters are copied into the buffer. */
2556       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
2557       g_assert (end != NULL);
2558       convlen = end - convbuf;
2559 
2560       /* Return 0 because the buffer isn't large enough. */
2561       retval = 0;
2562     }
2563   else
2564     retval = convlen;
2565 
2566   memcpy (s, convbuf, convlen);
2567   s[convlen] = '\0';
2568   g_free (convbuf);
2569 
2570   return retval;
2571 }
2572 
2573 #endif
2574 
2575 /**
2576  * g_date_strftime:
2577  * @s: destination buffer
2578  * @slen: buffer size
2579  * @format: format string
2580  * @date: valid #GDate
2581  *
2582  * Generates a printed representation of the date, in a
2583  * [locale][setlocale]-specific way.
2584  * Works just like the platform's C library strftime() function,
2585  * but only accepts date-related formats; time-related formats
2586  * give undefined results. Date must be valid. Unlike strftime()
2587  * (which uses the locale encoding), works on a UTF-8 format
2588  * string and stores a UTF-8 result.
2589  *
2590  * This function does not provide any conversion specifiers in
2591  * addition to those implemented by the platform's C library.
2592  * For example, don't expect that using g_date_strftime() would
2593  * make the \%F provided by the C99 strftime() work on Windows
2594  * where the C library only complies to C89.
2595  *
2596  * Returns: number of characters written to the buffer, or 0 the buffer was too small
2597  */
2598 #pragma GCC diagnostic push
2599 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
2600 
2601 gsize
g_date_strftime(gchar * s,gsize slen,const gchar * format,const GDate * d)2602 g_date_strftime (gchar       *s,
2603                  gsize        slen,
2604                  const gchar *format,
2605                  const GDate *d)
2606 {
2607   struct tm tm;
2608 #ifndef G_OS_WIN32
2609   gsize locale_format_len = 0;
2610   gchar *locale_format;
2611   gsize tmplen;
2612   gchar *tmpbuf;
2613   gsize tmpbufsize;
2614   gsize convlen = 0;
2615   gchar *convbuf;
2616   GError *error = NULL;
2617   gsize retval;
2618 #endif
2619 
2620   g_return_val_if_fail (g_date_valid (d), 0);
2621   g_return_val_if_fail (slen > 0, 0);
2622   g_return_val_if_fail (format != NULL, 0);
2623   g_return_val_if_fail (s != NULL, 0);
2624 
2625   g_date_to_struct_tm (d, &tm);
2626 
2627 #ifdef G_OS_WIN32
2628   if (!g_utf8_validate (format, -1, NULL))
2629     {
2630       s[0] = '\0';
2631       return 0;
2632     }
2633   return win32_strftime_helper (d, format, &tm, s, slen);
2634 #else
2635 
2636   locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
2637 
2638   if (error)
2639     {
2640       g_warning (G_STRLOC "Error converting format to locale encoding: %s", error->message);
2641       g_error_free (error);
2642 
2643       s[0] = '\0';
2644       return 0;
2645     }
2646 
2647   tmpbufsize = MAX (128, locale_format_len * 2);
2648   while (TRUE)
2649     {
2650       tmpbuf = g_malloc (tmpbufsize);
2651 
2652       /* Set the first byte to something other than '\0', to be able to
2653        * recognize whether strftime actually failed or just returned "".
2654        */
2655       tmpbuf[0] = '\1';
2656       tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
2657 
2658       if (tmplen == 0 && tmpbuf[0] != '\0')
2659         {
2660           g_free (tmpbuf);
2661           tmpbufsize *= 2;
2662 
2663           if (tmpbufsize > 65536)
2664             {
2665               g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up");
2666               g_free (locale_format);
2667 
2668               s[0] = '\0';
2669               return 0;
2670             }
2671         }
2672       else
2673         break;
2674     }
2675   g_free (locale_format);
2676 
2677   convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
2678   g_free (tmpbuf);
2679 
2680   if (error)
2681     {
2682       g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s", error->message);
2683       g_error_free (error);
2684 
2685       s[0] = '\0';
2686       return 0;
2687     }
2688 
2689   if (slen <= convlen)
2690     {
2691       /* Ensure only whole characters are copied into the buffer.
2692        */
2693       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
2694       g_assert (end != NULL);
2695       convlen = end - convbuf;
2696 
2697       /* Return 0 because the buffer isn't large enough.
2698        */
2699       retval = 0;
2700     }
2701   else
2702     retval = convlen;
2703 
2704   memcpy (s, convbuf, convlen);
2705   s[convlen] = '\0';
2706   g_free (convbuf);
2707 
2708   return retval;
2709 #endif
2710 }
2711 
2712 #pragma GCC diagnostic pop
2713