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