Coverage Report

Created: 2025-08-28 06:48

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