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

source code of gtk/subprojects/glib/glib/gdatetime.c