tizen: kdbus: make i explicitly positive in make_single_header_vector
[platform/upstream/glib.git] / glib / tests / gdatetime.c
1 /* gdatetime-tests.c
2  *
3  * Copyright (C) 2009-2010 Christian Hergert <chris@dronelabs.com>
4  * Copyright 2023 GNOME Foundation Inc.
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  *
8  * This is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this library; if not, see <http://www.gnu.org/licenses/>.
20  *
21  * Authors:
22  *  - Christian Hergert <chris@dronelabs.com>
23  *  - Philip Withnall <pwithnall@gnome.org>
24  */
25
26 #include "config.h"
27
28 #include <math.h>
29 #include <string.h>
30 #include <time.h>
31 #include <gi18n.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #include <locale.h>
35
36 #include "gdatetime-private.h"
37
38 #ifdef G_OS_WIN32
39 #define WIN32_LEAN_AND_MEAN
40 #include <windows.h>
41
42 #ifndef NAN
43 #define NAN HUGE_VAL * 0.0f
44 #endif
45 #endif
46
47 #define ASSERT_DATE(dt,y,m,d) G_STMT_START { \
48   g_assert_nonnull ((dt)); \
49   g_assert_cmpint ((y), ==, g_date_time_get_year ((dt))); \
50   g_assert_cmpint ((m), ==, g_date_time_get_month ((dt))); \
51   g_assert_cmpint ((d), ==, g_date_time_get_day_of_month ((dt))); \
52 } G_STMT_END
53 #define ASSERT_TIME(dt,H,M,S,U) G_STMT_START { \
54   g_assert_nonnull ((dt)); \
55   g_assert_cmpint ((H), ==, g_date_time_get_hour ((dt))); \
56   g_assert_cmpint ((M), ==, g_date_time_get_minute ((dt))); \
57   g_assert_cmpint ((S), ==, g_date_time_get_second ((dt))); \
58   g_assert_cmpint ((U), ==, g_date_time_get_microsecond ((dt))); \
59 } G_STMT_END
60
61 static gboolean
62 skip_if_running_uninstalled (void)
63 {
64   /* If running uninstalled (G_TEST_BUILDDIR is set), skip this test, since we
65    * need the translations to be installed. We can’t mess around with
66    * bindtextdomain() here, as the compiled .gmo files in po/ are not in the
67    * right installed directory hierarchy to be successfully loaded by gettext. */
68   if (g_getenv ("G_TEST_BUILDDIR") != NULL)
69     {
70       g_test_skip ("Skipping due to running uninstalled. "
71                    "This test can only be run when the translations are installed.");
72       return TRUE;
73     }
74
75   return FALSE;
76 }
77
78 static void
79 get_localtime_tm (time_t     time_,
80                   struct tm *retval)
81 {
82 #ifdef HAVE_LOCALTIME_R
83   localtime_r (&time_, retval);
84 #else
85   {
86     struct tm *ptm = localtime (&time_);
87
88     if (ptm == NULL)
89       {
90         /* Happens at least in Microsoft's C library if you pass a
91          * negative time_t. Use 2000-01-01 as default date.
92          */
93 #ifndef G_DISABLE_CHECKS
94         g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, "ptm != NULL");
95 #endif
96
97         retval->tm_mon = 0;
98         retval->tm_mday = 1;
99         retval->tm_year = 100;
100       }
101     else
102       memcpy ((void *) retval, (void *) ptm, sizeof (struct tm));
103   }
104 #endif /* HAVE_LOCALTIME_R */
105 }
106
107 static void
108 test_GDateTime_now (void)
109 {
110   GDateTime *dt;
111   struct tm tm;
112   time_t before;
113   time_t after;
114
115   /* before <= dt.to_unix() <= after, but the inequalities might not be
116    * equality if we're close to the boundary between seconds.
117    * We loop until before == after (and hence dt.to_unix() should equal both)
118    * to guard against that. */
119   do
120     {
121       before = g_get_real_time () / G_TIME_SPAN_SECOND;
122
123       memset (&tm, 0, sizeof (tm));
124       get_localtime_tm (before, &tm);
125
126       dt = g_date_time_new_now_local ();
127
128       after = g_get_real_time () / G_TIME_SPAN_SECOND;
129     }
130   while (before != after);
131
132   g_assert_cmpint (g_date_time_get_year (dt), ==, 1900 + tm.tm_year);
133   g_assert_cmpint (g_date_time_get_month (dt), ==, 1 + tm.tm_mon);
134   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, tm.tm_mday);
135   g_assert_cmpint (g_date_time_get_hour (dt), ==, tm.tm_hour);
136   g_assert_cmpint (g_date_time_get_minute (dt), ==, tm.tm_min);
137   g_assert_cmpint (g_date_time_get_second (dt), ==, tm.tm_sec);
138
139   g_date_time_unref (dt);
140 }
141
142 static void
143 test_GDateTime_new_from_unix (void)
144 {
145   GDateTime *dt;
146   struct tm  tm;
147   time_t     t;
148
149   memset (&tm, 0, sizeof (tm));
150   t = time (NULL);
151   g_assert_cmpint (t, !=, (time_t) -1);
152   get_localtime_tm (t, &tm);
153
154   dt = g_date_time_new_from_unix_local (t);
155   g_assert_cmpint (g_date_time_get_year (dt), ==, 1900 + tm.tm_year);
156   g_assert_cmpint (g_date_time_get_month (dt), ==, 1 + tm.tm_mon);
157   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, tm.tm_mday);
158   g_assert_cmpint (g_date_time_get_hour (dt), ==, tm.tm_hour);
159   g_assert_cmpint (g_date_time_get_minute (dt), ==, tm.tm_min);
160   g_assert_cmpint (g_date_time_get_second (dt), ==, tm.tm_sec);
161   g_date_time_unref (dt);
162
163   /* Choose 1990-01-01 04:00:00 because no DST leaps happened then. The more
164    * obvious 1990-01-01 00:00:00 was a DST leap in America/Lima (which has,
165    * confusingly, since stopped using DST). */
166   memset (&tm, 0, sizeof (tm));
167   tm.tm_year = 90;
168   tm.tm_mday = 1;
169   tm.tm_mon = 0;
170   tm.tm_hour = 4;
171   tm.tm_min = 0;
172   tm.tm_sec = 0;
173   tm.tm_isdst = -1;
174   t = mktime (&tm);
175
176   dt = g_date_time_new_from_unix_local (t);
177   g_assert_cmpint (g_date_time_get_year (dt), ==, 1990);
178   g_assert_cmpint (g_date_time_get_month (dt), ==, 1);
179   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, 1);
180   g_assert_cmpint (g_date_time_get_hour (dt), ==, 4);
181   g_assert_cmpint (g_date_time_get_minute (dt), ==, 0);
182   g_assert_cmpint (g_date_time_get_second (dt), ==, 0);
183   g_date_time_unref (dt);
184 }
185
186 /* Check that trying to create a #GDateTime too far in the future (or past) reliably
187  * fails. Previously, the checks for this overflowed and it silently returned
188  * an incorrect #GDateTime. */
189 static void
190 test_GDateTime_new_from_unix_overflow (void)
191 {
192   GDateTime *dt;
193
194   g_test_bug ("http://bugzilla.gnome.org/782089");
195
196   dt = g_date_time_new_from_unix_utc (G_MAXINT64);
197   g_assert_null (dt);
198
199   dt = g_date_time_new_from_unix_local (G_MAXINT64);
200   g_assert_null (dt);
201
202   dt = g_date_time_new_from_unix_utc (G_MININT64);
203   g_assert_null (dt);
204
205   dt = g_date_time_new_from_unix_local (G_MININT64);
206   g_assert_null (dt);
207 }
208
209 static void
210 test_GDateTime_invalid (void)
211 {
212   GDateTime *dt;
213
214   g_test_bug ("http://bugzilla.gnome.org/702674");
215
216   dt = g_date_time_new_utc (2013, -2147483647, 31, 17, 15, 48);
217   g_assert (dt == NULL);
218 }
219
220 static void
221 test_GDateTime_compare (void)
222 {
223   GDateTime *dt1, *dt2;
224   gint       i;
225
226   dt1 = g_date_time_new_utc (2000, 1, 1, 0, 0, 0);
227
228   for (i = 1; i < 2000; i++)
229     {
230       dt2 = g_date_time_new_utc (i, 12, 31, 0, 0, 0);
231       g_assert_cmpint (1, ==, g_date_time_compare (dt1, dt2));
232       g_date_time_unref (dt2);
233     }
234
235   dt2 = g_date_time_new_utc (1999, 12, 31, 23, 59, 59);
236   g_assert_cmpint (1, ==, g_date_time_compare (dt1, dt2));
237   g_date_time_unref (dt2);
238
239   dt2 = g_date_time_new_utc (2000, 1, 1, 0, 0, 1);
240   g_assert_cmpint (-1, ==, g_date_time_compare (dt1, dt2));
241   g_date_time_unref (dt2);
242
243   dt2 = g_date_time_new_utc (2000, 1, 1, 0, 0, 0);
244   g_assert_cmpint (0, ==, g_date_time_compare (dt1, dt2));
245   g_date_time_unref (dt2);
246   g_date_time_unref (dt1);
247 }
248
249 static void
250 test_GDateTime_equal (void)
251 {
252   GDateTime *dt1, *dt2;
253   GTimeZone *tz;
254
255   dt1 = g_date_time_new_local (2009, 10, 19, 0, 0, 0);
256   dt2 = g_date_time_new_local (2009, 10, 19, 0, 0, 0);
257   g_assert (g_date_time_equal (dt1, dt2));
258   g_date_time_unref (dt1);
259   g_date_time_unref (dt2);
260
261   dt1 = g_date_time_new_local (2009, 10, 18, 0, 0, 0);
262   dt2 = g_date_time_new_local (2009, 10, 19, 0, 0, 0);
263   g_assert (!g_date_time_equal (dt1, dt2));
264   g_date_time_unref (dt1);
265   g_date_time_unref (dt2);
266
267   /* UTC-0300 and not in DST */
268   tz = g_time_zone_new_identifier ("-03:00");
269   g_assert_nonnull (tz);
270   dt1 = g_date_time_new (tz, 2010, 5, 24,  8, 0, 0);
271   g_time_zone_unref (tz);
272   g_assert_cmpint (g_date_time_get_utc_offset (dt1) / G_USEC_PER_SEC, ==, (-3 * 3600));
273   /* UTC */
274   dt2 = g_date_time_new_utc (2010, 5, 24, 11, 0, 0);
275   g_assert_cmpint (g_date_time_get_utc_offset (dt2), ==, 0);
276
277   g_assert (g_date_time_equal (dt1, dt2));
278   g_date_time_unref (dt1);
279
280   /* America/Recife is in UTC-0300 */
281 #ifdef G_OS_UNIX
282   tz = g_time_zone_new_identifier ("America/Recife");
283 #elif defined G_OS_WIN32
284   tz = g_time_zone_new_identifier ("E. South America Standard Time");
285 #endif
286   g_assert_nonnull (tz);
287   dt1 = g_date_time_new (tz, 2010, 5, 24,  8, 0, 0);
288   g_time_zone_unref (tz);
289   g_assert_cmpint (g_date_time_get_utc_offset (dt1) / G_USEC_PER_SEC, ==, (-3 * 3600));
290   g_assert (g_date_time_equal (dt1, dt2));
291   g_date_time_unref (dt1);
292   g_date_time_unref (dt2);
293 }
294
295 static void
296 test_GDateTime_get_day_of_week (void)
297 {
298   GDateTime *dt;
299
300   dt = g_date_time_new_local (2009, 10, 19, 0, 0, 0);
301   g_assert_cmpint (1, ==, g_date_time_get_day_of_week (dt));
302   g_date_time_unref (dt);
303
304   dt = g_date_time_new_local (2000, 10, 1, 0, 0, 0);
305   g_assert_cmpint (7, ==, g_date_time_get_day_of_week (dt));
306   g_date_time_unref (dt);
307 }
308
309 static void
310 test_GDateTime_get_day_of_month (void)
311 {
312   GDateTime *dt;
313
314   dt = g_date_time_new_local (2009, 10, 19, 0, 0, 0);
315   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, 19);
316   g_date_time_unref (dt);
317
318   dt = g_date_time_new_local (1400, 3, 12, 0, 0, 0);
319   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, 12);
320   g_date_time_unref (dt);
321
322   dt = g_date_time_new_local (1800, 12, 31, 0, 0, 0);
323   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, 31);
324   g_date_time_unref (dt);
325
326   dt = g_date_time_new_local (1000, 1, 1, 0, 0, 0);
327   g_assert_cmpint (g_date_time_get_day_of_month (dt), ==, 1);
328   g_date_time_unref (dt);
329 }
330
331 static void
332 test_GDateTime_get_hour (void)
333 {
334   GDateTime *dt;
335
336   dt = g_date_time_new_utc (2009, 10, 19, 15, 13, 11);
337   g_assert_cmpint (15, ==, g_date_time_get_hour (dt));
338   g_date_time_unref (dt);
339
340   dt = g_date_time_new_utc (100, 10, 19, 1, 0, 0);
341   g_assert_cmpint (1, ==, g_date_time_get_hour (dt));
342   g_date_time_unref (dt);
343
344   dt = g_date_time_new_utc (100, 10, 19, 0, 0, 0);
345   g_assert_cmpint (0, ==, g_date_time_get_hour (dt));
346   g_date_time_unref (dt);
347
348   dt = g_date_time_new_utc (100, 10, 1, 23, 59, 59);
349   g_assert_cmpint (23, ==, g_date_time_get_hour (dt));
350   g_date_time_unref (dt);
351 }
352
353 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
354 static void
355 test_GDateTime_get_microsecond (void)
356 {
357   GTimeVal   tv;
358   GDateTime *dt;
359
360   g_get_current_time (&tv);
361   dt = g_date_time_new_from_timeval_local (&tv);
362   g_assert_cmpint (tv.tv_usec, ==, g_date_time_get_microsecond (dt));
363   g_date_time_unref (dt);
364 }
365 G_GNUC_END_IGNORE_DEPRECATIONS
366
367 static void
368 test_GDateTime_get_year (void)
369 {
370   GDateTime *dt;
371
372   dt = g_date_time_new_local (2009, 1, 1, 0, 0, 0);
373   g_assert_cmpint (2009, ==, g_date_time_get_year (dt));
374   g_date_time_unref (dt);
375
376   dt = g_date_time_new_local (1, 1, 1, 0, 0, 0);
377   g_assert_cmpint (1, ==, g_date_time_get_year (dt));
378   g_date_time_unref (dt);
379
380   dt = g_date_time_new_local (13, 1, 1, 0, 0, 0);
381   g_assert_cmpint (13, ==, g_date_time_get_year (dt));
382   g_date_time_unref (dt);
383
384   dt = g_date_time_new_local (3000, 1, 1, 0, 0, 0);
385   g_assert_cmpint (3000, ==, g_date_time_get_year (dt));
386   g_date_time_unref (dt);
387 }
388
389 static void
390 test_GDateTime_hash (void)
391 {
392   GHashTable *h;
393
394   h = g_hash_table_new_full (g_date_time_hash, g_date_time_equal,
395                              (GDestroyNotify)g_date_time_unref,
396                              NULL);
397   g_hash_table_add (h, g_date_time_new_now_local ());
398   g_hash_table_remove_all (h);
399   g_hash_table_destroy (h);
400 }
401
402 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
403 static void
404 test_GDateTime_new_from_timeval (void)
405 {
406   GDateTime *dt;
407   GTimeVal   tv, tv2;
408
409   g_get_current_time (&tv);
410   dt = g_date_time_new_from_timeval_local (&tv);
411
412   if (g_test_verbose ())
413     g_test_message ("DT%04d-%02d-%02dT%02d:%02d:%02d%s",
414              g_date_time_get_year (dt),
415              g_date_time_get_month (dt),
416              g_date_time_get_day_of_month (dt),
417              g_date_time_get_hour (dt),
418              g_date_time_get_minute (dt),
419              g_date_time_get_second (dt),
420              g_date_time_get_timezone_abbreviation (dt));
421
422   g_date_time_to_timeval (dt, &tv2);
423   g_assert_cmpint (tv.tv_sec, ==, tv2.tv_sec);
424   g_assert_cmpint (tv.tv_usec, ==, tv2.tv_usec);
425   g_date_time_unref (dt);
426 }
427
428 static glong
429 find_maximum_supported_tv_sec (void)
430 {
431   glong highest_success = 0, lowest_failure = G_MAXLONG;
432   GTimeVal tv;
433   GDateTime *dt = NULL;
434
435   tv.tv_usec = 0;
436
437   /* Corner case of all glong values being valid. */
438   tv.tv_sec = G_MAXLONG;
439   dt = g_date_time_new_from_timeval_utc (&tv);
440   if (dt != NULL)
441     {
442       highest_success = tv.tv_sec;
443       g_date_time_unref (dt);
444     }
445
446   while (highest_success < lowest_failure - 1)
447     {
448       tv.tv_sec = highest_success + (lowest_failure - highest_success) / 2;
449       dt = g_date_time_new_from_timeval_utc (&tv);
450
451       if (dt != NULL)
452         {
453           highest_success = tv.tv_sec;
454           g_date_time_unref (dt);
455         }
456       else
457         {
458           lowest_failure = tv.tv_sec;
459         }
460     }
461
462   return highest_success;
463 }
464
465 /* Check that trying to create a #GDateTime too far in the future reliably
466  * fails. With a #GTimeVal, this is subtle, as the tv_usec are added into the
467  * calculation part-way through.
468  *
469  * This varies a bit between 32- and 64-bit architectures, due to the
470  * differences in the size of glong (tv.tv_sec). */
471 static void
472 test_GDateTime_new_from_timeval_overflow (void)
473 {
474   GDateTime *dt;
475   GTimeVal tv;
476
477   g_test_bug ("http://bugzilla.gnome.org/782089");
478
479   tv.tv_sec = find_maximum_supported_tv_sec ();
480   tv.tv_usec = G_USEC_PER_SEC - 1;
481
482   g_test_message ("Maximum supported GTimeVal.tv_sec = %lu", tv.tv_sec);
483
484   /* Sanity check: do we support the year 2000? */
485   g_assert_cmpint (tv.tv_sec, >=, 946684800);
486
487   dt = g_date_time_new_from_timeval_utc (&tv);
488   g_assert_nonnull (dt);
489   g_date_time_unref (dt);
490
491   if (tv.tv_sec < G_MAXLONG)
492     {
493       tv.tv_sec++;
494       tv.tv_usec = 0;
495
496       dt = g_date_time_new_from_timeval_utc (&tv);
497       g_assert_null (dt);
498     }
499 }
500
501 static void
502 test_GDateTime_new_from_timeval_utc (void)
503 {
504   GDateTime *dt;
505   GTimeVal   tv, tv2;
506
507   g_get_current_time (&tv);
508   dt = g_date_time_new_from_timeval_utc (&tv);
509
510   if (g_test_verbose ())
511     g_test_message ("DT%04d-%02d-%02dT%02d:%02d:%02d%s",
512              g_date_time_get_year (dt),
513              g_date_time_get_month (dt),
514              g_date_time_get_day_of_month (dt),
515              g_date_time_get_hour (dt),
516              g_date_time_get_minute (dt),
517              g_date_time_get_second (dt),
518              g_date_time_get_timezone_abbreviation (dt));
519
520   g_date_time_to_timeval (dt, &tv2);
521   g_assert_cmpint (tv.tv_sec, ==, tv2.tv_sec);
522   g_assert_cmpint (tv.tv_usec, ==, tv2.tv_usec);
523   g_date_time_unref (dt);
524 }
525 G_GNUC_END_IGNORE_DEPRECATIONS
526
527 static void
528 test_GDateTime_new_from_iso8601 (void)
529 {
530   GDateTime *dt;
531   GTimeZone *tz;
532
533   /* Need non-empty string */
534   dt = g_date_time_new_from_iso8601 ("", NULL);
535   g_assert_null (dt);
536
537   /* Needs to be correctly formatted */
538   dt = g_date_time_new_from_iso8601 ("not a date", NULL);
539   g_assert_null (dt);
540
541   dt = g_date_time_new_from_iso8601 (" +55", NULL);
542   g_assert_null (dt);
543
544   /* Check common case */
545   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42Z", NULL);
546   ASSERT_DATE (dt, 2016, 8, 24);
547   ASSERT_TIME (dt, 22, 10, 42, 0);
548   g_date_time_unref (dt);
549
550   /* Timezone is required in text or passed as arg */
551   tz = g_time_zone_new_utc ();
552   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42", tz);
553   ASSERT_DATE (dt, 2016, 8, 24);
554   g_date_time_unref (dt);
555   g_time_zone_unref (tz);
556   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42", NULL);
557   g_assert_null (dt);
558
559   /* Can't have whitespace */
560   dt = g_date_time_new_from_iso8601 ("2016 08 24T22:10:42Z", NULL);
561   g_assert_null (dt);
562   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42Z ", NULL);
563   g_assert_null (dt);
564   dt = g_date_time_new_from_iso8601 (" 2016-08-24T22:10:42Z", NULL);
565   g_assert_null (dt);
566
567   /* Check lowercase time separator or space allowed */
568   dt = g_date_time_new_from_iso8601 ("2016-08-24t22:10:42Z", NULL);
569   ASSERT_DATE (dt, 2016, 8, 24);
570   ASSERT_TIME (dt, 22, 10, 42, 0);
571   g_date_time_unref (dt);
572   dt = g_date_time_new_from_iso8601 ("2016-08-24 22:10:42Z", NULL);
573   ASSERT_DATE (dt, 2016, 8, 24);
574   ASSERT_TIME (dt, 22, 10, 42, 0);
575   g_date_time_unref (dt);
576
577   /* Check dates without separators allowed */
578   dt = g_date_time_new_from_iso8601 ("20160824T22:10:42Z", NULL);
579   ASSERT_DATE (dt, 2016, 8, 24);
580   ASSERT_TIME (dt, 22, 10, 42, 0);
581   g_date_time_unref (dt);
582
583   /* Months are two digits */
584   dt = g_date_time_new_from_iso8601 ("2016-1-01T22:10:42Z", NULL);
585   g_assert_null (dt);
586
587   /* Days are two digits */
588   dt = g_date_time_new_from_iso8601 ("2016-01-1T22:10:42Z", NULL);
589   g_assert_null (dt);
590
591   /* Need consistent usage of separators */
592   dt = g_date_time_new_from_iso8601 ("2016-0824T22:10:42Z", NULL);
593   g_assert_null (dt);
594   dt = g_date_time_new_from_iso8601 ("201608-24T22:10:42Z", NULL);
595   g_assert_null (dt);
596
597   /* Check month within valid range */
598   dt = g_date_time_new_from_iso8601 ("2016-00-13T22:10:42Z", NULL);
599   g_assert_null (dt);
600   dt = g_date_time_new_from_iso8601 ("2016-13-13T22:10:42Z", NULL);
601   g_assert_null (dt);
602
603   /* Check day within valid range */
604   dt = g_date_time_new_from_iso8601 ("2016-01-00T22:10:42Z", NULL);
605   g_assert_null (dt);
606   dt = g_date_time_new_from_iso8601 ("2016-01-31T22:10:42Z", NULL);
607   ASSERT_DATE (dt, 2016, 1, 31);
608   g_date_time_unref (dt);
609   dt = g_date_time_new_from_iso8601 ("2016-01-32T22:10:42Z", NULL);
610   g_assert_null (dt);
611   dt = g_date_time_new_from_iso8601 ("2016-02-29T22:10:42Z", NULL);
612   ASSERT_DATE (dt, 2016, 2, 29);
613   g_date_time_unref (dt);
614   dt = g_date_time_new_from_iso8601 ("2016-02-30T22:10:42Z", NULL);
615   g_assert_null (dt);
616   dt = g_date_time_new_from_iso8601 ("2017-02-28T22:10:42Z", NULL);
617   ASSERT_DATE (dt, 2017, 2, 28);
618   g_date_time_unref (dt);
619   dt = g_date_time_new_from_iso8601 ("2017-02-29T22:10:42Z", NULL);
620   g_assert_null (dt);
621   dt = g_date_time_new_from_iso8601 ("2016-03-31T22:10:42Z", NULL);
622   ASSERT_DATE (dt, 2016, 3, 31);
623   g_date_time_unref (dt);
624   dt = g_date_time_new_from_iso8601 ("2016-03-32T22:10:42Z", NULL);
625   g_assert_null (dt);
626   dt = g_date_time_new_from_iso8601 ("2016-04-30T22:10:42Z", NULL);
627   ASSERT_DATE (dt, 2016, 4, 30);
628   g_date_time_unref (dt);
629   dt = g_date_time_new_from_iso8601 ("2016-04-31T22:10:42Z", NULL);
630   g_assert_null (dt);
631   dt = g_date_time_new_from_iso8601 ("2016-05-31T22:10:42Z", NULL);
632   ASSERT_DATE (dt, 2016, 5, 31);
633   g_date_time_unref (dt);
634   dt = g_date_time_new_from_iso8601 ("2016-05-32T22:10:42Z", NULL);
635   g_assert_null (dt);
636   dt = g_date_time_new_from_iso8601 ("2016-06-30T22:10:42Z", NULL);
637   ASSERT_DATE (dt, 2016, 6, 30);
638   g_date_time_unref (dt);
639   dt = g_date_time_new_from_iso8601 ("2016-06-31T22:10:42Z", NULL);
640   g_assert_null (dt);
641   dt = g_date_time_new_from_iso8601 ("2016-07-31T22:10:42Z", NULL);
642   ASSERT_DATE (dt, 2016, 7, 31);
643   g_date_time_unref (dt);
644   dt = g_date_time_new_from_iso8601 ("2016-07-32T22:10:42Z", NULL);
645   g_assert_null (dt);
646   dt = g_date_time_new_from_iso8601 ("2016-08-31T22:10:42Z", NULL);
647   ASSERT_DATE (dt, 2016, 8, 31);
648   g_date_time_unref (dt);
649   dt = g_date_time_new_from_iso8601 ("2016-08-32T22:10:42Z", NULL);
650   g_assert_null (dt);
651   dt = g_date_time_new_from_iso8601 ("2016-09-30T22:10:42Z", NULL);
652   ASSERT_DATE (dt, 2016, 9, 30);
653   g_date_time_unref (dt);
654   dt = g_date_time_new_from_iso8601 ("2016-09-31T22:10:42Z", NULL);
655   g_assert_null (dt);
656   dt = g_date_time_new_from_iso8601 ("2016-10-31T22:10:42Z", NULL);
657   ASSERT_DATE (dt, 2016, 10, 31);
658   g_date_time_unref (dt);
659   dt = g_date_time_new_from_iso8601 ("2016-10-32T22:10:42Z", NULL);
660   g_assert_null (dt);
661   dt = g_date_time_new_from_iso8601 ("2016-11-30T22:10:42Z", NULL);
662   ASSERT_DATE (dt, 2016, 11, 30);
663   g_date_time_unref (dt);
664   dt = g_date_time_new_from_iso8601 ("2016-11-31T22:10:42Z", NULL);
665   g_assert_null (dt);
666   dt = g_date_time_new_from_iso8601 ("2016-12-31T22:10:42Z", NULL);
667   ASSERT_DATE (dt, 2016, 12, 31);
668   g_date_time_unref (dt);
669   dt = g_date_time_new_from_iso8601 ("2016-12-32T22:10:42Z", NULL);
670   g_assert_null (dt);
671
672   /* Check ordinal days work */
673   dt = g_date_time_new_from_iso8601 ("2016-237T22:10:42Z", NULL);
674   ASSERT_DATE (dt, 2016, 8, 24);
675   ASSERT_TIME (dt, 22, 10, 42, 0);
676   g_date_time_unref (dt);
677   dt = g_date_time_new_from_iso8601 ("2016237T22:10:42Z", NULL);
678   ASSERT_DATE (dt, 2016, 8, 24);
679   ASSERT_TIME (dt, 22, 10, 42, 0);
680   g_date_time_unref (dt);
681
682   /* Check ordinal leap days */
683   dt = g_date_time_new_from_iso8601 ("2016-366T22:10:42Z", NULL);
684   ASSERT_DATE (dt, 2016, 12, 31);
685   ASSERT_TIME (dt, 22, 10, 42, 0);
686   g_date_time_unref (dt);
687   dt = g_date_time_new_from_iso8601 ("2017-365T22:10:42Z", NULL);
688   ASSERT_DATE (dt, 2017, 12, 31);
689   ASSERT_TIME (dt, 22, 10, 42, 0);
690   g_date_time_unref (dt);
691   dt = g_date_time_new_from_iso8601 ("2017-366T22:10:42Z", NULL);
692   g_assert_null (dt);
693
694   /* Days start at 1 */
695   dt = g_date_time_new_from_iso8601 ("2016-000T22:10:42Z", NULL);
696   g_assert_null (dt);
697
698   /* Limited to number of days in the year (2016 is a leap year) */
699   dt = g_date_time_new_from_iso8601 ("2016-367T22:10:42Z", NULL);
700   g_assert_null (dt);
701
702   /* Days are two digits */
703   dt = g_date_time_new_from_iso8601 ("2016-1T22:10:42Z", NULL);
704   g_assert_null (dt);
705   dt = g_date_time_new_from_iso8601 ("2016-12T22:10:42Z", NULL);
706   g_assert_null (dt);
707
708   /* Check week days work */
709   dt = g_date_time_new_from_iso8601 ("2016-W34-3T22:10:42Z", NULL);
710   ASSERT_DATE (dt, 2016, 8, 24);
711   ASSERT_TIME (dt, 22, 10, 42, 0);
712   g_date_time_unref (dt);
713   dt = g_date_time_new_from_iso8601 ("2016W343T22:10:42Z", NULL);
714   ASSERT_DATE (dt, 2016, 8, 24);
715   ASSERT_TIME (dt, 22, 10, 42, 0);
716   g_date_time_unref (dt);
717
718   /* We don't support weeks without weekdays (valid ISO 8601) */
719   dt = g_date_time_new_from_iso8601 ("2016-W34T22:10:42Z", NULL);
720   g_assert_null (dt);
721   dt = g_date_time_new_from_iso8601 ("2016W34T22:10:42Z", NULL);
722   g_assert_null (dt);
723
724   /* Weeks are two digits */
725   dt = g_date_time_new_from_iso8601 ("2016-W3-1T22:10:42Z", NULL);
726   g_assert_null (dt);
727
728   /* Weeks start at 1 */
729   dt = g_date_time_new_from_iso8601 ("2016-W00-1T22:10:42Z", NULL);
730   g_assert_null (dt);
731
732   /* Week one might be in the previous year */
733   dt = g_date_time_new_from_iso8601 ("2015-W01-1T22:10:42Z", NULL);
734   ASSERT_DATE (dt, 2014, 12, 29);
735   g_date_time_unref (dt);
736
737   /* Last week might be in next year */
738   dt = g_date_time_new_from_iso8601 ("2015-W53-7T22:10:42Z", NULL);
739   ASSERT_DATE (dt, 2016, 1, 3);
740   g_date_time_unref (dt);
741
742   /* Week 53 doesn't always exist */
743   dt = g_date_time_new_from_iso8601 ("2016-W53-1T22:10:42Z", NULL);
744   g_assert_null (dt);
745
746   /* Limited to number of days in the week */
747   dt = g_date_time_new_from_iso8601 ("2016-W34-0T22:10:42Z", NULL);
748   g_assert_null (dt);
749   dt = g_date_time_new_from_iso8601 ("2016-W34-8T22:10:42Z", NULL);
750   g_assert_null (dt);
751
752   /* Days are one digit */
753   dt = g_date_time_new_from_iso8601 ("2016-W34-99T22:10:42Z", NULL);
754   g_assert_null (dt);
755
756   /* Check week day changes depending on year */
757   dt = g_date_time_new_from_iso8601 ("2017-W34-1T22:10:42Z", NULL);
758   ASSERT_DATE (dt, 2017, 8, 21);
759   g_date_time_unref (dt);
760
761   /* Check week day changes depending on leap years */
762   dt = g_date_time_new_from_iso8601 ("1900-W01-1T22:10:42Z", NULL);
763   ASSERT_DATE (dt, 1900, 1, 1);
764   g_date_time_unref (dt);
765
766   /* YYYY-MM not allowed (NOT valid ISO 8601) */
767   dt = g_date_time_new_from_iso8601 ("2016-08T22:10:42Z", NULL);
768   g_assert_null (dt);
769
770   /* We don't support omitted year (valid ISO 8601) */
771   dt = g_date_time_new_from_iso8601 ("--08-24T22:10:42Z", NULL);
772   g_assert_null (dt);
773   dt = g_date_time_new_from_iso8601 ("--0824T22:10:42Z", NULL);
774   g_assert_null (dt);
775
776   /* Seconds must be two digits. */
777   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:4Z", NULL);
778   g_assert_null (dt);
779
780   /* Seconds must all be digits. */
781   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:4aZ", NULL);
782   g_assert_null (dt);
783
784   /* Check subseconds work */
785   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42.123456Z", NULL);
786   ASSERT_DATE (dt, 2016, 8, 24);
787   ASSERT_TIME (dt, 22, 10, 42, 123456);
788   g_date_time_unref (dt);
789   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42,123456Z", NULL);
790   ASSERT_DATE (dt, 2016, 8, 24);
791   ASSERT_TIME (dt, 22, 10, 42, 123456);
792   g_date_time_unref (dt);
793   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42.Z", NULL);
794   g_assert_null (dt);
795   dt = g_date_time_new_from_iso8601 ("2016-08-24T221042.123456Z", NULL);
796   ASSERT_DATE (dt, 2016, 8, 24);
797   ASSERT_TIME (dt, 22, 10, 42, 123456);
798   g_date_time_unref (dt);
799
800   /* Subseconds must all be digits. */
801   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:42.5aZ", NULL);
802   g_assert_null (dt);
803
804   /* Subseconds can be an arbitrary length, but must not overflow.
805    * The ASSERT_TIME() comparisons are constrained by only comparing up to
806    * microsecond granularity. */
807   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:09.222222222222222222Z", NULL);
808   ASSERT_DATE (dt, 2016, 8, 10);
809   ASSERT_TIME (dt, 22, 10, 9, 222222);
810   g_date_time_unref (dt);
811   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:09.2222222222222222222Z", NULL);
812   g_assert_null (dt);
813
814   /* Small numerator, large divisor when parsing the subseconds. */
815   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:00.0000000000000000001Z", NULL);
816   ASSERT_DATE (dt, 2016, 8, 10);
817   ASSERT_TIME (dt, 22, 10, 0, 0);
818   g_date_time_unref (dt);
819   dt = g_date_time_new_from_iso8601 ("2016-08-10T22:10:00.00000000000000000001Z", NULL);
820   g_assert_null (dt);
821
822   /* We don't support times without minutes / seconds (valid ISO 8601) */
823   dt = g_date_time_new_from_iso8601 ("2016-08-24T22Z", NULL);
824   g_assert_null (dt);
825   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10Z", NULL);
826   g_assert_null (dt);
827   dt = g_date_time_new_from_iso8601 ("2016-08-24T2210Z", NULL);
828   g_assert_null (dt);
829
830   /* UTC time uses 'Z' */
831   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42Z", NULL);
832   ASSERT_DATE (dt, 2016, 8, 24);
833   ASSERT_TIME (dt, 22, 10, 42, 0);
834   g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, 0);
835   g_date_time_unref (dt);
836
837   /* Check timezone works */
838   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42+12:00", NULL);
839   ASSERT_DATE (dt, 2016, 8, 24);
840   ASSERT_TIME (dt, 22, 10, 42, 0);
841   g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, 12 * G_TIME_SPAN_HOUR);
842   g_date_time_unref (dt);
843   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42+12", NULL);
844   ASSERT_DATE (dt, 2016, 8, 24);
845   ASSERT_TIME (dt, 22, 10, 42, 0);
846   g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, 12 * G_TIME_SPAN_HOUR);
847   g_date_time_unref (dt);
848   dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42-02", NULL);
849   ASSERT_DATE (dt, 2016, 8, 24);
850   ASSERT_TIME (dt, 22, 10, 42, 0);
851   g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, -2 * G_TIME_SPAN_HOUR);
852   g_date_time_unref (dt);
853
854   /* Timezone seconds not allowed */
855   dt = g_date_time_new_from_iso8601 ("2016-08-24T22-12:00:00", NULL);
856   g_assert_null (dt);
857   dt = g_date_time_new_from_iso8601 ("2016-08-24T22-12:00:00.000", NULL);
858   g_assert_null (dt);
859
860   /* Timezone hours two digits */
861   dt = g_date_time_new_from_iso8601 ("2016-08-24T22-2Z", NULL);
862   g_assert_null (dt);
863
864   /* Ordinal date (YYYYDDD), space separator, and then time as HHMMSS,SSS
865    * The interesting bit is that the seconds field is so long as to parse as
866    * NaN */
867   dt = g_date_time_new_from_iso8601 ("0005306 000001,666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666600080000-00", NULL);
868   g_assert_null (dt);
869 }
870
871 typedef struct {
872   gboolean success;
873   const gchar *in;
874
875   /* Expected result: */
876   guint year;
877   guint month;
878   guint day;
879   guint hour;
880   guint minute;
881   guint second;
882   guint microsecond;
883   GTimeSpan utc_offset;
884 } Iso8601ParseTest;
885
886 static void
887 test_GDateTime_new_from_iso8601_2 (void)
888 {
889   const Iso8601ParseTest tests[] = {
890     { TRUE, "1990-11-01T10:21:17Z", 1990, 11, 1, 10, 21, 17, 0, 0 },
891     { TRUE, "19901101T102117Z", 1990, 11, 1, 10, 21, 17, 0, 0 },
892     { TRUE, "1970-01-01T00:00:17.12Z", 1970, 1, 1, 0, 0, 17, 120000, 0 },
893     { TRUE, "1970-01-01T00:00:17.1234Z", 1970, 1, 1, 0, 0, 17, 123400, 0 },
894     { TRUE, "1970-01-01T00:00:17.123456Z", 1970, 1, 1, 0, 0, 17, 123456, 0 },
895     { TRUE, "1980-02-22T12:36:00+02:00", 1980, 2, 22, 12, 36, 0, 0, 2 * G_TIME_SPAN_HOUR },
896     { TRUE, "1990-12-31T15:59:60-08:00", 1990, 12, 31, 15, 59, 59, 0, -8 * G_TIME_SPAN_HOUR },
897     { FALSE, "   ", 0, 0, 0, 0, 0, 0, 0, 0 },
898     { FALSE, "x", 0, 0, 0, 0, 0, 0, 0, 0 },
899     { FALSE, "123x", 0, 0, 0, 0, 0, 0, 0, 0 },
900     { FALSE, "2001-10+x", 0, 0, 0, 0, 0, 0, 0, 0 },
901     { FALSE, "1980-02-22T", 0, 0, 0, 0, 0, 0, 0, 0 },
902     { FALSE, "2001-10-08Tx", 0, 0, 0, 0, 0, 0, 0, 0 },
903     { FALSE, "2001-10-08T10:11x", 0, 0, 0, 0, 0, 0, 0, 0 },
904     { FALSE, "Wed Dec 19 17:20:20 GMT 2007", 0, 0, 0, 0, 0, 0, 0, 0 },
905     { FALSE, "1980-02-22T10:36:00Zulu", 0, 0, 0, 0, 0, 0, 0, 0 },
906     { FALSE, "2T0+819855292164632335", 0, 0, 0, 0, 0, 0, 0, 0 },
907     { TRUE, "2018-08-03T14:08:05.446178377+01:00", 2018, 8, 3, 14, 8, 5, 446178, 1 * G_TIME_SPAN_HOUR },
908     { FALSE, "2147483648-08-03T14:08:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
909     { FALSE, "2018-13-03T14:08:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
910     { FALSE, "2018-00-03T14:08:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
911     { FALSE, "2018-08-00T14:08:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
912     { FALSE, "2018-08-32T14:08:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
913     { FALSE, "2018-08-03T24:08:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
914     { FALSE, "2018-08-03T14:60:05.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
915     { FALSE, "2018-08-03T14:08:63.446178377+01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
916     { FALSE, "2018-08-03T14:08:05.446178377+100:00", 0, 0, 0, 0, 0, 0, 0, 0 },
917     { TRUE, "20180803T140805.446178377+0100", 2018, 8, 3, 14, 8, 5, 446178, 1 * G_TIME_SPAN_HOUR },
918     { FALSE, "21474836480803T140805.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
919     { FALSE, "20181303T140805.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
920     { FALSE, "20180003T140805.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
921     { FALSE, "20180800T140805.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
922     { FALSE, "20180832T140805.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
923     { FALSE, "20180803T240805.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
924     { FALSE, "20180803T146005.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
925     { FALSE, "20180803T140863.446178377+0100", 0, 0, 0, 0, 0, 0, 0, 0 },
926     { FALSE, "20180803T140805.446178377+10000", 0, 0, 0, 0, 0, 0, 0, 0 },
927     { FALSE, "-0005-01-01T00:00:00Z", 0, 0, 0, 0, 0, 0, 0, 0 },
928     { FALSE, "2018-08-06", 0, 0, 0, 0, 0, 0, 0, 0 },
929     /* This is not accepted by g_time_val_from_iso8601(), but is accepted by g_date_time_new_from_iso8601():
930     { FALSE, "2018-08-06 13:51:00Z", 0, 0, 0, 0, 0, 0, 0, 0 },
931     * */
932     { TRUE, "20180803T140805,446178377+0100", 2018, 8, 3, 14, 8, 5, 446178, 1 * G_TIME_SPAN_HOUR },
933     { TRUE, "2018-08-03T14:08:05.446178377-01:00", 2018, 8, 3, 14, 8, 5, 446178, -1 * G_TIME_SPAN_HOUR },
934     { FALSE, "2018-08-03T14:08:05.446178377 01:00", 0, 0, 0, 0, 0, 0, 0, 0 },
935     { TRUE, "1990-11-01T10:21:17", 1990, 11, 1, 10, 21, 17, 0, 0 },
936     /* These are accepted by g_time_val_from_iso8601(), but not by g_date_time_new_from_iso8601():
937     { TRUE, "19901101T102117+5", 1990, 11, 1, 10, 21, 17, 0, 5 * G_TIME_SPAN_HOUR },
938     { TRUE, "19901101T102117+3:15", 1990, 11, 1, 10, 21, 17, 0, 3 * G_TIME_SPAN_HOUR + 15 * G_TIME_SPAN_MINUTE },
939     { TRUE, "  1990-11-01T10:21:17Z  ", 1990, 11, 1, 10, 21, 17, 0, 0 },
940     { FALSE, "2018-08-03T14:08:05.446178377+01:60", 0, 0, 0, 0, 0, 0, 0, 0 },
941     { FALSE, "20180803T140805.446178377+0160", 0, 0, 0, 0, 0, 0, 0, 0 },
942     { TRUE, "+1980-02-22T12:36:00+02:00", 1980, 2, 22, 12, 36, 0, 0, 2 * G_TIME_SPAN_HOUR },
943     { TRUE, "1990-11-01T10:21:17     ", 1990, 11, 1, 10, 21, 17, 0, 0 },
944     */
945     { FALSE, "1719W462 407777-07", 0, 0, 0, 0, 0, 0, 0, 0 },
946     { FALSE, "4011090 260528Z", 0, 0, 0, 0, 0, 0, 0, 0 },
947     { FALSE, "0000W011 228214-22", 0, 0, 0, 0, 0, 0, 0, 0 },
948   };
949   GTimeZone *tz = NULL;
950   GDateTime *dt = NULL;
951   gsize i;
952
953   g_test_summary ("Further parser tests for g_date_time_new_from_iso8601(), "
954                   "checking success and failure using test vectors.");
955
956   tz = g_time_zone_new_utc ();
957
958   for (i = 0; i < G_N_ELEMENTS (tests); i++)
959     {
960       g_test_message ("Vector %" G_GSIZE_FORMAT ": %s", i, tests[i].in);
961
962       dt = g_date_time_new_from_iso8601 (tests[i].in, tz);
963       if (tests[i].success)
964         {
965           g_assert_nonnull (dt);
966           ASSERT_DATE (dt, tests[i].year, tests[i].month, tests[i].day);
967           ASSERT_TIME (dt, tests[i].hour, tests[i].minute, tests[i].second, tests[i].microsecond);
968           g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, tests[i].utc_offset);
969         }
970       else
971         {
972           g_assert_null (dt);
973         }
974
975       g_clear_pointer (&dt, g_date_time_unref);
976     }
977
978   g_time_zone_unref (tz);
979 }
980
981 static void
982 test_GDateTime_to_unix (void)
983 {
984   GDateTime *dt;
985   time_t     t;
986
987   t = time (NULL);
988   g_assert_cmpint (t, !=, (time_t) -1);
989   dt = g_date_time_new_from_unix_local (t);
990   g_assert_cmpint (g_date_time_to_unix (dt), ==, t);
991   g_date_time_unref (dt);
992 }
993
994 static void
995 test_GDateTime_add_years (void)
996 {
997   GDateTime *dt, *dt2;
998
999   dt = g_date_time_new_local (2009, 10, 19, 0, 0, 0);
1000   dt2 = g_date_time_add_years (dt, 1);
1001   g_assert_cmpint (2010, ==, g_date_time_get_year (dt2));
1002   g_date_time_unref (dt);
1003   g_date_time_unref (dt2);
1004 }
1005
1006 static void
1007 test_GDateTime_add_months (void)
1008 {
1009 #define TEST_ADD_MONTHS(y,m,d,a,ny,nm,nd) G_STMT_START { \
1010   GDateTime *dt, *dt2; \
1011   dt = g_date_time_new_utc (y, m, d, 0, 0, 0); \
1012   dt2 = g_date_time_add_months (dt, a); \
1013   ASSERT_DATE (dt2, ny, nm, nd); \
1014   g_date_time_unref (dt); \
1015   g_date_time_unref (dt2); \
1016 } G_STMT_END
1017
1018   TEST_ADD_MONTHS (2009, 12, 31,    1, 2010, 1, 31);
1019   TEST_ADD_MONTHS (2009, 12, 31,    1, 2010, 1, 31);
1020   TEST_ADD_MONTHS (2009,  6, 15,    1, 2009, 7, 15);
1021   TEST_ADD_MONTHS (1400,  3,  1,    1, 1400, 4,  1);
1022   TEST_ADD_MONTHS (1400,  1, 31,    1, 1400, 2, 28);
1023   TEST_ADD_MONTHS (1400,  1, 31, 7200, 2000, 1, 31);
1024   TEST_ADD_MONTHS (2008,  2, 29,   12, 2009, 2, 28);
1025   TEST_ADD_MONTHS (2000,  8, 16,   -5, 2000, 3, 16);
1026   TEST_ADD_MONTHS (2000,  8, 16,  -12, 1999, 8, 16);
1027   TEST_ADD_MONTHS (2011,  2,  1,  -13, 2010, 1,  1);
1028   TEST_ADD_MONTHS (1776,  7,  4, 1200, 1876, 7,  4);
1029 }
1030
1031 static void
1032 test_GDateTime_add_days (void)
1033 {
1034 #define TEST_ADD_DAYS(y,m,d,a,ny,nm,nd) G_STMT_START { \
1035   GDateTime *dt, *dt2; \
1036   dt = g_date_time_new_local (y, m, d, 0, 0, 0); \
1037   dt2 = g_date_time_add_days (dt, a); \
1038   g_assert_cmpint (ny, ==, g_date_time_get_year (dt2)); \
1039   g_assert_cmpint (nm, ==, g_date_time_get_month (dt2)); \
1040   g_assert_cmpint (nd, ==, g_date_time_get_day_of_month (dt2)); \
1041   g_date_time_unref (dt); \
1042   g_date_time_unref (dt2); \
1043 } G_STMT_END
1044
1045   TEST_ADD_DAYS (2009, 1, 31, 1, 2009, 2, 1);
1046   TEST_ADD_DAYS (2009, 2, 1, -1, 2009, 1, 31);
1047   TEST_ADD_DAYS (2008, 2, 28, 1, 2008, 2, 29);
1048   TEST_ADD_DAYS (2008, 12, 31, 1, 2009, 1, 1);
1049   TEST_ADD_DAYS (1, 1, 1, 1, 1, 1, 2);
1050   TEST_ADD_DAYS (1955, 5, 24, 10, 1955, 6, 3);
1051   TEST_ADD_DAYS (1955, 5, 24, -10, 1955, 5, 14);
1052 }
1053
1054 static void
1055 test_GDateTime_add_weeks (void)
1056 {
1057 #define TEST_ADD_WEEKS(y,m,d,a,ny,nm,nd) G_STMT_START { \
1058   GDateTime *dt, *dt2; \
1059   dt = g_date_time_new_local (y, m, d, 0, 0, 0); \
1060   dt2 = g_date_time_add_weeks (dt, a); \
1061   g_assert_cmpint (ny, ==, g_date_time_get_year (dt2)); \
1062   g_assert_cmpint (nm, ==, g_date_time_get_month (dt2)); \
1063   g_assert_cmpint (nd, ==, g_date_time_get_day_of_month (dt2)); \
1064   g_date_time_unref (dt); \
1065   g_date_time_unref (dt2); \
1066 } G_STMT_END
1067
1068   TEST_ADD_WEEKS (2009, 1, 1, 1, 2009, 1, 8);
1069   TEST_ADD_WEEKS (2009, 8, 30, 1, 2009, 9, 6);
1070   TEST_ADD_WEEKS (2009, 12, 31, 1, 2010, 1, 7);
1071   TEST_ADD_WEEKS (2009, 1, 1, -1, 2008, 12, 25);
1072 }
1073
1074 static void
1075 test_GDateTime_add_hours (void)
1076 {
1077 #define TEST_ADD_HOURS(y,m,d,h,mi,s,a,ny,nm,nd,nh,nmi,ns) G_STMT_START { \
1078   GDateTime *dt, *dt2; \
1079   dt = g_date_time_new_utc (y, m, d, h, mi, s); \
1080   dt2 = g_date_time_add_hours (dt, a); \
1081   g_assert_cmpint (ny, ==, g_date_time_get_year (dt2)); \
1082   g_assert_cmpint (nm, ==, g_date_time_get_month (dt2)); \
1083   g_assert_cmpint (nd, ==, g_date_time_get_day_of_month (dt2)); \
1084   g_assert_cmpint (nh, ==, g_date_time_get_hour (dt2)); \
1085   g_assert_cmpint (nmi, ==, g_date_time_get_minute (dt2)); \
1086   g_assert_cmpint (ns, ==, g_date_time_get_second (dt2)); \
1087   g_date_time_unref (dt); \
1088   g_date_time_unref (dt2); \
1089 } G_STMT_END
1090
1091   TEST_ADD_HOURS (2009,  1,  1,  0, 0, 0, 1, 2009, 1, 1, 1, 0, 0);
1092   TEST_ADD_HOURS (2008, 12, 31, 23, 0, 0, 1, 2009, 1, 1, 0, 0, 0);
1093 }
1094
1095 static void
1096 test_GDateTime_add_full (void)
1097 {
1098 #define TEST_ADD_FULL(y,m,d,h,mi,s,ay,am,ad,ah,ami,as,ny,nm,nd,nh,nmi,ns) G_STMT_START { \
1099   GDateTime *dt, *dt2; \
1100   dt = g_date_time_new_utc (y, m, d, h, mi, s); \
1101   dt2 = g_date_time_add_full (dt, ay, am, ad, ah, ami, as); \
1102   g_assert_cmpint (ny, ==, g_date_time_get_year (dt2)); \
1103   g_assert_cmpint (nm, ==, g_date_time_get_month (dt2)); \
1104   g_assert_cmpint (nd, ==, g_date_time_get_day_of_month (dt2)); \
1105   g_assert_cmpint (nh, ==, g_date_time_get_hour (dt2)); \
1106   g_assert_cmpint (nmi, ==, g_date_time_get_minute (dt2)); \
1107   g_assert_cmpint (ns, ==, g_date_time_get_second (dt2)); \
1108   g_date_time_unref (dt); \
1109   g_date_time_unref (dt2); \
1110 } G_STMT_END
1111
1112   TEST_ADD_FULL (2009, 10, 21,  0,  0, 0,
1113                     1,  1,  1,  1,  1, 1,
1114                  2010, 11, 22,  1,  1, 1);
1115   TEST_ADD_FULL (2000,  1,  1,  1,  1, 1,
1116                     0,  1,  0,  0,  0, 0,
1117                  2000,  2,  1,  1,  1, 1);
1118   TEST_ADD_FULL (2000,  1,  1,  0,  0, 0,
1119                    -1,  1,  0,  0,  0, 0,
1120                  1999,  2,  1,  0,  0, 0);
1121   TEST_ADD_FULL (2010, 10, 31,  0,  0, 0,
1122                     0,  4,  0,  0,  0, 0,
1123                  2011,  2, 28,  0,  0, 0);
1124   TEST_ADD_FULL (2010,  8, 25, 22, 45, 0,
1125                     0,  1,  6,  1, 25, 0,
1126                  2010, 10,  2,  0, 10, 0);
1127 }
1128
1129 static void
1130 test_GDateTime_add_minutes (void)
1131 {
1132 #define TEST_ADD_MINUTES(i,o) G_STMT_START { \
1133   GDateTime *dt, *dt2; \
1134   dt = g_date_time_new_local (2000, 1, 1, 0, 0, 0); \
1135   dt2 = g_date_time_add_minutes (dt, i); \
1136   g_assert_cmpint (o, ==, g_date_time_get_minute (dt2)); \
1137   g_date_time_unref (dt); \
1138   g_date_time_unref (dt2); \
1139 } G_STMT_END
1140
1141   TEST_ADD_MINUTES (60, 0);
1142   TEST_ADD_MINUTES (100, 40);
1143   TEST_ADD_MINUTES (5, 5);
1144   TEST_ADD_MINUTES (1441, 1);
1145   TEST_ADD_MINUTES (-1441, 59);
1146 }
1147
1148 static void
1149 test_GDateTime_add_seconds (void)
1150 {
1151 #define TEST_ADD_SECONDS(i,o) G_STMT_START { \
1152   GDateTime *dt, *dt2; \
1153   dt = g_date_time_new_local (2000, 1, 1, 0, 0, 0); \
1154   dt2 = g_date_time_add_seconds (dt, i); \
1155   g_assert_cmpint (o, ==, g_date_time_get_second (dt2)); \
1156   g_date_time_unref (dt); \
1157   g_date_time_unref (dt2); \
1158 } G_STMT_END
1159
1160   TEST_ADD_SECONDS (1, 1);
1161   TEST_ADD_SECONDS (60, 0);
1162   TEST_ADD_SECONDS (61, 1);
1163   TEST_ADD_SECONDS (120, 0);
1164   TEST_ADD_SECONDS (-61, 59);
1165   TEST_ADD_SECONDS (86401, 1);
1166   TEST_ADD_SECONDS (-86401, 59);
1167   TEST_ADD_SECONDS (-31, 29);
1168   TEST_ADD_SECONDS (13, 13);
1169 }
1170
1171 static void
1172 test_GDateTime_diff (void)
1173 {
1174 #define TEST_DIFF(y,m,d,y2,m2,d2,u) G_STMT_START { \
1175   GDateTime *dt1, *dt2; \
1176   GTimeSpan  ts = 0; \
1177   dt1 = g_date_time_new_utc (y, m, d, 0, 0, 0); \
1178   dt2 = g_date_time_new_utc (y2, m2, d2, 0, 0, 0); \
1179   ts = g_date_time_difference (dt2, dt1); \
1180   g_assert_cmpint (ts, ==, u); \
1181   g_date_time_unref (dt1); \
1182   g_date_time_unref (dt2); \
1183 } G_STMT_END
1184
1185   TEST_DIFF (2009, 1, 1, 2009, 2, 1, G_TIME_SPAN_DAY * 31);
1186   TEST_DIFF (2009, 1, 1, 2010, 1, 1, G_TIME_SPAN_DAY * 365);
1187   TEST_DIFF (2008, 2, 28, 2008, 2, 29, G_TIME_SPAN_DAY);
1188   TEST_DIFF (2008, 2, 29, 2008, 2, 28, -G_TIME_SPAN_DAY);
1189
1190   /* TODO: Add usec tests */
1191 }
1192
1193 static void
1194 test_GDateTime_get_minute (void)
1195 {
1196   GDateTime *dt;
1197
1198   dt = g_date_time_new_utc (2009, 12, 1, 1, 31, 0);
1199   g_assert_cmpint (31, ==, g_date_time_get_minute (dt));
1200   g_date_time_unref (dt);
1201 }
1202
1203 static void
1204 test_GDateTime_get_month (void)
1205 {
1206   GDateTime *dt;
1207
1208   dt = g_date_time_new_utc (2009, 12, 1, 1, 31, 0);
1209   g_assert_cmpint (12, ==, g_date_time_get_month (dt));
1210   g_date_time_unref (dt);
1211 }
1212
1213 static void
1214 test_GDateTime_get_second (void)
1215 {
1216   GDateTime *dt;
1217
1218   dt = g_date_time_new_utc (2009, 12, 1, 1, 31, 44);
1219   g_assert_cmpint (44, ==, g_date_time_get_second (dt));
1220   g_date_time_unref (dt);
1221 }
1222
1223 static void
1224 test_GDateTime_new_full (void)
1225 {
1226   GTimeZone *tz, *dt_tz;
1227   GDateTime *dt;
1228
1229 #ifdef G_OS_WIN32
1230   LCID currLcid = GetThreadLocale ();
1231   LANGID currLangId = LANGIDFROMLCID (currLcid);
1232   LANGID en = MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US);
1233   SetThreadUILanguage (en);
1234 #endif
1235
1236   dt = g_date_time_new_utc (2009, 12, 11, 12, 11, 10);
1237   g_assert_cmpint (2009, ==, g_date_time_get_year (dt));
1238   g_assert_cmpint (12, ==, g_date_time_get_month (dt));
1239   g_assert_cmpint (11, ==, g_date_time_get_day_of_month (dt));
1240   g_assert_cmpint (12, ==, g_date_time_get_hour (dt));
1241   g_assert_cmpint (11, ==, g_date_time_get_minute (dt));
1242   g_assert_cmpint (10, ==, g_date_time_get_second (dt));
1243   g_date_time_unref (dt);
1244
1245 #ifdef G_OS_UNIX
1246   tz = g_time_zone_new_identifier ("America/Tijuana");
1247 #elif defined G_OS_WIN32
1248   tz = g_time_zone_new_identifier ("Pacific Standard Time");
1249 #endif
1250   g_assert_nonnull (tz);
1251   dt = g_date_time_new (tz, 2010, 11, 24, 8, 4, 0);
1252
1253   dt_tz = g_date_time_get_timezone (dt);
1254   g_assert_cmpstr (g_time_zone_get_identifier (dt_tz), ==,
1255                    g_time_zone_get_identifier (tz));
1256
1257   g_assert_cmpint (2010, ==, g_date_time_get_year (dt));
1258   g_assert_cmpint (11, ==, g_date_time_get_month (dt));
1259   g_assert_cmpint (24, ==, g_date_time_get_day_of_month (dt));
1260   g_assert_cmpint (8, ==, g_date_time_get_hour (dt));
1261   g_assert_cmpint (4, ==, g_date_time_get_minute (dt));
1262   g_assert_cmpint (0, ==, g_date_time_get_second (dt));
1263 #ifdef G_OS_UNIX
1264   g_assert_cmpstr ("PST", ==, g_date_time_get_timezone_abbreviation (dt));
1265   g_assert_cmpstr ("America/Tijuana", ==, g_time_zone_get_identifier (dt_tz));
1266 #elif defined G_OS_WIN32
1267   g_assert_cmpstr ("Pacific Standard Time", ==,
1268                     g_date_time_get_timezone_abbreviation (dt));
1269   g_assert_cmpstr ("Pacific Standard Time", ==,
1270                    g_time_zone_get_identifier (dt_tz));
1271   SetThreadUILanguage (currLangId);
1272 #endif
1273   g_assert (!g_date_time_is_daylight_savings (dt));
1274   g_date_time_unref (dt);
1275   g_time_zone_unref (tz);
1276
1277   /* Check month limits */
1278   dt = g_date_time_new_utc (2016, 1, 31, 22, 10, 42);
1279   ASSERT_DATE (dt, 2016, 1, 31);
1280   g_date_time_unref (dt);
1281   dt = g_date_time_new_utc (2016, 1, 32, 22, 10, 42);
1282   g_assert_null (dt);
1283   dt = g_date_time_new_utc (2016, 2, 29, 22, 10, 42);
1284   ASSERT_DATE (dt, 2016, 2, 29);
1285   g_date_time_unref (dt);
1286   dt = g_date_time_new_utc (2016, 2, 30, 22, 10, 42);
1287   g_assert_null (dt);
1288   dt = g_date_time_new_utc (2017, 2, 28, 22, 10, 42);
1289   ASSERT_DATE (dt, 2017, 2, 28);
1290   g_date_time_unref (dt);
1291   dt = g_date_time_new_utc (2017, 2, 29, 22, 10, 42);
1292   g_assert_null (dt);
1293   dt = g_date_time_new_utc (2016, 3, 31, 22, 10, 42);
1294   ASSERT_DATE (dt, 2016, 3, 31);
1295   g_date_time_unref (dt);
1296   dt = g_date_time_new_utc (2016, 3, 32, 22, 10, 42);
1297   g_assert_null (dt);
1298   dt = g_date_time_new_utc (2016, 4, 30, 22, 10, 42);
1299   ASSERT_DATE (dt, 2016, 4, 30);
1300   g_date_time_unref (dt);
1301   dt = g_date_time_new_utc (2016, 4, 31, 22, 10, 42);
1302   g_assert_null (dt);
1303   dt = g_date_time_new_utc (2016, 5, 31, 22, 10, 42);
1304   ASSERT_DATE (dt, 2016, 5, 31);
1305   g_date_time_unref (dt);
1306   dt = g_date_time_new_utc (2016, 5, 32, 22, 10, 42);
1307   g_assert_null (dt);
1308   dt = g_date_time_new_utc (2016, 6, 30, 22, 10, 42);
1309   ASSERT_DATE (dt, 2016, 6, 30);
1310   g_date_time_unref (dt);
1311   dt = g_date_time_new_utc (2016, 6, 31, 22, 10, 42);
1312   g_assert_null (dt);
1313   dt = g_date_time_new_utc (2016, 7, 31, 22, 10, 42);
1314   ASSERT_DATE (dt, 2016, 7, 31);
1315   g_date_time_unref (dt);
1316   dt = g_date_time_new_utc (2016, 7, 32, 22, 10, 42);
1317   g_assert_null (dt);
1318   dt = g_date_time_new_utc (2016, 8, 31, 22, 10, 42);
1319   ASSERT_DATE (dt, 2016, 8, 31);
1320   g_date_time_unref (dt);
1321   dt = g_date_time_new_utc (2016, 8, 32, 22, 10, 42);
1322   g_assert_null (dt);
1323   dt = g_date_time_new_utc (2016, 9, 30, 22, 10, 42);
1324   ASSERT_DATE (dt, 2016, 9, 30);
1325   g_date_time_unref (dt);
1326   dt = g_date_time_new_utc (2016, 9, 31, 22, 10, 42);
1327   g_assert_null (dt);
1328   dt = g_date_time_new_utc (2016, 10, 31, 22, 10, 42);
1329   ASSERT_DATE (dt, 2016, 10, 31);
1330   g_date_time_unref (dt);
1331   dt = g_date_time_new_utc (2016, 10, 32, 22, 10, 42);
1332   g_assert_null (dt);
1333   dt = g_date_time_new_utc (2016, 11, 30, 22, 10, 42);
1334   ASSERT_DATE (dt, 2016, 11, 30);
1335   g_date_time_unref (dt);
1336   dt = g_date_time_new_utc (2016, 11, 31, 22, 10, 42);
1337   g_assert_null (dt);
1338   dt = g_date_time_new_utc (2016, 12, 31, 22, 10, 42);
1339   ASSERT_DATE (dt, 2016, 12, 31);
1340   g_date_time_unref (dt);
1341   dt = g_date_time_new_utc (2016, 12, 32, 22, 10, 42);
1342   g_assert_null (dt);
1343
1344   /* Seconds limits. */
1345   dt = g_date_time_new_utc (2020, 12, 9, 14, 49, NAN);
1346   g_assert_null (dt);
1347   dt = g_date_time_new_utc (2020, 12, 9, 14, 49, -0.1);
1348   g_assert_null (dt);
1349   dt = g_date_time_new_utc (2020, 12, 9, 14, 49, 60.0);
1350   g_assert_null (dt);
1351
1352   /* Year limits */
1353   dt = g_date_time_new_utc (10000, 1, 1, 0, 0, 0);
1354   dt = g_date_time_new_utc (0, 1, 1, 0, 0, 0);
1355 }
1356
1357 static void
1358 test_GDateTime_now_utc (void)
1359 {
1360   GDateTime *dt;
1361   struct tm  tm;
1362   time_t     t;
1363   time_t     after;
1364
1365   /* t <= dt.to_unix() <= after, but the inequalities might not be
1366    * equality if we're close to the boundary between seconds.
1367    * We loop until t == after (and hence dt.to_unix() should equal both)
1368    * to guard against that. */
1369   do
1370     {
1371       t = g_get_real_time () / G_TIME_SPAN_SECOND;
1372 #ifdef HAVE_GMTIME_R
1373       gmtime_r (&t, &tm);
1374 #else
1375       {
1376         struct tm *tmp = gmtime (&t);
1377         /* Assume gmtime() can't fail as we got t from time(NULL). (Note
1378          * that on Windows, gmtime() *is* MT-safe, it uses a thread-local
1379          * buffer.)
1380          */
1381         memcpy (&tm, tmp, sizeof (struct tm));
1382       }
1383 #endif
1384       dt = g_date_time_new_now_utc ();
1385
1386       after = g_get_real_time () / G_TIME_SPAN_SECOND;
1387     }
1388   while (t != after);
1389
1390   g_assert_cmpint (tm.tm_year + 1900, ==, g_date_time_get_year (dt));
1391   g_assert_cmpint (tm.tm_mon + 1, ==, g_date_time_get_month (dt));
1392   g_assert_cmpint (tm.tm_mday, ==, g_date_time_get_day_of_month (dt));
1393   g_assert_cmpint (tm.tm_hour, ==, g_date_time_get_hour (dt));
1394   g_assert_cmpint (tm.tm_min, ==, g_date_time_get_minute (dt));
1395   g_assert_cmpint (tm.tm_sec, ==, g_date_time_get_second (dt));
1396   g_date_time_unref (dt);
1397 }
1398
1399 static void
1400 test_GDateTime_new_from_unix_utc (void)
1401 {
1402   GDateTime *dt;
1403   gint64 t;
1404
1405   t = g_get_real_time ();
1406
1407 #if 0
1408   dt = g_date_time_new_from_unix_utc (t);
1409   g_assert (dt == NULL);
1410 #endif
1411
1412   t = t / 1e6;  /* oops, this was microseconds */
1413
1414   dt = g_date_time_new_from_unix_utc (t);
1415   g_assert (dt != NULL);
1416
1417   g_assert (dt == g_date_time_ref (dt));
1418   g_date_time_unref (dt);
1419   g_assert_cmpint (g_date_time_to_unix (dt), ==, t);
1420   g_date_time_unref (dt);
1421 }
1422
1423 static void
1424 test_GDateTime_get_utc_offset (void)
1425 {
1426 #if defined (HAVE_STRUCT_TM_TM_GMTOFF) || defined (HAVE_STRUCT_TM___TM_GMTOFF)
1427   GDateTime *dt;
1428   GTimeSpan ts;
1429   struct tm tm;
1430
1431   memset (&tm, 0, sizeof (tm));
1432   get_localtime_tm (g_get_real_time () / G_TIME_SPAN_SECOND, &tm);
1433
1434   dt = g_date_time_new_now_local ();
1435   ts = g_date_time_get_utc_offset (dt);
1436 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
1437   g_assert_cmpint (ts, ==, (tm.tm_gmtoff * G_TIME_SPAN_SECOND));
1438 #endif
1439 #ifdef HAVE_STRUCT_TM___TM_GMTOFF
1440   g_assert_cmpint (ts, ==, (tm.__tm_gmtoff * G_TIME_SPAN_SECOND));
1441 #endif
1442   g_date_time_unref (dt);
1443 #endif
1444 }
1445
1446 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1447 static void
1448 test_GDateTime_to_timeval (void)
1449 {
1450   GTimeVal tv1, tv2;
1451   GDateTime *dt;
1452
1453   memset (&tv1, 0, sizeof (tv1));
1454   memset (&tv2, 0, sizeof (tv2));
1455
1456   g_get_current_time (&tv1);
1457   dt = g_date_time_new_from_timeval_local (&tv1);
1458   g_date_time_to_timeval (dt, &tv2);
1459   g_assert_cmpint (tv1.tv_sec, ==, tv2.tv_sec);
1460   g_assert_cmpint (tv1.tv_usec, ==, tv2.tv_usec);
1461   g_date_time_unref (dt);
1462 }
1463 G_GNUC_END_IGNORE_DEPRECATIONS
1464
1465 static void
1466 test_GDateTime_to_local (void)
1467 {
1468   GDateTime *utc = NULL, *now = NULL, *dt;
1469   time_t before, after;
1470
1471   /* before <= utc.to_unix() <= now.to_unix() <= after, but the inequalities
1472    * might not be equality if we're close to the boundary between seconds.
1473    * We loop until before == after (and hence the GDateTimes should match)
1474    * to guard against that. */
1475   do
1476     {
1477       before = g_get_real_time () / G_TIME_SPAN_SECOND;
1478       g_clear_pointer (&utc, g_date_time_unref);
1479       g_clear_pointer (&now, g_date_time_unref);
1480       utc = g_date_time_new_now_utc ();
1481       now = g_date_time_new_now_local ();
1482       after = g_get_real_time () / G_TIME_SPAN_SECOND;
1483     }
1484   while (before != after);
1485
1486   dt = g_date_time_to_local (utc);
1487
1488   g_assert_cmpint (g_date_time_get_year (now), ==, g_date_time_get_year (dt));
1489   g_assert_cmpint (g_date_time_get_month (now), ==, g_date_time_get_month (dt));
1490   g_assert_cmpint (g_date_time_get_day_of_month (now), ==, g_date_time_get_day_of_month (dt));
1491   g_assert_cmpint (g_date_time_get_hour (now), ==, g_date_time_get_hour (dt));
1492   g_assert_cmpint (g_date_time_get_minute (now), ==, g_date_time_get_minute (dt));
1493   g_assert_cmpint (g_date_time_get_second (now), ==, g_date_time_get_second (dt));
1494
1495   g_date_time_unref (now);
1496   g_date_time_unref (utc);
1497   g_date_time_unref (dt);
1498 }
1499
1500 static void
1501 test_GDateTime_to_utc (void)
1502 {
1503   GDateTime *dt, *dt2;
1504   time_t     t;
1505   struct tm  tm;
1506
1507   t = time (NULL);
1508   g_assert_cmpint (t, !=, (time_t) -1);
1509 #ifdef HAVE_GMTIME_R
1510   gmtime_r (&t, &tm);
1511 #else
1512   {
1513     struct tm *tmp = gmtime (&t);
1514     memcpy (&tm, tmp, sizeof (struct tm));
1515   }
1516 #endif
1517   dt2 = g_date_time_new_from_unix_local (t);
1518   dt = g_date_time_to_utc (dt2);
1519   g_assert_cmpint (tm.tm_year + 1900, ==, g_date_time_get_year (dt));
1520   g_assert_cmpint (tm.tm_mon + 1, ==, g_date_time_get_month (dt));
1521   g_assert_cmpint (tm.tm_mday, ==, g_date_time_get_day_of_month (dt));
1522   g_assert_cmpint (tm.tm_hour, ==, g_date_time_get_hour (dt));
1523   g_assert_cmpint (tm.tm_min, ==, g_date_time_get_minute (dt));
1524   g_assert_cmpint (tm.tm_sec, ==, g_date_time_get_second (dt));
1525   g_date_time_unref (dt);
1526   g_date_time_unref (dt2);
1527 }
1528
1529 static void
1530 test_GDateTime_get_day_of_year (void)
1531 {
1532 #define TEST_DAY_OF_YEAR(y,m,d,o)                       G_STMT_START {  \
1533   GDateTime *__dt = g_date_time_new_local ((y), (m), (d), 0, 0, 0);     \
1534   g_assert_cmpint ((o), ==, g_date_time_get_day_of_year (__dt));        \
1535   g_date_time_unref (__dt);                             } G_STMT_END
1536
1537   TEST_DAY_OF_YEAR (2009, 1, 1, 1);
1538   TEST_DAY_OF_YEAR (2009, 2, 1, 32);
1539   TEST_DAY_OF_YEAR (2009, 8, 16, 228);
1540   TEST_DAY_OF_YEAR (2008, 8, 16, 229);
1541 }
1542
1543 static void
1544 test_GDateTime_printf (void)
1545 {
1546   gchar *old_lc_all;
1547   gchar *old_lc_messages;
1548
1549 #ifdef G_OS_WIN32
1550   gchar *current_tz = NULL;
1551   DYNAMIC_TIME_ZONE_INFORMATION dtz_info;
1552 #endif
1553
1554 #define TEST_PRINTF(f,o)                        G_STMT_START {  \
1555   const char *expected = (o);                                   \
1556   GDateTime *__dt = g_date_time_new_utc (2009, 10, 24, 0, 0, 0);\
1557   gchar *actual = g_date_time_format (__dt, (f));               \
1558   g_test_message ("%s -> expected: %s", (f), expected);         \
1559   g_test_message ("%s -> actual:   %s", (f), actual);           \
1560   g_assert_cmpstr (actual, ==, expected);                       \
1561   g_date_time_unref (__dt);                                     \
1562   g_free (actual);                              } G_STMT_END
1563
1564 #define TEST_PRINTF_DATE(y,m,d,f,o)             G_STMT_START {  \
1565   const char *expected = (o);                                   \
1566   GDateTime *dt = g_date_time_new_utc (y, m, d, 0, 0, 0);       \
1567   gchar *actual = g_date_time_format (dt, (f));                 \
1568   gchar *expected_casefold = g_utf8_casefold (expected, -1);    \
1569   gchar *actual_casefold = g_utf8_casefold (actual, -1);        \
1570   g_test_message ("%s -> expected: %s", (f), expected);         \
1571   g_test_message ("%s -> actual:   %s", (f), actual);           \
1572   g_assert_cmpstr (expected_casefold, ==, (actual_casefold));   \
1573   g_date_time_unref (dt);                                       \
1574   g_free (expected_casefold);                                   \
1575   g_free (actual_casefold);                                     \
1576   g_free (actual);                                   } G_STMT_END
1577
1578 #define TEST_PRINTF_TIME(h,m,s,f,o)             G_STMT_START { \
1579   GDateTime *dt = g_date_time_new_utc (2009, 10, 24, (h), (m), (s)); \
1580   const char *expected = (o);                                   \
1581   gchar *actual = g_date_time_format (dt, (f));                 \
1582   g_test_message ("%s -> expected: %s", (f), expected);         \
1583   g_test_message ("%s -> actual:   %s", (f), actual);           \
1584   g_assert_cmpstr (actual, ==, expected);                       \
1585   g_date_time_unref (dt);                                       \
1586   g_free (actual);                              } G_STMT_END
1587
1588   old_lc_all = g_strdup (g_getenv ("LC_ALL"));
1589   g_unsetenv ("LC_ALL");
1590
1591   old_lc_messages = g_strdup (g_getenv ("LC_MESSAGES"));
1592   g_setenv ("LC_MESSAGES", "C", TRUE);
1593
1594   TEST_PRINTF ("%a", "Sat");
1595   TEST_PRINTF ("%A", "Saturday");
1596   TEST_PRINTF ("%b", "Oct");
1597   TEST_PRINTF ("%B", "October");
1598   TEST_PRINTF ("%d", "24");
1599   TEST_PRINTF_DATE (2009, 1, 1, "%d", "01");
1600   TEST_PRINTF ("%e", "24");
1601   TEST_PRINTF_DATE (2009, 1, 1, "%e", "\u20071");
1602   TEST_PRINTF_TIME (10, 10, 1.001, "%f", "001000");
1603   TEST_PRINTF ("%h", "Oct");
1604   TEST_PRINTF ("%H", "00");
1605   TEST_PRINTF_TIME (15, 0, 0, "%H", "15");
1606   TEST_PRINTF ("%I", "12");
1607   TEST_PRINTF_TIME (12, 0, 0, "%I", "12");
1608   TEST_PRINTF_TIME (15, 0, 0, "%I", "03");
1609   TEST_PRINTF ("%j", "297");
1610   TEST_PRINTF ("%k", "\u20070");
1611   TEST_PRINTF_TIME (13, 13, 13, "%k", "13");
1612   TEST_PRINTF ("%l", "12");
1613   TEST_PRINTF_TIME (12, 0, 0, "%I", "12");
1614   TEST_PRINTF_TIME (13, 13, 13, "%l", "\u20071");
1615   TEST_PRINTF_TIME (10, 13, 13, "%l", "10");
1616   TEST_PRINTF ("%m", "10");
1617   TEST_PRINTF ("%M", "00");
1618   TEST_PRINTF ("%p", "AM");
1619   TEST_PRINTF_TIME (13, 13, 13, "%p", "PM");
1620   TEST_PRINTF ("%P", "am");
1621   TEST_PRINTF_TIME (13, 13, 13, "%P", "pm");
1622   TEST_PRINTF ("%r", "12:00:00 AM");
1623   TEST_PRINTF_TIME (13, 13, 13, "%r", "01:13:13 PM");
1624   TEST_PRINTF ("%R", "00:00");
1625   TEST_PRINTF_TIME (13, 13, 31, "%R", "13:13");
1626   TEST_PRINTF ("%S", "00");
1627   TEST_PRINTF ("%t", "  ");
1628   TEST_PRINTF ("%u", "6");
1629   TEST_PRINTF ("%x", "10/24/09");
1630   TEST_PRINTF ("%X", "00:00:00");
1631   TEST_PRINTF_TIME (13, 14, 15, "%X", "13:14:15");
1632   TEST_PRINTF ("%y", "09");
1633   TEST_PRINTF ("%Y", "2009");
1634   TEST_PRINTF ("%%", "%");
1635   TEST_PRINTF ("%", "");
1636   TEST_PRINTF ("%9", NULL);
1637 #ifdef G_OS_UNIX
1638   TEST_PRINTF ("%Z", "UTC");
1639 #elif defined G_OS_WIN32
1640   g_assert (GetDynamicTimeZoneInformation (&dtz_info) != TIME_ZONE_ID_INVALID);
1641   if (wcscmp (dtz_info.StandardName, L"") != 0)
1642     current_tz = g_utf16_to_utf8 (dtz_info.StandardName, -1, NULL, NULL, NULL);
1643   else
1644     current_tz = g_utf16_to_utf8 (dtz_info.DaylightName, -1, NULL, NULL, NULL);
1645
1646   TEST_PRINTF ("%Z", current_tz);
1647   g_free (current_tz);
1648 #endif
1649
1650   if (old_lc_messages != NULL)
1651     g_setenv ("LC_MESSAGES", old_lc_messages, TRUE);
1652   else
1653     g_unsetenv ("LC_MESSAGES");
1654   g_free (old_lc_messages);
1655
1656   if (old_lc_all != NULL)
1657     g_setenv ("LC_ALL", old_lc_all, TRUE);
1658   g_free (old_lc_all);
1659 }
1660
1661 static void
1662 test_non_utf8_printf (void)
1663 {
1664   gchar *oldlocale;
1665
1666   if (skip_if_running_uninstalled())
1667     return;
1668
1669   oldlocale = g_strdup (setlocale (LC_ALL, NULL));
1670   setlocale (LC_ALL, "ja_JP.eucjp");
1671   if (strstr (setlocale (LC_ALL, NULL), "ja_JP") == NULL)
1672     {
1673       g_test_skip ("locale ja_JP.eucjp not available, skipping non-UTF8 tests");
1674       g_free (oldlocale);
1675       return;
1676     }
1677   if (g_get_charset (NULL))
1678     {
1679       g_test_skip ("locale ja_JP.eucjp may be available, but glib seems to think that it's equivalent to UTF-8, skipping non-UTF-8 tests. This is a known issue on Darwin");
1680       setlocale (LC_ALL, oldlocale);
1681       g_free (oldlocale);
1682       return;
1683     }
1684
1685   /* These are the outputs that ja_JP.UTF-8 generates; if everything
1686    * is working then ja_JP.eucjp should generate the same.
1687    */
1688   TEST_PRINTF ("%a", "\345\234\237");
1689   TEST_PRINTF ("%A", "\345\234\237\346\233\234\346\227\245");
1690 #ifndef __APPLE__ /* OSX just returns the number */
1691   TEST_PRINTF ("%b", "10\346\234\210");
1692 #endif
1693   TEST_PRINTF ("%B", "10\346\234\210");
1694   TEST_PRINTF ("%c", "2009年10月24日 00時00分00秒");
1695   TEST_PRINTF ("%C", "20");
1696   TEST_PRINTF ("%d", "24");
1697   TEST_PRINTF_DATE (2009, 1, 1, "%d", "01");
1698   TEST_PRINTF ("%e", "24"); // fixme
1699 #ifndef __APPLE__ /* OSX just returns the number */
1700   TEST_PRINTF ("%h", "10\346\234\210");
1701 #endif
1702   TEST_PRINTF ("%H", "00");
1703   TEST_PRINTF_TIME (15, 0, 0, "%H", "15");
1704   TEST_PRINTF ("%I", "12");
1705   TEST_PRINTF_TIME (12, 0, 0, "%I", "12");
1706   TEST_PRINTF_TIME (15, 0, 0, "%I", "03");
1707   TEST_PRINTF ("%j", "297");
1708   TEST_PRINTF ("%k", "\u20070");
1709   TEST_PRINTF_TIME (13, 13, 13, "%k", "13");
1710   TEST_PRINTF ("%l", "12");
1711   TEST_PRINTF_TIME (12, 0, 0, "%I", "12");
1712   TEST_PRINTF_TIME (13, 13, 13, "%l", "\u20071");
1713   TEST_PRINTF_TIME (10, 13, 13, "%l", "10");
1714   TEST_PRINTF ("%m", "10");
1715   TEST_PRINTF ("%M", "00");
1716 #ifndef __APPLE__ /* OSX returns latin "AM", not japanese */
1717   TEST_PRINTF ("%p", "\345\215\210\345\211\215");
1718   TEST_PRINTF_TIME (13, 13, 13, "%p", "\345\215\210\345\276\214");
1719   TEST_PRINTF ("%P", "\345\215\210\345\211\215");
1720   TEST_PRINTF_TIME (13, 13, 13, "%P", "\345\215\210\345\276\214");
1721   TEST_PRINTF ("%r", "\345\215\210\345\211\21512\346\231\20200\345\210\20600\347\247\222");
1722   TEST_PRINTF_TIME (13, 13, 13, "%r", "\345\215\210\345\276\21401\346\231\20213\345\210\20613\347\247\222");
1723 #endif
1724   TEST_PRINTF ("%R", "00:00");
1725   TEST_PRINTF_TIME (13, 13, 31, "%R", "13:13");
1726   TEST_PRINTF ("%S", "00");
1727   TEST_PRINTF ("%t", "  ");
1728   TEST_PRINTF ("%u", "6");
1729 #ifndef __APPLE__ /* OSX returns YYYY/MM/DD in ASCII */
1730   TEST_PRINTF ("%x", "2009\345\271\26410\346\234\21024\346\227\245");
1731 #endif
1732   TEST_PRINTF ("%X", "00\346\231\20200\345\210\20600\347\247\222");
1733   TEST_PRINTF_TIME (13, 14, 15, "%X", "13\346\231\20214\345\210\20615\347\247\222");
1734   TEST_PRINTF ("%y", "09");
1735   TEST_PRINTF ("%Y", "2009");
1736   TEST_PRINTF ("%%", "%");
1737   TEST_PRINTF ("%", "");
1738   TEST_PRINTF ("%9", NULL);
1739 #if defined(HAVE_LANGINFO_ERA) && (G_BYTE_ORDER == G_LITTLE_ENDIAN || GLIB_SIZEOF_VOID_P == 4)
1740   TEST_PRINTF ("%Ec", "平成21年10月24日 00時00分00秒");
1741   TEST_PRINTF ("%EC", "平成");
1742   TEST_PRINTF ("%Ex", "平成21年10月24日");
1743   TEST_PRINTF ("%EX", "00時00分00秒");
1744   TEST_PRINTF ("%Ey", "21");
1745   TEST_PRINTF ("%EY", "平成21年");
1746 #else
1747   TEST_PRINTF ("%Ec", "2009年10月24日 00時00分00秒");
1748   TEST_PRINTF ("%EC", "20");
1749   TEST_PRINTF ("%Ex", "2009\345\271\26410\346\234\21024\346\227\245");
1750   TEST_PRINTF ("%EX", "00\346\231\20200\345\210\20600\347\247\222");
1751   TEST_PRINTF ("%Ey", "09");
1752   TEST_PRINTF ("%EY", "2009");
1753 #endif
1754
1755   setlocale (LC_ALL, oldlocale);
1756   g_free (oldlocale);
1757 }
1758
1759 /* Checks that it is possible to use format string that
1760  * is unrepresentable in current locale charset. */
1761 static void
1762 test_format_unrepresentable (void)
1763 {
1764   gchar *oldlocale = g_strdup (setlocale (LC_ALL, NULL));
1765   setlocale (LC_ALL, "POSIX");
1766
1767   TEST_PRINTF ("ąśćł", "ąśćł");
1768
1769   /* We are using Unicode ratio symbol here, which is outside ASCII. */
1770   TEST_PRINTF_TIME (23, 15, 0, "%H∶%M", "23∶15");
1771
1772   /* Test again, this time in locale with non ASCII charset. */
1773   if (setlocale (LC_ALL, "pl_PL.ISO-8859-2") != NULL)
1774     TEST_PRINTF_TIME (23, 15, 0, "%H∶%M", "23∶15");
1775   else
1776     g_test_skip ("locale pl_PL.ISO-8859-2 not available, skipping test");
1777
1778   setlocale (LC_ALL, oldlocale);
1779   g_free (oldlocale);
1780 }
1781
1782 static void
1783 test_modifiers (void)
1784 {
1785   gchar *oldlocale;
1786
1787   TEST_PRINTF_DATE (2009, 1,  1,  "%d", "01");
1788   TEST_PRINTF_DATE (2009, 1,  1, "%_d", " 1");
1789   TEST_PRINTF_DATE (2009, 1,  1, "%-d", "1");
1790   TEST_PRINTF_DATE (2009, 1,  1, "%0d", "01");
1791   TEST_PRINTF_DATE (2009, 1, 21,  "%d", "21");
1792   TEST_PRINTF_DATE (2009, 1, 21, "%_d", "21");
1793   TEST_PRINTF_DATE (2009, 1, 21, "%-d", "21");
1794   TEST_PRINTF_DATE (2009, 1, 21, "%0d", "21");
1795
1796   TEST_PRINTF_DATE (2009, 1,  1,  "%e", "\u20071");
1797   TEST_PRINTF_DATE (2009, 1,  1, "%_e", " 1");
1798   TEST_PRINTF_DATE (2009, 1,  1, "%-e", "1");
1799   TEST_PRINTF_DATE (2009, 1,  1, "%0e", "01");
1800   TEST_PRINTF_DATE (2009, 1, 21,  "%e", "21");
1801   TEST_PRINTF_DATE (2009, 1, 21, "%_e", "21");
1802   TEST_PRINTF_DATE (2009, 1, 21, "%-e", "21");
1803   TEST_PRINTF_DATE (2009, 1, 21, "%0e", "21");
1804
1805   TEST_PRINTF_DATE (2009, 1, 1, "%a", "Thu");
1806   TEST_PRINTF_DATE (2009, 1, 1, "%^a", "THU");
1807   TEST_PRINTF_DATE (2009, 1, 1, "%#a", "THU");
1808
1809   TEST_PRINTF_DATE (2009, 1, 1, "%A", "Thursday");
1810   TEST_PRINTF_DATE (2009, 1, 1, "%^A", "THURSDAY");
1811   TEST_PRINTF_DATE (2009, 1, 1, "%#A", "THURSDAY");
1812
1813   TEST_PRINTF_DATE (2009, 1, 1, "%b", "Jan");
1814   TEST_PRINTF_DATE (2009, 1, 1, "%^b", "JAN");
1815   TEST_PRINTF_DATE (2009, 1, 1, "%#b", "JAN");
1816
1817   TEST_PRINTF_DATE (2009, 1, 1, "%B", "January");
1818   TEST_PRINTF_DATE (2009, 1, 1, "%^B", "JANUARY");
1819   TEST_PRINTF_DATE (2009, 1, 1, "%#B", "JANUARY");
1820
1821   TEST_PRINTF_DATE (2009, 1, 1, "%h", "Jan");
1822   TEST_PRINTF_DATE (2009, 1, 1, "%^h", "JAN");
1823   TEST_PRINTF_DATE (2009, 1, 1, "%#h", "JAN");
1824
1825   TEST_PRINTF_DATE (2009, 1, 1, "%Z", "UTC");
1826   TEST_PRINTF_DATE (2009, 1, 1, "%^Z", "UTC");
1827   TEST_PRINTF_DATE (2009, 1, 1, "%#Z", "utc");
1828
1829   TEST_PRINTF_TIME ( 1, 0, 0,  "%H", "01");
1830   TEST_PRINTF_TIME ( 1, 0, 0, "%_H", " 1");
1831   TEST_PRINTF_TIME ( 1, 0, 0, "%-H", "1");
1832   TEST_PRINTF_TIME ( 1, 0, 0, "%0H", "01");
1833   TEST_PRINTF_TIME (21, 0, 0,  "%H", "21");
1834   TEST_PRINTF_TIME (21, 0, 0, "%_H", "21");
1835   TEST_PRINTF_TIME (21, 0, 0, "%-H", "21");
1836   TEST_PRINTF_TIME (21, 0, 0, "%0H", "21");
1837
1838   TEST_PRINTF_TIME ( 1, 0, 0,  "%I", "01");
1839   TEST_PRINTF_TIME ( 1, 0, 0, "%_I", " 1");
1840   TEST_PRINTF_TIME ( 1, 0, 0, "%-I", "1");
1841   TEST_PRINTF_TIME ( 1, 0, 0, "%0I", "01");
1842   TEST_PRINTF_TIME (23, 0, 0,  "%I", "11");
1843   TEST_PRINTF_TIME (23, 0, 0, "%_I", "11");
1844   TEST_PRINTF_TIME (23, 0, 0, "%-I", "11");
1845   TEST_PRINTF_TIME (23, 0, 0, "%0I", "11");
1846
1847   TEST_PRINTF_TIME ( 1, 0, 0,  "%k", "\u20071");
1848   TEST_PRINTF_TIME ( 1, 0, 0, "%_k", " 1");
1849   TEST_PRINTF_TIME ( 1, 0, 0, "%-k", "1");
1850   TEST_PRINTF_TIME ( 1, 0, 0, "%0k", "01");
1851
1852   TEST_PRINTF_TIME ( 1, 0, 0,  "%l", "\u20071");
1853   TEST_PRINTF_TIME ( 1, 0, 0, "%_l", " 1");
1854   TEST_PRINTF_TIME ( 1, 0, 0, "%-l", "1");
1855   TEST_PRINTF_TIME ( 1, 0, 0, "%0l", "01");
1856   TEST_PRINTF_TIME (23, 0, 0,  "%l", "11");
1857   TEST_PRINTF_TIME (23, 0, 0, "%_l", "11");
1858   TEST_PRINTF_TIME (23, 0, 0, "%-l", "11");
1859   TEST_PRINTF_TIME (23, 0, 0, "%0l", "11");
1860
1861   TEST_PRINTF_TIME (1, 0, 0, "%p", "AM");
1862   TEST_PRINTF_TIME (1, 0, 0, "%^p", "AM");
1863   TEST_PRINTF_TIME (1, 0, 0, "%#p", "am");
1864
1865   TEST_PRINTF_TIME (1, 0, 0, "%P", "am");
1866   TEST_PRINTF_TIME (1, 0, 0, "%^P", "AM");
1867   TEST_PRINTF_TIME (1, 0, 0, "%#P", "am");
1868
1869   oldlocale = g_strdup (setlocale (LC_ALL, NULL));
1870
1871   setlocale (LC_ALL, "fa_IR.utf-8");
1872 #ifdef HAVE_LANGINFO_OUTDIGIT
1873   if (strstr (setlocale (LC_ALL, NULL), "fa_IR") != NULL)
1874     {
1875       TEST_PRINTF_TIME (23, 0, 0, "%OH", "\333\262\333\263");    /* '23' */
1876       TEST_PRINTF_TIME (23, 0, 0, "%OI", "\333\261\333\261");    /* '11' */
1877       TEST_PRINTF_TIME (23, 0, 0, "%OM", "\333\260\333\260");    /* '00' */
1878
1879       TEST_PRINTF_DATE (2011, 7, 1, "%Om", "\333\260\333\267");  /* '07' */
1880       TEST_PRINTF_DATE (2011, 7, 1, "%0Om", "\333\260\333\267"); /* '07' */
1881       TEST_PRINTF_DATE (2011, 7, 1, "%-Om", "\333\267");         /* '7' */
1882       TEST_PRINTF_DATE (2011, 7, 1, "%_Om", " \333\267");        /* ' 7' */
1883     }
1884   else
1885     g_test_skip ("locale fa_IR not available, skipping O modifier tests");
1886 #else
1887     g_test_skip ("langinfo not available, skipping O modifier tests");
1888 #endif
1889
1890   setlocale (LC_ALL, "gu_IN.utf-8");
1891 #ifdef HAVE_LANGINFO_OUTDIGIT
1892   if (strstr (setlocale (LC_ALL, NULL), "gu_IN") != NULL)
1893     {
1894       TEST_PRINTF_TIME (23, 0, 0, "%OH", "૨૩");    /* '23' */
1895       TEST_PRINTF_TIME (23, 0, 0, "%OI", "૧૧");    /* '11' */
1896       TEST_PRINTF_TIME (23, 0, 0, "%OM", "૦૦");    /* '00' */
1897
1898       TEST_PRINTF_DATE (2011, 7, 1, "%Om", "૦૭");  /* '07' */
1899       TEST_PRINTF_DATE (2011, 7, 1, "%0Om", "૦૭"); /* '07' */
1900       TEST_PRINTF_DATE (2011, 7, 1, "%-Om", "૭");         /* '7' */
1901       TEST_PRINTF_DATE (2011, 7, 1, "%_Om", " ૭");        /* ' 7' */
1902     }
1903   else
1904     g_test_skip ("locale gu_IN not available, skipping O modifier tests");
1905 #else
1906     g_test_skip ("langinfo not available, skipping O modifier tests");
1907 #endif
1908
1909   setlocale (LC_ALL, "en_GB.utf-8");
1910   if (strstr (setlocale (LC_ALL, NULL), "en_GB") != NULL)
1911     {
1912 #ifndef __APPLE__
1913       TEST_PRINTF_DATE (2009, 1, 1, "%c", "thu 01 jan 2009 00:00:00 utc");
1914       TEST_PRINTF_DATE (2009, 1, 1, "%Ec", "thu 01 jan 2009 00:00:00 utc");
1915 #else
1916       /* macOS uses a figure space (U+2007) to pad the day */
1917       TEST_PRINTF_DATE (2009, 1, 1, "%c", "thu " "\xe2\x80\x87" "1 jan 00:00:00 2009");
1918       TEST_PRINTF_DATE (2009, 1, 1, "%Ec", "thu " "\xe2\x80\x87" "1 jan 00:00:00 2009");
1919 #endif
1920
1921       TEST_PRINTF_DATE (2009, 1, 1, "%C", "20");
1922       TEST_PRINTF_DATE (2009, 1, 1, "%EC", "20");
1923
1924 #ifndef __APPLE__
1925       TEST_PRINTF_DATE (2009, 1, 2, "%x", "02/01/09");
1926       TEST_PRINTF_DATE (2009, 1, 2, "%Ex", "02/01/09");
1927 #else
1928       TEST_PRINTF_DATE (2009, 1, 2, "%x", "02/01/2009");
1929       TEST_PRINTF_DATE (2009, 1, 2, "%Ex", "02/01/2009");
1930 #endif
1931
1932       TEST_PRINTF_TIME (1, 2, 3, "%X", "01:02:03");
1933       TEST_PRINTF_TIME (1, 2, 3, "%EX", "01:02:03");
1934
1935       TEST_PRINTF_DATE (2009, 1, 1, "%y", "09");
1936       TEST_PRINTF_DATE (2009, 1, 1, "%Ey", "09");
1937
1938       TEST_PRINTF_DATE (2009, 1, 1, "%Y", "2009");
1939       TEST_PRINTF_DATE (2009, 1, 1, "%EY", "2009");
1940     }
1941   else
1942     g_test_skip ("locale en_GB not available, skipping E modifier tests");
1943
1944   setlocale (LC_ALL, oldlocale);
1945   g_free (oldlocale);
1946 }
1947
1948 /* Test that the `O` modifier for g_date_time_format() works with %B, %b and %h;
1949  * i.e. whether genitive month names are supported. */
1950 static void
1951 test_month_names (void)
1952 {
1953   gchar *oldlocale;
1954
1955   g_test_bug ("http://bugzilla.gnome.org/749206");
1956
1957   if (skip_if_running_uninstalled())
1958     return;
1959
1960   oldlocale = g_strdup (setlocale (LC_ALL, NULL));
1961
1962   /* Make sure that nothing has been changed in western European languages.  */
1963   setlocale (LC_ALL, "en_GB.utf-8");
1964   if (strstr (setlocale (LC_ALL, NULL), "en_GB") != NULL)
1965     {
1966       TEST_PRINTF_DATE (2018,  1,  1,  "%B", "January");
1967       TEST_PRINTF_DATE (2018,  2,  1, "%OB", "February");
1968       TEST_PRINTF_DATE (2018,  3,  1,  "%b", "Mar");
1969       TEST_PRINTF_DATE (2018,  4,  1, "%Ob", "Apr");
1970       TEST_PRINTF_DATE (2018,  5,  1,  "%h", "May");
1971       TEST_PRINTF_DATE (2018,  6,  1, "%Oh", "Jun");
1972     }
1973   else
1974     g_test_skip ("locale en_GB not available, skipping English month names test");
1975
1976   setlocale (LC_ALL, "de_DE.utf-8");
1977   if (strstr (setlocale (LC_ALL, NULL), "de_DE") != NULL)
1978     {
1979       TEST_PRINTF_DATE (2018,  7,  1,  "%B", "Juli");
1980       TEST_PRINTF_DATE (2018,  8,  1, "%OB", "August");
1981       TEST_PRINTF_DATE (2018,  9,  1,  "%b", "Sep");
1982       TEST_PRINTF_DATE (2018, 10,  1, "%Ob", "Okt");
1983       TEST_PRINTF_DATE (2018, 11,  1,  "%h", "Nov");
1984       TEST_PRINTF_DATE (2018, 12,  1, "%Oh", "Dez");
1985     }
1986   else
1987     g_test_skip ("locale de_DE not available, skipping German month names test");
1988
1989   setlocale (LC_ALL, "es_ES.utf-8");
1990   if (strstr (setlocale (LC_ALL, NULL), "es_ES") != NULL)
1991     {
1992       TEST_PRINTF_DATE (2018,  1,  1,  "%B", "enero");
1993       TEST_PRINTF_DATE (2018,  2,  1, "%OB", "febrero");
1994       TEST_PRINTF_DATE (2018,  3,  1,  "%b", "mar");
1995       TEST_PRINTF_DATE (2018,  4,  1, "%Ob", "abr");
1996       TEST_PRINTF_DATE (2018,  5,  1,  "%h", "may");
1997       TEST_PRINTF_DATE (2018,  6,  1, "%Oh", "jun");
1998     }
1999   else
2000     g_test_skip ("locale es_ES not available, skipping Spanish month names test");
2001
2002   setlocale (LC_ALL, "fr_FR.utf-8");
2003   if (strstr (setlocale (LC_ALL, NULL), "fr_FR") != NULL)
2004     {
2005       TEST_PRINTF_DATE (2018,  7,  1,  "%B", "juillet");
2006       TEST_PRINTF_DATE (2018,  8,  1, "%OB", "août");
2007       TEST_PRINTF_DATE (2018,  9,  1,  "%b", "sept.");
2008       TEST_PRINTF_DATE (2018, 10,  1, "%Ob", "oct.");
2009       TEST_PRINTF_DATE (2018, 11,  1,  "%h", "nov.");
2010       TEST_PRINTF_DATE (2018, 12,  1, "%Oh", "déc.");
2011     }
2012   else
2013     g_test_skip ("locale fr_FR not available, skipping French month names test");
2014
2015   /* Make sure that there are visible changes in some European languages.  */
2016   setlocale (LC_ALL, "el_GR.utf-8");
2017   if (strstr (setlocale (LC_ALL, NULL), "el_GR") != NULL)
2018     {
2019       TEST_PRINTF_DATE (2018,  1,  1,  "%B", "Ιανουαρίου");
2020       TEST_PRINTF_DATE (2018,  2,  1,  "%B", "Φεβρουαρίου");
2021       TEST_PRINTF_DATE (2018,  3,  1,  "%B", "Μαρτίου");
2022       TEST_PRINTF_DATE (2018,  4,  1, "%OB", "Απρίλιος");
2023       TEST_PRINTF_DATE (2018,  5,  1, "%OB", "Μάιος");
2024       TEST_PRINTF_DATE (2018,  6,  1, "%OB", "Ιούνιος");
2025       TEST_PRINTF_DATE (2018,  7,  1,  "%b", "Ιουλ");
2026       TEST_PRINTF_DATE (2018,  8,  1, "%Ob", "Αύγ");
2027     }
2028   else
2029     g_test_skip ("locale el_GR not available, skipping Greek month names test");
2030
2031   setlocale (LC_ALL, "hr_HR.utf-8");
2032   if (strstr (setlocale (LC_ALL, NULL), "hr_HR") != NULL)
2033     {
2034       TEST_PRINTF_DATE (2018,  5,  1,  "%B", "svibnja");
2035       TEST_PRINTF_DATE (2018,  6,  1,  "%B", "lipnja");
2036       TEST_PRINTF_DATE (2018,  7,  1,  "%B", "srpnja");
2037       TEST_PRINTF_DATE (2018,  8,  1, "%OB", "Kolovoz");
2038       TEST_PRINTF_DATE (2018,  9,  1, "%OB", "Rujan");
2039       TEST_PRINTF_DATE (2018, 10,  1, "%OB", "Listopad");
2040       TEST_PRINTF_DATE (2018, 11,  1,  "%b", "Stu");
2041       TEST_PRINTF_DATE (2018, 12,  1, "%Ob", "Pro");
2042     }
2043   else
2044     g_test_skip ("locale hr_HR not available, skipping Croatian month names test");
2045
2046   setlocale (LC_ALL, "lt_LT.utf-8");
2047   if (strstr (setlocale (LC_ALL, NULL), "lt_LT") != NULL)
2048     {
2049       TEST_PRINTF_DATE (2018,  1,  1,  "%B", "sausio");
2050       TEST_PRINTF_DATE (2018,  2,  1,  "%B", "vasario");
2051       TEST_PRINTF_DATE (2018,  3,  1,  "%B", "kovo");
2052       TEST_PRINTF_DATE (2018,  4,  1, "%OB", "balandis");
2053       TEST_PRINTF_DATE (2018,  5,  1, "%OB", "gegužė");
2054       TEST_PRINTF_DATE (2018,  6,  1, "%OB", "birželis");
2055       TEST_PRINTF_DATE (2018,  7,  1,  "%b", "liep.");
2056       TEST_PRINTF_DATE (2018,  8,  1, "%Ob", "rugp.");
2057     }
2058   else
2059     g_test_skip ("locale lt_LT not available, skipping Lithuanian month names test");
2060
2061   setlocale (LC_ALL, "pl_PL.utf-8");
2062   if (strstr (setlocale (LC_ALL, NULL), "pl_PL") != NULL)
2063     {
2064       TEST_PRINTF_DATE (2018,  5,  1,  "%B", "maja");
2065       TEST_PRINTF_DATE (2018,  6,  1,  "%B", "czerwca");
2066       TEST_PRINTF_DATE (2018,  7,  1,  "%B", "lipca");
2067       TEST_PRINTF_DATE (2018,  8,  1, "%OB", "sierpień");
2068       TEST_PRINTF_DATE (2018,  9,  1, "%OB", "wrzesień");
2069       TEST_PRINTF_DATE (2018, 10,  1, "%OB", "październik");
2070       TEST_PRINTF_DATE (2018, 11,  1,  "%b", "lis");
2071       TEST_PRINTF_DATE (2018, 12,  1, "%Ob", "gru");
2072     }
2073   else
2074     g_test_skip ("locale pl_PL not available, skipping Polish month names test");
2075
2076   setlocale (LC_ALL, "ru_RU.utf-8");
2077   if (strstr (setlocale (LC_ALL, NULL), "ru_RU") != NULL)
2078     {
2079       TEST_PRINTF_DATE (2018,  1,  1,  "%B", "января");
2080       TEST_PRINTF_DATE (2018,  2,  1,  "%B", "февраля");
2081       TEST_PRINTF_DATE (2018,  3,  1,  "%B", "марта");
2082       TEST_PRINTF_DATE (2018,  4,  1, "%OB", "Апрель");
2083       TEST_PRINTF_DATE (2018,  5,  1, "%OB", "Май");
2084       TEST_PRINTF_DATE (2018,  6,  1, "%OB", "Июнь");
2085       TEST_PRINTF_DATE (2018,  7,  1,  "%b", "июл");
2086       TEST_PRINTF_DATE (2018,  8,  1, "%Ob", "авг");
2087       /* This difference is very important in Russian:  */
2088       TEST_PRINTF_DATE (2018,  5,  1,  "%b", "мая");
2089       TEST_PRINTF_DATE (2018,  5,  1, "%Ob", "май");
2090     }
2091   else
2092     g_test_skip ("locale ru_RU not available, skipping Russian month names test");
2093
2094   setlocale (LC_ALL, oldlocale);
2095   g_free (oldlocale);
2096 }
2097
2098 static void
2099 test_GDateTime_dst (void)
2100 {
2101   GDateTime *dt1, *dt2;
2102   GTimeZone *tz;
2103
2104   /* this date has the DST state set for Europe/London */
2105 #ifdef G_OS_UNIX
2106   tz = g_time_zone_new_identifier ("Europe/London");
2107 #elif defined G_OS_WIN32
2108   tz = g_time_zone_new_identifier ("GMT Standard Time");
2109 #endif
2110   g_assert_nonnull (tz);
2111   dt1 = g_date_time_new (tz, 2009, 8, 15, 3, 0, 1);
2112   g_assert (g_date_time_is_daylight_savings (dt1));
2113   g_assert_cmpint (g_date_time_get_utc_offset (dt1) / G_USEC_PER_SEC, ==, 3600);
2114   g_assert_cmpint (g_date_time_get_hour (dt1), ==, 3);
2115
2116   /* add 6 months to clear the DST flag but keep the same time */
2117   dt2 = g_date_time_add_months (dt1, 6);
2118   g_assert (!g_date_time_is_daylight_savings (dt2));
2119   g_assert_cmpint (g_date_time_get_utc_offset (dt2) / G_USEC_PER_SEC, ==, 0);
2120   g_assert_cmpint (g_date_time_get_hour (dt2), ==, 3);
2121
2122   g_date_time_unref (dt2);
2123   g_date_time_unref (dt1);
2124
2125   /* now do the reverse: start with a non-DST state and move to DST */
2126   dt1 = g_date_time_new (tz, 2009, 2, 15, 2, 0, 1);
2127   g_assert (!g_date_time_is_daylight_savings (dt1));
2128   g_assert_cmpint (g_date_time_get_hour (dt1), ==, 2);
2129
2130   dt2 = g_date_time_add_months (dt1, 6);
2131   g_assert (g_date_time_is_daylight_savings (dt2));
2132   g_assert_cmpint (g_date_time_get_hour (dt2), ==, 2);
2133
2134   g_date_time_unref (dt2);
2135   g_date_time_unref (dt1);
2136   g_time_zone_unref (tz);
2137 }
2138
2139 static inline gboolean
2140 is_leap_year (gint year)
2141 {
2142   g_assert (1 <= year && year <= 9999);
2143
2144   return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
2145 }
2146
2147 static inline gint
2148 days_in_month (gint year, gint month)
2149 {
2150   const gint table[2][13] = {
2151     {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
2152     {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
2153   };
2154
2155   g_assert (1 <= month && month <= 12);
2156
2157   return table[is_leap_year (year)][month];
2158 }
2159
2160 static void
2161 test_all_dates (void)
2162 {
2163   gint year, month, day;
2164   GTimeZone *timezone;
2165   gint64 unix_time;
2166   gint day_of_year;
2167   gint week_year;
2168   gint week_num;
2169   gint weekday;
2170
2171   /* save some time by hanging on to this. */
2172   timezone = g_time_zone_new_utc ();
2173
2174   unix_time = G_GINT64_CONSTANT(-62135596800);
2175
2176   /* 0001-01-01 is 0001-W01-1 */
2177   week_year = 1;
2178   week_num = 1;
2179   weekday = 1;
2180
2181
2182   /* The calendar makes a full cycle every 400 years, so we could
2183    * theoretically just test years 1 through 400.  That assumes that our
2184    * software has no bugs, so probably we should just test them all. :)
2185    */
2186   for (year = 1; year <= 9999; year++)
2187     {
2188       day_of_year = 1;
2189
2190       for (month = 1; month <= 12; month++)
2191         for (day = 1; day <= days_in_month (year, month); day++)
2192           {
2193             GDateTime *dt;
2194
2195             dt = g_date_time_new (timezone, year, month, day, 0, 0, 0);
2196
2197 #if 0
2198             g_test_message ("%04d-%02d-%02d = %04d-W%02d-%d = %04d-%03d",
2199                      year, month, day,
2200                      week_year, week_num, weekday,
2201                      year, day_of_year);
2202 #endif
2203
2204             /* sanity check */
2205             if G_UNLIKELY (g_date_time_get_year (dt) != year ||
2206                            g_date_time_get_month (dt) != month ||
2207                            g_date_time_get_day_of_month (dt) != day)
2208               g_error ("%04d-%02d-%02d comes out as %04d-%02d-%02d",
2209                        year, month, day,
2210                        g_date_time_get_year (dt),
2211                        g_date_time_get_month (dt),
2212                        g_date_time_get_day_of_month (dt));
2213
2214             if G_UNLIKELY (g_date_time_get_week_numbering_year (dt) != week_year ||
2215                            g_date_time_get_week_of_year (dt) != week_num ||
2216                            g_date_time_get_day_of_week (dt) != weekday)
2217               g_error ("%04d-%02d-%02d should be %04d-W%02d-%d but "
2218                        "comes out as %04d-W%02d-%d", year, month, day,
2219                        week_year, week_num, weekday,
2220                        g_date_time_get_week_numbering_year (dt),
2221                        g_date_time_get_week_of_year (dt),
2222                        g_date_time_get_day_of_week (dt));
2223
2224             if G_UNLIKELY (g_date_time_to_unix (dt) != unix_time)
2225               g_error ("%04d-%02d-%02d 00:00:00 UTC should have unix time %"
2226                        G_GINT64_FORMAT " but comes out as %"G_GINT64_FORMAT,
2227                        year, month, day, unix_time, g_date_time_to_unix (dt));
2228
2229             if G_UNLIKELY (g_date_time_get_day_of_year (dt) != day_of_year)
2230               g_error ("%04d-%02d-%02d should be day of year %d"
2231                        " but comes out as %d", year, month, day,
2232                        day_of_year, g_date_time_get_day_of_year (dt));
2233
2234             if G_UNLIKELY (g_date_time_get_hour (dt) != 0 ||
2235                            g_date_time_get_minute (dt) != 0 ||
2236                            g_date_time_get_seconds (dt) != 0)
2237               g_error ("%04d-%02d-%02d 00:00:00 UTC comes out "
2238                        "as %02d:%02d:%02.6f", year, month, day,
2239                        g_date_time_get_hour (dt),
2240                        g_date_time_get_minute (dt),
2241                        g_date_time_get_seconds (dt));
2242             /* done */
2243
2244             /* add 24 hours to unix time */
2245             unix_time += 24 * 60 * 60;
2246
2247             /* move day of year forward */
2248             day_of_year++;
2249
2250             /* move the week date forward */
2251             if (++weekday == 8)
2252               {
2253                 weekday = 1; /* Sunday -> Monday */
2254
2255                 /* NOTE: year/month/day is the final day of the week we
2256                  * just finished.
2257                  *
2258                  * If we just finished the last week of last year then
2259                  * we are definitely starting the first week of this
2260                  * year.
2261                  *
2262                  * Otherwise, if we're still in this year, but Sunday
2263                  * fell on or after December 28 then December 29, 30, 31
2264                  * could be days within the next year's first year.
2265                  */
2266                 if (year != week_year || (month == 12 && day >= 28))
2267                   {
2268                     /* first week of the new year */
2269                     week_num = 1;
2270                     week_year++;
2271                   }
2272                 else
2273                   week_num++;
2274               }
2275
2276             g_date_time_unref (dt);
2277           }
2278     }
2279
2280   g_time_zone_unref (timezone);
2281 }
2282
2283 static void
2284 test_date_time_eras_japan (void)
2285 {
2286 #if defined(HAVE_LANGINFO_ERA) && (G_BYTE_ORDER == G_LITTLE_ENDIAN || GLIB_SIZEOF_VOID_P == 4)
2287   gchar *oldlocale;
2288
2289   oldlocale = g_strdup (setlocale (LC_ALL, NULL));
2290   setlocale (LC_ALL, "ja_JP.utf-8");
2291   if (strstr (setlocale (LC_ALL, NULL), "ja_JP") == NULL)
2292     {
2293       g_test_skip ("locale ja_JP.utf-8 not available, skipping Japanese era tests");
2294       g_free (oldlocale);
2295       return;
2296     }
2297
2298   /* See https://en.wikipedia.org/wiki/Japanese_era_name
2299    * First test the Reiwa era (令和) */
2300   TEST_PRINTF_DATE (2023, 06, 01, "%Ec", "令和05年06月01日 00時00分00秒");
2301   TEST_PRINTF_DATE (2023, 06, 01, "%EC", "令和");
2302   TEST_PRINTF_DATE (2023, 06, 01, "%Ex", "令和05年06月01日");
2303   TEST_PRINTF_DATE (2023, 06, 01, "%EX", "00時00分00秒");
2304   TEST_PRINTF_DATE (2023, 06, 01, "%Ey", "05");
2305   TEST_PRINTF_DATE (2023, 06, 01, "%EY", "令和05年");
2306
2307   /* Heisei era (平成) */
2308   TEST_PRINTF_DATE (2019, 04, 30, "%Ec", "平成31年04月30日 00時00分00秒");
2309   TEST_PRINTF_DATE (2019, 04, 30, "%EC", "平成");
2310   TEST_PRINTF_DATE (2019, 04, 30, "%Ex", "平成31年04月30日");
2311   TEST_PRINTF_DATE (2019, 04, 30, "%EX", "00時00分00秒");
2312   TEST_PRINTF_DATE (2019, 04, 30, "%Ey", "31");
2313   TEST_PRINTF_DATE (2019, 04, 30, "%EY", "平成31年");
2314
2315   /* Shōwa era (昭和) */
2316   TEST_PRINTF_DATE (1926, 12, 25, "%Ec", "昭和元年12月25日 00時00分00秒");
2317   TEST_PRINTF_DATE (1926, 12, 25, "%EC", "昭和");
2318   TEST_PRINTF_DATE (1926, 12, 25, "%Ex", "昭和元年12月25日");
2319   TEST_PRINTF_DATE (1926, 12, 25, "%EX", "00時00分00秒");
2320   TEST_PRINTF_DATE (1926, 12, 25, "%Ey", "01");
2321   TEST_PRINTF_DATE (1926, 12, 25, "%EY", "昭和元年");
2322
2323   setlocale (LC_ALL, oldlocale);
2324   g_free (oldlocale);
2325 #else
2326   g_test_skip ("nl_langinfo(ERA) not supported, skipping era tests");
2327 #endif
2328 }
2329
2330 static void
2331 test_date_time_eras_thailand (void)
2332 {
2333 #if defined(HAVE_LANGINFO_ERA) && (G_BYTE_ORDER == G_LITTLE_ENDIAN || GLIB_SIZEOF_VOID_P == 4)
2334   gchar *oldlocale;
2335
2336   oldlocale = g_strdup (setlocale (LC_ALL, NULL));
2337   setlocale (LC_ALL, "th_TH.utf-8");
2338   if (strstr (setlocale (LC_ALL, NULL), "th_TH") == NULL)
2339     {
2340       g_test_skip ("locale th_TH.utf-8 not available, skipping Thai era tests");
2341       g_free (oldlocale);
2342       return;
2343     }
2344
2345   /* See https://en.wikipedia.org/wiki/Thai_solar_calendar */
2346   TEST_PRINTF_DATE (2023, 06, 01, "%Ec", "วันพฤหัสบดีที่  1 มิถุนายน พ.ศ. 2566, 00.00.00 น.");
2347   TEST_PRINTF_DATE (2023, 06, 01, "%EC", "พ.ศ.");
2348   TEST_PRINTF_DATE (2023, 06, 01, "%Ex", " 1 มิ.ย. 2566");
2349   TEST_PRINTF_DATE (2023, 06, 01, "%EX", "00.00.00 น.");
2350   TEST_PRINTF_DATE (2023, 06, 01, "%Ey", "2566");
2351   TEST_PRINTF_DATE (2023, 06, 01, "%EY", "พ.ศ. 2566");
2352
2353   TEST_PRINTF_DATE (01, 06, 01, "%Ex", " 1 มิ.ย. 544");
2354
2355   setlocale (LC_ALL, oldlocale);
2356   g_free (oldlocale);
2357 #else
2358   g_test_skip ("nl_langinfo(ERA) not supported, skipping era tests");
2359 #endif
2360 }
2361
2362 static void
2363 test_date_time_eras_parsing (void)
2364 {
2365   struct
2366     {
2367       const char *desc;
2368       gboolean expected_success;
2369       size_t expected_n_segments;
2370     }
2371   vectors[] =
2372     {
2373       /* Some successful parsing: */
2374       { "", TRUE, 0 },
2375       /* From https://github.com/bminor/glibc/blob/9fd3409842b3e2d31cff5dbd6f96066c430f0aa2/localedata/locales/th_TH#L233: */
2376       { "+:1:-543/01/01:+*:พ.ศ.:%EC %Ey", TRUE, 1 },
2377       /* From https://github.com/bminor/glibc/blob/9fd3409842b3e2d31cff5dbd6f96066c430f0aa2/localedata/locales/ja_JP#L14967C5-L14977C60: */
2378       { "+:2:2020/01/01:+*:令和:%EC%Ey年;"
2379         "+:1:2019/05/01:2019/12/31:令和:%EC元年;"
2380         "+:2:1990/01/01:2019/04/30:平成:%EC%Ey年;"
2381         "+:1:1989/01/08:1989/12/31:平成:%EC元年;"
2382         "+:2:1927/01/01:1989/01/07:昭和:%EC%Ey年;"
2383         "+:1:1926/12/25:1926/12/31:昭和:%EC元年;"
2384         "+:2:1913/01/01:1926/12/24:大正:%EC%Ey年;"
2385         "+:1:1912/07/30:1912/12/31:大正:%EC元年;"
2386         "+:6:1873/01/01:1912/07/29:明治:%EC%Ey年;"
2387         "+:1:0001/01/01:1872/12/31:西暦:%EC%Ey年;"
2388         "+:1:-0001/12/31:-*:紀元前:%EC%Ey年", TRUE, 11 },
2389       { "-:2:2020/01/01:-*:令和:%EC%Ey年", TRUE, 1 },
2390       { "+:2:2020/01/01:2020/01/01:令和:%EC%Ey年", TRUE, 1 },
2391       { "+:2:+2020/01/01:+*:令和:%EC%Ey年", TRUE, 1 },
2392       /* Some errors: */
2393       { ".:2:2020/01/01:+*:令和:%EC%Ey年", FALSE, 0 },
2394       { "+.2:2020/01/01:+*:令和:%EC%Ey年", FALSE, 0 },
2395       { "+", FALSE, 0 },
2396       { "+:", FALSE, 0 },
2397       { "+::", FALSE, 0 },
2398       { "+:200", FALSE, 0 },
2399       { "+:2nonsense", FALSE, 0 },
2400       { "+:2nonsense:", FALSE, 0 },
2401       { "+:2:", FALSE, 0 },
2402       { "+:2::", FALSE, 0 },
2403       { "+:2:2020-01/01:+*:令和:%EC%Ey年", FALSE, 0 },
2404       { "+:2:2020nonsense/01/01:+*:令和:%EC%Ey年", FALSE, 0 },
2405       { "+:2:2020:+*:令和:%EC%Ey年", FALSE, 0 },
2406       { "+:2:18446744073709551615/01/01:+*:令和:%EC%Ey年", FALSE, 0 },
2407       { "+:2:2020/01-01:+*:令和:%EC%Ey年", FALSE, 0 },
2408       { "+:2:2020/01nonsense/01:+*:令和:%EC%Ey年", FALSE, 0 },
2409       { "+:2:2020/01:+*:令和:%EC%Ey年", FALSE, 0 },
2410       { "+:2:2020/00/01:+*:令和:%EC%Ey年", FALSE, 0 },
2411       { "+:2:2020/13/01:+*:令和:%EC%Ey年", FALSE, 0 },
2412       { "+:2:2020/01/00:+*:令和:%EC%Ey年", FALSE, 0 },
2413       { "+:2:2020/01/32:+*:令和:%EC%Ey年", FALSE, 0 },
2414       { "+:2:2020/01/01nonsense:+*:令和:%EC%Ey年", FALSE, 0 },
2415       { "+:2:2020/01/01", FALSE, 0 },
2416       { "+:2:2020/01/01:", FALSE, 0 },
2417       { "+:2:2020/01/01::", FALSE, 0 },
2418       { "+:2:2020/01/01:2021-01-01:令和:%EC%Ey年", FALSE, 0 },
2419       { "+:2:2020/01/01:+*", FALSE, 0 },
2420       { "+:2:2020/01/01:+*:", FALSE, 0 },
2421       { "+:2:2020/01/01:+*::", FALSE, 0 },
2422       { "+:2:2020/01/01:+*:令和", FALSE, 0 },
2423       { "+:2:2020/01/01:+*:令和:", FALSE, 0 },
2424       { "+:2:2020/01/01:+*:令和:;", FALSE, 0 },
2425     };
2426
2427   for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
2428     {
2429       GPtrArray *segments = NULL;
2430
2431       g_test_message ("Vector %" G_GSIZE_FORMAT ": %s", i, vectors[i].desc);
2432
2433       segments = _g_era_description_parse (vectors[i].desc);
2434
2435       if (vectors[i].expected_success)
2436         {
2437           g_assert_nonnull (segments);
2438           g_assert_cmpuint (segments->len, ==, vectors[i].expected_n_segments);
2439         }
2440       else
2441         {
2442           g_assert_null (segments);
2443         }
2444
2445       g_clear_pointer (&segments, g_ptr_array_unref);
2446     }
2447 }
2448
2449 static void
2450 test_z (void)
2451 {
2452   GTimeZone *tz;
2453   GDateTime *dt;
2454   gchar *p;
2455
2456   g_test_bug ("http://bugzilla.gnome.org/642935");
2457
2458   tz = g_time_zone_new_identifier ("-08:00");
2459   g_assert_nonnull (tz);
2460   dt = g_date_time_new (tz, 1, 1, 1, 0, 0, 0);
2461
2462   p = g_date_time_format (dt, "%z");
2463   g_assert_cmpstr (p, ==, "-0800");
2464   g_free (p);
2465
2466   p = g_date_time_format (dt, "%:z");
2467   g_assert_cmpstr (p, ==, "-08:00");
2468   g_free (p);
2469
2470   p = g_date_time_format (dt, "%::z");
2471   g_assert_cmpstr (p, ==, "-08:00:00");
2472   g_free (p);
2473
2474   p = g_date_time_format (dt, "%:::z");
2475   g_assert_cmpstr (p, ==, "-08");
2476   g_free (p);
2477
2478   g_date_time_unref (dt);
2479   g_time_zone_unref (tz);
2480
2481   tz = g_time_zone_new_identifier ("+00:00");
2482   g_assert_nonnull (tz);
2483   dt = g_date_time_new (tz, 1, 1, 1, 0, 0, 0);
2484   p = g_date_time_format (dt, "%:::z");
2485   g_assert_cmpstr (p, ==, "+00");
2486   g_free (p);
2487   g_date_time_unref (dt);
2488   g_time_zone_unref (tz);
2489
2490   tz = g_time_zone_new_identifier ("+08:23");
2491   g_assert_nonnull (tz);
2492   dt = g_date_time_new (tz, 1, 1, 1, 0, 0, 0);
2493   p = g_date_time_format (dt, "%:::z");
2494   g_assert_cmpstr (p, ==, "+08:23");
2495   g_free (p);
2496   g_date_time_unref (dt);
2497   g_time_zone_unref (tz);
2498
2499   tz = g_time_zone_new_identifier ("+08:23:45");
2500   g_assert_nonnull (tz);
2501   dt = g_date_time_new (tz, 1, 1, 1, 0, 0, 0);
2502   p = g_date_time_format (dt, "%:::z");
2503   g_assert_cmpstr (p, ==, "+08:23:45");
2504   g_free (p);
2505   g_date_time_unref (dt);
2506   g_time_zone_unref (tz);
2507
2508   tz = g_time_zone_new_identifier ("-00:15");
2509   g_assert_nonnull (tz);
2510   dt = g_date_time_new (tz, 1, 1, 1, 0, 0, 0);
2511
2512   p = g_date_time_format (dt, "%z");
2513   g_assert_cmpstr (p, ==, "-0015");
2514   g_free (p);
2515
2516   p = g_date_time_format (dt, "%:z");
2517   g_assert_cmpstr (p, ==, "-00:15");
2518   g_free (p);
2519
2520   p = g_date_time_format (dt, "%::z");
2521   g_assert_cmpstr (p, ==, "-00:15:00");
2522   g_free (p);
2523
2524   p = g_date_time_format (dt, "%:::z");
2525   g_assert_cmpstr (p, ==, "-00:15");
2526   g_free (p);
2527
2528   g_date_time_unref (dt);
2529   g_time_zone_unref (tz);
2530 }
2531
2532 static void
2533 test_6_days_until_end_of_the_month (void)
2534 {
2535   GTimeZone *tz;
2536   GDateTime *dt;
2537   gchar *p;
2538
2539   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2215");
2540
2541 #ifdef G_OS_UNIX
2542   /* This is the footertz string from `Europe/Paris` from tzdata 2020b. It’s
2543    * used by GLib when the tzdata file was compiled with `zic -b slim`, which is
2544    * the default in tzcode ≥2020b.
2545    *
2546    * The `M10.5.0` part indicates that the summer time end transition happens on
2547    * the Sunday (`0`) in the last week (`5`) of October (`10`). That’s 6 days
2548    * before the end of the month, and hence was triggering issue #2215.
2549    *
2550    * References:
2551    *  - https://tools.ietf.org/id/draft-murchison-tzdist-tzif-15.html#rfc.section.3.3
2552    *  - https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
2553    */
2554   tz = g_time_zone_new_identifier ("CET-1CEST,M3.5.0,M10.5.0/3");
2555 #elif defined (G_OS_WIN32)
2556   tz = g_time_zone_new_identifier ("Romance Standard Time");
2557 #endif
2558   g_assert_nonnull (tz);
2559   dt = g_date_time_new (tz, 2020, 10, 5, 1, 1, 1);
2560
2561   p = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S%z");
2562   /* Incorrect output is  "2020-10-05 01:01:01+0100" */
2563   g_assert_cmpstr (p, ==, "2020-10-05 01:01:01+0200");
2564   g_free (p);
2565
2566   g_date_time_unref (dt);
2567   g_time_zone_unref (tz);
2568 }
2569
2570 static void
2571 test_format_iso8601 (void)
2572 {
2573   GTimeZone *tz = NULL;
2574   GDateTime *dt = NULL;
2575   gchar *p = NULL;
2576
2577   tz = g_time_zone_new_utc ();
2578   dt = g_date_time_new (tz, 2019, 6, 26, 15, 1, 5);
2579   p = g_date_time_format_iso8601 (dt);
2580   g_assert_cmpstr (p, ==, "2019-06-26T15:01:05Z");
2581   g_free (p);
2582   g_date_time_unref (dt);
2583   g_time_zone_unref (tz);
2584
2585   tz = g_time_zone_new_offset (-60 * 60);
2586   dt = g_date_time_new (tz, 2019, 6, 26, 15, 1, 5);
2587   p = g_date_time_format_iso8601 (dt);
2588   g_assert_cmpstr (p, ==, "2019-06-26T15:01:05-01");
2589   g_free (p);
2590   g_date_time_unref (dt);
2591   g_time_zone_unref (tz);
2592
2593   tz = g_time_zone_new_utc ();
2594   dt = g_date_time_new (tz, 2020, 8, 5, 12, 30, 55.000001);
2595   p = g_date_time_format_iso8601 (dt);
2596   g_assert_cmpstr (p, ==, "2020-08-05T12:30:55.000001Z");
2597   g_free (p);
2598   g_date_time_unref (dt);
2599   g_time_zone_unref (tz);
2600
2601   tz = g_time_zone_new_utc ();
2602   dt = g_date_time_new (tz, 9, 1, 2, 3, 4, 55);
2603   p = g_date_time_format_iso8601 (dt);
2604   g_assert_cmpstr (p, ==, "0009-01-02T03:04:55Z");
2605   g_free (p);
2606   g_date_time_unref (dt);
2607   g_time_zone_unref (tz);
2608
2609   tz = g_time_zone_new_utc ();
2610   dt = g_date_time_new (tz, 9990, 1, 2, 3, 4, 55.000001);
2611   p = g_date_time_format_iso8601 (dt);
2612   g_assert_cmpstr (p, ==, "9990-01-02T03:04:55.000001Z");
2613   g_free (p);
2614   g_date_time_unref (dt);
2615   g_time_zone_unref (tz);
2616 }
2617
2618 typedef struct
2619 {
2620   gboolean utf8_messages;
2621   gboolean utf8_time;
2622 } MixedUtf8TestData;
2623
2624 static const MixedUtf8TestData utf8_time_non_utf8_messages = {
2625   .utf8_messages = FALSE,
2626   .utf8_time = TRUE
2627 };
2628
2629 static const MixedUtf8TestData non_utf8_time_utf8_messages = {
2630   .utf8_messages = TRUE,
2631   .utf8_time = FALSE
2632 };
2633
2634 static const MixedUtf8TestData utf8_time_utf8_messages = {
2635   .utf8_messages = TRUE,
2636   .utf8_time = TRUE
2637 };
2638
2639 static const MixedUtf8TestData non_utf8_time_non_utf8_messages = {
2640   .utf8_messages = FALSE,
2641   .utf8_time = FALSE
2642 };
2643
2644 static gboolean
2645 check_and_set_locale (int          category,
2646                       const gchar *name)
2647 {
2648   setlocale (category, name);
2649   if (strstr (setlocale (category, NULL), name) == NULL)
2650     {
2651       g_test_message ("Unavailable '%s' locale", name);
2652       g_test_skip ("required locale not available, skipping tests");
2653       return FALSE;
2654     }
2655   return TRUE;
2656 }
2657
2658 static void
2659 test_format_time_mixed_utf8 (gconstpointer data)
2660 {
2661 #ifdef _MSC_VER
2662   g_test_skip ("setlocale (LC_MESSAGES) asserts on ucrt");
2663 #else
2664   const MixedUtf8TestData *test_data;
2665   gchar *old_time_locale;
2666   gchar *old_messages_locale;
2667   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2055");
2668
2669   test_data = (MixedUtf8TestData *) data;
2670   old_time_locale = g_strdup (setlocale (LC_TIME, NULL));
2671   old_messages_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
2672   if (test_data->utf8_time)
2673     {
2674       if (!check_and_set_locale (LC_TIME, "C.UTF-8"))
2675         {
2676           g_free (old_time_locale);
2677           setlocale (LC_MESSAGES, old_messages_locale);
2678           g_free (old_messages_locale);
2679           return;
2680         }
2681     }
2682   else
2683     {
2684       if (!check_and_set_locale (LC_TIME, "de_DE.iso88591"))
2685         {
2686           g_free (old_time_locale);
2687           setlocale (LC_MESSAGES, old_messages_locale);
2688           g_free (old_messages_locale);
2689           return;
2690         }
2691     }
2692   if (test_data->utf8_messages)
2693     {
2694       if (!check_and_set_locale (LC_MESSAGES, "C.UTF-8"))
2695         {
2696           g_free (old_messages_locale);
2697           setlocale (LC_TIME, old_time_locale);
2698           g_free (old_time_locale);
2699           return;
2700         }
2701     }
2702   else
2703     {
2704       if (!check_and_set_locale (LC_MESSAGES, "de_DE.iso88591"))
2705         {
2706           g_free (old_messages_locale);
2707           setlocale (LC_TIME, old_time_locale);
2708           g_free (old_time_locale);
2709           return;
2710         }
2711     }
2712
2713   if (!test_data->utf8_time)
2714     {
2715       /* March to have März in german */
2716       TEST_PRINTF_DATE (2020, 3, 1, "%b", "Mär");
2717       TEST_PRINTF_DATE (2020, 3, 1, "%B", "März");
2718     }
2719   else
2720     {
2721       TEST_PRINTF_DATE (2020, 3, 1, "%b", "mar");
2722       TEST_PRINTF_DATE (2020, 3, 1, "%B", "march");
2723     }
2724
2725   setlocale (LC_TIME, old_time_locale);
2726   setlocale (LC_MESSAGES, old_messages_locale);
2727   g_free (old_time_locale);
2728   g_free (old_messages_locale);
2729 #endif
2730 }
2731
2732 #ifdef __linux__
2733 static gchar *
2734 str_utf8_replace (const gchar *str,
2735                   gunichar     from,
2736                   gunichar     to)
2737 {
2738   GString *str_out = g_string_new ("");
2739
2740   for (; *str != '\0'; str = g_utf8_next_char (str))
2741     {
2742       gunichar c = g_utf8_get_char (str);
2743       g_string_append_unichar (str_out, (c == from) ? to : c);
2744     }
2745
2746   return g_string_free (g_steal_pointer (&str_out), FALSE);
2747 }
2748 #endif
2749
2750 #pragma GCC diagnostic push
2751 #pragma GCC diagnostic ignored "-Wformat-y2k"
2752 static void
2753 test_strftime (void)
2754 {
2755 #ifdef __linux__
2756 #define TEST_FORMAT \
2757   "a%a A%A b%b B%B c%c C%C d%d e%e F%F g%g G%G h%h H%H I%I j%j m%m M%M " \
2758   "n%n p%p r%r R%R S%S t%t T%T u%u V%V w%w x%x X%X y%y Y%Y z%z Z%Z %%"
2759   time_t t;
2760
2761   /* 127997 is prime, 1315005118 is now */
2762   for (t = 0; t < 1315005118; t += 127997)
2763     {
2764       GDateTime *date_time;
2765       gchar c_str[1000];
2766       gchar *dt_str;
2767       gchar *dt_str_replaced = NULL, *c_str_replaced = NULL;
2768
2769       date_time = g_date_time_new_from_unix_local (t);
2770       dt_str = g_date_time_format (date_time, TEST_FORMAT);
2771       strftime (c_str, sizeof c_str, TEST_FORMAT, localtime (&t));
2772
2773       /* Ensure the comparison is done insensitively to spaces.
2774        * g_date_time_format() sometimes uses figure spaces (U+2007) whereas
2775        * strftime() currently doesn’t, and that’s fine. */
2776       dt_str_replaced = str_utf8_replace (dt_str, 0x2007, 0x20);
2777       c_str_replaced = str_utf8_replace  (c_str, 0x2007, 0x20);
2778
2779       g_assert_cmpstr (c_str_replaced, ==, dt_str_replaced);
2780
2781       g_date_time_unref (date_time);
2782       g_free (dt_str);
2783       g_free (dt_str_replaced);
2784       g_free (c_str_replaced);
2785     }
2786 #endif
2787 }
2788 #pragma GCC diagnostic pop
2789
2790 /* Check that g_date_time_format() correctly returns %NULL for format
2791  * placeholders which are not supported in the current locale. */
2792 static void
2793 test_GDateTime_strftime_error_handling (void)
2794 {
2795   gchar *oldlocale;
2796 #ifdef G_OS_WIN32
2797   LCID old_lcid;
2798 #endif
2799
2800   if (skip_if_running_uninstalled())
2801     return;
2802
2803 #ifdef G_OS_WIN32
2804   old_lcid = GetThreadLocale ();
2805   SetThreadLocale (MAKELCID (MAKELANGID (LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT));
2806 #endif
2807
2808   oldlocale = g_strdup (setlocale (LC_ALL, NULL));
2809   setlocale (LC_ALL, "de_DE.utf-8");
2810   if (strstr (setlocale (LC_ALL, NULL), "de_DE") != NULL)
2811     {
2812       /* de_DE doesn’t ever write time in 12-hour notation, so %r is
2813        * unsupported for it. */
2814       TEST_PRINTF_TIME (23, 0, 0, "%r", NULL);
2815     }
2816   else
2817     g_test_skip ("locale de_DE not available, skipping error handling tests");
2818   setlocale (LC_ALL, oldlocale);
2819   g_free (oldlocale);
2820
2821 #ifdef G_OS_WIN32
2822   SetThreadLocale (old_lcid);
2823 #endif
2824 }
2825
2826 static void
2827 test_find_interval (void)
2828 {
2829   GTimeZone *tz;
2830   GDateTime *dt;
2831   gint64 u;
2832   gint i1, i2;
2833
2834 #ifdef G_OS_UNIX
2835   tz = g_time_zone_new_identifier ("America/Toronto");
2836 #elif defined G_OS_WIN32
2837   tz = g_time_zone_new_identifier ("Eastern Standard Time");
2838 #endif
2839   g_assert_nonnull (tz);
2840   dt = g_date_time_new_utc (2010, 11, 7, 1, 30, 0);
2841   u = g_date_time_to_unix (dt);
2842
2843   i1 = g_time_zone_find_interval (tz, G_TIME_TYPE_STANDARD, u);
2844   i2 = g_time_zone_find_interval (tz, G_TIME_TYPE_DAYLIGHT, u);
2845
2846   g_assert_cmpint (i1, !=, i2);
2847
2848   g_date_time_unref (dt);
2849
2850   dt = g_date_time_new_utc (2010, 3, 14, 2, 0, 0);
2851   u = g_date_time_to_unix (dt);
2852
2853   i1 = g_time_zone_find_interval (tz, G_TIME_TYPE_STANDARD, u);
2854   g_assert_cmpint (i1, ==, -1);
2855
2856   g_date_time_unref (dt);
2857   g_time_zone_unref (tz);
2858 }
2859
2860 static void
2861 test_adjust_time (void)
2862 {
2863   GTimeZone *tz;
2864   GDateTime *dt;
2865   gint64 u, u2;
2866   gint i1, i2;
2867
2868 #ifdef G_OS_UNIX
2869   tz = g_time_zone_new_identifier ("America/Toronto");
2870 #elif defined G_OS_WIN32
2871   tz = g_time_zone_new_identifier ("Eastern Standard Time");
2872 #endif
2873   g_assert_nonnull (tz);
2874   dt = g_date_time_new_utc (2010, 11, 7, 1, 30, 0);
2875   u = g_date_time_to_unix (dt);
2876   u2 = u;
2877
2878   i1 = g_time_zone_find_interval (tz, G_TIME_TYPE_DAYLIGHT, u);
2879   i2 = g_time_zone_adjust_time (tz, G_TIME_TYPE_DAYLIGHT, &u2);
2880
2881   g_assert_cmpint (i1, ==, i2);
2882   g_assert (u == u2);
2883
2884   g_date_time_unref (dt);
2885
2886   dt = g_date_time_new_utc (2010, 3, 14, 2, 30, 0);
2887   u2 = g_date_time_to_unix (dt);
2888   g_date_time_unref (dt);
2889
2890   dt = g_date_time_new_utc (2010, 3, 14, 3, 0, 0);
2891   u = g_date_time_to_unix (dt);
2892   g_date_time_unref (dt);
2893
2894   i1 = g_time_zone_adjust_time (tz, G_TIME_TYPE_DAYLIGHT, &u2);
2895   g_assert_cmpint (i1, >=, 0);
2896   g_assert (u == u2);
2897
2898   g_time_zone_unref (tz);
2899 }
2900
2901 static void
2902 test_no_header (void)
2903 {
2904   GTimeZone *tz;
2905
2906   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2907   tz = g_time_zone_new ("blabla");
2908   G_GNUC_END_IGNORE_DEPRECATIONS
2909
2910   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
2911   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "UTC");
2912   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 0);
2913   g_assert (!g_time_zone_is_dst (tz, 0));
2914
2915   g_time_zone_unref (tz);
2916 }
2917
2918 static void
2919 test_no_header_identifier (void)
2920 {
2921   GTimeZone *tz;
2922
2923   tz = g_time_zone_new_identifier ("blabla");
2924
2925   g_assert_null (tz);
2926 }
2927
2928 static void
2929 test_posix_parse (void)
2930 {
2931   GTimeZone *tz;
2932   GDateTime *gdt1, *gdt2;
2933
2934   /* Check that an unknown zone name falls back to UTC. */
2935   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2936   tz = g_time_zone_new ("nonexistent");
2937   G_GNUC_END_IGNORE_DEPRECATIONS
2938   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
2939   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "UTC");
2940   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 0);
2941   g_assert (!g_time_zone_is_dst (tz, 0));
2942   g_time_zone_unref (tz);
2943
2944   /* An existent zone name should not fall back to UTC. */
2945   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2946   tz = g_time_zone_new ("PST8");
2947   G_GNUC_END_IGNORE_DEPRECATIONS
2948   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "PST8");
2949   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "PST");
2950   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, - 8 * 3600);
2951   g_assert (!g_time_zone_is_dst (tz, 0));
2952   g_time_zone_unref (tz);
2953
2954 /* This fails rules_from_identifier on Unix (though not on Windows)
2955  * but passes anyway because PST8PDT is a zone name.
2956  */
2957   tz = g_time_zone_new_identifier ("PST8PDT");
2958   g_assert_nonnull (tz);
2959   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "PST8PDT");
2960   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "PST");
2961   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, - 8 * 3600);
2962   g_assert (!g_time_zone_is_dst (tz, 0));
2963   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "PDT");
2964   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==,- 7 * 3600);
2965   g_assert (g_time_zone_is_dst (tz, 1));
2966   g_time_zone_unref (tz);
2967
2968   tz = g_time_zone_new_identifier ("PST8PDT6:32:15");
2969 #ifdef G_OS_WIN32
2970   g_assert_nonnull (tz);
2971   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "PST8PDT6:32:15");
2972   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "PST");
2973   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, - 8 * 3600);
2974   g_assert (!g_time_zone_is_dst (tz, 0));
2975   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "PDT");
2976   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, - 6 * 3600 - 32 *60 - 15);
2977   g_assert (g_time_zone_is_dst (tz, 1));
2978   gdt1 = g_date_time_new (tz, 2012, 12, 6, 11, 15, 23.0);
2979   gdt2 = g_date_time_new (tz, 2012, 6, 6, 11, 15, 23.0);
2980   g_assert (!g_date_time_is_daylight_savings (gdt1));
2981   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) /  1000000, ==, -28800);
2982   g_assert (g_date_time_is_daylight_savings (gdt2));
2983   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, -23535);
2984   g_date_time_unref (gdt1);
2985   g_date_time_unref (gdt2);
2986 #else
2987   g_assert_null (tz);
2988 #endif
2989   g_clear_pointer (&tz, g_time_zone_unref);
2990
2991   tz = g_time_zone_new_identifier ("NZST-12:00:00NZDT-13:00:00,M10.1.0,M3.3.0");
2992   g_assert_nonnull (tz);
2993   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "NZST-12:00:00NZDT-13:00:00,M10.1.0,M3.3.0");
2994   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "NZST");
2995   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 12 * 3600);
2996   g_assert (!g_time_zone_is_dst (tz, 0));
2997   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "NZDT");
2998   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, 13 * 3600);
2999   g_assert (g_time_zone_is_dst (tz, 1));
3000   gdt1 = g_date_time_new (tz, 2012, 3, 18, 0, 15, 23.0);
3001   gdt2 = g_date_time_new (tz, 2012, 3, 18, 3, 15, 23.0);
3002   g_assert (g_date_time_is_daylight_savings (gdt1));
3003   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3004   g_assert (!g_date_time_is_daylight_savings (gdt2));
3005   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3006   g_date_time_unref (gdt1);
3007   g_date_time_unref (gdt2);
3008   gdt1 = g_date_time_new (tz, 2012, 10, 7, 3, 15, 23.0);
3009   gdt2 = g_date_time_new (tz, 2012, 10, 7, 1, 15, 23.0);
3010   g_assert (g_date_time_is_daylight_savings (gdt1));
3011   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3012   g_assert (!g_date_time_is_daylight_savings (gdt2));
3013   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3014   g_date_time_unref (gdt1);
3015   g_date_time_unref (gdt2);
3016   g_time_zone_unref (tz);
3017
3018   tz = g_time_zone_new_identifier ("NZST-12:00:00NZDT-13:00:00,279,76");
3019   g_assert_nonnull (tz);
3020   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "NZST-12:00:00NZDT-13:00:00,279,76");
3021   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "NZST");
3022   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 12 * 3600);
3023   g_assert (!g_time_zone_is_dst (tz, 0));
3024   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "NZDT");
3025   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, 13 * 3600);
3026   g_assert (g_time_zone_is_dst (tz, 1));
3027   gdt1 = g_date_time_new (tz, 2012, 3, 18, 0, 15, 23.0);
3028   gdt2 = g_date_time_new (tz, 2012, 3, 18, 3, 15, 23.0);
3029   g_assert (g_date_time_is_daylight_savings (gdt1));
3030   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3031   g_assert (!g_date_time_is_daylight_savings (gdt2));
3032   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3033   g_date_time_unref (gdt1);
3034   g_date_time_unref (gdt2);
3035   gdt1 = g_date_time_new (tz, 2012, 10, 7, 3, 15, 23.0);
3036   gdt2 = g_date_time_new (tz, 2012, 10, 7, 1, 15, 23.0);
3037   g_assert (g_date_time_is_daylight_savings (gdt1));
3038   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3039   g_assert (!g_date_time_is_daylight_savings (gdt2));
3040   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3041   g_date_time_unref (gdt1);
3042   g_date_time_unref (gdt2);
3043   g_time_zone_unref (tz);
3044
3045   tz = g_time_zone_new_identifier ("NZST-12:00:00NZDT-13:00:00,J279,J76");
3046   g_assert_nonnull (tz);
3047   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "NZST-12:00:00NZDT-13:00:00,J279,J76");
3048   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "NZST");
3049   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 12 * 3600);
3050   g_assert (!g_time_zone_is_dst (tz, 0));
3051   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "NZDT");
3052   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, 13 * 3600);
3053   g_assert (g_time_zone_is_dst (tz, 1));
3054   gdt1 = g_date_time_new (tz, 2012, 3, 18, 0, 15, 23.0);
3055   gdt2 = g_date_time_new (tz, 2012, 3, 18, 3, 15, 23.0);
3056   g_assert (g_date_time_is_daylight_savings (gdt1));
3057   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3058   g_assert (!g_date_time_is_daylight_savings (gdt2));
3059   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3060   g_date_time_unref (gdt1);
3061   g_date_time_unref (gdt2);
3062   gdt1 = g_date_time_new (tz, 2012, 10, 7, 3, 15, 23.0);
3063   gdt2 = g_date_time_new (tz, 2012, 10, 7, 1, 15, 23.0);
3064   g_assert (g_date_time_is_daylight_savings (gdt1));
3065   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3066   g_assert (!g_date_time_is_daylight_savings (gdt2));
3067   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3068   g_date_time_unref (gdt1);
3069   g_date_time_unref (gdt2);
3070   g_time_zone_unref (tz);
3071
3072   tz = g_time_zone_new_identifier ("NZST-12:00:00NZDT-13:00:00,M10.1.0/07:00,M3.3.0/07:00");
3073   g_assert_nonnull (tz);
3074   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "NZST-12:00:00NZDT-13:00:00,M10.1.0/07:00,M3.3.0/07:00");
3075   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "NZST");
3076   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, 12 * 3600);
3077   g_assert (!g_time_zone_is_dst (tz, 0));
3078   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "NZDT");
3079   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, 13 * 3600);
3080   g_assert (g_time_zone_is_dst (tz, 1));
3081   gdt1 = g_date_time_new (tz, 2012, 3, 18, 5, 15, 23.0);
3082   gdt2 = g_date_time_new (tz, 2012, 3, 18, 8, 15, 23.0);
3083   g_assert (g_date_time_is_daylight_savings (gdt1));
3084   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3085   g_assert (!g_date_time_is_daylight_savings (gdt2));
3086   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3087   g_date_time_unref (gdt1);
3088   g_date_time_unref (gdt2);
3089   gdt1 = g_date_time_new (tz, 2012, 10, 7, 8, 15, 23.0);
3090   gdt2 = g_date_time_new (tz, 2012, 10, 7, 6, 15, 23.0);
3091   g_assert (g_date_time_is_daylight_savings (gdt1));
3092   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3093   g_assert (!g_date_time_is_daylight_savings (gdt2));
3094   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3095   g_date_time_unref (gdt1);
3096   g_date_time_unref (gdt2);
3097   gdt1 = g_date_time_new (tz, 1902, 10, 7, 8, 15, 23.0);
3098   gdt2 = g_date_time_new (tz, 1902, 10, 7, 6, 15, 23.0);
3099   g_assert (!g_date_time_is_daylight_savings (gdt1));
3100   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 43200);
3101   g_assert (!g_date_time_is_daylight_savings (gdt2));
3102   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3103   g_date_time_unref (gdt1);
3104   g_date_time_unref (gdt2);
3105   gdt1 = g_date_time_new (tz, 2142, 10, 7, 8, 15, 23.0);
3106   gdt2 = g_date_time_new (tz, 2142, 10, 7, 6, 15, 23.0);
3107   g_assert (g_date_time_is_daylight_savings (gdt1));
3108   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 46800);
3109   g_assert (!g_date_time_is_daylight_savings (gdt2));
3110   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3111   g_date_time_unref (gdt1);
3112   g_date_time_unref (gdt2);
3113   gdt1 = g_date_time_new (tz, 3212, 10, 7, 8, 15, 23.0);
3114   gdt2 = g_date_time_new (tz, 3212, 10, 7, 6, 15, 23.0);
3115   g_assert (!g_date_time_is_daylight_savings (gdt1));
3116   g_assert_cmpint (g_date_time_get_utc_offset (gdt1) / 1000000, ==, 43200);
3117   g_assert (!g_date_time_is_daylight_savings (gdt2));
3118   g_assert_cmpint (g_date_time_get_utc_offset (gdt2) / 1000000, ==, 43200);
3119   g_date_time_unref (gdt1);
3120   g_date_time_unref (gdt2);
3121   g_time_zone_unref (tz);
3122
3123   tz = g_time_zone_new_identifier ("VIR-00:30");
3124   g_assert_nonnull (tz);
3125   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-00:30");
3126   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
3127   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (30 * 60));
3128   g_assert_false (g_time_zone_is_dst (tz, 0));
3129
3130   tz = g_time_zone_new_identifier ("VIR-00:30VID,0/00:00:00,365/23:59:59");
3131   g_assert_nonnull (tz);
3132   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-00:30VID,0/00:00:00,365/23:59:59");
3133   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
3134   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (30 * 60));
3135   g_assert_false (g_time_zone_is_dst (tz, 0));
3136   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
3137   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (90 * 60));
3138   g_assert_true (g_time_zone_is_dst (tz, 1));
3139
3140   tz = g_time_zone_new_identifier ("VIR-02:30VID,0/00:00:00,365/23:59:59");
3141   g_assert_nonnull (tz);
3142   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-02:30VID,0/00:00:00,365/23:59:59");
3143   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
3144   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (150 * 60));
3145   g_assert_false (g_time_zone_is_dst (tz, 0));
3146   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
3147   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (210 * 60));
3148   g_assert_true (g_time_zone_is_dst (tz, 1));
3149
3150   tz = g_time_zone_new_identifier ("VIR-02:30VID-04:30,0/00:00:00,365/23:59:59");
3151   g_assert_nonnull (tz);
3152   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-02:30VID-04:30,0/00:00:00,365/23:59:59");
3153   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
3154   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (150 * 60));
3155   g_assert_false (g_time_zone_is_dst (tz, 0));
3156   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
3157   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (270 * 60));
3158   g_assert_true (g_time_zone_is_dst (tz, 1));
3159
3160   tz = g_time_zone_new_identifier ("VIR-12:00VID-13:00,0/00:00:00,365/23:59:59");
3161   g_assert_nonnull (tz);
3162   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "VIR-12:00VID-13:00,0/00:00:00,365/23:59:59");
3163   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "VIR");
3164   g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, (720 * 60));
3165   g_assert_false (g_time_zone_is_dst (tz, 0));
3166   g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "VID");
3167   g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==, (780 * 60));
3168   g_assert_true (g_time_zone_is_dst (tz, 1));
3169 }
3170
3171 static void
3172 test_GDateTime_floating_point (void)
3173 {
3174   GDateTime *dt;
3175   GTimeZone *tz;
3176
3177   g_test_bug ("http://bugzilla.gnome.org/697715");
3178
3179   tz = g_time_zone_new_identifier ("-03:00");
3180   g_assert_nonnull (tz);
3181   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "-03:00");
3182   dt = g_date_time_new (tz, 2010, 5, 24,  8, 0, 1.000001);
3183   g_time_zone_unref (tz);
3184   g_assert_cmpint (g_date_time_get_microsecond (dt), ==, 1);
3185   g_date_time_unref (dt);
3186 }
3187
3188 /* Check that g_time_zone_get_identifier() returns the identifier given to
3189  * g_time_zone_new(), or "UTC" if loading the timezone failed. */
3190 static void
3191 test_identifier (void)
3192 {
3193   GTimeZone *tz;
3194   gchar *old_tz = g_strdup (g_getenv ("TZ"));
3195
3196 #ifdef G_OS_WIN32
3197   const char *recife_tz = "SA Eastern Standard Time";
3198 #else
3199   const char *recife_tz = "America/Recife";
3200 #endif
3201
3202   tz = g_time_zone_new_identifier ("UTC");
3203   g_assert_nonnull (tz);
3204   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
3205   g_time_zone_unref (tz);
3206
3207   tz = g_time_zone_new_utc ();
3208   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
3209   g_time_zone_unref (tz);
3210
3211   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3212   tz = g_time_zone_new ("some rubbish");
3213   G_GNUC_END_IGNORE_DEPRECATIONS
3214   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
3215   g_time_zone_unref (tz);
3216
3217   tz = g_time_zone_new_identifier ("Z");
3218   g_assert_nonnull (tz);
3219   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "Z");
3220   g_time_zone_unref (tz);
3221
3222   tz = g_time_zone_new_identifier ("+03:15");
3223   g_assert_nonnull (tz);
3224   g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "+03:15");
3225   g_time_zone_unref (tz);
3226
3227   /* System timezone. We can’t change this, but we can at least assert that
3228    * the identifier is non-NULL and non-empty. */
3229   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3230   tz = g_time_zone_new (NULL);
3231   G_GNUC_END_IGNORE_DEPRECATIONS
3232   g_test_message ("System time zone identifier: %s", g_time_zone_get_identifier (tz));
3233   g_assert_nonnull (g_time_zone_get_identifier (tz));
3234   g_assert_cmpstr (g_time_zone_get_identifier (tz), !=, "");
3235   g_time_zone_unref (tz);
3236
3237   /* Local timezone tests. */
3238   if (g_setenv ("TZ", recife_tz, TRUE))
3239     {
3240       tz = g_time_zone_new_local ();
3241       g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, recife_tz);
3242       g_time_zone_unref (tz);
3243     }
3244
3245   if (g_setenv ("TZ", "some rubbish", TRUE))
3246     {
3247       tz = g_time_zone_new_local ();
3248       g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
3249       g_time_zone_unref (tz);
3250     }
3251
3252   if (old_tz != NULL)
3253     g_assert_true (g_setenv ("TZ", old_tz, TRUE));
3254   else
3255     g_unsetenv ("TZ");
3256
3257   g_free (old_tz);
3258 }
3259
3260 /* Test various calls to g_time_zone_new_offset(). */
3261 static void
3262 test_new_offset (void)
3263 {
3264   const struct
3265     {
3266       gint32 offset;
3267       gboolean expected_success;
3268     }
3269   vectors[] =
3270     {
3271       { -158400, FALSE },
3272       { -10000, TRUE },
3273       { -3600, TRUE },
3274       { -61, TRUE },
3275       { -60, TRUE },
3276       { -59, TRUE },
3277       { 0, TRUE },
3278       { 59, TRUE },
3279       { 60, TRUE },
3280       { 61, TRUE },
3281       { 3600, TRUE },
3282       { 10000, TRUE },
3283       { 158400, FALSE },
3284     };
3285   gsize i;
3286
3287   for (i = 0; i < G_N_ELEMENTS (vectors); i++)
3288     {
3289       GTimeZone *tz = NULL;
3290
3291       g_test_message ("Vector %" G_GSIZE_FORMAT ": %d", i, vectors[i].offset);
3292
3293       tz = g_time_zone_new_offset (vectors[i].offset);
3294       g_assert_nonnull (tz);
3295
3296       if (vectors[i].expected_success)
3297         {
3298           g_assert_cmpstr (g_time_zone_get_identifier (tz), !=, "UTC");
3299           g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, vectors[i].offset);
3300         }
3301       else
3302         {
3303           g_assert_cmpstr (g_time_zone_get_identifier (tz), ==, "UTC");
3304         }
3305
3306       g_time_zone_unref (tz);
3307     }
3308 }
3309
3310 static void
3311 test_time_zone_parse_rfc8536 (void)
3312 {
3313 #ifndef G_OS_WIN32
3314   const gchar *test_files[] =
3315     {
3316       /* Generated with `zic -b slim`; see
3317        * https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1533#note_842235 */
3318       "Amsterdam-slim",
3319       /* Generated with `zic -b fat` */
3320       "Amsterdam-fat",
3321     };
3322   gsize i;
3323
3324   g_test_summary ("Test parsing time zone files in RFC 8536 version 3 format");
3325   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2129");
3326
3327   for (i = 0; i < G_N_ELEMENTS (test_files); i++)
3328     {
3329       gchar *path = NULL;
3330       GTimeZone *tz = NULL;
3331
3332       path = g_test_build_filename (G_TEST_DIST, "time-zones", test_files[i], NULL);
3333       g_assert_true (g_path_is_absolute (path));
3334       tz = g_time_zone_new_identifier (path);
3335       g_assert_nonnull (tz);
3336       g_time_zone_unref (tz);
3337       g_free (path);
3338     }
3339 #else
3340   g_test_skip ("RFC 8536 format time zone files are not available on Windows");
3341 #endif
3342 }
3343
3344 /* Check GTimeZone instances are cached. */
3345 static void
3346 test_time_zone_caching (void)
3347 {
3348   GTimeZone *tz1 = NULL, *tz2 = NULL;
3349
3350   g_test_summary ("GTimeZone instances are cached");
3351
3352   /* Check a specific (arbitrary) timezone. These are only cached while third
3353    * party code holds a ref to at least one instance. */
3354 #ifdef G_OS_UNIX
3355   tz1 = g_time_zone_new_identifier ("Europe/London");
3356   g_assert_nonnull (tz1);
3357   tz2 = g_time_zone_new_identifier ("Europe/London");
3358   g_assert_nonnull (tz2);
3359   g_time_zone_unref (tz1);
3360   g_time_zone_unref (tz2);
3361 #elif defined G_OS_WIN32
3362   tz1 = g_time_zone_new_identifier ("GMT Standard Time");
3363   g_assert_nonnull (tz1);
3364   tz2 = g_time_zone_new_identifier ("GMT Standard Time");
3365   g_assert_nonnull (tz2);
3366   g_time_zone_unref (tz1);
3367   g_time_zone_unref (tz2);
3368 #endif
3369
3370   /* Only compare pointers */
3371   g_assert_true (tz1 == tz2);
3372
3373   /* Check the default timezone, local and UTC. These are cached internally in
3374    * GLib, so should persist even after the last third party reference is
3375    * dropped.
3376    *
3377    * The default timezone could be NULL on some platforms (FreeBSD) if
3378    * `/etc/localtime` is not set correctly. */
3379   tz1 = g_time_zone_new_identifier (NULL);
3380   if (tz1 != NULL)
3381     {
3382       g_assert_nonnull (tz1);
3383       g_time_zone_unref (tz1);
3384       tz2 = g_time_zone_new_identifier (NULL);
3385       g_assert_nonnull (tz2);
3386       g_time_zone_unref (tz2);
3387
3388       g_assert_true (tz1 == tz2);
3389     }
3390
3391   tz1 = g_time_zone_new_utc ();
3392   g_time_zone_unref (tz1);
3393   tz2 = g_time_zone_new_utc ();
3394   g_time_zone_unref (tz2);
3395
3396   g_assert_true (tz1 == tz2);
3397
3398   tz1 = g_time_zone_new_local ();
3399   g_time_zone_unref (tz1);
3400   tz2 = g_time_zone_new_local ();
3401   g_time_zone_unref (tz2);
3402
3403   g_assert_true (tz1 == tz2);
3404 }
3405
3406 static void
3407 test_date_time_unix_usec (void)
3408 {
3409   gint64 usecs = g_get_real_time ();
3410   gint64 secs = usecs / G_USEC_PER_SEC;
3411   GDateTime *dt;
3412   GDateTime *local;
3413
3414   dt = g_date_time_new_from_unix_utc (secs);
3415   g_assert_cmpint (g_date_time_to_unix_usec (dt), ==, secs * G_USEC_PER_SEC);
3416   g_assert_cmpint (g_date_time_to_unix (dt), ==, secs);
3417   g_date_time_unref (dt);
3418
3419   dt = g_date_time_new_from_unix_utc_usec (usecs);
3420   g_assert_cmpint (g_date_time_to_unix_usec (dt), ==, usecs);
3421   g_assert_cmpint (g_date_time_to_unix (dt), ==, secs);
3422   g_date_time_unref (dt);
3423
3424   local = g_date_time_new_from_unix_local (secs);
3425   dt = g_date_time_to_utc (local);
3426   g_assert_cmpint (g_date_time_to_unix_usec (dt), ==, secs * G_USEC_PER_SEC);
3427   g_assert_cmpint (g_date_time_to_unix (dt), ==, secs);
3428   g_date_time_unref (dt);
3429   g_date_time_unref (local);
3430
3431   local = g_date_time_new_from_unix_local_usec (usecs);
3432   dt = g_date_time_to_utc (local);
3433   g_assert_cmpint (g_date_time_to_unix_usec (dt), ==, usecs);
3434   g_assert_cmpint (g_date_time_to_unix (dt), ==, secs);
3435   g_date_time_unref (dt);
3436   g_date_time_unref (local);
3437 }
3438
3439 gint
3440 main (gint   argc,
3441       gchar *argv[])
3442 {
3443   /* In glibc, LANGUAGE is used as highest priority guess for category value.
3444    * Unset it to avoid interference with tests using setlocale and translation. */
3445   g_unsetenv ("LANGUAGE");
3446
3447   /* GLib uses CHARSET to allow overriding the character set used for all locale
3448    * categories. Unset it to avoid interference with tests. */
3449   g_unsetenv ("CHARSET");
3450
3451   setlocale (LC_ALL, "C.UTF-8");
3452   g_test_init (&argc, &argv, NULL);
3453
3454   /* GDateTime Tests */
3455   bind_textdomain_codeset ("glib20", "UTF-8");
3456
3457   g_test_add_func ("/GDateTime/invalid", test_GDateTime_invalid);
3458   g_test_add_func ("/GDateTime/add_days", test_GDateTime_add_days);
3459   g_test_add_func ("/GDateTime/add_full", test_GDateTime_add_full);
3460   g_test_add_func ("/GDateTime/add_hours", test_GDateTime_add_hours);
3461   g_test_add_func ("/GDateTime/add_minutes", test_GDateTime_add_minutes);
3462   g_test_add_func ("/GDateTime/add_months", test_GDateTime_add_months);
3463   g_test_add_func ("/GDateTime/add_seconds", test_GDateTime_add_seconds);
3464   g_test_add_func ("/GDateTime/add_weeks", test_GDateTime_add_weeks);
3465   g_test_add_func ("/GDateTime/add_years", test_GDateTime_add_years);
3466   g_test_add_func ("/GDateTime/compare", test_GDateTime_compare);
3467   g_test_add_func ("/GDateTime/diff", test_GDateTime_diff);
3468   g_test_add_func ("/GDateTime/equal", test_GDateTime_equal);
3469   g_test_add_func ("/GDateTime/get_day_of_week", test_GDateTime_get_day_of_week);
3470   g_test_add_func ("/GDateTime/get_day_of_month", test_GDateTime_get_day_of_month);
3471   g_test_add_func ("/GDateTime/get_day_of_year", test_GDateTime_get_day_of_year);
3472   g_test_add_func ("/GDateTime/get_hour", test_GDateTime_get_hour);
3473   g_test_add_func ("/GDateTime/get_microsecond", test_GDateTime_get_microsecond);
3474   g_test_add_func ("/GDateTime/get_minute", test_GDateTime_get_minute);
3475   g_test_add_func ("/GDateTime/get_month", test_GDateTime_get_month);
3476   g_test_add_func ("/GDateTime/get_second", test_GDateTime_get_second);
3477   g_test_add_func ("/GDateTime/get_utc_offset", test_GDateTime_get_utc_offset);
3478   g_test_add_func ("/GDateTime/get_year", test_GDateTime_get_year);
3479   g_test_add_func ("/GDateTime/hash", test_GDateTime_hash);
3480   g_test_add_func ("/GDateTime/new_from_unix", test_GDateTime_new_from_unix);
3481   g_test_add_func ("/GDateTime/new_from_unix_utc", test_GDateTime_new_from_unix_utc);
3482   g_test_add_func ("/GDateTime/new_from_unix/overflow", test_GDateTime_new_from_unix_overflow);
3483   g_test_add_func ("/GDateTime/new_from_timeval", test_GDateTime_new_from_timeval);
3484   g_test_add_func ("/GDateTime/new_from_timeval_utc", test_GDateTime_new_from_timeval_utc);
3485   g_test_add_func ("/GDateTime/new_from_timeval/overflow", test_GDateTime_new_from_timeval_overflow);
3486   g_test_add_func ("/GDateTime/new_from_iso8601", test_GDateTime_new_from_iso8601);
3487   g_test_add_func ("/GDateTime/new_from_iso8601/2", test_GDateTime_new_from_iso8601_2);
3488   g_test_add_func ("/GDateTime/new_full", test_GDateTime_new_full);
3489   g_test_add_func ("/GDateTime/now", test_GDateTime_now);
3490   g_test_add_func ("/GDateTime/test-6-days-until-end-of-the-month", test_6_days_until_end_of_the_month);
3491   g_test_add_func ("/GDateTime/printf", test_GDateTime_printf);
3492   g_test_add_func ("/GDateTime/non_utf8_printf", test_non_utf8_printf);
3493   g_test_add_func ("/GDateTime/format_unrepresentable", test_format_unrepresentable);
3494   g_test_add_func ("/GDateTime/format_iso8601", test_format_iso8601);
3495   g_test_add_data_func ("/GDateTime/format_mixed/utf8_time_non_utf8_messages",
3496                         &utf8_time_non_utf8_messages,
3497                         test_format_time_mixed_utf8);
3498   g_test_add_data_func ("/GDateTime/format_mixed/utf8_time_utf8_messages",
3499                         &utf8_time_utf8_messages,
3500                         test_format_time_mixed_utf8);
3501   g_test_add_data_func ("/GDateTime/format_mixed/non_utf8_time_non_utf8_messages",
3502                         &non_utf8_time_non_utf8_messages,
3503                         test_format_time_mixed_utf8);
3504   g_test_add_data_func ("/GDateTime/format_mixed/non_utf8_time_utf8_messages",
3505                         &non_utf8_time_utf8_messages,
3506                         test_format_time_mixed_utf8);
3507   g_test_add_func ("/GDateTime/strftime", test_strftime);
3508   g_test_add_func ("/GDateTime/strftime/error_handling", test_GDateTime_strftime_error_handling);
3509   g_test_add_func ("/GDateTime/modifiers", test_modifiers);
3510   g_test_add_func ("/GDateTime/month_names", test_month_names);
3511   g_test_add_func ("/GDateTime/to_local", test_GDateTime_to_local);
3512   g_test_add_func ("/GDateTime/to_unix", test_GDateTime_to_unix);
3513   g_test_add_func ("/GDateTime/to_timeval", test_GDateTime_to_timeval);
3514   g_test_add_func ("/GDateTime/to_utc", test_GDateTime_to_utc);
3515   g_test_add_func ("/GDateTime/now_utc", test_GDateTime_now_utc);
3516   g_test_add_func ("/GDateTime/dst", test_GDateTime_dst);
3517   g_test_add_func ("/GDateTime/test_z", test_z);
3518   g_test_add_func ("/GDateTime/test-all-dates", test_all_dates);
3519   g_test_add_func ("/GDateTime/eras/japan", test_date_time_eras_japan);
3520   g_test_add_func ("/GDateTime/eras/thailand", test_date_time_eras_thailand);
3521   g_test_add_func ("/GDateTime/eras/parsing", test_date_time_eras_parsing);
3522   g_test_add_func ("/GDateTime/unix_usec", test_date_time_unix_usec);
3523
3524   g_test_add_func ("/GTimeZone/find-interval", test_find_interval);
3525   g_test_add_func ("/GTimeZone/adjust-time", test_adjust_time);
3526   g_test_add_func ("/GTimeZone/no-header", test_no_header);
3527   g_test_add_func ("/GTimeZone/no-header-identifier", test_no_header_identifier);
3528   g_test_add_func ("/GTimeZone/posix-parse", test_posix_parse);
3529   g_test_add_func ("/GTimeZone/floating-point", test_GDateTime_floating_point);
3530   g_test_add_func ("/GTimeZone/identifier", test_identifier);
3531   g_test_add_func ("/GTimeZone/new-offset", test_new_offset);
3532   g_test_add_func ("/GTimeZone/parse-rfc8536", test_time_zone_parse_rfc8536);
3533   g_test_add_func ("/GTimeZone/caching", test_time_zone_caching);
3534
3535   return g_test_run ();
3536 }