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