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