• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* gdatetime.c
2  *
3  * Copyright (C) 2009-2010 Christian Hergert <chris@dronelabs.com>
4  * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
5  * Copyright (C) 2010 Emmanuele Bassi <ebassi@linux.intel.com>
6  * Copyright © 2010 Codethink Limited
7  * Copyright © 2018 Tomasz Miąsko
8  *
9  * This library is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as
11  * published by the Free Software Foundation; either version 2.1 of the
12  * licence, or (at your option) any later version.
13  *
14  * This is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, see <http://www.gnu.org/licenses/>.
21  *
22  * Authors: Christian Hergert <chris@dronelabs.com>
23  *          Thiago Santos <thiago.sousa.santos@collabora.co.uk>
24  *          Emmanuele Bassi <ebassi@linux.intel.com>
25  *          Ryan Lortie <desrt@desrt.ca>
26  *          Robert Ancell <robert.ancell@canonical.com>
27  */
28 
29 /* Algorithms within this file are based on the Calendar FAQ by
30  * Claus Tondering.  It can be found at
31  * http://www.tondering.dk/claus/cal/calendar29.txt
32  *
33  * Copyright and disclaimer
34  * ------------------------
35  *   This document is Copyright (C) 2008 by Claus Tondering.
36  *   E-mail: claus@tondering.dk. (Please include the word
37  *   "calendar" in the subject line.)
38  *   The document may be freely distributed, provided this
39  *   copyright notice is included and no money is charged for
40  *   the document.
41  *
42  *   This document is provided "as is". No warranties are made as
43  *   to its correctness.
44  */
45 
46 /* Prologue {{{1 */
47 
48 #include "config.h"
49 
50 /* langinfo.h in glibc 2.27 defines ALTMON_* only if _GNU_SOURCE is defined.  */
51 #ifndef _GNU_SOURCE
52 #define _GNU_SOURCE 1
53 #endif
54 
55 #include <stdlib.h>
56 #include <string.h>
57 
58 #ifdef HAVE_LANGINFO_TIME
59 #include <langinfo.h>
60 #endif
61 
62 #include "gdatetime.h"
63 
64 #include "gslice.h"
65 #include "gatomic.h"
66 #include "gcharset.h"
67 #include "gconvert.h"
68 #include "gfileutils.h"
69 #include "ghash.h"
70 #include "gmain.h"
71 #include "gmappedfile.h"
72 #include "gstrfuncs.h"
73 #include "gtestutils.h"
74 #include "gthread.h"
75 #include "gtimezone.h"
76 
77 #include "glibintl.h"
78 
79 #ifndef G_OS_WIN32
80 #include <sys/time.h>
81 #include <time.h>
82 #endif /* !G_OS_WIN32 */
83 
84 /**
85  * SECTION:date-time
86  * @title: GDateTime
87  * @short_description: a structure representing Date and Time
88  * @see_also: #GTimeZone
89  *
90  * #GDateTime is a structure that combines a Gregorian date and time
91  * into a single structure.  It provides many conversion and methods to
92  * manipulate dates and times.  Time precision is provided down to
93  * microseconds and the time can range (proleptically) from 0001-01-01
94  * 00:00:00 to 9999-12-31 23:59:59.999999.  #GDateTime follows POSIX
95  * time in the sense that it is oblivious to leap seconds.
96  *
97  * #GDateTime is an immutable object; once it has been created it cannot
98  * be modified further.  All modifiers will create a new #GDateTime.
99  * Nearly all such functions can fail due to the date or time going out
100  * of range, in which case %NULL will be returned.
101  *
102  * #GDateTime is reference counted: the reference count is increased by calling
103  * g_date_time_ref() and decreased by calling g_date_time_unref(). When the
104  * reference count drops to 0, the resources allocated by the #GDateTime
105  * structure are released.
106  *
107  * Many parts of the API may produce non-obvious results.  As an
108  * example, adding two months to January 31st will yield March 31st
109  * whereas adding one month and then one month again will yield either
110  * March 28th or March 29th.  Also note that adding 24 hours is not
111  * always the same as adding one day (since days containing daylight
112  * savings time transitions are either 23 or 25 hours in length).
113  *
114  * #GDateTime is available since GLib 2.26.
115  */
116 
117 struct _GDateTime
118 {
119   /* Microsecond timekeeping within Day */
120   guint64 usec;
121 
122   /* TimeZone information */
123   GTimeZone *tz;
124   gint interval;
125 
126   /* 1 is 0001-01-01 in Proleptic Gregorian */
127   gint32 days;
128 
129   volatile gint ref_count;
130 };
131 
132 /* Time conversion {{{1 */
133 
134 #define UNIX_EPOCH_START     719163
135 #define INSTANT_TO_UNIX(instant) \
136   ((instant)/USEC_PER_SECOND - UNIX_EPOCH_START * SEC_PER_DAY)
137 #define INSTANT_TO_UNIX_USECS(instant) \
138   ((instant) - UNIX_EPOCH_START * SEC_PER_DAY * USEC_PER_SECOND)
139 #define UNIX_TO_INSTANT(unix) \
140   (((gint64) (unix) + UNIX_EPOCH_START * SEC_PER_DAY) * USEC_PER_SECOND)
141 #define UNIX_USECS_TO_INSTANT(unix_usecs) \
142   ((gint64) (unix_usecs) + UNIX_EPOCH_START * SEC_PER_DAY * USEC_PER_SECOND)
143 #define UNIX_TO_INSTANT_IS_VALID(unix) \
144   ((gint64) (unix) <= INSTANT_TO_UNIX (G_MAXINT64))
145 #define UNIX_USECS_TO_INSTANT_IS_VALID(unix_usecs) \
146   ((gint64) (unix_usecs) <= INSTANT_TO_UNIX_USECS (G_MAXINT64))
147 
148 #define DAYS_IN_4YEARS    1461    /* days in 4 years */
149 #define DAYS_IN_100YEARS  36524   /* days in 100 years */
150 #define DAYS_IN_400YEARS  146097  /* days in 400 years  */
151 
152 #define USEC_PER_SECOND      (G_GINT64_CONSTANT (1000000))
153 #define USEC_PER_MINUTE      (G_GINT64_CONSTANT (60000000))
154 #define USEC_PER_HOUR        (G_GINT64_CONSTANT (3600000000))
155 #define USEC_PER_MILLISECOND (G_GINT64_CONSTANT (1000))
156 #define USEC_PER_DAY         (G_GINT64_CONSTANT (86400000000))
157 #define SEC_PER_DAY          (G_GINT64_CONSTANT (86400))
158 
159 #define SECS_PER_MINUTE (60)
160 #define SECS_PER_HOUR   (60 * SECS_PER_MINUTE)
161 #define SECS_PER_DAY    (24 * SECS_PER_HOUR)
162 #define SECS_PER_YEAR   (365 * SECS_PER_DAY)
163 #define SECS_PER_JULIAN (DAYS_PER_PERIOD * SECS_PER_DAY)
164 
165 #define GREGORIAN_LEAP(y)    ((((y) % 4) == 0) && (!((((y) % 100) == 0) && (((y) % 400) != 0))))
166 #define JULIAN_YEAR(d)       ((d)->julian / 365.25)
167 #define DAYS_PER_PERIOD      (G_GINT64_CONSTANT (2914695))
168 
169 static const guint16 days_in_months[2][13] =
170 {
171   { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
172   { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
173 };
174 
175 static const guint16 days_in_year[2][13] =
176 {
177   {  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
178   {  0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
179 };
180 
181 #ifdef HAVE_LANGINFO_TIME
182 
183 #define GET_AMPM(d) ((g_date_time_get_hour (d) < 12) ? \
184                      nl_langinfo (AM_STR) : \
185                      nl_langinfo (PM_STR))
186 #define GET_AMPM_IS_LOCALE TRUE
187 
188 #define PREFERRED_DATE_TIME_FMT nl_langinfo (D_T_FMT)
189 #define PREFERRED_DATE_FMT nl_langinfo (D_FMT)
190 #define PREFERRED_TIME_FMT nl_langinfo (T_FMT)
191 #define PREFERRED_12HR_TIME_FMT nl_langinfo (T_FMT_AMPM)
192 
193 static const gint weekday_item[2][7] =
194 {
195   { ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABDAY_1 },
196   { DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7, DAY_1 }
197 };
198 
199 static const gint month_item[2][12] =
200 {
201   { ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12 },
202   { MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12 },
203 };
204 
205 #define WEEKDAY_ABBR(d) nl_langinfo (weekday_item[0][g_date_time_get_day_of_week (d) - 1])
206 #define WEEKDAY_ABBR_IS_LOCALE TRUE
207 #define WEEKDAY_FULL(d) nl_langinfo (weekday_item[1][g_date_time_get_day_of_week (d) - 1])
208 #define WEEKDAY_FULL_IS_LOCALE TRUE
209 #define MONTH_ABBR(d) nl_langinfo (month_item[0][g_date_time_get_month (d) - 1])
210 #define MONTH_ABBR_IS_LOCALE TRUE
211 #define MONTH_FULL(d) nl_langinfo (month_item[1][g_date_time_get_month (d) - 1])
212 #define MONTH_FULL_IS_LOCALE TRUE
213 
214 #else
215 
216 #define GET_AMPM(d)          (get_fallback_ampm (g_date_time_get_hour (d)))
217 #define GET_AMPM_IS_LOCALE   FALSE
218 
219 /* Translators: this is the preferred format for expressing the date and the time */
220 #define PREFERRED_DATE_TIME_FMT C_("GDateTime", "%a %b %e %H:%M:%S %Y")
221 
222 /* Translators: this is the preferred format for expressing the date */
223 #define PREFERRED_DATE_FMT C_("GDateTime", "%m/%d/%y")
224 
225 /* Translators: this is the preferred format for expressing the time */
226 #define PREFERRED_TIME_FMT C_("GDateTime", "%H:%M:%S")
227 
228 /* Translators: this is the preferred format for expressing 12 hour time */
229 #define PREFERRED_12HR_TIME_FMT C_("GDateTime", "%I:%M:%S %p")
230 
231 #define WEEKDAY_ABBR(d)       (get_weekday_name_abbr (g_date_time_get_day_of_week (d)))
232 #define WEEKDAY_ABBR_IS_LOCALE FALSE
233 #define WEEKDAY_FULL(d)       (get_weekday_name (g_date_time_get_day_of_week (d)))
234 #define WEEKDAY_FULL_IS_LOCALE FALSE
235 /* We don't yet know if nl_langinfo (MON_n) returns standalone or complete-date
236  * format forms but if nl_langinfo (ALTMON_n) is not supported then we will
237  * have to use MONTH_FULL as standalone.  The same if nl_langinfo () does not
238  * exist at all.  MONTH_ABBR is similar: if nl_langinfo (_NL_ABALTMON_n) is not
239  * supported then we will use MONTH_ABBR as standalone.
240  */
241 #define MONTH_ABBR(d)         (get_month_name_abbr_standalone (g_date_time_get_month (d)))
242 #define MONTH_ABBR_IS_LOCALE  FALSE
243 #define MONTH_FULL(d)         (get_month_name_standalone (g_date_time_get_month (d)))
244 #define MONTH_FULL_IS_LOCALE  FALSE
245 
246 static const gchar *
get_month_name_standalone(gint month)247 get_month_name_standalone (gint month)
248 {
249   switch (month)
250     {
251     case 1:
252       /* Translators: Some languages (Baltic, Slavic, Greek, and some more)
253        * need different grammatical forms of month names depending on whether
254        * they are standalone or in a complete date context, with the day
255        * number.  Some other languages may prefer starting with uppercase when
256        * they are standalone and with lowercase when they are in a complete
257        * date context.  Here are full month names in a form appropriate when
258        * they are used standalone.  If your system is Linux with the glibc
259        * version 2.27 (released Feb 1, 2018) or newer or if it is from the BSD
260        * family (which includes OS X) then you can refer to the date command
261        * line utility and see what the command `date +%OB' produces.  Also in
262        * the latest Linux the command `locale alt_mon' in your native locale
263        * produces a complete list of month names almost ready to copy and
264        * paste here.  Note that in most of the languages (western European,
265        * non-European) there is no difference between the standalone and
266        * complete date form.
267        */
268       return C_("full month name", "January");
269     case 2:
270       return C_("full month name", "February");
271     case 3:
272       return C_("full month name", "March");
273     case 4:
274       return C_("full month name", "April");
275     case 5:
276       return C_("full month name", "May");
277     case 6:
278       return C_("full month name", "June");
279     case 7:
280       return C_("full month name", "July");
281     case 8:
282       return C_("full month name", "August");
283     case 9:
284       return C_("full month name", "September");
285     case 10:
286       return C_("full month name", "October");
287     case 11:
288       return C_("full month name", "November");
289     case 12:
290       return C_("full month name", "December");
291 
292     default:
293       g_warning ("Invalid month number %d", month);
294     }
295 
296   return NULL;
297 }
298 
299 static const gchar *
get_month_name_abbr_standalone(gint month)300 get_month_name_abbr_standalone (gint month)
301 {
302   switch (month)
303     {
304     case 1:
305       /* Translators: Some languages need different grammatical forms of
306        * month names depending on whether they are standalone or in a complete
307        * date context, with the day number.  Some may prefer starting with
308        * uppercase when they are standalone and with lowercase when they are
309        * in a full date context.  However, as these names are abbreviated
310        * the grammatical difference is visible probably only in Belarusian
311        * and Russian.  In other languages there is no difference between
312        * the standalone and complete date form when they are abbreviated.
313        * If your system is Linux with the glibc version 2.27 (released
314        * Feb 1, 2018) or newer then you can refer to the date command line
315        * utility and see what the command `date +%Ob' produces.  Also in
316        * the latest Linux the command `locale ab_alt_mon' in your native
317        * locale produces a complete list of month names almost ready to copy
318        * and paste here.  Note that this feature is not yet supported by any
319        * other platform.  Here are abbreviated month names in a form
320        * appropriate when they are used standalone.
321        */
322       return C_("abbreviated month name", "Jan");
323     case 2:
324       return C_("abbreviated month name", "Feb");
325     case 3:
326       return C_("abbreviated month name", "Mar");
327     case 4:
328       return C_("abbreviated month name", "Apr");
329     case 5:
330       return C_("abbreviated month name", "May");
331     case 6:
332       return C_("abbreviated month name", "Jun");
333     case 7:
334       return C_("abbreviated month name", "Jul");
335     case 8:
336       return C_("abbreviated month name", "Aug");
337     case 9:
338       return C_("abbreviated month name", "Sep");
339     case 10:
340       return C_("abbreviated month name", "Oct");
341     case 11:
342       return C_("abbreviated month name", "Nov");
343     case 12:
344       return C_("abbreviated month name", "Dec");
345 
346     default:
347       g_warning ("Invalid month number %d", month);
348     }
349 
350   return NULL;
351 }
352 
353 static const gchar *
get_weekday_name(gint day)354 get_weekday_name (gint day)
355 {
356   switch (day)
357     {
358     case 1:
359       return C_("full weekday name", "Monday");
360     case 2:
361       return C_("full weekday name", "Tuesday");
362     case 3:
363       return C_("full weekday name", "Wednesday");
364     case 4:
365       return C_("full weekday name", "Thursday");
366     case 5:
367       return C_("full weekday name", "Friday");
368     case 6:
369       return C_("full weekday name", "Saturday");
370     case 7:
371       return C_("full weekday name", "Sunday");
372 
373     default:
374       g_warning ("Invalid week day number %d", day);
375     }
376 
377   return NULL;
378 }
379 
380 static const gchar *
get_weekday_name_abbr(gint day)381 get_weekday_name_abbr (gint day)
382 {
383   switch (day)
384     {
385     case 1:
386       return C_("abbreviated weekday name", "Mon");
387     case 2:
388       return C_("abbreviated weekday name", "Tue");
389     case 3:
390       return C_("abbreviated weekday name", "Wed");
391     case 4:
392       return C_("abbreviated weekday name", "Thu");
393     case 5:
394       return C_("abbreviated weekday name", "Fri");
395     case 6:
396       return C_("abbreviated weekday name", "Sat");
397     case 7:
398       return C_("abbreviated weekday name", "Sun");
399 
400     default:
401       g_warning ("Invalid week day number %d", day);
402     }
403 
404   return NULL;
405 }
406 
407 #endif  /* HAVE_LANGINFO_TIME */
408 
409 #ifdef HAVE_LANGINFO_ALTMON
410 
411 /* If nl_langinfo () supports ALTMON_n then MON_n returns full date format
412  * forms and ALTMON_n returns standalone forms.
413  */
414 
415 #define MONTH_FULL_WITH_DAY(d) MONTH_FULL(d)
416 #define MONTH_FULL_WITH_DAY_IS_LOCALE MONTH_FULL_IS_LOCALE
417 
418 static const gint alt_month_item[12] =
419 {
420   ALTMON_1, ALTMON_2, ALTMON_3, ALTMON_4, ALTMON_5, ALTMON_6,
421   ALTMON_7, ALTMON_8, ALTMON_9, ALTMON_10, ALTMON_11, ALTMON_12
422 };
423 
424 #define MONTH_FULL_STANDALONE(d) nl_langinfo (alt_month_item[g_date_time_get_month (d) - 1])
425 #define MONTH_FULL_STANDALONE_IS_LOCALE TRUE
426 
427 #else
428 
429 /* If nl_langinfo () does not support ALTMON_n then either MON_n returns
430  * standalone forms or nl_langinfo (MON_n) does not work so we have defined
431  * it as standalone form.
432  */
433 
434 #define MONTH_FULL_STANDALONE(d) MONTH_FULL(d)
435 #define MONTH_FULL_STANDALONE_IS_LOCALE MONTH_FULL_IS_LOCALE
436 #define MONTH_FULL_WITH_DAY(d) (get_month_name_with_day (g_date_time_get_month (d)))
437 #define MONTH_FULL_WITH_DAY_IS_LOCALE FALSE
438 
439 static const gchar *
get_month_name_with_day(gint month)440 get_month_name_with_day (gint month)
441 {
442   switch (month)
443     {
444     case 1:
445       /* Translators: Some languages need different grammatical forms of
446        * month names depending on whether they are standalone or in a full
447        * date context, with the day number.  Some may prefer starting with
448        * uppercase when they are standalone and with lowercase when they are
449        * in a full date context.  Here are full month names in a form
450        * appropriate when they are used in a full date context, with the
451        * day number.  If your system is Linux with the glibc version 2.27
452        * (released Feb 1, 2018) or newer or if it is from the BSD family
453        * (which includes OS X) then you can refer to the date command line
454        * utility and see what the command `date +%B' produces.  Also in
455        * the latest Linux the command `locale mon' in your native locale
456        * produces a complete list of month names almost ready to copy and
457        * paste here.  In older Linux systems due to a bug the result is
458        * incorrect in some languages.  Note that in most of the languages
459        * (western European, non-European) there is no difference between the
460        * standalone and complete date form.
461        */
462       return C_("full month name with day", "January");
463     case 2:
464       return C_("full month name with day", "February");
465     case 3:
466       return C_("full month name with day", "March");
467     case 4:
468       return C_("full month name with day", "April");
469     case 5:
470       return C_("full month name with day", "May");
471     case 6:
472       return C_("full month name with day", "June");
473     case 7:
474       return C_("full month name with day", "July");
475     case 8:
476       return C_("full month name with day", "August");
477     case 9:
478       return C_("full month name with day", "September");
479     case 10:
480       return C_("full month name with day", "October");
481     case 11:
482       return C_("full month name with day", "November");
483     case 12:
484       return C_("full month name with day", "December");
485 
486     default:
487       g_warning ("Invalid month number %d", month);
488     }
489 
490   return NULL;
491 }
492 
493 #endif  /* HAVE_LANGINFO_ALTMON */
494 
495 #ifdef HAVE_LANGINFO_ABALTMON
496 
497 /* If nl_langinfo () supports _NL_ABALTMON_n then ABMON_n returns full
498  * date format forms and _NL_ABALTMON_n returns standalone forms.
499  */
500 
501 #define MONTH_ABBR_WITH_DAY(d) MONTH_ABBR(d)
502 #define MONTH_ABBR_WITH_DAY_IS_LOCALE MONTH_ABBR_IS_LOCALE
503 
504 static const gint ab_alt_month_item[12] =
505 {
506   _NL_ABALTMON_1, _NL_ABALTMON_2, _NL_ABALTMON_3, _NL_ABALTMON_4,
507   _NL_ABALTMON_5, _NL_ABALTMON_6, _NL_ABALTMON_7, _NL_ABALTMON_8,
508   _NL_ABALTMON_9, _NL_ABALTMON_10, _NL_ABALTMON_11, _NL_ABALTMON_12
509 };
510 
511 #define MONTH_ABBR_STANDALONE(d) nl_langinfo (ab_alt_month_item[g_date_time_get_month (d) - 1])
512 #define MONTH_ABBR_STANDALONE_IS_LOCALE TRUE
513 
514 #else
515 
516 /* If nl_langinfo () does not support _NL_ABALTMON_n then either ABMON_n
517  * returns standalone forms or nl_langinfo (ABMON_n) does not work so we
518  * have defined it as standalone form. Now it's time to swap.
519  */
520 
521 #define MONTH_ABBR_STANDALONE(d) MONTH_ABBR(d)
522 #define MONTH_ABBR_STANDALONE_IS_LOCALE MONTH_ABBR_IS_LOCALE
523 #define MONTH_ABBR_WITH_DAY(d) (get_month_name_abbr_with_day (g_date_time_get_month (d)))
524 #define MONTH_ABBR_WITH_DAY_IS_LOCALE FALSE
525 
526 static const gchar *
get_month_name_abbr_with_day(gint month)527 get_month_name_abbr_with_day (gint month)
528 {
529   switch (month)
530     {
531     case 1:
532       /* Translators: Some languages need different grammatical forms of
533        * month names depending on whether they are standalone or in a full
534        * date context, with the day number.  Some may prefer starting with
535        * uppercase when they are standalone and with lowercase when they are
536        * in a full date context.  Here are abbreviated month names in a form
537        * appropriate when they are used in a full date context, with the
538        * day number.  However, as these names are abbreviated the grammatical
539        * difference is visible probably only in Belarusian and Russian.
540        * In other languages there is no difference between the standalone
541        * and complete date form when they are abbreviated.  If your system
542        * is Linux with the glibc version 2.27 (released Feb 1, 2018) or newer
543        * then you can refer to the date command line utility and see what the
544        * command `date +%b' produces.  Also in the latest Linux the command
545        * `locale abmon' in your native locale produces a complete list of
546        * month names almost ready to copy and paste here.  In other systems
547        * due to a bug the result is incorrect in some languages.
548        */
549       return C_("abbreviated month name with day", "Jan");
550     case 2:
551       return C_("abbreviated month name with day", "Feb");
552     case 3:
553       return C_("abbreviated month name with day", "Mar");
554     case 4:
555       return C_("abbreviated month name with day", "Apr");
556     case 5:
557       return C_("abbreviated month name with day", "May");
558     case 6:
559       return C_("abbreviated month name with day", "Jun");
560     case 7:
561       return C_("abbreviated month name with day", "Jul");
562     case 8:
563       return C_("abbreviated month name with day", "Aug");
564     case 9:
565       return C_("abbreviated month name with day", "Sep");
566     case 10:
567       return C_("abbreviated month name with day", "Oct");
568     case 11:
569       return C_("abbreviated month name with day", "Nov");
570     case 12:
571       return C_("abbreviated month name with day", "Dec");
572 
573     default:
574       g_warning ("Invalid month number %d", month);
575     }
576 
577   return NULL;
578 }
579 
580 #endif  /* HAVE_LANGINFO_ABALTMON */
581 
582 /* Format AM/PM indicator if the locale does not have a localized version. */
583 static const gchar *
get_fallback_ampm(gint hour)584 get_fallback_ampm (gint hour)
585 {
586   if (hour < 12)
587     /* Translators: 'before midday' indicator */
588     return C_("GDateTime", "AM");
589   else
590     /* Translators: 'after midday' indicator */
591     return C_("GDateTime", "PM");
592 }
593 
594 static inline gint
ymd_to_days(gint year,gint month,gint day)595 ymd_to_days (gint year,
596              gint month,
597              gint day)
598 {
599   gint64 days;
600 
601   days = (year - 1) * 365 + ((year - 1) / 4) - ((year - 1) / 100)
602       + ((year - 1) / 400);
603 
604   days += days_in_year[0][month - 1];
605   if (GREGORIAN_LEAP (year) && month > 2)
606     day++;
607 
608   days += day;
609 
610   return days;
611 }
612 
613 static void
g_date_time_get_week_number(GDateTime * datetime,gint * week_number,gint * day_of_week,gint * day_of_year)614 g_date_time_get_week_number (GDateTime *datetime,
615                              gint      *week_number,
616                              gint      *day_of_week,
617                              gint      *day_of_year)
618 {
619   gint a, b, c, d, e, f, g, n, s, month, day, year;
620 
621   g_date_time_get_ymd (datetime, &year, &month, &day);
622 
623   if (month <= 2)
624     {
625       a = g_date_time_get_year (datetime) - 1;
626       b = (a / 4) - (a / 100) + (a / 400);
627       c = ((a - 1) / 4) - ((a - 1) / 100) + ((a - 1) / 400);
628       s = b - c;
629       e = 0;
630       f = day - 1 + (31 * (month - 1));
631     }
632   else
633     {
634       a = year;
635       b = (a / 4) - (a / 100) + (a / 400);
636       c = ((a - 1) / 4) - ((a - 1) / 100) + ((a - 1) / 400);
637       s = b - c;
638       e = s + 1;
639       f = day + (((153 * (month - 3)) + 2) / 5) + 58 + s;
640     }
641 
642   g = (a + b) % 7;
643   d = (f + g - e) % 7;
644   n = f + 3 - d;
645 
646   if (week_number)
647     {
648       if (n < 0)
649         *week_number = 53 - ((g - s) / 5);
650       else if (n > 364 + s)
651         *week_number = 1;
652       else
653         *week_number = (n / 7) + 1;
654     }
655 
656   if (day_of_week)
657     *day_of_week = d + 1;
658 
659   if (day_of_year)
660     *day_of_year = f + 1;
661 }
662 
663 /* Lifecycle {{{1 */
664 
665 static GDateTime *
g_date_time_alloc(GTimeZone * tz)666 g_date_time_alloc (GTimeZone *tz)
667 {
668   GDateTime *datetime;
669 
670   datetime = g_slice_new0 (GDateTime);
671   datetime->tz = g_time_zone_ref (tz);
672   datetime->ref_count = 1;
673 
674   return datetime;
675 }
676 
677 /**
678  * g_date_time_ref:
679  * @datetime: a #GDateTime
680  *
681  * Atomically increments the reference count of @datetime by one.
682  *
683  * Returns: the #GDateTime with the reference count increased
684  *
685  * Since: 2.26
686  */
687 GDateTime *
g_date_time_ref(GDateTime * datetime)688 g_date_time_ref (GDateTime *datetime)
689 {
690   g_return_val_if_fail (datetime != NULL, NULL);
691   g_return_val_if_fail (datetime->ref_count > 0, NULL);
692 
693   g_atomic_int_inc (&datetime->ref_count);
694 
695   return datetime;
696 }
697 
698 /**
699  * g_date_time_unref:
700  * @datetime: a #GDateTime
701  *
702  * Atomically decrements the reference count of @datetime by one.
703  *
704  * When the reference count reaches zero, the resources allocated by
705  * @datetime are freed
706  *
707  * Since: 2.26
708  */
709 void
g_date_time_unref(GDateTime * datetime)710 g_date_time_unref (GDateTime *datetime)
711 {
712   g_return_if_fail (datetime != NULL);
713   g_return_if_fail (datetime->ref_count > 0);
714 
715   if (g_atomic_int_dec_and_test (&datetime->ref_count))
716     {
717       g_time_zone_unref (datetime->tz);
718       g_slice_free (GDateTime, datetime);
719     }
720 }
721 
722 /* Internal state transformers {{{1 */
723 /*< internal >
724  * g_date_time_to_instant:
725  * @datetime: a #GDateTime
726  *
727  * Convert a @datetime into an instant.
728  *
729  * An instant is a number that uniquely describes a particular
730  * microsecond in time, taking time zone considerations into account.
731  * (ie: "03:00 -0400" is the same instant as "02:00 -0500").
732  *
733  * An instant is always positive but we use a signed return value to
734  * avoid troubles with C.
735  */
736 static gint64
g_date_time_to_instant(GDateTime * datetime)737 g_date_time_to_instant (GDateTime *datetime)
738 {
739   gint64 offset;
740 
741   offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
742   offset *= USEC_PER_SECOND;
743 
744   return datetime->days * USEC_PER_DAY + datetime->usec - offset;
745 }
746 
747 /*< internal >
748  * g_date_time_from_instant:
749  * @tz: a #GTimeZone
750  * @instant: an instant in time
751  *
752  * Creates a #GDateTime from a time zone and an instant.
753  *
754  * This might fail if the time ends up being out of range.
755  */
756 static GDateTime *
g_date_time_from_instant(GTimeZone * tz,gint64 instant)757 g_date_time_from_instant (GTimeZone *tz,
758                           gint64     instant)
759 {
760   GDateTime *datetime;
761   gint64 offset;
762 
763   if (instant < 0 || instant > G_GINT64_CONSTANT (1000000000000000000))
764     return NULL;
765 
766   datetime = g_date_time_alloc (tz);
767   datetime->interval = g_time_zone_find_interval (tz,
768                                                   G_TIME_TYPE_UNIVERSAL,
769                                                   INSTANT_TO_UNIX (instant));
770   offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
771   offset *= USEC_PER_SECOND;
772 
773   instant += offset;
774 
775   datetime->days = instant / USEC_PER_DAY;
776   datetime->usec = instant % USEC_PER_DAY;
777 
778   if (datetime->days < 1 || 3652059 < datetime->days)
779     {
780       g_date_time_unref (datetime);
781       datetime = NULL;
782     }
783 
784   return datetime;
785 }
786 
787 
788 /*< internal >
789  * g_date_time_deal_with_date_change:
790  * @datetime: a #GDateTime
791  *
792  * This function should be called whenever the date changes by adding
793  * days, months or years.  It does three things.
794  *
795  * First, we ensure that the date falls between 0001-01-01 and
796  * 9999-12-31 and return %FALSE if it does not.
797  *
798  * Next we update the ->interval field.
799  *
800  * Finally, we ensure that the resulting date and time pair exists (by
801  * ensuring that our time zone has an interval containing it) and
802  * adjusting as required.  For example, if we have the time 02:30:00 on
803  * March 13 2010 in Toronto and we add 1 day to it, we would end up with
804  * 2:30am on March 14th, which doesn't exist.  In that case, we bump the
805  * time up to 3:00am.
806  */
807 static gboolean
g_date_time_deal_with_date_change(GDateTime * datetime)808 g_date_time_deal_with_date_change (GDateTime *datetime)
809 {
810   GTimeType was_dst;
811   gint64 full_time;
812   gint64 usec;
813 
814   if (datetime->days < 1 || datetime->days > 3652059)
815     return FALSE;
816 
817   was_dst = g_time_zone_is_dst (datetime->tz, datetime->interval);
818 
819   full_time = datetime->days * USEC_PER_DAY + datetime->usec;
820 
821 
822   usec = full_time % USEC_PER_SECOND;
823   full_time /= USEC_PER_SECOND;
824   full_time -= UNIX_EPOCH_START * SEC_PER_DAY;
825 
826   datetime->interval = g_time_zone_adjust_time (datetime->tz,
827                                                 was_dst,
828                                                 &full_time);
829   full_time += UNIX_EPOCH_START * SEC_PER_DAY;
830   full_time *= USEC_PER_SECOND;
831   full_time += usec;
832 
833   datetime->days = full_time / USEC_PER_DAY;
834   datetime->usec = full_time % USEC_PER_DAY;
835 
836   /* maybe daylight time caused us to shift to a different day,
837    * but it definitely didn't push us into a different year */
838   return TRUE;
839 }
840 
841 static GDateTime *
g_date_time_replace_days(GDateTime * datetime,gint days)842 g_date_time_replace_days (GDateTime *datetime,
843                           gint       days)
844 {
845   GDateTime *new;
846 
847   new = g_date_time_alloc (datetime->tz);
848   new->interval = datetime->interval;
849   new->usec = datetime->usec;
850   new->days = days;
851 
852   if (!g_date_time_deal_with_date_change (new))
853     {
854       g_date_time_unref (new);
855       new = NULL;
856     }
857 
858   return new;
859 }
860 
861 /* now/unix/timeval Constructors {{{1 */
862 
863 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
864 /*< internal >
865  * g_date_time_new_from_timeval:
866  * @tz: a #GTimeZone
867  * @tv: a #GTimeVal
868  *
869  * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
870  * given time zone @tz.
871  *
872  * The time contained in a #GTimeVal is always stored in the form of
873  * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
874  * given time zone.
875  *
876  * This call can fail (returning %NULL) if @tv represents a time outside
877  * of the supported range of #GDateTime.
878  *
879  * You should release the return value by calling g_date_time_unref()
880  * when you are done with it.
881  *
882  * Returns: a new #GDateTime, or %NULL
883  *
884  * Since: 2.26
885  **/
886 static GDateTime *
g_date_time_new_from_timeval(GTimeZone * tz,const GTimeVal * tv)887 g_date_time_new_from_timeval (GTimeZone      *tz,
888                               const GTimeVal *tv)
889 {
890   if ((gint64) tv->tv_sec > G_MAXINT64 - 1 ||
891       !UNIX_TO_INSTANT_IS_VALID ((gint64) tv->tv_sec + 1))
892     return NULL;
893 
894   return g_date_time_from_instant (tz, tv->tv_usec +
895                                    UNIX_TO_INSTANT (tv->tv_sec));
896 }
897 G_GNUC_END_IGNORE_DEPRECATIONS
898 
899 /*< internal >
900  * g_date_time_new_from_unix:
901  * @tz: a #GTimeZone
902  * @usecs: the Unix time, in microseconds since the epoch
903  *
904  * Creates a #GDateTime corresponding to the given Unix time @t_us in the
905  * given time zone @tz.
906  *
907  * Unix time is the number of seconds that have elapsed since 1970-01-01
908  * 00:00:00 UTC, regardless of the time zone given.
909  *
910  * This call can fail (returning %NULL) if @t represents a time outside
911  * of the supported range of #GDateTime.
912  *
913  * You should release the return value by calling g_date_time_unref()
914  * when you are done with it.
915  *
916  * Returns: a new #GDateTime, or %NULL
917  *
918  * Since: 2.26
919  **/
920 static GDateTime *
g_date_time_new_from_unix(GTimeZone * tz,gint64 usecs)921 g_date_time_new_from_unix (GTimeZone *tz,
922                            gint64     usecs)
923 {
924   if (!UNIX_USECS_TO_INSTANT_IS_VALID (usecs))
925     return NULL;
926 
927   return g_date_time_from_instant (tz, UNIX_USECS_TO_INSTANT (usecs));
928 }
929 
930 /**
931  * g_date_time_new_now:
932  * @tz: a #GTimeZone
933  *
934  * Creates a #GDateTime corresponding to this exact instant in the given
935  * time zone @tz.  The time is as accurate as the system allows, to a
936  * maximum accuracy of 1 microsecond.
937  *
938  * This function will always succeed unless the system clock is set to
939  * truly insane values (or unless GLib is still being used after the
940  * year 9999).
941  *
942  * You should release the return value by calling g_date_time_unref()
943  * when you are done with it.
944  *
945  * Returns: a new #GDateTime, or %NULL
946  *
947  * Since: 2.26
948  **/
949 GDateTime *
g_date_time_new_now(GTimeZone * tz)950 g_date_time_new_now (GTimeZone *tz)
951 {
952   gint64 now_us;
953 
954   now_us = g_get_real_time ();
955 
956   return g_date_time_new_from_unix (tz, now_us);
957 }
958 
959 /**
960  * g_date_time_new_now_local:
961  *
962  * Creates a #GDateTime corresponding to this exact instant in the local
963  * time zone.
964  *
965  * This is equivalent to calling g_date_time_new_now() with the time
966  * zone returned by g_time_zone_new_local().
967  *
968  * Returns: a new #GDateTime, or %NULL
969  *
970  * Since: 2.26
971  **/
972 GDateTime *
g_date_time_new_now_local(void)973 g_date_time_new_now_local (void)
974 {
975   GDateTime *datetime;
976   GTimeZone *local;
977 
978   local = g_time_zone_new_local ();
979   datetime = g_date_time_new_now (local);
980   g_time_zone_unref (local);
981 
982   return datetime;
983 }
984 
985 /**
986  * g_date_time_new_now_utc:
987  *
988  * Creates a #GDateTime corresponding to this exact instant in UTC.
989  *
990  * This is equivalent to calling g_date_time_new_now() with the time
991  * zone returned by g_time_zone_new_utc().
992  *
993  * Returns: a new #GDateTime, or %NULL
994  *
995  * Since: 2.26
996  **/
997 GDateTime *
g_date_time_new_now_utc(void)998 g_date_time_new_now_utc (void)
999 {
1000   GDateTime *datetime;
1001   GTimeZone *utc;
1002 
1003   utc = g_time_zone_new_utc ();
1004   datetime = g_date_time_new_now (utc);
1005   g_time_zone_unref (utc);
1006 
1007   return datetime;
1008 }
1009 
1010 /**
1011  * g_date_time_new_from_unix_local:
1012  * @t: the Unix time
1013  *
1014  * Creates a #GDateTime corresponding to the given Unix time @t in the
1015  * local time zone.
1016  *
1017  * Unix time is the number of seconds that have elapsed since 1970-01-01
1018  * 00:00:00 UTC, regardless of the local time offset.
1019  *
1020  * This call can fail (returning %NULL) if @t represents a time outside
1021  * of the supported range of #GDateTime.
1022  *
1023  * You should release the return value by calling g_date_time_unref()
1024  * when you are done with it.
1025  *
1026  * Returns: a new #GDateTime, or %NULL
1027  *
1028  * Since: 2.26
1029  **/
1030 GDateTime *
g_date_time_new_from_unix_local(gint64 t)1031 g_date_time_new_from_unix_local (gint64 t)
1032 {
1033   GDateTime *datetime;
1034   GTimeZone *local;
1035 
1036   if (t > G_MAXINT64 / USEC_PER_SECOND)
1037     return NULL;
1038 
1039   local = g_time_zone_new_local ();
1040   datetime = g_date_time_new_from_unix (local, t * USEC_PER_SECOND);
1041   g_time_zone_unref (local);
1042 
1043   return datetime;
1044 }
1045 
1046 /**
1047  * g_date_time_new_from_unix_utc:
1048  * @t: the Unix time
1049  *
1050  * Creates a #GDateTime corresponding to the given Unix time @t in UTC.
1051  *
1052  * Unix time is the number of seconds that have elapsed since 1970-01-01
1053  * 00:00:00 UTC.
1054  *
1055  * This call can fail (returning %NULL) if @t represents a time outside
1056  * of the supported range of #GDateTime.
1057  *
1058  * You should release the return value by calling g_date_time_unref()
1059  * when you are done with it.
1060  *
1061  * Returns: a new #GDateTime, or %NULL
1062  *
1063  * Since: 2.26
1064  **/
1065 GDateTime *
g_date_time_new_from_unix_utc(gint64 t)1066 g_date_time_new_from_unix_utc (gint64 t)
1067 {
1068   GDateTime *datetime;
1069   GTimeZone *utc;
1070 
1071   if (t > G_MAXINT64 / USEC_PER_SECOND)
1072     return NULL;
1073 
1074   utc = g_time_zone_new_utc ();
1075   datetime = g_date_time_new_from_unix (utc, t * USEC_PER_SECOND);
1076   g_time_zone_unref (utc);
1077 
1078   return datetime;
1079 }
1080 
1081 /**
1082  * g_date_time_new_from_timeval_local:
1083  * @tv: a #GTimeVal
1084  *
1085  * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
1086  * local time zone.
1087  *
1088  * The time contained in a #GTimeVal is always stored in the form of
1089  * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
1090  * local time offset.
1091  *
1092  * This call can fail (returning %NULL) if @tv represents a time outside
1093  * of the supported range of #GDateTime.
1094  *
1095  * You should release the return value by calling g_date_time_unref()
1096  * when you are done with it.
1097  *
1098  * Returns: a new #GDateTime, or %NULL
1099  *
1100  * Since: 2.26
1101  * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
1102  *    g_date_time_new_from_unix_local() instead.
1103  **/
1104 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1105 GDateTime *
g_date_time_new_from_timeval_local(const GTimeVal * tv)1106 g_date_time_new_from_timeval_local (const GTimeVal *tv)
1107 {
1108   GDateTime *datetime;
1109   GTimeZone *local;
1110 
1111   local = g_time_zone_new_local ();
1112   datetime = g_date_time_new_from_timeval (local, tv);
1113   g_time_zone_unref (local);
1114 
1115   return datetime;
1116 }
1117 G_GNUC_END_IGNORE_DEPRECATIONS
1118 
1119 /**
1120  * g_date_time_new_from_timeval_utc:
1121  * @tv: a #GTimeVal
1122  *
1123  * Creates a #GDateTime corresponding to the given #GTimeVal @tv in UTC.
1124  *
1125  * The time contained in a #GTimeVal is always stored in the form of
1126  * seconds elapsed since 1970-01-01 00:00:00 UTC.
1127  *
1128  * This call can fail (returning %NULL) if @tv represents a time outside
1129  * of the supported range of #GDateTime.
1130  *
1131  * You should release the return value by calling g_date_time_unref()
1132  * when you are done with it.
1133  *
1134  * Returns: a new #GDateTime, or %NULL
1135  *
1136  * Since: 2.26
1137  * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
1138  *    g_date_time_new_from_unix_utc() instead.
1139  **/
1140 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1141 GDateTime *
g_date_time_new_from_timeval_utc(const GTimeVal * tv)1142 g_date_time_new_from_timeval_utc (const GTimeVal *tv)
1143 {
1144   GDateTime *datetime;
1145   GTimeZone *utc;
1146 
1147   utc = g_time_zone_new_utc ();
1148   datetime = g_date_time_new_from_timeval (utc, tv);
1149   g_time_zone_unref (utc);
1150 
1151   return datetime;
1152 }
1153 G_GNUC_END_IGNORE_DEPRECATIONS
1154 
1155 /* Parse integers in the form d (week days), dd (hours etc), ddd (ordinal days) or dddd (years) */
1156 static gboolean
get_iso8601_int(const gchar * text,gsize length,gint * value)1157 get_iso8601_int (const gchar *text, gsize length, gint *value)
1158 {
1159   gsize i;
1160   guint v = 0;
1161 
1162   if (length < 1 || length > 4)
1163     return FALSE;
1164 
1165   for (i = 0; i < length; i++)
1166     {
1167       const gchar c = text[i];
1168       if (c < '0' || c > '9')
1169         return FALSE;
1170       v = v * 10 + (c - '0');
1171     }
1172 
1173   *value = v;
1174   return TRUE;
1175 }
1176 
1177 /* Parse seconds in the form ss or ss.sss (variable length decimal) */
1178 static gboolean
get_iso8601_seconds(const gchar * text,gsize length,gdouble * value)1179 get_iso8601_seconds (const gchar *text, gsize length, gdouble *value)
1180 {
1181   gsize i;
1182   gdouble divisor = 1, v = 0;
1183 
1184   if (length < 2)
1185     return FALSE;
1186 
1187   for (i = 0; i < 2; i++)
1188     {
1189       const gchar c = text[i];
1190       if (c < '0' || c > '9')
1191         return FALSE;
1192       v = v * 10 + (c - '0');
1193     }
1194 
1195   if (length > 2 && !(text[i] == '.' || text[i] == ','))
1196     return FALSE;
1197 
1198   /* Ignore leap seconds, see g_date_time_new_from_iso8601() */
1199   if (v >= 60.0 && v <= 61.0)
1200     v = 59.0;
1201 
1202   i++;
1203   if (i == length)
1204     return FALSE;
1205 
1206   for (; i < length; i++)
1207     {
1208       const gchar c = text[i];
1209       if (c < '0' || c > '9')
1210         return FALSE;
1211       v = v * 10 + (c - '0');
1212       divisor *= 10;
1213     }
1214 
1215   *value = v / divisor;
1216   return TRUE;
1217 }
1218 
1219 static GDateTime *
g_date_time_new_ordinal(GTimeZone * tz,gint year,gint ordinal_day,gint hour,gint minute,gdouble seconds)1220 g_date_time_new_ordinal (GTimeZone *tz, gint year, gint ordinal_day, gint hour, gint minute, gdouble seconds)
1221 {
1222   GDateTime *dt;
1223 
1224   if (ordinal_day < 1 || ordinal_day > (GREGORIAN_LEAP (year) ? 366 : 365))
1225     return NULL;
1226 
1227   dt = g_date_time_new (tz, year, 1, 1, hour, minute, seconds);
1228   if (dt == NULL)
1229     return NULL;
1230   dt->days += ordinal_day - 1;
1231 
1232   return dt;
1233 }
1234 
1235 static GDateTime *
g_date_time_new_week(GTimeZone * tz,gint year,gint week,gint week_day,gint hour,gint minute,gdouble seconds)1236 g_date_time_new_week (GTimeZone *tz, gint year, gint week, gint week_day, gint hour, gint minute, gdouble seconds)
1237 {
1238   gint64 p;
1239   gint max_week, jan4_week_day, ordinal_day;
1240   GDateTime *dt;
1241 
1242   p = (year * 365 + (year / 4) - (year / 100) + (year / 400)) % 7;
1243   max_week = p == 4 ? 53 : 52;
1244 
1245   if (week < 1 || week > max_week || week_day < 1 || week_day > 7)
1246     return NULL;
1247 
1248   dt = g_date_time_new (tz, year, 1, 4, 0, 0, 0);
1249   if (dt == NULL)
1250     return NULL;
1251   g_date_time_get_week_number (dt, NULL, &jan4_week_day, NULL);
1252   g_date_time_unref (dt);
1253 
1254   ordinal_day = (week * 7) + week_day - (jan4_week_day + 3);
1255   if (ordinal_day < 0)
1256     {
1257       year--;
1258       ordinal_day += GREGORIAN_LEAP (year) ? 366 : 365;
1259     }
1260   else if (ordinal_day > (GREGORIAN_LEAP (year) ? 366 : 365))
1261     {
1262       ordinal_day -= (GREGORIAN_LEAP (year) ? 366 : 365);
1263       year++;
1264     }
1265 
1266   return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
1267 }
1268 
1269 static GDateTime *
parse_iso8601_date(const gchar * text,gsize length,gint hour,gint minute,gdouble seconds,GTimeZone * tz)1270 parse_iso8601_date (const gchar *text, gsize length,
1271                     gint hour, gint minute, gdouble seconds, GTimeZone *tz)
1272 {
1273   /* YYYY-MM-DD */
1274   if (length == 10 && text[4] == '-' && text[7] == '-')
1275     {
1276       int year, month, day;
1277       if (!get_iso8601_int (text, 4, &year) ||
1278           !get_iso8601_int (text + 5, 2, &month) ||
1279           !get_iso8601_int (text + 8, 2, &day))
1280         return NULL;
1281       return g_date_time_new (tz, year, month, day, hour, minute, seconds);
1282     }
1283   /* YYYY-DDD */
1284   else if (length == 8 && text[4] == '-')
1285     {
1286       gint year, ordinal_day;
1287       if (!get_iso8601_int (text, 4, &year) ||
1288           !get_iso8601_int (text + 5, 3, &ordinal_day))
1289         return NULL;
1290       return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
1291     }
1292   /* YYYY-Www-D */
1293   else if (length == 10 && text[4] == '-' && text[5] == 'W' && text[8] == '-')
1294     {
1295       gint year, week, week_day;
1296       if (!get_iso8601_int (text, 4, &year) ||
1297           !get_iso8601_int (text + 6, 2, &week) ||
1298           !get_iso8601_int (text + 9, 1, &week_day))
1299         return NULL;
1300       return g_date_time_new_week (tz, year, week, week_day, hour, minute, seconds);
1301     }
1302   /* YYYYWwwD */
1303   else if (length == 8 && text[4] == 'W')
1304     {
1305       gint year, week, week_day;
1306       if (!get_iso8601_int (text, 4, &year) ||
1307           !get_iso8601_int (text + 5, 2, &week) ||
1308           !get_iso8601_int (text + 7, 1, &week_day))
1309         return NULL;
1310       return g_date_time_new_week (tz, year, week, week_day, hour, minute, seconds);
1311     }
1312   /* YYYYMMDD */
1313   else if (length == 8)
1314     {
1315       int year, month, day;
1316       if (!get_iso8601_int (text, 4, &year) ||
1317           !get_iso8601_int (text + 4, 2, &month) ||
1318           !get_iso8601_int (text + 6, 2, &day))
1319         return NULL;
1320       return g_date_time_new (tz, year, month, day, hour, minute, seconds);
1321     }
1322   /* YYYYDDD */
1323   else if (length == 7)
1324     {
1325       gint year, ordinal_day;
1326       if (!get_iso8601_int (text, 4, &year) ||
1327           !get_iso8601_int (text + 4, 3, &ordinal_day))
1328         return NULL;
1329       return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
1330     }
1331   else
1332     return FALSE;
1333 }
1334 
1335 static GTimeZone *
parse_iso8601_timezone(const gchar * text,gsize length,gssize * tz_offset)1336 parse_iso8601_timezone (const gchar *text, gsize length, gssize *tz_offset)
1337 {
1338   gint i, tz_length, offset_hours, offset_minutes;
1339   gint offset_sign = 1;
1340   GTimeZone *tz;
1341 
1342   /* UTC uses Z suffix  */
1343   if (length > 0 && text[length - 1] == 'Z')
1344     {
1345       *tz_offset = length - 1;
1346       return g_time_zone_new_utc ();
1347     }
1348 
1349   /* Look for '+' or '-' of offset */
1350   for (i = length - 1; i >= 0; i--)
1351     if (text[i] == '+' || text[i] == '-')
1352       {
1353         offset_sign = text[i] == '-' ? -1 : 1;
1354         break;
1355       }
1356   if (i < 0)
1357     return NULL;
1358   tz_length = length - i;
1359 
1360   /* +hh:mm or -hh:mm */
1361   if (tz_length == 6 && text[i+3] == ':')
1362     {
1363       if (!get_iso8601_int (text + i + 1, 2, &offset_hours) ||
1364           !get_iso8601_int (text + i + 4, 2, &offset_minutes))
1365         return NULL;
1366     }
1367   /* +hhmm or -hhmm */
1368   else if (tz_length == 5)
1369     {
1370       if (!get_iso8601_int (text + i + 1, 2, &offset_hours) ||
1371           !get_iso8601_int (text + i + 3, 2, &offset_minutes))
1372         return NULL;
1373     }
1374   /* +hh or -hh */
1375   else if (tz_length == 3)
1376     {
1377       if (!get_iso8601_int (text + i + 1, 2, &offset_hours))
1378         return NULL;
1379       offset_minutes = 0;
1380     }
1381   else
1382     return NULL;
1383 
1384   *tz_offset = i;
1385   tz = g_time_zone_new (text + i);
1386 
1387   /* Double-check that the GTimeZone matches our interpretation of the timezone.
1388    * This can fail because our interpretation is less strict than (for example)
1389    * parse_time() in gtimezone.c, which restricts the range of the parsed
1390    * integers. */
1391   if (g_time_zone_get_offset (tz, 0) != offset_sign * (offset_hours * 3600 + offset_minutes * 60))
1392     {
1393       g_time_zone_unref (tz);
1394       return NULL;
1395     }
1396 
1397   return tz;
1398 }
1399 
1400 static gboolean
parse_iso8601_time(const gchar * text,gsize length,gint * hour,gint * minute,gdouble * seconds,GTimeZone ** tz)1401 parse_iso8601_time (const gchar *text, gsize length,
1402                     gint *hour, gint *minute, gdouble *seconds, GTimeZone **tz)
1403 {
1404   gssize tz_offset = -1;
1405 
1406   /* Check for timezone suffix */
1407   *tz = parse_iso8601_timezone (text, length, &tz_offset);
1408   if (tz_offset >= 0)
1409     length = tz_offset;
1410 
1411   /* hh:mm:ss(.sss) */
1412   if (length >= 8 && text[2] == ':' && text[5] == ':')
1413     {
1414       return get_iso8601_int (text, 2, hour) &&
1415              get_iso8601_int (text + 3, 2, minute) &&
1416              get_iso8601_seconds (text + 6, length - 6, seconds);
1417     }
1418   /* hhmmss(.sss) */
1419   else if (length >= 6)
1420     {
1421       return get_iso8601_int (text, 2, hour) &&
1422              get_iso8601_int (text + 2, 2, minute) &&
1423              get_iso8601_seconds (text + 4, length - 4, seconds);
1424     }
1425   else
1426     return FALSE;
1427 }
1428 
1429 /**
1430  * g_date_time_new_from_iso8601:
1431  * @text: an ISO 8601 formatted time string.
1432  * @default_tz: (nullable): a #GTimeZone to use if the text doesn't contain a
1433  *                          timezone, or %NULL.
1434  *
1435  * Creates a #GDateTime corresponding to the given
1436  * [ISO 8601 formatted string](https://en.wikipedia.org/wiki/ISO_8601)
1437  * @text. ISO 8601 strings of the form <date><sep><time><tz> are supported.
1438  *
1439  * Note that as #GDateTime "is oblivious to leap seconds", leap seconds information
1440  * in an ISO-8601 string will be ignored, so a `23:59:60` time would be parsed as
1441  * `23:59:59`.
1442  *
1443  * <sep> is the separator and can be either 'T', 't' or ' '.
1444  *
1445  * <date> is in the form:
1446  *
1447  * - `YYYY-MM-DD` - Year/month/day, e.g. 2016-08-24.
1448  * - `YYYYMMDD` - Same as above without dividers.
1449  * - `YYYY-DDD` - Ordinal day where DDD is from 001 to 366, e.g. 2016-237.
1450  * - `YYYYDDD` - Same as above without dividers.
1451  * - `YYYY-Www-D` - Week day where ww is from 01 to 52 and D from 1-7,
1452  *   e.g. 2016-W34-3.
1453  * - `YYYYWwwD` - Same as above without dividers.
1454  *
1455  * <time> is in the form:
1456  *
1457  * - `hh:mm:ss(.sss)` - Hours, minutes, seconds (subseconds), e.g. 22:10:42.123.
1458  * - `hhmmss(.sss)` - Same as above without dividers.
1459  *
1460  * <tz> is an optional timezone suffix of the form:
1461  *
1462  * - `Z` - UTC.
1463  * - `+hh:mm` or `-hh:mm` - Offset from UTC in hours and minutes, e.g. +12:00.
1464  * - `+hh` or `-hh` - Offset from UTC in hours, e.g. +12.
1465  *
1466  * If the timezone is not provided in @text it must be provided in @default_tz
1467  * (this field is otherwise ignored).
1468  *
1469  * This call can fail (returning %NULL) if @text is not a valid ISO 8601
1470  * formatted string.
1471  *
1472  * You should release the return value by calling g_date_time_unref()
1473  * when you are done with it.
1474  *
1475  * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
1476  *
1477  * Since: 2.56
1478  */
1479 GDateTime *
g_date_time_new_from_iso8601(const gchar * text,GTimeZone * default_tz)1480 g_date_time_new_from_iso8601 (const gchar *text, GTimeZone *default_tz)
1481 {
1482   gint length, date_length = -1;
1483   gint hour = 0, minute = 0;
1484   gdouble seconds = 0.0;
1485   GTimeZone *tz = NULL;
1486   GDateTime *datetime = NULL;
1487 
1488   g_return_val_if_fail (text != NULL, NULL);
1489 
1490   /* Count length of string and find date / time separator ('T', 't', or ' ') */
1491   for (length = 0; text[length] != '\0'; length++)
1492     {
1493       if (date_length < 0 && (text[length] == 'T' || text[length] == 't' || text[length] == ' '))
1494         date_length = length;
1495     }
1496 
1497   if (date_length < 0)
1498     return NULL;
1499 
1500   if (!parse_iso8601_time (text + date_length + 1, length - (date_length + 1),
1501                            &hour, &minute, &seconds, &tz))
1502     goto out;
1503   if (tz == NULL && default_tz == NULL)
1504     return NULL;
1505 
1506   datetime = parse_iso8601_date (text, date_length, hour, minute, seconds, tz ? tz : default_tz);
1507 
1508 out:
1509     if (tz != NULL)
1510       g_time_zone_unref (tz);
1511     return datetime;
1512 }
1513 
1514 /* full new functions {{{1 */
1515 
1516 /**
1517  * g_date_time_new:
1518  * @tz: a #GTimeZone
1519  * @year: the year component of the date
1520  * @month: the month component of the date
1521  * @day: the day component of the date
1522  * @hour: the hour component of the date
1523  * @minute: the minute component of the date
1524  * @seconds: the number of seconds past the minute
1525  *
1526  * Creates a new #GDateTime corresponding to the given date and time in
1527  * the time zone @tz.
1528  *
1529  * The @year must be between 1 and 9999, @month between 1 and 12 and @day
1530  * between 1 and 28, 29, 30 or 31 depending on the month and the year.
1531  *
1532  * @hour must be between 0 and 23 and @minute must be between 0 and 59.
1533  *
1534  * @seconds must be at least 0.0 and must be strictly less than 60.0.
1535  * It will be rounded down to the nearest microsecond.
1536  *
1537  * If the given time is not representable in the given time zone (for
1538  * example, 02:30 on March 14th 2010 in Toronto, due to daylight savings
1539  * time) then the time will be rounded up to the nearest existing time
1540  * (in this case, 03:00).  If this matters to you then you should verify
1541  * the return value for containing the same as the numbers you gave.
1542  *
1543  * In the case that the given time is ambiguous in the given time zone
1544  * (for example, 01:30 on November 7th 2010 in Toronto, due to daylight
1545  * savings time) then the time falling within standard (ie:
1546  * non-daylight) time is taken.
1547  *
1548  * It not considered a programmer error for the values to this function
1549  * to be out of range, but in the case that they are, the function will
1550  * return %NULL.
1551  *
1552  * You should release the return value by calling g_date_time_unref()
1553  * when you are done with it.
1554  *
1555  * Returns: a new #GDateTime, or %NULL
1556  *
1557  * Since: 2.26
1558  **/
1559 GDateTime *
g_date_time_new(GTimeZone * tz,gint year,gint month,gint day,gint hour,gint minute,gdouble seconds)1560 g_date_time_new (GTimeZone *tz,
1561                  gint       year,
1562                  gint       month,
1563                  gint       day,
1564                  gint       hour,
1565                  gint       minute,
1566                  gdouble    seconds)
1567 {
1568   GDateTime *datetime;
1569   gint64 full_time;
1570   /* keep these variables as volatile. We do not want them ending up in
1571    * registers - them doing so may cause us to hit precision problems on i386.
1572    * See: https://bugzilla.gnome.org/show_bug.cgi?id=792410 */
1573   volatile gint64 usec;
1574   volatile gdouble usecd;
1575 
1576   g_return_val_if_fail (tz != NULL, NULL);
1577 
1578   if (year < 1 || year > 9999 ||
1579       month < 1 || month > 12 ||
1580       day < 1 || day > days_in_months[GREGORIAN_LEAP (year)][month] ||
1581       hour < 0 || hour > 23 ||
1582       minute < 0 || minute > 59 ||
1583       seconds < 0.0 || seconds >= 60.0)
1584     return NULL;
1585 
1586   datetime = g_date_time_alloc (tz);
1587   datetime->days = ymd_to_days (year, month, day);
1588   datetime->usec = (hour   * USEC_PER_HOUR)
1589                  + (minute * USEC_PER_MINUTE)
1590                  + (gint64) (seconds * USEC_PER_SECOND);
1591 
1592   full_time = SEC_PER_DAY *
1593                 (ymd_to_days (year, month, day) - UNIX_EPOCH_START) +
1594               SECS_PER_HOUR * hour +
1595               SECS_PER_MINUTE * minute +
1596               (int) seconds;
1597 
1598   datetime->interval = g_time_zone_adjust_time (datetime->tz,
1599                                                 G_TIME_TYPE_STANDARD,
1600                                                 &full_time);
1601 
1602   /* This is the correct way to convert a scaled FP value to integer.
1603    * If this surprises you, please observe that (int)(1.000001 * 1e6)
1604    * is 1000000.  This is not a problem with precision, it's just how
1605    * FP numbers work.
1606    * See https://bugzilla.gnome.org/show_bug.cgi?id=697715. */
1607   usec = seconds * USEC_PER_SECOND;
1608   usecd = (usec + 1) * 1e-6;
1609   if (usecd <= seconds) {
1610     usec++;
1611   }
1612 
1613   full_time += UNIX_EPOCH_START * SEC_PER_DAY;
1614   datetime->days = full_time / SEC_PER_DAY;
1615   datetime->usec = (full_time % SEC_PER_DAY) * USEC_PER_SECOND;
1616   datetime->usec += usec % USEC_PER_SECOND;
1617 
1618   return datetime;
1619 }
1620 
1621 /**
1622  * g_date_time_new_local:
1623  * @year: the year component of the date
1624  * @month: the month component of the date
1625  * @day: the day component of the date
1626  * @hour: the hour component of the date
1627  * @minute: the minute component of the date
1628  * @seconds: the number of seconds past the minute
1629  *
1630  * Creates a new #GDateTime corresponding to the given date and time in
1631  * the local time zone.
1632  *
1633  * This call is equivalent to calling g_date_time_new() with the time
1634  * zone returned by g_time_zone_new_local().
1635  *
1636  * Returns: a #GDateTime, or %NULL
1637  *
1638  * Since: 2.26
1639  **/
1640 GDateTime *
g_date_time_new_local(gint year,gint month,gint day,gint hour,gint minute,gdouble seconds)1641 g_date_time_new_local (gint    year,
1642                        gint    month,
1643                        gint    day,
1644                        gint    hour,
1645                        gint    minute,
1646                        gdouble seconds)
1647 {
1648   GDateTime *datetime;
1649   GTimeZone *local;
1650 
1651   local = g_time_zone_new_local ();
1652   datetime = g_date_time_new (local, year, month, day, hour, minute, seconds);
1653   g_time_zone_unref (local);
1654 
1655   return datetime;
1656 }
1657 
1658 /**
1659  * g_date_time_new_utc:
1660  * @year: the year component of the date
1661  * @month: the month component of the date
1662  * @day: the day component of the date
1663  * @hour: the hour component of the date
1664  * @minute: the minute component of the date
1665  * @seconds: the number of seconds past the minute
1666  *
1667  * Creates a new #GDateTime corresponding to the given date and time in
1668  * UTC.
1669  *
1670  * This call is equivalent to calling g_date_time_new() with the time
1671  * zone returned by g_time_zone_new_utc().
1672  *
1673  * Returns: a #GDateTime, or %NULL
1674  *
1675  * Since: 2.26
1676  **/
1677 GDateTime *
g_date_time_new_utc(gint year,gint month,gint day,gint hour,gint minute,gdouble seconds)1678 g_date_time_new_utc (gint    year,
1679                      gint    month,
1680                      gint    day,
1681                      gint    hour,
1682                      gint    minute,
1683                      gdouble seconds)
1684 {
1685   GDateTime *datetime;
1686   GTimeZone *utc;
1687 
1688   utc = g_time_zone_new_utc ();
1689   datetime = g_date_time_new (utc, year, month, day, hour, minute, seconds);
1690   g_time_zone_unref (utc);
1691 
1692   return datetime;
1693 }
1694 
1695 /* Adders {{{1 */
1696 
1697 /**
1698  * g_date_time_add:
1699  * @datetime: a #GDateTime
1700  * @timespan: a #GTimeSpan
1701  *
1702  * Creates a copy of @datetime and adds the specified timespan to the copy.
1703  *
1704  * Returns: the newly created #GDateTime which should be freed with
1705  *   g_date_time_unref().
1706  *
1707  * Since: 2.26
1708  */
1709 GDateTime*
g_date_time_add(GDateTime * datetime,GTimeSpan timespan)1710 g_date_time_add (GDateTime *datetime,
1711                  GTimeSpan  timespan)
1712 {
1713   return g_date_time_from_instant (datetime->tz, timespan +
1714                                    g_date_time_to_instant (datetime));
1715 }
1716 
1717 /**
1718  * g_date_time_add_years:
1719  * @datetime: a #GDateTime
1720  * @years: the number of years
1721  *
1722  * Creates a copy of @datetime and adds the specified number of years to the
1723  * copy. Add negative values to subtract years.
1724  *
1725  * As with g_date_time_add_months(), if the resulting date would be 29th
1726  * February on a non-leap year, the day will be clamped to 28th February.
1727  *
1728  * Returns: the newly created #GDateTime which should be freed with
1729  *   g_date_time_unref().
1730  *
1731  * Since: 2.26
1732  */
1733 GDateTime *
g_date_time_add_years(GDateTime * datetime,gint years)1734 g_date_time_add_years (GDateTime *datetime,
1735                        gint       years)
1736 {
1737   gint year, month, day;
1738 
1739   g_return_val_if_fail (datetime != NULL, NULL);
1740 
1741   if (years < -10000 || years > 10000)
1742     return NULL;
1743 
1744   g_date_time_get_ymd (datetime, &year, &month, &day);
1745   year += years;
1746 
1747   /* only possible issue is if we've entered a year with no February 29
1748    */
1749   if (month == 2 && day == 29 && !GREGORIAN_LEAP (year))
1750     day = 28;
1751 
1752   return g_date_time_replace_days (datetime, ymd_to_days (year, month, day));
1753 }
1754 
1755 /**
1756  * g_date_time_add_months:
1757  * @datetime: a #GDateTime
1758  * @months: the number of months
1759  *
1760  * Creates a copy of @datetime and adds the specified number of months to the
1761  * copy. Add negative values to subtract months.
1762  *
1763  * The day of the month of the resulting #GDateTime is clamped to the number
1764  * of days in the updated calendar month. For example, if adding 1 month to
1765  * 31st January 2018, the result would be 28th February 2018. In 2020 (a leap
1766  * year), the result would be 29th February.
1767  *
1768  * Returns: the newly created #GDateTime which should be freed with
1769  *   g_date_time_unref().
1770  *
1771  * Since: 2.26
1772  */
1773 GDateTime*
g_date_time_add_months(GDateTime * datetime,gint months)1774 g_date_time_add_months (GDateTime *datetime,
1775                         gint       months)
1776 {
1777   gint year, month, day;
1778 
1779   g_return_val_if_fail (datetime != NULL, NULL);
1780   g_date_time_get_ymd (datetime, &year, &month, &day);
1781 
1782   if (months < -120000 || months > 120000)
1783     return NULL;
1784 
1785   year += months / 12;
1786   month += months % 12;
1787   if (month < 1)
1788     {
1789       month += 12;
1790       year--;
1791     }
1792   else if (month > 12)
1793     {
1794       month -= 12;
1795       year++;
1796     }
1797 
1798   day = MIN (day, days_in_months[GREGORIAN_LEAP (year)][month]);
1799 
1800   return g_date_time_replace_days (datetime, ymd_to_days (year, month, day));
1801 }
1802 
1803 /**
1804  * g_date_time_add_weeks:
1805  * @datetime: a #GDateTime
1806  * @weeks: the number of weeks
1807  *
1808  * Creates a copy of @datetime and adds the specified number of weeks to the
1809  * copy. Add negative values to subtract weeks.
1810  *
1811  * Returns: the newly created #GDateTime which should be freed with
1812  *   g_date_time_unref().
1813  *
1814  * Since: 2.26
1815  */
1816 GDateTime*
g_date_time_add_weeks(GDateTime * datetime,gint weeks)1817 g_date_time_add_weeks (GDateTime *datetime,
1818                        gint             weeks)
1819 {
1820   g_return_val_if_fail (datetime != NULL, NULL);
1821 
1822   return g_date_time_add_days (datetime, weeks * 7);
1823 }
1824 
1825 /**
1826  * g_date_time_add_days:
1827  * @datetime: a #GDateTime
1828  * @days: the number of days
1829  *
1830  * Creates a copy of @datetime and adds the specified number of days to the
1831  * copy. Add negative values to subtract days.
1832  *
1833  * Returns: the newly created #GDateTime which should be freed with
1834  *   g_date_time_unref().
1835  *
1836  * Since: 2.26
1837  */
1838 GDateTime*
g_date_time_add_days(GDateTime * datetime,gint days)1839 g_date_time_add_days (GDateTime *datetime,
1840                       gint       days)
1841 {
1842   g_return_val_if_fail (datetime != NULL, NULL);
1843 
1844   if (days < -3660000 || days > 3660000)
1845     return NULL;
1846 
1847   return g_date_time_replace_days (datetime, datetime->days + days);
1848 }
1849 
1850 /**
1851  * g_date_time_add_hours:
1852  * @datetime: a #GDateTime
1853  * @hours: the number of hours to add
1854  *
1855  * Creates a copy of @datetime and adds the specified number of hours.
1856  * Add negative values to subtract hours.
1857  *
1858  * Returns: the newly created #GDateTime which should be freed with
1859  *   g_date_time_unref().
1860  *
1861  * Since: 2.26
1862  */
1863 GDateTime*
g_date_time_add_hours(GDateTime * datetime,gint hours)1864 g_date_time_add_hours (GDateTime *datetime,
1865                        gint       hours)
1866 {
1867   return g_date_time_add (datetime, hours * USEC_PER_HOUR);
1868 }
1869 
1870 /**
1871  * g_date_time_add_minutes:
1872  * @datetime: a #GDateTime
1873  * @minutes: the number of minutes to add
1874  *
1875  * Creates a copy of @datetime adding the specified number of minutes.
1876  * Add negative values to subtract minutes.
1877  *
1878  * Returns: the newly created #GDateTime which should be freed with
1879  *   g_date_time_unref().
1880  *
1881  * Since: 2.26
1882  */
1883 GDateTime*
g_date_time_add_minutes(GDateTime * datetime,gint minutes)1884 g_date_time_add_minutes (GDateTime *datetime,
1885                          gint             minutes)
1886 {
1887   return g_date_time_add (datetime, minutes * USEC_PER_MINUTE);
1888 }
1889 
1890 
1891 /**
1892  * g_date_time_add_seconds:
1893  * @datetime: a #GDateTime
1894  * @seconds: the number of seconds to add
1895  *
1896  * Creates a copy of @datetime and adds the specified number of seconds.
1897  * Add negative values to subtract seconds.
1898  *
1899  * Returns: the newly created #GDateTime which should be freed with
1900  *   g_date_time_unref().
1901  *
1902  * Since: 2.26
1903  */
1904 GDateTime*
g_date_time_add_seconds(GDateTime * datetime,gdouble seconds)1905 g_date_time_add_seconds (GDateTime *datetime,
1906                          gdouble    seconds)
1907 {
1908   return g_date_time_add (datetime, seconds * USEC_PER_SECOND);
1909 }
1910 
1911 /**
1912  * g_date_time_add_full:
1913  * @datetime: a #GDateTime
1914  * @years: the number of years to add
1915  * @months: the number of months to add
1916  * @days: the number of days to add
1917  * @hours: the number of hours to add
1918  * @minutes: the number of minutes to add
1919  * @seconds: the number of seconds to add
1920  *
1921  * Creates a new #GDateTime adding the specified values to the current date and
1922  * time in @datetime. Add negative values to subtract.
1923  *
1924  * Returns: the newly created #GDateTime that should be freed with
1925  *   g_date_time_unref().
1926  *
1927  * Since: 2.26
1928  */
1929 GDateTime *
g_date_time_add_full(GDateTime * datetime,gint years,gint months,gint days,gint hours,gint minutes,gdouble seconds)1930 g_date_time_add_full (GDateTime *datetime,
1931                       gint       years,
1932                       gint       months,
1933                       gint       days,
1934                       gint       hours,
1935                       gint       minutes,
1936                       gdouble    seconds)
1937 {
1938   gint year, month, day;
1939   gint64 full_time;
1940   GDateTime *new;
1941   gint interval;
1942 
1943   g_return_val_if_fail (datetime != NULL, NULL);
1944   g_date_time_get_ymd (datetime, &year, &month, &day);
1945 
1946   months += years * 12;
1947 
1948   if (months < -120000 || months > 120000)
1949     return NULL;
1950 
1951   if (days < -3660000 || days > 3660000)
1952     return NULL;
1953 
1954   year += months / 12;
1955   month += months % 12;
1956   if (month < 1)
1957     {
1958       month += 12;
1959       year--;
1960     }
1961   else if (month > 12)
1962     {
1963       month -= 12;
1964       year++;
1965     }
1966 
1967   day = MIN (day, days_in_months[GREGORIAN_LEAP (year)][month]);
1968 
1969   /* full_time is now in unix (local) time */
1970   full_time = datetime->usec / USEC_PER_SECOND + SEC_PER_DAY *
1971     (ymd_to_days (year, month, day) + days - UNIX_EPOCH_START);
1972 
1973   interval = g_time_zone_adjust_time (datetime->tz,
1974                                       g_time_zone_is_dst (datetime->tz,
1975                                                           datetime->interval),
1976                                       &full_time);
1977 
1978   /* move to UTC unix time */
1979   full_time -= g_time_zone_get_offset (datetime->tz, interval);
1980 
1981   /* convert back to an instant, add back fractional seconds */
1982   full_time += UNIX_EPOCH_START * SEC_PER_DAY;
1983   full_time = full_time * USEC_PER_SECOND +
1984               datetime->usec % USEC_PER_SECOND;
1985 
1986   /* do the actual addition now */
1987   full_time += (hours * USEC_PER_HOUR) +
1988                (minutes * USEC_PER_MINUTE) +
1989                (gint64) (seconds * USEC_PER_SECOND);
1990 
1991   /* find the new interval */
1992   interval = g_time_zone_find_interval (datetime->tz,
1993                                         G_TIME_TYPE_UNIVERSAL,
1994                                         INSTANT_TO_UNIX (full_time));
1995 
1996   /* convert back into local time */
1997   full_time += USEC_PER_SECOND *
1998                g_time_zone_get_offset (datetime->tz, interval);
1999 
2000   /* split into days and usec of a new datetime */
2001   new = g_date_time_alloc (datetime->tz);
2002   new->interval = interval;
2003   new->days = full_time / USEC_PER_DAY;
2004   new->usec = full_time % USEC_PER_DAY;
2005 
2006   /* XXX validate */
2007 
2008   return new;
2009 }
2010 
2011 /* Compare, difference, hash, equal {{{1 */
2012 /**
2013  * g_date_time_compare:
2014  * @dt1: (not nullable): first #GDateTime to compare
2015  * @dt2: (not nullable): second #GDateTime to compare
2016  *
2017  * A comparison function for #GDateTimes that is suitable
2018  * as a #GCompareFunc. Both #GDateTimes must be non-%NULL.
2019  *
2020  * Returns: -1, 0 or 1 if @dt1 is less than, equal to or greater
2021  *   than @dt2.
2022  *
2023  * Since: 2.26
2024  */
2025 gint
g_date_time_compare(gconstpointer dt1,gconstpointer dt2)2026 g_date_time_compare (gconstpointer dt1,
2027                      gconstpointer dt2)
2028 {
2029   gint64 difference;
2030 
2031   difference = g_date_time_difference ((GDateTime *) dt1, (GDateTime *) dt2);
2032 
2033   if (difference < 0)
2034     return -1;
2035 
2036   else if (difference > 0)
2037     return 1;
2038 
2039   else
2040     return 0;
2041 }
2042 
2043 /**
2044  * g_date_time_difference:
2045  * @end: a #GDateTime
2046  * @begin: a #GDateTime
2047  *
2048  * Calculates the difference in time between @end and @begin.  The
2049  * #GTimeSpan that is returned is effectively @end - @begin (ie:
2050  * positive if the first parameter is larger).
2051  *
2052  * Returns: the difference between the two #GDateTime, as a time
2053  *   span expressed in microseconds.
2054  *
2055  * Since: 2.26
2056  */
2057 GTimeSpan
g_date_time_difference(GDateTime * end,GDateTime * begin)2058 g_date_time_difference (GDateTime *end,
2059                         GDateTime *begin)
2060 {
2061   g_return_val_if_fail (begin != NULL, 0);
2062   g_return_val_if_fail (end != NULL, 0);
2063 
2064   return g_date_time_to_instant (end) -
2065          g_date_time_to_instant (begin);
2066 }
2067 
2068 /**
2069  * g_date_time_hash:
2070  * @datetime: (not nullable): a #GDateTime
2071  *
2072  * Hashes @datetime into a #guint, suitable for use within #GHashTable.
2073  *
2074  * Returns: a #guint containing the hash
2075  *
2076  * Since: 2.26
2077  */
2078 guint
g_date_time_hash(gconstpointer datetime)2079 g_date_time_hash (gconstpointer datetime)
2080 {
2081   return g_date_time_to_instant ((GDateTime *) datetime);
2082 }
2083 
2084 /**
2085  * g_date_time_equal:
2086  * @dt1: (not nullable): a #GDateTime
2087  * @dt2: (not nullable): a #GDateTime
2088  *
2089  * Checks to see if @dt1 and @dt2 are equal.
2090  *
2091  * Equal here means that they represent the same moment after converting
2092  * them to the same time zone.
2093  *
2094  * Returns: %TRUE if @dt1 and @dt2 are equal
2095  *
2096  * Since: 2.26
2097  */
2098 gboolean
g_date_time_equal(gconstpointer dt1,gconstpointer dt2)2099 g_date_time_equal (gconstpointer dt1,
2100                    gconstpointer dt2)
2101 {
2102   return g_date_time_difference ((GDateTime *) dt1, (GDateTime *) dt2) == 0;
2103 }
2104 
2105 /* Year, Month, Day Getters {{{1 */
2106 /**
2107  * g_date_time_get_ymd:
2108  * @datetime: a #GDateTime.
2109  * @year: (out) (optional): the return location for the gregorian year, or %NULL.
2110  * @month: (out) (optional): the return location for the month of the year, or %NULL.
2111  * @day: (out) (optional): the return location for the day of the month, or %NULL.
2112  *
2113  * Retrieves the Gregorian day, month, and year of a given #GDateTime.
2114  *
2115  * Since: 2.26
2116  **/
2117 void
g_date_time_get_ymd(GDateTime * datetime,gint * year,gint * month,gint * day)2118 g_date_time_get_ymd (GDateTime *datetime,
2119                      gint      *year,
2120                      gint      *month,
2121                      gint      *day)
2122 {
2123   gint the_year;
2124   gint the_month;
2125   gint the_day;
2126   gint remaining_days;
2127   gint y100_cycles;
2128   gint y4_cycles;
2129   gint y1_cycles;
2130   gint preceding;
2131   gboolean leap;
2132 
2133   g_return_if_fail (datetime != NULL);
2134 
2135   remaining_days = datetime->days;
2136 
2137   /*
2138    * We need to convert an offset in days to its year/month/day representation.
2139    * Leap years makes this a little trickier than it should be, so we use
2140    * 400, 100 and 4 years cycles here to get to the correct year.
2141    */
2142 
2143   /* Our days offset starts sets 0001-01-01 as day 1, if it was day 0 our
2144    * math would be simpler, so let's do it */
2145   remaining_days--;
2146 
2147   the_year = (remaining_days / DAYS_IN_400YEARS) * 400 + 1;
2148   remaining_days = remaining_days % DAYS_IN_400YEARS;
2149 
2150   y100_cycles = remaining_days / DAYS_IN_100YEARS;
2151   remaining_days = remaining_days % DAYS_IN_100YEARS;
2152   the_year += y100_cycles * 100;
2153 
2154   y4_cycles = remaining_days / DAYS_IN_4YEARS;
2155   remaining_days = remaining_days % DAYS_IN_4YEARS;
2156   the_year += y4_cycles * 4;
2157 
2158   y1_cycles = remaining_days / 365;
2159   the_year += y1_cycles;
2160   remaining_days = remaining_days % 365;
2161 
2162   if (y1_cycles == 4 || y100_cycles == 4) {
2163     g_assert (remaining_days == 0);
2164 
2165     /* special case that indicates that the date is actually one year before,
2166      * in the 31th of December */
2167     the_year--;
2168     the_month = 12;
2169     the_day = 31;
2170     goto end;
2171   }
2172 
2173   /* now get the month and the day */
2174   leap = y1_cycles == 3 && (y4_cycles != 24 || y100_cycles == 3);
2175 
2176   g_assert (leap == GREGORIAN_LEAP(the_year));
2177 
2178   the_month = (remaining_days + 50) >> 5;
2179   preceding = (days_in_year[0][the_month - 1] + (the_month > 2 && leap));
2180   if (preceding > remaining_days)
2181     {
2182       /* estimate is too large */
2183       the_month -= 1;
2184       preceding -= leap ? days_in_months[1][the_month]
2185                         : days_in_months[0][the_month];
2186     }
2187 
2188   remaining_days -= preceding;
2189   g_assert(0 <= remaining_days);
2190 
2191   the_day = remaining_days + 1;
2192 
2193 end:
2194   if (year)
2195     *year = the_year;
2196   if (month)
2197     *month = the_month;
2198   if (day)
2199     *day = the_day;
2200 }
2201 
2202 /**
2203  * g_date_time_get_year:
2204  * @datetime: A #GDateTime
2205  *
2206  * Retrieves the year represented by @datetime in the Gregorian calendar.
2207  *
2208  * Returns: the year represented by @datetime
2209  *
2210  * Since: 2.26
2211  */
2212 gint
g_date_time_get_year(GDateTime * datetime)2213 g_date_time_get_year (GDateTime *datetime)
2214 {
2215   gint year;
2216 
2217   g_return_val_if_fail (datetime != NULL, 0);
2218 
2219   g_date_time_get_ymd (datetime, &year, NULL, NULL);
2220 
2221   return year;
2222 }
2223 
2224 /**
2225  * g_date_time_get_month:
2226  * @datetime: a #GDateTime
2227  *
2228  * Retrieves the month of the year represented by @datetime in the Gregorian
2229  * calendar.
2230  *
2231  * Returns: the month represented by @datetime
2232  *
2233  * Since: 2.26
2234  */
2235 gint
g_date_time_get_month(GDateTime * datetime)2236 g_date_time_get_month (GDateTime *datetime)
2237 {
2238   gint month;
2239 
2240   g_return_val_if_fail (datetime != NULL, 0);
2241 
2242   g_date_time_get_ymd (datetime, NULL, &month, NULL);
2243 
2244   return month;
2245 }
2246 
2247 /**
2248  * g_date_time_get_day_of_month:
2249  * @datetime: a #GDateTime
2250  *
2251  * Retrieves the day of the month represented by @datetime in the gregorian
2252  * calendar.
2253  *
2254  * Returns: the day of the month
2255  *
2256  * Since: 2.26
2257  */
2258 gint
g_date_time_get_day_of_month(GDateTime * datetime)2259 g_date_time_get_day_of_month (GDateTime *datetime)
2260 {
2261   gint           day_of_year,
2262                  i;
2263   const guint16 *days;
2264   guint16        last = 0;
2265 
2266   g_return_val_if_fail (datetime != NULL, 0);
2267 
2268   days = days_in_year[GREGORIAN_LEAP (g_date_time_get_year (datetime)) ? 1 : 0];
2269   g_date_time_get_week_number (datetime, NULL, NULL, &day_of_year);
2270 
2271   for (i = 1; i <= 12; i++)
2272     {
2273       if (days [i] >= day_of_year)
2274         return day_of_year - last;
2275       last = days [i];
2276     }
2277 
2278   g_warn_if_reached ();
2279   return 0;
2280 }
2281 
2282 /* Week of year / day of week getters {{{1 */
2283 /**
2284  * g_date_time_get_week_numbering_year:
2285  * @datetime: a #GDateTime
2286  *
2287  * Returns the ISO 8601 week-numbering year in which the week containing
2288  * @datetime falls.
2289  *
2290  * This function, taken together with g_date_time_get_week_of_year() and
2291  * g_date_time_get_day_of_week() can be used to determine the full ISO
2292  * week date on which @datetime falls.
2293  *
2294  * This is usually equal to the normal Gregorian year (as returned by
2295  * g_date_time_get_year()), except as detailed below:
2296  *
2297  * For Thursday, the week-numbering year is always equal to the usual
2298  * calendar year.  For other days, the number is such that every day
2299  * within a complete week (Monday to Sunday) is contained within the
2300  * same week-numbering year.
2301  *
2302  * For Monday, Tuesday and Wednesday occurring near the end of the year,
2303  * this may mean that the week-numbering year is one greater than the
2304  * calendar year (so that these days have the same week-numbering year
2305  * as the Thursday occurring early in the next year).
2306  *
2307  * For Friday, Saturday and Sunday occurring near the start of the year,
2308  * this may mean that the week-numbering year is one less than the
2309  * calendar year (so that these days have the same week-numbering year
2310  * as the Thursday occurring late in the previous year).
2311  *
2312  * An equivalent description is that the week-numbering year is equal to
2313  * the calendar year containing the majority of the days in the current
2314  * week (Monday to Sunday).
2315  *
2316  * Note that January 1 0001 in the proleptic Gregorian calendar is a
2317  * Monday, so this function never returns 0.
2318  *
2319  * Returns: the ISO 8601 week-numbering year for @datetime
2320  *
2321  * Since: 2.26
2322  **/
2323 gint
g_date_time_get_week_numbering_year(GDateTime * datetime)2324 g_date_time_get_week_numbering_year (GDateTime *datetime)
2325 {
2326   gint year, month, day, weekday;
2327 
2328   g_date_time_get_ymd (datetime, &year, &month, &day);
2329   weekday = g_date_time_get_day_of_week (datetime);
2330 
2331   /* January 1, 2, 3 might be in the previous year if they occur after
2332    * Thursday.
2333    *
2334    *   Jan 1:  Friday, Saturday, Sunday    =>  day 1:  weekday 5, 6, 7
2335    *   Jan 2:  Saturday, Sunday            =>  day 2:  weekday 6, 7
2336    *   Jan 3:  Sunday                      =>  day 3:  weekday 7
2337    *
2338    * So we have a special case if (day - weekday) <= -4
2339    */
2340   if (month == 1 && (day - weekday) <= -4)
2341     return year - 1;
2342 
2343   /* December 29, 30, 31 might be in the next year if they occur before
2344    * Thursday.
2345    *
2346    *   Dec 31: Monday, Tuesday, Wednesday  =>  day 31: weekday 1, 2, 3
2347    *   Dec 30: Monday, Tuesday             =>  day 30: weekday 1, 2
2348    *   Dec 29: Monday                      =>  day 29: weekday 1
2349    *
2350    * So we have a special case if (day - weekday) >= 28
2351    */
2352   else if (month == 12 && (day - weekday) >= 28)
2353     return year + 1;
2354 
2355   else
2356     return year;
2357 }
2358 
2359 /**
2360  * g_date_time_get_week_of_year:
2361  * @datetime: a #GDateTime
2362  *
2363  * Returns the ISO 8601 week number for the week containing @datetime.
2364  * The ISO 8601 week number is the same for every day of the week (from
2365  * Moday through Sunday).  That can produce some unusual results
2366  * (described below).
2367  *
2368  * The first week of the year is week 1.  This is the week that contains
2369  * the first Thursday of the year.  Equivalently, this is the first week
2370  * that has more than 4 of its days falling within the calendar year.
2371  *
2372  * The value 0 is never returned by this function.  Days contained
2373  * within a year but occurring before the first ISO 8601 week of that
2374  * year are considered as being contained in the last week of the
2375  * previous year.  Similarly, the final days of a calendar year may be
2376  * considered as being part of the first ISO 8601 week of the next year
2377  * if 4 or more days of that week are contained within the new year.
2378  *
2379  * Returns: the ISO 8601 week number for @datetime.
2380  *
2381  * Since: 2.26
2382  */
2383 gint
g_date_time_get_week_of_year(GDateTime * datetime)2384 g_date_time_get_week_of_year (GDateTime *datetime)
2385 {
2386   gint weeknum;
2387 
2388   g_return_val_if_fail (datetime != NULL, 0);
2389 
2390   g_date_time_get_week_number (datetime, &weeknum, NULL, NULL);
2391 
2392   return weeknum;
2393 }
2394 
2395 /**
2396  * g_date_time_get_day_of_week:
2397  * @datetime: a #GDateTime
2398  *
2399  * Retrieves the ISO 8601 day of the week on which @datetime falls (1 is
2400  * Monday, 2 is Tuesday... 7 is Sunday).
2401  *
2402  * Returns: the day of the week
2403  *
2404  * Since: 2.26
2405  */
2406 gint
g_date_time_get_day_of_week(GDateTime * datetime)2407 g_date_time_get_day_of_week (GDateTime *datetime)
2408 {
2409   g_return_val_if_fail (datetime != NULL, 0);
2410 
2411   return (datetime->days - 1) % 7 + 1;
2412 }
2413 
2414 /* Day of year getter {{{1 */
2415 /**
2416  * g_date_time_get_day_of_year:
2417  * @datetime: a #GDateTime
2418  *
2419  * Retrieves the day of the year represented by @datetime in the Gregorian
2420  * calendar.
2421  *
2422  * Returns: the day of the year
2423  *
2424  * Since: 2.26
2425  */
2426 gint
g_date_time_get_day_of_year(GDateTime * datetime)2427 g_date_time_get_day_of_year (GDateTime *datetime)
2428 {
2429   gint doy = 0;
2430 
2431   g_return_val_if_fail (datetime != NULL, 0);
2432 
2433   g_date_time_get_week_number (datetime, NULL, NULL, &doy);
2434   return doy;
2435 }
2436 
2437 /* Time component getters {{{1 */
2438 
2439 /**
2440  * g_date_time_get_hour:
2441  * @datetime: a #GDateTime
2442  *
2443  * Retrieves the hour of the day represented by @datetime
2444  *
2445  * Returns: the hour of the day
2446  *
2447  * Since: 2.26
2448  */
2449 gint
g_date_time_get_hour(GDateTime * datetime)2450 g_date_time_get_hour (GDateTime *datetime)
2451 {
2452   g_return_val_if_fail (datetime != NULL, 0);
2453 
2454   return (datetime->usec / USEC_PER_HOUR);
2455 }
2456 
2457 /**
2458  * g_date_time_get_minute:
2459  * @datetime: a #GDateTime
2460  *
2461  * Retrieves the minute of the hour represented by @datetime
2462  *
2463  * Returns: the minute of the hour
2464  *
2465  * Since: 2.26
2466  */
2467 gint
g_date_time_get_minute(GDateTime * datetime)2468 g_date_time_get_minute (GDateTime *datetime)
2469 {
2470   g_return_val_if_fail (datetime != NULL, 0);
2471 
2472   return (datetime->usec % USEC_PER_HOUR) / USEC_PER_MINUTE;
2473 }
2474 
2475 /**
2476  * g_date_time_get_second:
2477  * @datetime: a #GDateTime
2478  *
2479  * Retrieves the second of the minute represented by @datetime
2480  *
2481  * Returns: the second represented by @datetime
2482  *
2483  * Since: 2.26
2484  */
2485 gint
g_date_time_get_second(GDateTime * datetime)2486 g_date_time_get_second (GDateTime *datetime)
2487 {
2488   g_return_val_if_fail (datetime != NULL, 0);
2489 
2490   return (datetime->usec % USEC_PER_MINUTE) / USEC_PER_SECOND;
2491 }
2492 
2493 /**
2494  * g_date_time_get_microsecond:
2495  * @datetime: a #GDateTime
2496  *
2497  * Retrieves the microsecond of the date represented by @datetime
2498  *
2499  * Returns: the microsecond of the second
2500  *
2501  * Since: 2.26
2502  */
2503 gint
g_date_time_get_microsecond(GDateTime * datetime)2504 g_date_time_get_microsecond (GDateTime *datetime)
2505 {
2506   g_return_val_if_fail (datetime != NULL, 0);
2507 
2508   return (datetime->usec % USEC_PER_SECOND);
2509 }
2510 
2511 /**
2512  * g_date_time_get_seconds:
2513  * @datetime: a #GDateTime
2514  *
2515  * Retrieves the number of seconds since the start of the last minute,
2516  * including the fractional part.
2517  *
2518  * Returns: the number of seconds
2519  *
2520  * Since: 2.26
2521  **/
2522 gdouble
g_date_time_get_seconds(GDateTime * datetime)2523 g_date_time_get_seconds (GDateTime *datetime)
2524 {
2525   g_return_val_if_fail (datetime != NULL, 0);
2526 
2527   return (datetime->usec % USEC_PER_MINUTE) / 1000000.0;
2528 }
2529 
2530 /* Exporters {{{1 */
2531 /**
2532  * g_date_time_to_unix:
2533  * @datetime: a #GDateTime
2534  *
2535  * Gives the Unix time corresponding to @datetime, rounding down to the
2536  * nearest second.
2537  *
2538  * Unix time is the number of seconds that have elapsed since 1970-01-01
2539  * 00:00:00 UTC, regardless of the time zone associated with @datetime.
2540  *
2541  * Returns: the Unix time corresponding to @datetime
2542  *
2543  * Since: 2.26
2544  **/
2545 gint64
g_date_time_to_unix(GDateTime * datetime)2546 g_date_time_to_unix (GDateTime *datetime)
2547 {
2548   return INSTANT_TO_UNIX (g_date_time_to_instant (datetime));
2549 }
2550 
2551 /**
2552  * g_date_time_to_timeval:
2553  * @datetime: a #GDateTime
2554  * @tv: a #GTimeVal to modify
2555  *
2556  * Stores the instant in time that @datetime represents into @tv.
2557  *
2558  * The time contained in a #GTimeVal is always stored in the form of
2559  * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the time
2560  * zone associated with @datetime.
2561  *
2562  * On systems where 'long' is 32bit (ie: all 32bit systems and all
2563  * Windows systems), a #GTimeVal is incapable of storing the entire
2564  * range of values that #GDateTime is capable of expressing.  On those
2565  * systems, this function returns %FALSE to indicate that the time is
2566  * out of range.
2567  *
2568  * On systems where 'long' is 64bit, this function never fails.
2569  *
2570  * Returns: %TRUE if successful, else %FALSE
2571  *
2572  * Since: 2.26
2573  * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
2574  *    g_date_time_to_unix() instead.
2575  **/
2576 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2577 gboolean
g_date_time_to_timeval(GDateTime * datetime,GTimeVal * tv)2578 g_date_time_to_timeval (GDateTime *datetime,
2579                         GTimeVal  *tv)
2580 {
2581   tv->tv_sec = INSTANT_TO_UNIX (g_date_time_to_instant (datetime));
2582   tv->tv_usec = datetime->usec % USEC_PER_SECOND;
2583 
2584   return TRUE;
2585 }
2586 G_GNUC_END_IGNORE_DEPRECATIONS
2587 
2588 /* Timezone queries {{{1 */
2589 /**
2590  * g_date_time_get_utc_offset:
2591  * @datetime: a #GDateTime
2592  *
2593  * Determines the offset to UTC in effect at the time and in the time
2594  * zone of @datetime.
2595  *
2596  * The offset is the number of microseconds that you add to UTC time to
2597  * arrive at local time for the time zone (ie: negative numbers for time
2598  * zones west of GMT, positive numbers for east).
2599  *
2600  * If @datetime represents UTC time, then the offset is always zero.
2601  *
2602  * Returns: the number of microseconds that should be added to UTC to
2603  *          get the local time
2604  *
2605  * Since: 2.26
2606  **/
2607 GTimeSpan
g_date_time_get_utc_offset(GDateTime * datetime)2608 g_date_time_get_utc_offset (GDateTime *datetime)
2609 {
2610   gint offset;
2611 
2612   g_return_val_if_fail (datetime != NULL, 0);
2613 
2614   offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
2615 
2616   return (gint64) offset * USEC_PER_SECOND;
2617 }
2618 
2619 /**
2620  * g_date_time_get_timezone:
2621  * @datetime: a #GDateTime
2622  *
2623  * Get the time zone for this @datetime.
2624  *
2625  * Returns: (transfer none): the time zone
2626  * Since: 2.58
2627  */
2628 GTimeZone *
g_date_time_get_timezone(GDateTime * datetime)2629 g_date_time_get_timezone (GDateTime *datetime)
2630 {
2631   g_return_val_if_fail (datetime != NULL, NULL);
2632 
2633   g_assert (datetime->tz != NULL);
2634   return datetime->tz;
2635 }
2636 
2637 /**
2638  * g_date_time_get_timezone_abbreviation:
2639  * @datetime: a #GDateTime
2640  *
2641  * Determines the time zone abbreviation to be used at the time and in
2642  * the time zone of @datetime.
2643  *
2644  * For example, in Toronto this is currently "EST" during the winter
2645  * months and "EDT" during the summer months when daylight savings
2646  * time is in effect.
2647  *
2648  * Returns: (transfer none): the time zone abbreviation. The returned
2649  *          string is owned by the #GDateTime and it should not be
2650  *          modified or freed
2651  *
2652  * Since: 2.26
2653  **/
2654 const gchar *
g_date_time_get_timezone_abbreviation(GDateTime * datetime)2655 g_date_time_get_timezone_abbreviation (GDateTime *datetime)
2656 {
2657   g_return_val_if_fail (datetime != NULL, NULL);
2658 
2659   return g_time_zone_get_abbreviation (datetime->tz, datetime->interval);
2660 }
2661 
2662 /**
2663  * g_date_time_is_daylight_savings:
2664  * @datetime: a #GDateTime
2665  *
2666  * Determines if daylight savings time is in effect at the time and in
2667  * the time zone of @datetime.
2668  *
2669  * Returns: %TRUE if daylight savings time is in effect
2670  *
2671  * Since: 2.26
2672  **/
2673 gboolean
g_date_time_is_daylight_savings(GDateTime * datetime)2674 g_date_time_is_daylight_savings (GDateTime *datetime)
2675 {
2676   g_return_val_if_fail (datetime != NULL, FALSE);
2677 
2678   return g_time_zone_is_dst (datetime->tz, datetime->interval);
2679 }
2680 
2681 /* Timezone convert {{{1 */
2682 /**
2683  * g_date_time_to_timezone:
2684  * @datetime: a #GDateTime
2685  * @tz: the new #GTimeZone
2686  *
2687  * Create a new #GDateTime corresponding to the same instant in time as
2688  * @datetime, but in the time zone @tz.
2689  *
2690  * This call can fail in the case that the time goes out of bounds.  For
2691  * example, converting 0001-01-01 00:00:00 UTC to a time zone west of
2692  * Greenwich will fail (due to the year 0 being out of range).
2693  *
2694  * You should release the return value by calling g_date_time_unref()
2695  * when you are done with it.
2696  *
2697  * Returns: a new #GDateTime, or %NULL
2698  *
2699  * Since: 2.26
2700  **/
2701 GDateTime *
g_date_time_to_timezone(GDateTime * datetime,GTimeZone * tz)2702 g_date_time_to_timezone (GDateTime *datetime,
2703                          GTimeZone *tz)
2704 {
2705   return g_date_time_from_instant (tz, g_date_time_to_instant (datetime));
2706 }
2707 
2708 /**
2709  * g_date_time_to_local:
2710  * @datetime: a #GDateTime
2711  *
2712  * Creates a new #GDateTime corresponding to the same instant in time as
2713  * @datetime, but in the local time zone.
2714  *
2715  * This call is equivalent to calling g_date_time_to_timezone() with the
2716  * time zone returned by g_time_zone_new_local().
2717  *
2718  * Returns: the newly created #GDateTime
2719  *
2720  * Since: 2.26
2721  **/
2722 GDateTime *
g_date_time_to_local(GDateTime * datetime)2723 g_date_time_to_local (GDateTime *datetime)
2724 {
2725   GDateTime *new;
2726   GTimeZone *local;
2727 
2728   local = g_time_zone_new_local ();
2729   new = g_date_time_to_timezone (datetime, local);
2730   g_time_zone_unref (local);
2731 
2732   return new;
2733 }
2734 
2735 /**
2736  * g_date_time_to_utc:
2737  * @datetime: a #GDateTime
2738  *
2739  * Creates a new #GDateTime corresponding to the same instant in time as
2740  * @datetime, but in UTC.
2741  *
2742  * This call is equivalent to calling g_date_time_to_timezone() with the
2743  * time zone returned by g_time_zone_new_utc().
2744  *
2745  * Returns: the newly created #GDateTime
2746  *
2747  * Since: 2.26
2748  **/
2749 GDateTime *
g_date_time_to_utc(GDateTime * datetime)2750 g_date_time_to_utc (GDateTime *datetime)
2751 {
2752   GDateTime *new;
2753   GTimeZone *utc;
2754 
2755   utc = g_time_zone_new_utc ();
2756   new = g_date_time_to_timezone (datetime, utc);
2757   g_time_zone_unref (utc);
2758 
2759   return new;
2760 }
2761 
2762 /* Format {{{1 */
2763 
2764 static gboolean
format_z(GString * outstr,gint offset,guint colons)2765 format_z (GString *outstr,
2766           gint     offset,
2767           guint    colons)
2768 {
2769   gint hours;
2770   gint minutes;
2771   gint seconds;
2772   gchar sign = offset >= 0 ? '+' : '-';
2773 
2774   offset = ABS (offset);
2775   hours = offset / 3600;
2776   minutes = offset / 60 % 60;
2777   seconds = offset % 60;
2778 
2779   switch (colons)
2780     {
2781     case 0:
2782       g_string_append_printf (outstr, "%c%02d%02d",
2783                               sign,
2784                               hours,
2785                               minutes);
2786       break;
2787 
2788     case 1:
2789       g_string_append_printf (outstr, "%c%02d:%02d",
2790                               sign,
2791                               hours,
2792                               minutes);
2793       break;
2794 
2795     case 2:
2796       g_string_append_printf (outstr, "%c%02d:%02d:%02d",
2797                               sign,
2798                               hours,
2799                               minutes,
2800                               seconds);
2801       break;
2802 
2803     case 3:
2804       g_string_append_printf (outstr, "%c%02d", sign, hours);
2805 
2806       if (minutes != 0 || seconds != 0)
2807         {
2808           g_string_append_printf (outstr, ":%02d", minutes);
2809 
2810           if (seconds != 0)
2811             g_string_append_printf (outstr, ":%02d", seconds);
2812         }
2813       break;
2814 
2815     default:
2816       return FALSE;
2817     }
2818 
2819   return TRUE;
2820 }
2821 
2822 #ifdef HAVE_LANGINFO_OUTDIGIT
2823 /* Initializes the array with UTF-8 encoded alternate digits suitable for use
2824  * in current locale. Returns NULL when current locale does not use alternate
2825  * digits or there was an error converting them to UTF-8.
2826  */
2827 static const gchar * const *
initialize_alt_digits(void)2828 initialize_alt_digits (void)
2829 {
2830   guint i;
2831   gsize digit_len;
2832   gchar *digit;
2833   const gchar *locale_digit;
2834 #define N_DIGITS 10
2835 #define MAX_UTF8_ENCODING_LEN 4
2836   static gchar buffer[N_DIGITS * (MAX_UTF8_ENCODING_LEN + 1 /* null separator */)];
2837 #undef N_DIGITS
2838 #undef MAX_UTF8_ENCODING_LEN
2839   gchar *buffer_end = buffer;
2840   static const gchar *alt_digits[10];
2841 
2842   for (i = 0; i != 10; ++i)
2843     {
2844       locale_digit = nl_langinfo (_NL_CTYPE_OUTDIGIT0_MB + i);
2845 
2846       if (g_strcmp0 (locale_digit, "") == 0)
2847         return NULL;
2848 
2849       digit = g_locale_to_utf8 (locale_digit, -1, NULL, &digit_len, NULL);
2850       if (digit == NULL)
2851         return NULL;
2852 
2853       g_assert (digit_len < (gsize) (buffer + sizeof (buffer) - buffer_end));
2854 
2855       alt_digits[i] = buffer_end;
2856       buffer_end = g_stpcpy (buffer_end, digit);
2857       /* skip trailing null byte */
2858       buffer_end += 1;
2859 
2860       g_free (digit);
2861     }
2862 
2863   return alt_digits;
2864 }
2865 #endif /* HAVE_LANGINFO_OUTDIGIT */
2866 
2867 static void
format_number(GString * str,gboolean use_alt_digits,const gchar * pad,gint width,guint32 number)2868 format_number (GString     *str,
2869                gboolean     use_alt_digits,
2870                const gchar *pad,
2871                gint         width,
2872                guint32      number)
2873 {
2874   const gchar *ascii_digits[10] = {
2875     "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
2876   };
2877   const gchar * const *digits = ascii_digits;
2878   const gchar *tmp[10];
2879   gint i = 0;
2880 
2881   g_return_if_fail (width <= 10);
2882 
2883 #ifdef HAVE_LANGINFO_OUTDIGIT
2884   if (use_alt_digits)
2885     {
2886       static const gchar * const *alt_digits = NULL;
2887       static gsize initialised;
2888 
2889       if G_UNLIKELY (g_once_init_enter (&initialised))
2890         {
2891           alt_digits = initialize_alt_digits ();
2892 
2893           if (alt_digits == NULL)
2894             alt_digits = ascii_digits;
2895 
2896           g_once_init_leave (&initialised, TRUE);
2897         }
2898 
2899       digits = alt_digits;
2900     }
2901 #endif /* HAVE_LANGINFO_OUTDIGIT */
2902 
2903   do
2904     {
2905       tmp[i++] = digits[number % 10];
2906       number /= 10;
2907     }
2908   while (number);
2909 
2910   while (pad && i < width)
2911     tmp[i++] = *pad == '0' ? digits[0] : pad;
2912 
2913   /* should really be impossible */
2914   g_assert (i <= 10);
2915 
2916   while (i)
2917     g_string_append (str, tmp[--i]);
2918 }
2919 
2920 static gboolean
format_ampm(GDateTime * datetime,GString * outstr,gboolean locale_is_utf8,gboolean uppercase)2921 format_ampm (GDateTime *datetime,
2922              GString   *outstr,
2923              gboolean   locale_is_utf8,
2924              gboolean   uppercase)
2925 {
2926   const gchar *ampm;
2927   gchar       *tmp = NULL, *ampm_dup;
2928 
2929   ampm = GET_AMPM (datetime);
2930 
2931   if (!ampm || ampm[0] == '\0')
2932     ampm = get_fallback_ampm (g_date_time_get_hour (datetime));
2933 
2934   if (!locale_is_utf8 && GET_AMPM_IS_LOCALE)
2935     {
2936       /* This assumes that locale encoding can't have embedded NULs */
2937       ampm = tmp = g_locale_to_utf8 (ampm, -1, NULL, NULL, NULL);
2938       if (tmp == NULL)
2939         return FALSE;
2940     }
2941   if (uppercase)
2942     ampm_dup = g_utf8_strup (ampm, -1);
2943   else
2944     ampm_dup = g_utf8_strdown (ampm, -1);
2945   g_free (tmp);
2946 
2947   g_string_append (outstr, ampm_dup);
2948   g_free (ampm_dup);
2949 
2950   return TRUE;
2951 }
2952 
2953 static gboolean g_date_time_format_utf8 (GDateTime   *datetime,
2954 					 const gchar *format,
2955 					 GString     *outstr,
2956 					 gboolean     locale_is_utf8);
2957 
2958 /* g_date_time_format() subroutine that takes a locale-encoded format
2959  * string and produces a UTF-8 encoded date/time string.
2960  */
2961 static gboolean
g_date_time_format_locale(GDateTime * datetime,const gchar * locale_format,GString * outstr,gboolean locale_is_utf8)2962 g_date_time_format_locale (GDateTime   *datetime,
2963 			   const gchar *locale_format,
2964 			   GString     *outstr,
2965 			   gboolean     locale_is_utf8)
2966 {
2967   gchar *utf8_format;
2968   gboolean success;
2969 
2970   if (locale_is_utf8)
2971     return g_date_time_format_utf8 (datetime, locale_format, outstr, locale_is_utf8);
2972 
2973   utf8_format = g_locale_to_utf8 (locale_format, -1, NULL, NULL, NULL);
2974   if (utf8_format == NULL)
2975     return FALSE;
2976 
2977   success = g_date_time_format_utf8 (datetime, utf8_format, outstr,
2978                                      locale_is_utf8);
2979   g_free (utf8_format);
2980   return success;
2981 }
2982 
2983 static inline gboolean
string_append(GString * string,const gchar * s,gboolean s_is_utf8)2984 string_append (GString     *string,
2985                const gchar *s,
2986                gboolean     s_is_utf8)
2987 {
2988   gchar *utf8;
2989   gsize  utf8_len;
2990 
2991   if (s_is_utf8)
2992     {
2993       g_string_append (string, s);
2994     }
2995   else
2996     {
2997       utf8 = g_locale_to_utf8 (s, -1, NULL, &utf8_len, NULL);
2998       if (utf8 == NULL)
2999         return FALSE;
3000       g_string_append_len (string, utf8, utf8_len);
3001       g_free (utf8);
3002     }
3003 
3004   return TRUE;
3005 }
3006 
3007 /* g_date_time_format() subroutine that takes a UTF-8 encoded format
3008  * string and produces a UTF-8 encoded date/time string.
3009  */
3010 static gboolean
g_date_time_format_utf8(GDateTime * datetime,const gchar * utf8_format,GString * outstr,gboolean locale_is_utf8)3011 g_date_time_format_utf8 (GDateTime   *datetime,
3012 			 const gchar *utf8_format,
3013 			 GString     *outstr,
3014 			 gboolean     locale_is_utf8)
3015 {
3016   guint     len;
3017   guint     colons;
3018   gunichar  c;
3019   gboolean  alt_digits = FALSE;
3020   gboolean  pad_set = FALSE;
3021   gboolean  name_is_utf8;
3022   const gchar *pad = "";
3023   const gchar *name;
3024   const gchar *tz;
3025 
3026   while (*utf8_format)
3027     {
3028       len = strcspn (utf8_format, "%");
3029       if (len)
3030         g_string_append_len (outstr, utf8_format, len);
3031 
3032       utf8_format += len;
3033       if (!*utf8_format)
3034 	break;
3035 
3036       g_assert (*utf8_format == '%');
3037       utf8_format++;
3038       if (!*utf8_format)
3039 	break;
3040 
3041       colons = 0;
3042       alt_digits = FALSE;
3043       pad_set = FALSE;
3044 
3045     next_mod:
3046       c = g_utf8_get_char (utf8_format);
3047       utf8_format = g_utf8_next_char (utf8_format);
3048       switch (c)
3049 	{
3050 	case 'a':
3051 	  name = WEEKDAY_ABBR (datetime);
3052           if (g_strcmp0 (name, "") == 0)
3053             return FALSE;
3054 
3055           name_is_utf8 = locale_is_utf8 || !WEEKDAY_ABBR_IS_LOCALE;
3056 
3057           if (!string_append (outstr, name, name_is_utf8))
3058             return FALSE;
3059 
3060 	  break;
3061 	case 'A':
3062 	  name = WEEKDAY_FULL (datetime);
3063           if (g_strcmp0 (name, "") == 0)
3064             return FALSE;
3065 
3066           name_is_utf8 = locale_is_utf8 || !WEEKDAY_FULL_IS_LOCALE;
3067 
3068           if (!string_append (outstr, name, name_is_utf8))
3069             return FALSE;
3070 
3071 	  break;
3072 	case 'b':
3073 	  name = alt_digits ? MONTH_ABBR_STANDALONE (datetime)
3074 			    : MONTH_ABBR_WITH_DAY (datetime);
3075           if (g_strcmp0 (name, "") == 0)
3076             return FALSE;
3077 
3078           name_is_utf8 = locale_is_utf8 ||
3079             ((alt_digits && !MONTH_ABBR_STANDALONE_IS_LOCALE) ||
3080              (!alt_digits && !MONTH_ABBR_WITH_DAY_IS_LOCALE));
3081 
3082           if (!string_append (outstr, name, name_is_utf8))
3083             return FALSE;
3084 
3085 	  break;
3086 	case 'B':
3087 	  name = alt_digits ? MONTH_FULL_STANDALONE (datetime)
3088 			    : MONTH_FULL_WITH_DAY (datetime);
3089           if (g_strcmp0 (name, "") == 0)
3090             return FALSE;
3091 
3092           name_is_utf8 = locale_is_utf8 ||
3093             ((alt_digits && !MONTH_FULL_STANDALONE_IS_LOCALE) ||
3094              (!alt_digits && !MONTH_FULL_WITH_DAY_IS_LOCALE));
3095 
3096           if (!string_append (outstr, name, name_is_utf8))
3097               return FALSE;
3098 
3099 	  break;
3100 	case 'c':
3101 	  {
3102             if (g_strcmp0 (PREFERRED_DATE_TIME_FMT, "") == 0)
3103               return FALSE;
3104             if (!g_date_time_format_locale (datetime, PREFERRED_DATE_TIME_FMT,
3105                                             outstr, locale_is_utf8))
3106               return FALSE;
3107 	  }
3108 	  break;
3109 	case 'C':
3110 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3111 			 g_date_time_get_year (datetime) / 100);
3112 	  break;
3113 	case 'd':
3114 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3115 			 g_date_time_get_day_of_month (datetime));
3116 	  break;
3117 	case 'e':
3118 	  format_number (outstr, alt_digits, pad_set ? pad : " ", 2,
3119 			 g_date_time_get_day_of_month (datetime));
3120 	  break;
3121 	case 'F':
3122 	  g_string_append_printf (outstr, "%d-%02d-%02d",
3123 				  g_date_time_get_year (datetime),
3124 				  g_date_time_get_month (datetime),
3125 				  g_date_time_get_day_of_month (datetime));
3126 	  break;
3127 	case 'g':
3128 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3129 			 g_date_time_get_week_numbering_year (datetime) % 100);
3130 	  break;
3131 	case 'G':
3132 	  format_number (outstr, alt_digits, pad_set ? pad : 0, 0,
3133 			 g_date_time_get_week_numbering_year (datetime));
3134 	  break;
3135 	case 'h':
3136 	  name = alt_digits ? MONTH_ABBR_STANDALONE (datetime)
3137 			    : MONTH_ABBR_WITH_DAY (datetime);
3138           if (g_strcmp0 (name, "") == 0)
3139             return FALSE;
3140 
3141           name_is_utf8 = locale_is_utf8 ||
3142             ((alt_digits && !MONTH_ABBR_STANDALONE_IS_LOCALE) ||
3143              (!alt_digits && !MONTH_ABBR_WITH_DAY_IS_LOCALE));
3144 
3145           if (!string_append (outstr, name, name_is_utf8))
3146             return FALSE;
3147 
3148 	  break;
3149 	case 'H':
3150 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3151 			 g_date_time_get_hour (datetime));
3152 	  break;
3153 	case 'I':
3154 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3155 			 (g_date_time_get_hour (datetime) + 11) % 12 + 1);
3156 	  break;
3157 	case 'j':
3158 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 3,
3159 			 g_date_time_get_day_of_year (datetime));
3160 	  break;
3161 	case 'k':
3162 	  format_number (outstr, alt_digits, pad_set ? pad : " ", 2,
3163 			 g_date_time_get_hour (datetime));
3164 	  break;
3165 	case 'l':
3166 	  format_number (outstr, alt_digits, pad_set ? pad : " ", 2,
3167 			 (g_date_time_get_hour (datetime) + 11) % 12 + 1);
3168 	  break;
3169 	case 'n':
3170 	  g_string_append_c (outstr, '\n');
3171 	  break;
3172 	case 'm':
3173 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3174 			 g_date_time_get_month (datetime));
3175 	  break;
3176 	case 'M':
3177 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3178 			 g_date_time_get_minute (datetime));
3179 	  break;
3180 	case 'O':
3181 	  alt_digits = TRUE;
3182 	  goto next_mod;
3183 	case 'p':
3184           if (!format_ampm (datetime, outstr, locale_is_utf8, TRUE))
3185             return FALSE;
3186           break;
3187 	case 'P':
3188           if (!format_ampm (datetime, outstr, locale_is_utf8, FALSE))
3189             return FALSE;
3190 	  break;
3191 	case 'r':
3192 	  {
3193             if (g_strcmp0 (PREFERRED_12HR_TIME_FMT, "") == 0)
3194               return FALSE;
3195 	    if (!g_date_time_format_locale (datetime, PREFERRED_12HR_TIME_FMT,
3196 					    outstr, locale_is_utf8))
3197 	      return FALSE;
3198 	  }
3199 	  break;
3200 	case 'R':
3201 	  g_string_append_printf (outstr, "%02d:%02d",
3202 				  g_date_time_get_hour (datetime),
3203 				  g_date_time_get_minute (datetime));
3204 	  break;
3205 	case 's':
3206 	  g_string_append_printf (outstr, "%" G_GINT64_FORMAT, g_date_time_to_unix (datetime));
3207 	  break;
3208 	case 'S':
3209 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3210 			 g_date_time_get_second (datetime));
3211 	  break;
3212 	case 't':
3213 	  g_string_append_c (outstr, '\t');
3214 	  break;
3215 	case 'T':
3216 	  g_string_append_printf (outstr, "%02d:%02d:%02d",
3217 				  g_date_time_get_hour (datetime),
3218 				  g_date_time_get_minute (datetime),
3219 				  g_date_time_get_second (datetime));
3220 	  break;
3221 	case 'u':
3222 	  format_number (outstr, alt_digits, 0, 0,
3223 			 g_date_time_get_day_of_week (datetime));
3224 	  break;
3225 	case 'V':
3226 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3227 			 g_date_time_get_week_of_year (datetime));
3228 	  break;
3229 	case 'w':
3230 	  format_number (outstr, alt_digits, 0, 0,
3231 			 g_date_time_get_day_of_week (datetime) % 7);
3232 	  break;
3233 	case 'x':
3234 	  {
3235             if (g_strcmp0 (PREFERRED_DATE_FMT, "") == 0)
3236               return FALSE;
3237 	    if (!g_date_time_format_locale (datetime, PREFERRED_DATE_FMT,
3238 					    outstr, locale_is_utf8))
3239 	      return FALSE;
3240 	  }
3241 	  break;
3242 	case 'X':
3243 	  {
3244             if (g_strcmp0 (PREFERRED_TIME_FMT, "") == 0)
3245               return FALSE;
3246 	    if (!g_date_time_format_locale (datetime, PREFERRED_TIME_FMT,
3247 					    outstr, locale_is_utf8))
3248 	      return FALSE;
3249 	  }
3250 	  break;
3251 	case 'y':
3252 	  format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
3253 			 g_date_time_get_year (datetime) % 100);
3254 	  break;
3255 	case 'Y':
3256 	  format_number (outstr, alt_digits, 0, 0,
3257 			 g_date_time_get_year (datetime));
3258 	  break;
3259 	case 'z':
3260 	  {
3261 	    gint64 offset;
3262 	    offset = g_date_time_get_utc_offset (datetime) / USEC_PER_SECOND;
3263 	    if (!format_z (outstr, (int) offset, colons))
3264 	      return FALSE;
3265 	  }
3266 	  break;
3267 	case 'Z':
3268 	  tz = g_date_time_get_timezone_abbreviation (datetime);
3269           g_string_append (outstr, tz);
3270 	  break;
3271 	case '%':
3272 	  g_string_append_c (outstr, '%');
3273 	  break;
3274 	case '-':
3275 	  pad_set = TRUE;
3276 	  pad = "";
3277 	  goto next_mod;
3278 	case '_':
3279 	  pad_set = TRUE;
3280 	  pad = " ";
3281 	  goto next_mod;
3282 	case '0':
3283 	  pad_set = TRUE;
3284 	  pad = "0";
3285 	  goto next_mod;
3286 	case ':':
3287 	  /* Colons are only allowed before 'z' */
3288 	  if (*utf8_format && *utf8_format != 'z' && *utf8_format != ':')
3289 	    return FALSE;
3290 	  colons++;
3291 	  goto next_mod;
3292 	default:
3293 	  return FALSE;
3294 	}
3295     }
3296 
3297   return TRUE;
3298 }
3299 
3300 /**
3301  * g_date_time_format:
3302  * @datetime: A #GDateTime
3303  * @format: a valid UTF-8 string, containing the format for the
3304  *          #GDateTime
3305  *
3306  * Creates a newly allocated string representing the requested @format.
3307  *
3308  * The format strings understood by this function are a subset of the
3309  * strftime() format language as specified by C99.  The \%D, \%U and \%W
3310  * conversions are not supported, nor is the 'E' modifier.  The GNU
3311  * extensions \%k, \%l, \%s and \%P are supported, however, as are the
3312  * '0', '_' and '-' modifiers.
3313  *
3314  * In contrast to strftime(), this function always produces a UTF-8
3315  * string, regardless of the current locale.  Note that the rendering of
3316  * many formats is locale-dependent and may not match the strftime()
3317  * output exactly.
3318  *
3319  * The following format specifiers are supported:
3320  *
3321  * - \%a: the abbreviated weekday name according to the current locale
3322  * - \%A: the full weekday name according to the current locale
3323  * - \%b: the abbreviated month name according to the current locale
3324  * - \%B: the full month name according to the current locale
3325  * - \%c: the preferred date and time representation for the current locale
3326  * - \%C: the century number (year/100) as a 2-digit integer (00-99)
3327  * - \%d: the day of the month as a decimal number (range 01 to 31)
3328  * - \%e: the day of the month as a decimal number (range  1 to 31)
3329  * - \%F: equivalent to `%Y-%m-%d` (the ISO 8601 date format)
3330  * - \%g: the last two digits of the ISO 8601 week-based year as a
3331  *   decimal number (00-99). This works well with \%V and \%u.
3332  * - \%G: the ISO 8601 week-based year as a decimal number. This works
3333  *   well with \%V and \%u.
3334  * - \%h: equivalent to \%b
3335  * - \%H: the hour as a decimal number using a 24-hour clock (range 00 to 23)
3336  * - \%I: the hour as a decimal number using a 12-hour clock (range 01 to 12)
3337  * - \%j: the day of the year as a decimal number (range 001 to 366)
3338  * - \%k: the hour (24-hour clock) as a decimal number (range 0 to 23);
3339  *   single digits are preceded by a blank
3340  * - \%l: the hour (12-hour clock) as a decimal number (range 1 to 12);
3341  *   single digits are preceded by a blank
3342  * - \%m: the month as a decimal number (range 01 to 12)
3343  * - \%M: the minute as a decimal number (range 00 to 59)
3344  * - \%p: either "AM" or "PM" according to the given time value, or the
3345  *   corresponding  strings for the current locale.  Noon is treated as
3346  *   "PM" and midnight as "AM".
3347  * - \%P: like \%p but lowercase: "am" or "pm" or a corresponding string for
3348  *   the current locale
3349  * - \%r: the time in a.m. or p.m. notation
3350  * - \%R: the time in 24-hour notation (\%H:\%M)
3351  * - \%s: the number of seconds since the Epoch, that is, since 1970-01-01
3352  *   00:00:00 UTC
3353  * - \%S: the second as a decimal number (range 00 to 60)
3354  * - \%t: a tab character
3355  * - \%T: the time in 24-hour notation with seconds (\%H:\%M:\%S)
3356  * - \%u: the ISO 8601 standard day of the week as a decimal, range 1 to 7,
3357  *    Monday being 1. This works well with \%G and \%V.
3358  * - \%V: the ISO 8601 standard week number of the current year as a decimal
3359  *   number, range 01 to 53, where week 1 is the first week that has at
3360  *   least 4 days in the new year. See g_date_time_get_week_of_year().
3361  *   This works well with \%G and \%u.
3362  * - \%w: the day of the week as a decimal, range 0 to 6, Sunday being 0.
3363  *   This is not the ISO 8601 standard format -- use \%u instead.
3364  * - \%x: the preferred date representation for the current locale without
3365  *   the time
3366  * - \%X: the preferred time representation for the current locale without
3367  *   the date
3368  * - \%y: the year as a decimal number without the century
3369  * - \%Y: the year as a decimal number including the century
3370  * - \%z: the time zone as an offset from UTC (+hhmm)
3371  * - \%:z: the time zone as an offset from UTC (+hh:mm).
3372  *   This is a gnulib strftime() extension. Since: 2.38
3373  * - \%::z: the time zone as an offset from UTC (+hh:mm:ss). This is a
3374  *   gnulib strftime() extension. Since: 2.38
3375  * - \%:::z: the time zone as an offset from UTC, with : to necessary
3376  *   precision (e.g., -04, +05:30). This is a gnulib strftime() extension. Since: 2.38
3377  * - \%Z: the time zone or name or abbreviation
3378  * - \%\%: a literal \% character
3379  *
3380  * Some conversion specifications can be modified by preceding the
3381  * conversion specifier by one or more modifier characters. The
3382  * following modifiers are supported for many of the numeric
3383  * conversions:
3384  *
3385  * - O: Use alternative numeric symbols, if the current locale supports those.
3386  * - _: Pad a numeric result with spaces. This overrides the default padding
3387  *   for the specifier.
3388  * - -: Do not pad a numeric result. This overrides the default padding
3389  *   for the specifier.
3390  * - 0: Pad a numeric result with zeros. This overrides the default padding
3391  *   for the specifier.
3392  *
3393  * Additionally, when O is used with B, b, or h, it produces the alternative
3394  * form of a month name. The alternative form should be used when the month
3395  * name is used without a day number (e.g., standalone). It is required in
3396  * some languages (Baltic, Slavic, Greek, and more) due to their grammatical
3397  * rules. For other languages there is no difference. \%OB is a GNU and BSD
3398  * strftime() extension expected to be added to the future POSIX specification,
3399  * \%Ob and \%Oh are GNU strftime() extensions. Since: 2.56
3400  *
3401  * Returns: a newly allocated string formatted to the requested format
3402  *     or %NULL in the case that there was an error (such as a format specifier
3403  *     not being supported in the current locale). The string
3404  *     should be freed with g_free().
3405  *
3406  * Since: 2.26
3407  */
3408 gchar *
g_date_time_format(GDateTime * datetime,const gchar * format)3409 g_date_time_format (GDateTime   *datetime,
3410                     const gchar *format)
3411 {
3412   GString  *outstr;
3413   const gchar *charset;
3414   /* Avoid conversions from locale charset to UTF-8 if charset is compatible
3415    * with UTF-8 already. Check for UTF-8 and synonymous canonical names of
3416    * ASCII. */
3417   gboolean locale_is_utf8_compatible = g_get_charset (&charset) ||
3418     g_strcmp0 ("ASCII", charset) == 0 ||
3419     g_strcmp0 ("ANSI_X3.4-1968", charset) == 0;
3420 
3421   g_return_val_if_fail (datetime != NULL, NULL);
3422   g_return_val_if_fail (format != NULL, NULL);
3423   g_return_val_if_fail (g_utf8_validate (format, -1, NULL), NULL);
3424 
3425   outstr = g_string_sized_new (strlen (format) * 2);
3426 
3427   if (!g_date_time_format_utf8 (datetime, format, outstr,
3428                                 locale_is_utf8_compatible))
3429     {
3430       g_string_free (outstr, TRUE);
3431       return NULL;
3432     }
3433 
3434   return g_string_free (outstr, FALSE);
3435 }
3436 
3437 /**
3438  * g_date_time_format_iso8601:
3439  * @datetime: A #GDateTime
3440  *
3441  * Format @datetime in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601),
3442  * including the date, time and time zone, and return that as a UTF-8 encoded
3443  * string.
3444  *
3445  * Returns: a newly allocated string formatted in ISO 8601 format
3446  *     or %NULL in the case that there was an error. The string
3447  *     should be freed with g_free().
3448  * Since: 2.62
3449  */
3450 gchar *
g_date_time_format_iso8601(GDateTime * datetime)3451 g_date_time_format_iso8601 (GDateTime *datetime)
3452 {
3453   GString *outstr = NULL;
3454   gchar *main_date = NULL;
3455   gint64 offset;
3456 
3457   /* Main date and time. */
3458   main_date = g_date_time_format (datetime, "%Y-%m-%dT%H:%M:%S");
3459   outstr = g_string_new (main_date);
3460   g_free (main_date);
3461 
3462   /* Timezone. Format it as `%:::z` unless the offset is zero, in which case
3463    * we can simply use `Z`. */
3464   offset = g_date_time_get_utc_offset (datetime);
3465 
3466   if (offset == 0)
3467     {
3468       g_string_append_c (outstr, 'Z');
3469     }
3470   else
3471     {
3472       gchar *time_zone = g_date_time_format (datetime, "%:::z");
3473       g_string_append (outstr, time_zone);
3474       g_free (time_zone);
3475     }
3476 
3477   return g_string_free (outstr, FALSE);
3478 }
3479 
3480 
3481 /* Epilogue {{{1 */
3482 /* vim:set foldmethod=marker: */
3483