• 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 by
87  * calling g_date_clear(). A cleared date is safe; 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 safe. 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 safe 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 (((gint) m > G_DATE_BAD_MONTH) && ((gint) 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 (((gint) w > G_DATE_BAD_WEEKDAY) && ((gint) 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 safe 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;
1146               G_GNUC_FALLTHROUGH;
1147             case 1976:
1148               dmy_order[i] = G_DATE_YEAR;
1149               break;
1150             default:
1151               /* assume locale era */
1152               locale_era_adjust = 1976 - testpt.n[i];
1153               dmy_order[i] = G_DATE_YEAR;
1154               break;
1155             }
1156           ++i;
1157         }
1158 
1159 #if defined(G_ENABLE_DEBUG) && 0
1160       DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
1161       i = 1;
1162       while (i < 13)
1163         {
1164           DEBUG_MSG (("  %s   %s", long_month_names[i], short_month_names[i]));
1165           ++i;
1166         }
1167       DEBUG_MSG (("Alternative month names:"));
1168       i = 1;
1169       while (i < 13)
1170         {
1171           DEBUG_MSG (("  %s   %s", long_month_names_alternative[i], short_month_names_alternative[i]));
1172           ++i;
1173         }
1174       if (using_twodigit_years)
1175         {
1176 	  DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
1177         }
1178       {
1179         gchar *strings[3];
1180         i = 0;
1181         while (i < 3)
1182           {
1183             switch (dmy_order[i])
1184               {
1185               case G_DATE_MONTH:
1186                 strings[i] = "Month";
1187                 break;
1188               case G_DATE_YEAR:
1189                 strings[i] = "Year";
1190                 break;
1191               case G_DATE_DAY:
1192                 strings[i] = "Day";
1193                 break;
1194               default:
1195                 strings[i] = NULL;
1196                 break;
1197               }
1198             ++i;
1199           }
1200         DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
1201         DEBUG_MSG (("**Sample date in this locale: '%s'", buf));
1202       }
1203 #endif
1204     }
1205 
1206   g_date_fill_parse_tokens (str, pt);
1207 }
1208 
1209 /**
1210  * g_date_set_parse:
1211  * @date: a #GDate to fill in
1212  * @str: string to parse
1213  *
1214  * Parses a user-inputted string @str, and try to figure out what date it
1215  * represents, taking the [current locale][setlocale] into account. If the
1216  * string is successfully parsed, the date will be valid after the call.
1217  * Otherwise, it will be invalid. You should check using g_date_valid()
1218  * to see whether the parsing succeeded.
1219  *
1220  * This function is not appropriate for file formats and the like; it
1221  * isn't very precise, and its exact behavior varies with the locale.
1222  * It's intended to be a heuristic routine that guesses what the user
1223  * means by a given string (and it does work pretty well in that
1224  * capacity).
1225  */
1226 void
g_date_set_parse(GDate * d,const gchar * str)1227 g_date_set_parse (GDate       *d,
1228                   const gchar *str)
1229 {
1230   GDateParseTokens pt;
1231   guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
1232   gsize str_len;
1233 
1234   g_return_if_fail (d != NULL);
1235 
1236   /* set invalid */
1237   g_date_clear (d, 1);
1238 
1239   /* Anything longer than this is ridiculous and could take a while to normalize.
1240    * This limit is chosen arbitrarily. */
1241   str_len = strlen (str);
1242   if (str_len > 200)
1243     return;
1244 
1245   /* The input has to be valid UTF-8. */
1246   if (!g_utf8_validate_len (str, str_len, NULL))
1247     return;
1248 
1249   G_LOCK (g_date_global);
1250 
1251   g_date_prepare_to_parse (str, &pt);
1252 
1253   DEBUG_MSG (("Found %d ints, '%d' '%d' '%d' and written out month %d",
1254 	      pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
1255 
1256 
1257   if (pt.num_ints == 4)
1258     {
1259       G_UNLOCK (g_date_global);
1260       return; /* presumably a typo; bail out. */
1261     }
1262 
1263   if (pt.num_ints > 1)
1264     {
1265       int i = 0;
1266       int j = 0;
1267 
1268       g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
1269 
1270       while (i < pt.num_ints && j < 3)
1271         {
1272           switch (dmy_order[j])
1273             {
1274             case G_DATE_MONTH:
1275 	    {
1276 	      if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
1277 		{
1278 		  m = pt.month;
1279 		  ++j;      /* skip months, but don't skip this number */
1280 		  continue;
1281 		}
1282 	      else
1283 		m = pt.n[i];
1284 	    }
1285 	    break;
1286             case G_DATE_DAY:
1287 	    {
1288 	      if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
1289 		{
1290 		  day = 1;
1291 		  ++j;      /* skip days, since we may have month/year */
1292 		  continue;
1293 		}
1294 	      day = pt.n[i];
1295 	    }
1296 	    break;
1297             case G_DATE_YEAR:
1298 	    {
1299 	      y  = pt.n[i];
1300 
1301 	      if (locale_era_adjust != 0)
1302 	        {
1303 		  y += locale_era_adjust;
1304 	        }
1305 	      else if (using_twodigit_years && y < 100)
1306 		{
1307 		  guint two     =  twodigit_start_year % 100;
1308 		  guint century = (twodigit_start_year / 100) * 100;
1309 
1310 		  if (y < two)
1311 		    century += 100;
1312 
1313 		  y += century;
1314 		}
1315 	    }
1316 	    break;
1317             default:
1318               break;
1319             }
1320 
1321           ++i;
1322           ++j;
1323         }
1324 
1325 
1326       if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
1327         {
1328           /* Try YYYY MM DD */
1329           y   = pt.n[0];
1330           m   = pt.n[1];
1331           day = pt.n[2];
1332 
1333           if (using_twodigit_years && y < 100)
1334             y = G_DATE_BAD_YEAR; /* avoids ambiguity */
1335         }
1336       else if (pt.num_ints == 2)
1337 	{
1338 	  if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
1339 	    m = pt.month;
1340 	}
1341     }
1342   else if (pt.num_ints == 1)
1343     {
1344       if (pt.month != G_DATE_BAD_MONTH)
1345         {
1346           /* Month name and year? */
1347           m    = pt.month;
1348           day  = 1;
1349           y = pt.n[0];
1350         }
1351       else
1352         {
1353           /* Try yyyymmdd and yymmdd */
1354 
1355           m   = (pt.n[0]/100) % 100;
1356           day = pt.n[0] % 100;
1357           y   = pt.n[0]/10000;
1358 
1359           /* FIXME move this into a separate function */
1360           if (using_twodigit_years && y < 100)
1361             {
1362               guint two     =  twodigit_start_year % 100;
1363               guint century = (twodigit_start_year / 100) * 100;
1364 
1365               if (y < two)
1366                 century += 100;
1367 
1368               y += century;
1369             }
1370         }
1371     }
1372 
1373   /* See if we got anything valid out of all this. */
1374   /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
1375   if (y < 8000 && g_date_valid_dmy (day, m, y))
1376     {
1377       d->month = m;
1378       d->day   = day;
1379       d->year  = y;
1380       d->dmy   = TRUE;
1381     }
1382 #ifdef G_ENABLE_DEBUG
1383   else
1384     {
1385       DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
1386     }
1387 #endif
1388   G_UNLOCK (g_date_global);
1389 }
1390 
1391 /**
1392  * g_date_set_time_t:
1393  * @date: a #GDate
1394  * @timet: time_t value to set
1395  *
1396  * Sets the value of a date to the date corresponding to a time
1397  * specified as a time_t. The time to date conversion is done using
1398  * the user's current timezone.
1399  *
1400  * To set the value of a date to the current day, you could write:
1401  * |[<!-- language="C" -->
1402  *  time_t now = time (NULL);
1403  *  if (now == (time_t) -1)
1404  *    // handle the error
1405  *  g_date_set_time_t (date, now);
1406  * ]|
1407  *
1408  * Since: 2.10
1409  */
1410 void
g_date_set_time_t(GDate * date,time_t timet)1411 g_date_set_time_t (GDate *date,
1412 		   time_t timet)
1413 {
1414   struct tm tm;
1415 
1416   g_return_if_fail (date != NULL);
1417 
1418 #ifdef HAVE_LOCALTIME_R
1419   localtime_r (&timet, &tm);
1420 #else
1421   {
1422     struct tm *ptm = localtime (&timet);
1423 
1424     if (ptm == NULL)
1425       {
1426 	/* Happens at least in Microsoft's C library if you pass a
1427 	 * negative time_t. Use 2000-01-01 as default date.
1428 	 */
1429 #ifndef G_DISABLE_CHECKS
1430 	g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
1431 #endif
1432 
1433 	tm.tm_mon = 0;
1434 	tm.tm_mday = 1;
1435 	tm.tm_year = 100;
1436       }
1437     else
1438       memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
1439   }
1440 #endif
1441 
1442   date->julian = FALSE;
1443 
1444   date->month = tm.tm_mon + 1;
1445   date->day   = tm.tm_mday;
1446   date->year  = tm.tm_year + 1900;
1447 
1448   g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year));
1449 
1450   date->dmy    = TRUE;
1451 }
1452 
1453 
1454 /**
1455  * g_date_set_time:
1456  * @date: a #GDate.
1457  * @time_: #GTime value to set.
1458  *
1459  * Sets the value of a date from a #GTime value.
1460  * The time to date conversion is done using the user's current timezone.
1461  *
1462  * Deprecated: 2.10: Use g_date_set_time_t() instead.
1463  */
1464 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1465 void
g_date_set_time(GDate * date,GTime time_)1466 g_date_set_time (GDate *date,
1467 		 GTime  time_)
1468 {
1469   g_date_set_time_t (date, (time_t) time_);
1470 }
1471 G_GNUC_END_IGNORE_DEPRECATIONS
1472 
1473 /**
1474  * g_date_set_time_val:
1475  * @date: a #GDate
1476  * @timeval: #GTimeVal value to set
1477  *
1478  * Sets the value of a date from a #GTimeVal value.  Note that the
1479  * @tv_usec member is ignored, because #GDate can't make use of the
1480  * additional precision.
1481  *
1482  * The time to date conversion is done using the user's current timezone.
1483  *
1484  * Since: 2.10
1485  * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use g_date_set_time_t()
1486  *    instead.
1487  */
1488 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1489 void
g_date_set_time_val(GDate * date,GTimeVal * timeval)1490 g_date_set_time_val (GDate    *date,
1491 		     GTimeVal *timeval)
1492 {
1493   g_date_set_time_t (date, (time_t) timeval->tv_sec);
1494 }
1495 G_GNUC_END_IGNORE_DEPRECATIONS
1496 
1497 /**
1498  * g_date_set_month:
1499  * @date: a #GDate
1500  * @month: month to set
1501  *
1502  * Sets the month of the year for a #GDate.  If the resulting
1503  * day-month-year triplet is invalid, the date will be invalid.
1504  */
1505 void
g_date_set_month(GDate * d,GDateMonth m)1506 g_date_set_month (GDate     *d,
1507                   GDateMonth m)
1508 {
1509   g_return_if_fail (d != NULL);
1510   g_return_if_fail (g_date_valid_month (m));
1511 
1512   if (d->julian && !d->dmy) g_date_update_dmy(d);
1513   d->julian = FALSE;
1514 
1515   d->month = m;
1516 
1517   if (g_date_valid_dmy (d->day, d->month, d->year))
1518     d->dmy = TRUE;
1519   else
1520     d->dmy = FALSE;
1521 }
1522 
1523 /**
1524  * g_date_set_day:
1525  * @date: a #GDate
1526  * @day: day to set
1527  *
1528  * Sets the day of the month for a #GDate. If the resulting
1529  * day-month-year triplet is invalid, the date will be invalid.
1530  */
1531 void
g_date_set_day(GDate * d,GDateDay day)1532 g_date_set_day (GDate    *d,
1533                 GDateDay  day)
1534 {
1535   g_return_if_fail (d != NULL);
1536   g_return_if_fail (g_date_valid_day (day));
1537 
1538   if (d->julian && !d->dmy) g_date_update_dmy(d);
1539   d->julian = FALSE;
1540 
1541   d->day = day;
1542 
1543   if (g_date_valid_dmy (d->day, d->month, d->year))
1544     d->dmy = TRUE;
1545   else
1546     d->dmy = FALSE;
1547 }
1548 
1549 /**
1550  * g_date_set_year:
1551  * @date: a #GDate
1552  * @year: year to set
1553  *
1554  * Sets the year for a #GDate. If the resulting day-month-year
1555  * triplet is invalid, the date will be invalid.
1556  */
1557 void
g_date_set_year(GDate * d,GDateYear y)1558 g_date_set_year (GDate     *d,
1559                  GDateYear  y)
1560 {
1561   g_return_if_fail (d != NULL);
1562   g_return_if_fail (g_date_valid_year (y));
1563 
1564   if (d->julian && !d->dmy) g_date_update_dmy(d);
1565   d->julian = FALSE;
1566 
1567   d->year = y;
1568 
1569   if (g_date_valid_dmy (d->day, d->month, d->year))
1570     d->dmy = TRUE;
1571   else
1572     d->dmy = FALSE;
1573 }
1574 
1575 /**
1576  * g_date_set_dmy:
1577  * @date: a #GDate
1578  * @day: day
1579  * @month: month
1580  * @y: year
1581  *
1582  * Sets the value of a #GDate from a day, month, and year.
1583  * The day-month-year triplet must be valid; if you aren't
1584  * sure it is, call g_date_valid_dmy() to check before you
1585  * set it.
1586  */
1587 void
g_date_set_dmy(GDate * d,GDateDay day,GDateMonth m,GDateYear y)1588 g_date_set_dmy (GDate      *d,
1589                 GDateDay    day,
1590                 GDateMonth  m,
1591                 GDateYear   y)
1592 {
1593   g_return_if_fail (d != NULL);
1594   g_return_if_fail (g_date_valid_dmy (day, m, y));
1595 
1596   d->julian = FALSE;
1597 
1598   d->month = m;
1599   d->day   = day;
1600   d->year  = y;
1601 
1602   d->dmy = TRUE;
1603 }
1604 
1605 /**
1606  * g_date_set_julian:
1607  * @date: a #GDate
1608  * @julian_date: Julian day number (days since January 1, Year 1)
1609  *
1610  * Sets the value of a #GDate from a Julian day number.
1611  */
1612 void
g_date_set_julian(GDate * d,guint32 j)1613 g_date_set_julian (GDate   *d,
1614                    guint32  j)
1615 {
1616   g_return_if_fail (d != NULL);
1617   g_return_if_fail (g_date_valid_julian (j));
1618 
1619   d->julian_days = j;
1620   d->julian = TRUE;
1621   d->dmy = FALSE;
1622 }
1623 
1624 /**
1625  * g_date_is_first_of_month:
1626  * @date: a #GDate to check
1627  *
1628  * Returns %TRUE if the date is on the first of a month.
1629  * The date must be valid.
1630  *
1631  * Returns: %TRUE if the date is the first of the month
1632  */
1633 gboolean
g_date_is_first_of_month(const GDate * d)1634 g_date_is_first_of_month (const GDate *d)
1635 {
1636   g_return_val_if_fail (g_date_valid (d), FALSE);
1637 
1638   if (!d->dmy)
1639     g_date_update_dmy (d);
1640 
1641   g_return_val_if_fail (d->dmy, FALSE);
1642 
1643   if (d->day == 1) return TRUE;
1644   else return FALSE;
1645 }
1646 
1647 /**
1648  * g_date_is_last_of_month:
1649  * @date: a #GDate to check
1650  *
1651  * Returns %TRUE if the date is the last day of the month.
1652  * The date must be valid.
1653  *
1654  * Returns: %TRUE if the date is the last day of the month
1655  */
1656 gboolean
g_date_is_last_of_month(const GDate * d)1657 g_date_is_last_of_month (const GDate *d)
1658 {
1659   gint idx;
1660 
1661   g_return_val_if_fail (g_date_valid (d), FALSE);
1662 
1663   if (!d->dmy)
1664     g_date_update_dmy (d);
1665 
1666   g_return_val_if_fail (d->dmy, FALSE);
1667 
1668   idx = g_date_is_leap_year (d->year) ? 1 : 0;
1669 
1670   if (d->day == days_in_months[idx][d->month]) return TRUE;
1671   else return FALSE;
1672 }
1673 
1674 /**
1675  * g_date_add_days:
1676  * @date: a #GDate to increment
1677  * @n_days: number of days to move the date forward
1678  *
1679  * Increments a date some number of days.
1680  * To move forward by weeks, add weeks*7 days.
1681  * The date must be valid.
1682  */
1683 void
g_date_add_days(GDate * d,guint ndays)1684 g_date_add_days (GDate *d,
1685                  guint  ndays)
1686 {
1687   g_return_if_fail (g_date_valid (d));
1688 
1689   if (!d->julian)
1690     g_date_update_julian (d);
1691 
1692   g_return_if_fail (d->julian);
1693   g_return_if_fail (ndays <= G_MAXUINT32 - d->julian_days);
1694 
1695   d->julian_days += ndays;
1696   d->dmy = FALSE;
1697 }
1698 
1699 /**
1700  * g_date_subtract_days:
1701  * @date: a #GDate to decrement
1702  * @n_days: number of days to move
1703  *
1704  * Moves a date some number of days into the past.
1705  * To move by weeks, just move by weeks*7 days.
1706  * The date must be valid.
1707  */
1708 void
g_date_subtract_days(GDate * d,guint ndays)1709 g_date_subtract_days (GDate *d,
1710                       guint  ndays)
1711 {
1712   g_return_if_fail (g_date_valid (d));
1713 
1714   if (!d->julian)
1715     g_date_update_julian (d);
1716 
1717   g_return_if_fail (d->julian);
1718   g_return_if_fail (d->julian_days > ndays);
1719 
1720   d->julian_days -= ndays;
1721   d->dmy = FALSE;
1722 }
1723 
1724 /**
1725  * g_date_add_months:
1726  * @date: a #GDate to increment
1727  * @n_months: number of months to move forward
1728  *
1729  * Increments a date by some number of months.
1730  * If the day of the month is greater than 28,
1731  * this routine may change the day of the month
1732  * (because the destination month may not have
1733  * the current day in it). The date must be valid.
1734  */
1735 void
g_date_add_months(GDate * d,guint nmonths)1736 g_date_add_months (GDate *d,
1737                    guint  nmonths)
1738 {
1739   guint years, months;
1740   gint idx;
1741 
1742   g_return_if_fail (g_date_valid (d));
1743 
1744   if (!d->dmy)
1745     g_date_update_dmy (d);
1746 
1747   g_return_if_fail (d->dmy != 0);
1748   g_return_if_fail (nmonths <= G_MAXUINT - (d->month - 1));
1749 
1750   nmonths += d->month - 1;
1751 
1752   years  = nmonths/12;
1753   months = nmonths%12;
1754 
1755   g_return_if_fail (years <= (guint) (G_MAXUINT16 - d->year));
1756 
1757   d->month = months + 1;
1758   d->year  += years;
1759 
1760   idx = g_date_is_leap_year (d->year) ? 1 : 0;
1761 
1762   if (d->day > days_in_months[idx][d->month])
1763     d->day = days_in_months[idx][d->month];
1764 
1765   d->julian = FALSE;
1766 
1767   g_return_if_fail (g_date_valid (d));
1768 }
1769 
1770 /**
1771  * g_date_subtract_months:
1772  * @date: a #GDate to decrement
1773  * @n_months: number of months to move
1774  *
1775  * Moves a date some number of months into the past.
1776  * If the current day of the month doesn't exist in
1777  * the destination month, the day of the month
1778  * may change. The date must be valid.
1779  */
1780 void
g_date_subtract_months(GDate * d,guint nmonths)1781 g_date_subtract_months (GDate *d,
1782                         guint  nmonths)
1783 {
1784   guint years, months;
1785   gint idx;
1786 
1787   g_return_if_fail (g_date_valid (d));
1788 
1789   if (!d->dmy)
1790     g_date_update_dmy (d);
1791 
1792   g_return_if_fail (d->dmy != 0);
1793 
1794   years  = nmonths/12;
1795   months = nmonths%12;
1796 
1797   g_return_if_fail (d->year > years);
1798 
1799   d->year  -= years;
1800 
1801   if (d->month > months) d->month -= months;
1802   else
1803     {
1804       months -= d->month;
1805       d->month = 12 - months;
1806       d->year -= 1;
1807     }
1808 
1809   idx = g_date_is_leap_year (d->year) ? 1 : 0;
1810 
1811   if (d->day > days_in_months[idx][d->month])
1812     d->day = days_in_months[idx][d->month];
1813 
1814   d->julian = FALSE;
1815 
1816   g_return_if_fail (g_date_valid (d));
1817 }
1818 
1819 /**
1820  * g_date_add_years:
1821  * @date: a #GDate to increment
1822  * @n_years: number of years to move forward
1823  *
1824  * Increments a date by some number of years.
1825  * If the date is February 29, and the destination
1826  * year is not a leap year, the date will be changed
1827  * to February 28. The date must be valid.
1828  */
1829 void
g_date_add_years(GDate * d,guint nyears)1830 g_date_add_years (GDate *d,
1831                   guint  nyears)
1832 {
1833   g_return_if_fail (g_date_valid (d));
1834 
1835   if (!d->dmy)
1836     g_date_update_dmy (d);
1837 
1838   g_return_if_fail (d->dmy != 0);
1839   g_return_if_fail (nyears <= (guint) (G_MAXUINT16 - d->year));
1840 
1841   d->year += nyears;
1842 
1843   if (d->month == 2 && d->day == 29)
1844     {
1845       if (!g_date_is_leap_year (d->year))
1846         d->day = 28;
1847     }
1848 
1849   d->julian = FALSE;
1850 }
1851 
1852 /**
1853  * g_date_subtract_years:
1854  * @date: a #GDate to decrement
1855  * @n_years: number of years to move
1856  *
1857  * Moves a date some number of years into the past.
1858  * If the current day doesn't exist in the destination
1859  * year (i.e. it's February 29 and you move to a non-leap-year)
1860  * then the day is changed to February 29. The date
1861  * must be valid.
1862  */
1863 void
g_date_subtract_years(GDate * d,guint nyears)1864 g_date_subtract_years (GDate *d,
1865                        guint  nyears)
1866 {
1867   g_return_if_fail (g_date_valid (d));
1868 
1869   if (!d->dmy)
1870     g_date_update_dmy (d);
1871 
1872   g_return_if_fail (d->dmy != 0);
1873   g_return_if_fail (d->year > nyears);
1874 
1875   d->year -= nyears;
1876 
1877   if (d->month == 2 && d->day == 29)
1878     {
1879       if (!g_date_is_leap_year (d->year))
1880         d->day = 28;
1881     }
1882 
1883   d->julian = FALSE;
1884 }
1885 
1886 /**
1887  * g_date_is_leap_year:
1888  * @year: year to check
1889  *
1890  * Returns %TRUE if the year is a leap year.
1891  *
1892  * For the purposes of this function, leap year is every year
1893  * divisible by 4 unless that year is divisible by 100. If it
1894  * is divisible by 100 it would be a leap year only if that year
1895  * is also divisible by 400.
1896  *
1897  * Returns: %TRUE if the year is a leap year
1898  */
1899 gboolean
g_date_is_leap_year(GDateYear year)1900 g_date_is_leap_year (GDateYear year)
1901 {
1902   g_return_val_if_fail (g_date_valid_year (year), FALSE);
1903 
1904   return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
1905            (year % 400) == 0 );
1906 }
1907 
1908 /**
1909  * g_date_get_days_in_month:
1910  * @month: month
1911  * @year: year
1912  *
1913  * Returns the number of days in a month, taking leap
1914  * years into account.
1915  *
1916  * Returns: number of days in @month during the @year
1917  */
1918 guint8
g_date_get_days_in_month(GDateMonth month,GDateYear year)1919 g_date_get_days_in_month (GDateMonth month,
1920                           GDateYear  year)
1921 {
1922   gint idx;
1923 
1924   g_return_val_if_fail (g_date_valid_year (year), 0);
1925   g_return_val_if_fail (g_date_valid_month (month), 0);
1926 
1927   idx = g_date_is_leap_year (year) ? 1 : 0;
1928 
1929   return days_in_months[idx][month];
1930 }
1931 
1932 /**
1933  * g_date_get_monday_weeks_in_year:
1934  * @year: a year
1935  *
1936  * Returns the number of weeks in the year, where weeks
1937  * are taken to start on Monday. Will be 52 or 53. The
1938  * date must be valid. (Years always have 52 7-day periods,
1939  * plus 1 or 2 extra days depending on whether it's a leap
1940  * year. This function is basically telling you how many
1941  * Mondays are in the year, i.e. there are 53 Mondays if
1942  * one of the extra days happens to be a Monday.)
1943  *
1944  * Returns: number of Mondays in the year
1945  */
1946 guint8
g_date_get_monday_weeks_in_year(GDateYear year)1947 g_date_get_monday_weeks_in_year (GDateYear year)
1948 {
1949   GDate d;
1950 
1951   g_return_val_if_fail (g_date_valid_year (year), 0);
1952 
1953   g_date_clear (&d, 1);
1954   g_date_set_dmy (&d, 1, 1, year);
1955   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1956   g_date_set_dmy (&d, 31, 12, year);
1957   if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1958   if (g_date_is_leap_year (year))
1959     {
1960       g_date_set_dmy (&d, 2, 1, year);
1961       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1962       g_date_set_dmy (&d, 30, 12, year);
1963       if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
1964     }
1965   return 52;
1966 }
1967 
1968 /**
1969  * g_date_get_sunday_weeks_in_year:
1970  * @year: year to count weeks in
1971  *
1972  * Returns the number of weeks in the year, where weeks
1973  * are taken to start on Sunday. Will be 52 or 53. The
1974  * date must be valid. (Years always have 52 7-day periods,
1975  * plus 1 or 2 extra days depending on whether it's a leap
1976  * year. This function is basically telling you how many
1977  * Sundays are in the year, i.e. there are 53 Sundays if
1978  * one of the extra days happens to be a Sunday.)
1979  *
1980  * Returns: the number of weeks in @year
1981  */
1982 guint8
g_date_get_sunday_weeks_in_year(GDateYear year)1983 g_date_get_sunday_weeks_in_year (GDateYear year)
1984 {
1985   GDate d;
1986 
1987   g_return_val_if_fail (g_date_valid_year (year), 0);
1988 
1989   g_date_clear (&d, 1);
1990   g_date_set_dmy (&d, 1, 1, year);
1991   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1992   g_date_set_dmy (&d, 31, 12, year);
1993   if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1994   if (g_date_is_leap_year (year))
1995     {
1996       g_date_set_dmy (&d, 2, 1, year);
1997       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
1998       g_date_set_dmy (&d, 30, 12, year);
1999       if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
2000     }
2001   return 52;
2002 }
2003 
2004 /**
2005  * g_date_compare:
2006  * @lhs: first date to compare
2007  * @rhs: second date to compare
2008  *
2009  * qsort()-style comparison function for dates.
2010  * Both dates must be valid.
2011  *
2012  * Returns: 0 for equal, less than zero if @lhs is less than @rhs,
2013  *     greater than zero if @lhs is greater than @rhs
2014  */
2015 gint
g_date_compare(const GDate * lhs,const GDate * rhs)2016 g_date_compare (const GDate *lhs,
2017                 const GDate *rhs)
2018 {
2019   g_return_val_if_fail (lhs != NULL, 0);
2020   g_return_val_if_fail (rhs != NULL, 0);
2021   g_return_val_if_fail (g_date_valid (lhs), 0);
2022   g_return_val_if_fail (g_date_valid (rhs), 0);
2023 
2024   /* Remember the self-comparison case! I think it works right now. */
2025 
2026   while (TRUE)
2027     {
2028       if (lhs->julian && rhs->julian)
2029         {
2030           if (lhs->julian_days < rhs->julian_days) return -1;
2031           else if (lhs->julian_days > rhs->julian_days) return 1;
2032           else                                          return 0;
2033         }
2034       else if (lhs->dmy && rhs->dmy)
2035         {
2036           if (lhs->year < rhs->year)               return -1;
2037           else if (lhs->year > rhs->year)               return 1;
2038           else
2039             {
2040               if (lhs->month < rhs->month)         return -1;
2041               else if (lhs->month > rhs->month)         return 1;
2042               else
2043                 {
2044                   if (lhs->day < rhs->day)              return -1;
2045                   else if (lhs->day > rhs->day)              return 1;
2046                   else                                       return 0;
2047                 }
2048 
2049             }
2050 
2051         }
2052       else
2053         {
2054           if (!lhs->julian) g_date_update_julian (lhs);
2055           if (!rhs->julian) g_date_update_julian (rhs);
2056           g_return_val_if_fail (lhs->julian, 0);
2057           g_return_val_if_fail (rhs->julian, 0);
2058         }
2059 
2060     }
2061   return 0; /* warnings */
2062 }
2063 
2064 /**
2065  * g_date_to_struct_tm:
2066  * @date: a #GDate to set the struct tm from
2067  * @tm: (not nullable): struct tm to fill
2068  *
2069  * Fills in the date-related bits of a struct tm using the @date value.
2070  * Initializes the non-date parts with something safe but meaningless.
2071  */
2072 void
g_date_to_struct_tm(const GDate * d,struct tm * tm)2073 g_date_to_struct_tm (const GDate *d,
2074                      struct tm   *tm)
2075 {
2076   GDateWeekday day;
2077 
2078   g_return_if_fail (g_date_valid (d));
2079   g_return_if_fail (tm != NULL);
2080 
2081   if (!d->dmy)
2082     g_date_update_dmy (d);
2083 
2084   g_return_if_fail (d->dmy != 0);
2085 
2086   /* zero all the irrelevant fields to be sure they're valid */
2087 
2088   /* On Linux and maybe other systems, there are weird non-POSIX
2089    * fields on the end of struct tm that choke strftime if they
2090    * contain garbage.  So we need to 0 the entire struct, not just the
2091    * fields we know to exist.
2092    */
2093 
2094   memset (tm, 0x0, sizeof (struct tm));
2095 
2096   tm->tm_mday = d->day;
2097   tm->tm_mon  = d->month - 1; /* 0-11 goes in tm */
2098   tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
2099 
2100   day = g_date_get_weekday (d);
2101   if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
2102 
2103   tm->tm_wday = (int)day;
2104 
2105   tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
2106   tm->tm_isdst = -1; /* -1 means "information not available" */
2107 }
2108 
2109 /**
2110  * g_date_clamp:
2111  * @date: a #GDate to clamp
2112  * @min_date: minimum accepted value for @date
2113  * @max_date: maximum accepted value for @date
2114  *
2115  * If @date is prior to @min_date, sets @date equal to @min_date.
2116  * If @date falls after @max_date, sets @date equal to @max_date.
2117  * Otherwise, @date is unchanged.
2118  * Either of @min_date and @max_date may be %NULL.
2119  * All non-%NULL dates must be valid.
2120  */
2121 void
g_date_clamp(GDate * date,const GDate * min_date,const GDate * max_date)2122 g_date_clamp (GDate       *date,
2123 	      const GDate *min_date,
2124 	      const GDate *max_date)
2125 {
2126   g_return_if_fail (g_date_valid (date));
2127 
2128   if (min_date != NULL)
2129     g_return_if_fail (g_date_valid (min_date));
2130 
2131   if (max_date != NULL)
2132     g_return_if_fail (g_date_valid (max_date));
2133 
2134   if (min_date != NULL && max_date != NULL)
2135     g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
2136 
2137   if (min_date && g_date_compare (date, min_date) < 0)
2138     *date = *min_date;
2139 
2140   if (max_date && g_date_compare (max_date, date) < 0)
2141     *date = *max_date;
2142 }
2143 
2144 /**
2145  * g_date_order:
2146  * @date1: the first date
2147  * @date2: the second date
2148  *
2149  * Checks if @date1 is less than or equal to @date2,
2150  * and swap the values if this is not the case.
2151  */
2152 void
g_date_order(GDate * date1,GDate * date2)2153 g_date_order (GDate *date1,
2154               GDate *date2)
2155 {
2156   g_return_if_fail (g_date_valid (date1));
2157   g_return_if_fail (g_date_valid (date2));
2158 
2159   if (g_date_compare (date1, date2) > 0)
2160     {
2161       GDate tmp = *date1;
2162       *date1 = *date2;
2163       *date2 = tmp;
2164     }
2165 }
2166 
2167 #ifdef G_OS_WIN32
2168 static gboolean
append_month_name(GArray * result,LCID lcid,SYSTEMTIME * systemtime,gboolean abbreviated,gboolean alternative)2169 append_month_name (GArray     *result,
2170 		   LCID        lcid,
2171 		   SYSTEMTIME *systemtime,
2172 		   gboolean    abbreviated,
2173 		   gboolean    alternative)
2174 {
2175   int n;
2176   WORD base;
2177   LPCWSTR lpFormat;
2178 
2179   if (alternative)
2180     {
2181       base = abbreviated ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1;
2182       n = GetLocaleInfoW (lcid, base + systemtime->wMonth - 1, NULL, 0);
2183       if (n == 0)
2184         return FALSE;
2185 
2186       g_array_set_size (result, result->len + n);
2187       if (GetLocaleInfoW (lcid, base + systemtime->wMonth - 1,
2188                           ((wchar_t *) result->data) + result->len - n, n) != n)
2189         return FALSE;
2190 
2191       g_array_set_size (result, result->len - 1);
2192     }
2193   else
2194     {
2195       /* According to MSDN, this is the correct method to obtain
2196        * the form of the month name used when formatting a full
2197        * date; it must be a genitive case in some languages.
2198        *
2199        * (n == 0) indicates an error, whereas (n < 2) is something we’d never
2200        * expect from the given format string, and would break the subsequent code.
2201        */
2202       lpFormat = abbreviated ? L"ddMMM" : L"ddMMMM";
2203       n = GetDateFormatW (lcid, 0, systemtime, lpFormat, NULL, 0);
2204       if (n < 2)
2205         return FALSE;
2206 
2207       g_array_set_size (result, result->len + n);
2208       if (GetDateFormatW (lcid, 0, systemtime, lpFormat,
2209                           ((wchar_t *) result->data) + result->len - n, n) != n)
2210         return FALSE;
2211 
2212       /* We have obtained a day number as two digits and the month name.
2213        * Now let's get rid of those two digits: overwrite them with the
2214        * month name.
2215        */
2216       memmove (((wchar_t *) result->data) + result->len - n,
2217 	       ((wchar_t *) result->data) + result->len - n + 2,
2218 	       (n - 2) * sizeof (wchar_t));
2219       g_array_set_size (result, result->len - 3);
2220     }
2221 
2222   return TRUE;
2223 }
2224 
2225 static gsize
win32_strftime_helper(const GDate * d,const gchar * format,const struct tm * tm,gchar * s,gsize slen)2226 win32_strftime_helper (const GDate     *d,
2227 		       const gchar     *format,
2228 		       const struct tm *tm,
2229 		       gchar           *s,
2230 		       gsize	        slen)
2231 {
2232   SYSTEMTIME systemtime;
2233   TIME_ZONE_INFORMATION tzinfo;
2234   LCID lcid;
2235   int n, k;
2236   GArray *result;
2237   const gchar *p;
2238   gunichar c, modifier;
2239   const wchar_t digits[] = L"0123456789";
2240   gchar *convbuf;
2241   glong convlen = 0;
2242   gsize retval;
2243 
2244   systemtime.wYear = tm->tm_year + 1900;
2245   systemtime.wMonth = tm->tm_mon + 1;
2246   systemtime.wDayOfWeek = tm->tm_wday;
2247   systemtime.wDay = tm->tm_mday;
2248   systemtime.wHour = tm->tm_hour;
2249   systemtime.wMinute = tm->tm_min;
2250   systemtime.wSecond = tm->tm_sec;
2251   systemtime.wMilliseconds = 0;
2252 
2253   lcid = GetThreadLocale ();
2254   result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2));
2255 
2256   p = format;
2257   while (*p)
2258     {
2259       c = g_utf8_get_char (p);
2260       if (c == '%')
2261 	{
2262 	  p = g_utf8_next_char (p);
2263 	  if (!*p)
2264 	    {
2265 	      s[0] = '\0';
2266 	      g_array_free (result, TRUE);
2267 
2268 	      return 0;
2269 	    }
2270 
2271 	  modifier = '\0';
2272 	  c = g_utf8_get_char (p);
2273 	  if (c == 'E' || c == 'O')
2274 	    {
2275 	      /* "%OB", "%Ob", and "%Oh" are supported, ignore other modified
2276 	       * conversion specifiers for now.
2277 	       */
2278 	      modifier = c;
2279 	      p = g_utf8_next_char (p);
2280 	      if (!*p)
2281 		{
2282 		  s[0] = '\0';
2283 		  g_array_free (result, TRUE);
2284 
2285 		  return 0;
2286 		}
2287 
2288 	      c = g_utf8_get_char (p);
2289 	    }
2290 
2291 	  switch (c)
2292 	    {
2293 	    case 'a':
2294 	      if (systemtime.wDayOfWeek == 0)
2295 		k = 6;
2296 	      else
2297 		k = systemtime.wDayOfWeek - 1;
2298 	      n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0);
2299 	      g_array_set_size (result, result->len + n);
2300 	      GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
2301 	      g_array_set_size (result, result->len - 1);
2302 	      break;
2303 	    case 'A':
2304 	      if (systemtime.wDayOfWeek == 0)
2305 		k = 6;
2306 	      else
2307 		k = systemtime.wDayOfWeek - 1;
2308 	      n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0);
2309 	      g_array_set_size (result, result->len + n);
2310 	      GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
2311 	      g_array_set_size (result, result->len - 1);
2312 	      break;
2313 	    case 'b':
2314 	    case 'h':
2315               if (!append_month_name (result, lcid, &systemtime, TRUE, modifier == 'O'))
2316                 {
2317                   /* Ignore the error; this placeholder will be replaced with nothing */
2318                 }
2319 	      break;
2320 	    case 'B':
2321               if (!append_month_name (result, lcid, &systemtime, FALSE, modifier == 'O'))
2322                 {
2323                   /* Ignore the error; this placeholder will be replaced with nothing */
2324                 }
2325 	      break;
2326 	    case 'c':
2327 	      n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2328 	      if (n > 0)
2329 		{
2330 		  g_array_set_size (result, result->len + n);
2331 		  GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2332 		  g_array_set_size (result, result->len - 1);
2333 		}
2334 	      g_array_append_vals (result, L" ", 1);
2335 	      n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2336 	      if (n > 0)
2337 		{
2338 		  g_array_set_size (result, result->len + n);
2339 		  GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2340 		  g_array_set_size (result, result->len - 1);
2341 		}
2342 	      break;
2343 	    case 'C':
2344 	      g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
2345 	      g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1);
2346 	      break;
2347 	    case 'd':
2348 	      g_array_append_vals (result, digits + systemtime.wDay/10, 1);
2349 	      g_array_append_vals (result, digits + systemtime.wDay%10, 1);
2350 	      break;
2351 	    case 'D':
2352 	      g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
2353 	      g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
2354 	      g_array_append_vals (result, L"/", 1);
2355 	      g_array_append_vals (result, digits + systemtime.wDay/10, 1);
2356 	      g_array_append_vals (result, digits + systemtime.wDay%10, 1);
2357 	      g_array_append_vals (result, L"/", 1);
2358 	      g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
2359 	      g_array_append_vals (result, digits + systemtime.wYear%10, 1);
2360 	      break;
2361 	    case 'e':
2362 	      if (systemtime.wDay >= 10)
2363 		g_array_append_vals (result, digits + systemtime.wDay/10, 1);
2364 	      else
2365 		g_array_append_vals (result, L" ", 1);
2366 	      g_array_append_vals (result, digits + systemtime.wDay%10, 1);
2367 	      break;
2368 
2369 	      /* A GDate has no time fields, so for now we can
2370 	       * hardcode all time conversions into zeros (or 12 for
2371 	       * %I). The alternative code snippets in the #else
2372 	       * branches are here ready to be taken into use when
2373 	       * needed by a g_strftime() or g_date_and_time_format()
2374 	       * or whatever.
2375 	       */
2376 	    case 'H':
2377 #if 1
2378 	      g_array_append_vals (result, L"00", 2);
2379 #else
2380 	      g_array_append_vals (result, digits + systemtime.wHour/10, 1);
2381 	      g_array_append_vals (result, digits + systemtime.wHour%10, 1);
2382 #endif
2383 	      break;
2384 	    case 'I':
2385 #if 1
2386 	      g_array_append_vals (result, L"12", 2);
2387 #else
2388 	      if (systemtime.wHour == 0)
2389 		g_array_append_vals (result, L"12", 2);
2390 	      else
2391 		{
2392 		  g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
2393 		  g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
2394 		}
2395 #endif
2396 	      break;
2397 	    case  'j':
2398 	      g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1);
2399 	      g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1);
2400 	      g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1);
2401 	      break;
2402 	    case 'm':
2403 	      g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
2404 	      g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
2405 	      break;
2406 	    case 'M':
2407 #if 1
2408 	      g_array_append_vals (result, L"00", 2);
2409 #else
2410 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2411 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2412 #endif
2413 	      break;
2414 	    case 'n':
2415 	      g_array_append_vals (result, L"\n", 1);
2416 	      break;
2417 	    case 'p':
2418 	      n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
2419 	      if (n > 0)
2420 		{
2421 		  g_array_set_size (result, result->len + n);
2422 		  GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
2423 		  g_array_set_size (result, result->len - 1);
2424 		}
2425 	      break;
2426 	    case 'r':
2427 	      /* This is a rather odd format. Hard to say what to do.
2428 	       * Let's always use the POSIX %I:%M:%S %p
2429 	       */
2430 #if 1
2431 	      g_array_append_vals (result, L"12:00:00", 8);
2432 #else
2433 	      if (systemtime.wHour == 0)
2434 		g_array_append_vals (result, L"12", 2);
2435 	      else
2436 		{
2437 		  g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
2438 		  g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
2439 		}
2440 	      g_array_append_vals (result, L":", 1);
2441 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2442 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2443 	      g_array_append_vals (result, L":", 1);
2444 	      g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
2445 	      g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
2446 	      g_array_append_vals (result, L" ", 1);
2447 #endif
2448 	      n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
2449 	      if (n > 0)
2450 		{
2451 		  g_array_set_size (result, result->len + n);
2452 		  GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
2453 		  g_array_set_size (result, result->len - 1);
2454 		}
2455 	      break;
2456 	    case 'R':
2457 #if 1
2458 	      g_array_append_vals (result, L"00:00", 5);
2459 #else
2460 	      g_array_append_vals (result, digits + systemtime.wHour/10, 1);
2461 	      g_array_append_vals (result, digits + systemtime.wHour%10, 1);
2462 	      g_array_append_vals (result, L":", 1);
2463 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2464 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2465 #endif
2466 	      break;
2467 	    case 'S':
2468 #if 1
2469 	      g_array_append_vals (result, L"00", 2);
2470 #else
2471 	      g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
2472 	      g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
2473 #endif
2474 	      break;
2475 	    case 't':
2476 	      g_array_append_vals (result, L"\t", 1);
2477 	      break;
2478 	    case 'T':
2479 #if 1
2480 	      g_array_append_vals (result, L"00:00:00", 8);
2481 #else
2482 	      g_array_append_vals (result, digits + systemtime.wHour/10, 1);
2483 	      g_array_append_vals (result, digits + systemtime.wHour%10, 1);
2484 	      g_array_append_vals (result, L":", 1);
2485 	      g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
2486 	      g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
2487 	      g_array_append_vals (result, L":", 1);
2488 	      g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
2489 	      g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
2490 #endif
2491 	      break;
2492 	    case 'u':
2493 	      if (systemtime.wDayOfWeek == 0)
2494 		g_array_append_vals (result, L"7", 1);
2495 	      else
2496 		g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
2497 	      break;
2498 	    case 'U':
2499 	      n = g_date_get_sunday_week_of_year (d);
2500 	      g_array_append_vals (result, digits + n/10, 1);
2501 	      g_array_append_vals (result, digits + n%10, 1);
2502 	      break;
2503 	    case 'V':
2504 	      n = g_date_get_iso8601_week_of_year (d);
2505 	      g_array_append_vals (result, digits + n/10, 1);
2506 	      g_array_append_vals (result, digits + n%10, 1);
2507 	      break;
2508 	    case 'w':
2509 	      g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
2510 	      break;
2511 	    case 'W':
2512 	      n = g_date_get_monday_week_of_year (d);
2513 	      g_array_append_vals (result, digits + n/10, 1);
2514 	      g_array_append_vals (result, digits + n%10, 1);
2515 	      break;
2516 	    case 'x':
2517 	      n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2518 	      if (n > 0)
2519 		{
2520 		  g_array_set_size (result, result->len + n);
2521 		  GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2522 		  g_array_set_size (result, result->len - 1);
2523 		}
2524 	      break;
2525 	    case 'X':
2526 	      n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
2527 	      if (n > 0)
2528 		{
2529 		  g_array_set_size (result, result->len + n);
2530 		  GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
2531 		  g_array_set_size (result, result->len - 1);
2532 		}
2533 	      break;
2534 	    case 'y':
2535 	      g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
2536 	      g_array_append_vals (result, digits + systemtime.wYear%10, 1);
2537 	      break;
2538 	    case 'Y':
2539 	      g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
2540 	      g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1);
2541 	      g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
2542 	      g_array_append_vals (result, digits + systemtime.wYear%10, 1);
2543 	      break;
2544 	    case 'Z':
2545 	      n = GetTimeZoneInformation (&tzinfo);
2546 	      if (n == TIME_ZONE_ID_UNKNOWN)
2547 		;
2548 	      else if (n == TIME_ZONE_ID_STANDARD)
2549 		g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName));
2550 	      else if (n == TIME_ZONE_ID_DAYLIGHT)
2551 		g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName));
2552 	      break;
2553 	    case '%':
2554 	      g_array_append_vals (result, L"%", 1);
2555 	      break;
2556 	    }
2557 	}
2558       else if (c <= 0xFFFF)
2559 	{
2560 	  wchar_t wc = c;
2561 	  g_array_append_vals (result, &wc, 1);
2562 	}
2563       else
2564 	{
2565 	  glong nwc;
2566 	  wchar_t *ws;
2567 
2568 	  ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL);
2569 	  g_array_append_vals (result, ws, nwc);
2570 	  g_free (ws);
2571 	}
2572       p = g_utf8_next_char (p);
2573     }
2574 
2575   convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL);
2576   g_array_free (result, TRUE);
2577 
2578   if (!convbuf)
2579     {
2580       s[0] = '\0';
2581       return 0;
2582     }
2583 
2584   if (slen <= convlen)
2585     {
2586       /* Ensure only whole characters are copied into the buffer. */
2587       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
2588       g_assert (end != NULL);
2589       convlen = end - convbuf;
2590 
2591       /* Return 0 because the buffer isn't large enough. */
2592       retval = 0;
2593     }
2594   else
2595     retval = convlen;
2596 
2597   memcpy (s, convbuf, convlen);
2598   s[convlen] = '\0';
2599   g_free (convbuf);
2600 
2601   return retval;
2602 }
2603 
2604 #endif
2605 
2606 /**
2607  * g_date_strftime:
2608  * @s: destination buffer
2609  * @slen: buffer size
2610  * @format: format string
2611  * @date: valid #GDate
2612  *
2613  * Generates a printed representation of the date, in a
2614  * [locale][setlocale]-specific way.
2615  * Works just like the platform's C library strftime() function,
2616  * but only accepts date-related formats; time-related formats
2617  * give undefined results. Date must be valid. Unlike strftime()
2618  * (which uses the locale encoding), works on a UTF-8 format
2619  * string and stores a UTF-8 result.
2620  *
2621  * This function does not provide any conversion specifiers in
2622  * addition to those implemented by the platform's C library.
2623  * For example, don't expect that using g_date_strftime() would
2624  * make the \%F provided by the C99 strftime() work on Windows
2625  * where the C library only complies to C89.
2626  *
2627  * Returns: number of characters written to the buffer, or 0 the buffer was too small
2628  */
2629 #pragma GCC diagnostic push
2630 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
2631 
2632 gsize
g_date_strftime(gchar * s,gsize slen,const gchar * format,const GDate * d)2633 g_date_strftime (gchar       *s,
2634                  gsize        slen,
2635                  const gchar *format,
2636                  const GDate *d)
2637 {
2638   struct tm tm;
2639 #ifndef G_OS_WIN32
2640   gsize locale_format_len = 0;
2641   gchar *locale_format;
2642   gsize tmplen;
2643   gchar *tmpbuf;
2644   gsize tmpbufsize;
2645   gsize convlen = 0;
2646   gchar *convbuf;
2647   GError *error = NULL;
2648   gsize retval;
2649 #endif
2650 
2651   g_return_val_if_fail (g_date_valid (d), 0);
2652   g_return_val_if_fail (slen > 0, 0);
2653   g_return_val_if_fail (format != NULL, 0);
2654   g_return_val_if_fail (s != NULL, 0);
2655 
2656   g_date_to_struct_tm (d, &tm);
2657 
2658 #ifdef G_OS_WIN32
2659   if (!g_utf8_validate (format, -1, NULL))
2660     {
2661       s[0] = '\0';
2662       return 0;
2663     }
2664   return win32_strftime_helper (d, format, &tm, s, slen);
2665 #else
2666 
2667   locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
2668 
2669   if (error)
2670     {
2671       g_warning (G_STRLOC "Error converting format to locale encoding: %s", error->message);
2672       g_error_free (error);
2673 
2674       s[0] = '\0';
2675       return 0;
2676     }
2677 
2678   tmpbufsize = MAX (128, locale_format_len * 2);
2679   while (TRUE)
2680     {
2681       tmpbuf = g_malloc (tmpbufsize);
2682 
2683       /* Set the first byte to something other than '\0', to be able to
2684        * recognize whether strftime actually failed or just returned "".
2685        */
2686       tmpbuf[0] = '\1';
2687       tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
2688 
2689       if (tmplen == 0 && tmpbuf[0] != '\0')
2690         {
2691           g_free (tmpbuf);
2692           tmpbufsize *= 2;
2693 
2694           if (tmpbufsize > 65536)
2695             {
2696               g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up");
2697               g_free (locale_format);
2698 
2699               s[0] = '\0';
2700               return 0;
2701             }
2702         }
2703       else
2704         break;
2705     }
2706   g_free (locale_format);
2707 
2708   convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
2709   g_free (tmpbuf);
2710 
2711   if (error)
2712     {
2713       g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s", error->message);
2714       g_error_free (error);
2715 
2716       s[0] = '\0';
2717       return 0;
2718     }
2719 
2720   if (slen <= convlen)
2721     {
2722       /* Ensure only whole characters are copied into the buffer.
2723        */
2724       gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
2725       g_assert (end != NULL);
2726       convlen = end - convbuf;
2727 
2728       /* Return 0 because the buffer isn't large enough.
2729        */
2730       retval = 0;
2731     }
2732   else
2733     retval = convlen;
2734 
2735   memcpy (s, convbuf, convlen);
2736   s[convlen] = '\0';
2737   g_free (convbuf);
2738 
2739   return retval;
2740 #endif
2741 }
2742 
2743 #pragma GCC diagnostic pop
2744