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