summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Volk <f_l_k@t-online.de>2024-11-22 13:27:05 +0100
committerSteve Sakoman <steve@sakoman.com>2024-11-30 05:41:59 -0800
commitd7cd2e6f521dc517365fe2e737655cb68737db2f (patch)
tree31e1e9242653a3a5866957a07e1b5b7bf732c856
parent814bde9a6b9bb9267a073b05e5307da018a6040f (diff)
downloadpoky-d7cd2e6f521dc517365fe2e737655cb68737db2f.tar.gz
gcc: add a backport patch to fix an issue with tzdata 2024b
There is an issue in the std::chrono::tzdb parser that causes problems since the tzdata-2024b release started using %z in the main format. As a real world problem I encounter an issue with the waybar clock module, which ignores the timezone setting and only shows system time. (From OE-Core rev: 08dfd3849bd804f4760ebeca226645e65709a65a) Signed-off-by: Markus Volk <f_l_k@t-online.de> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> (cherry picked from commit 39018429f05511053ab12e23e7f4487ea25ee529) Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/gcc/gcc-14.2.inc1
-rw-r--r--meta/recipes-devtools/gcc/gcc/gcc.git-ab884fffe3fc82a710bea66ad651720d71c938b8.patch549
2 files changed, 550 insertions, 0 deletions
diff --git a/meta/recipes-devtools/gcc/gcc-14.2.inc b/meta/recipes-devtools/gcc/gcc-14.2.inc
index f05484cfc0..9cfb246294 100644
--- a/meta/recipes-devtools/gcc/gcc-14.2.inc
+++ b/meta/recipes-devtools/gcc/gcc-14.2.inc
@@ -68,6 +68,7 @@ SRC_URI = "${BASEURI} \
68 file://0023-Fix-install-path-of-linux64.h.patch \ 68 file://0023-Fix-install-path-of-linux64.h.patch \
69 file://0024-Avoid-hardcoded-build-paths-into-ppc-libgcc.patch \ 69 file://0024-Avoid-hardcoded-build-paths-into-ppc-libgcc.patch \
70 file://0025-gcc-testsuite-tweaks-for-mips-OE.patch \ 70 file://0025-gcc-testsuite-tweaks-for-mips-OE.patch \
71 file://gcc.git-ab884fffe3fc82a710bea66ad651720d71c938b8.patch \
71" 72"
72 73
73S = "${TMPDIR}/work-shared/gcc-${PV}-${PR}/${SOURCEDIR}" 74S = "${TMPDIR}/work-shared/gcc-${PV}-${PR}/${SOURCEDIR}"
diff --git a/meta/recipes-devtools/gcc/gcc/gcc.git-ab884fffe3fc82a710bea66ad651720d71c938b8.patch b/meta/recipes-devtools/gcc/gcc/gcc.git-ab884fffe3fc82a710bea66ad651720d71c938b8.patch
new file mode 100644
index 0000000000..e5abdcc703
--- /dev/null
+++ b/meta/recipes-devtools/gcc/gcc/gcc.git-ab884fffe3fc82a710bea66ad651720d71c938b8.patch
@@ -0,0 +1,549 @@
1From ab884fffe3fc82a710bea66ad651720d71c938b8 Mon Sep 17 00:00:00 2001
2From: Jonathan Wakely <jwakely@redhat.com>
3Date: Tue, 30 Apr 2024 09:52:13 +0100
4Subject: [PATCH] libstdc++: Fix std::chrono::tzdb to work with vanguard format
5
6I found some issues in the std::chrono::tzdb parser by testing the
7tzdata "vanguard" format, which uses new features that aren't enabled in
8the "main" and "rearguard" data formats.
9
10Since 2024a the keyword "minimum" is no longer valid for the FROM and TO
11fields in a Rule line, which means that "m" is now a valid abbreviation
12for "maximum". Previously we expected either "mi" or "ma". For backwards
13compatibility, a FROM field beginning with "mi" is still supported and
14is treated as 1900. The "maximum" keyword is only allowed in TO now,
15because it makes no sense in FROM. To support these changes the
16minmax_year and minmax_year2 classes for parsing FROM and TO are
17replaced with a single years_from_to class that reads both fields.
18
19The vanguard format makes use of %z in Zone FORMAT fields, which caused
20an exception to be thrown from ZoneInfo::set_abbrev because no % or /
21characters were expected when a Zone doesn't use a named Rule. The
22ZoneInfo::to(sys_info&) function now uses format_abbrev_str to replace
23any %z with the current offset. Although format_abbrev_str also checks
24for %s and STD/DST formats, those only make sense when a named Rule is
25in effect, so won't occur when ZoneInfo::to(sys_info&) is used.
26
27Since making this change on trunk, the tzdata-2024b release started
28using %z in the main format, not just vanguard. This makes a backport to
29release branches necessary (see PR 116657).
30
31This change also implements a feature that has always been missing from
32time_zone::_M_get_sys_info: finding the Rule that is active before the
33specified time point, so that we can correctly handle %s in the FORMAT
34for the first new sys_info that gets created. This requires implementing
35a poorly documented feature of zic, to get the LETTERS field from a
36later transition, as described at
37https://mm.icann.org/pipermail/tz/2024-April/058891.html
38In order for this to work we need to be able to distinguish an empty
39letters field (as used by CE%sT where the variable part is either empty
40or "S") from "the letters field is not known for this transition". The
41tzdata file uses "-" for an empty letters field, which libstdc++ was
42previously replacing with "" when the Rule was parsed. Instead, we now
43preserve the "-" in the Rule object, so that "" can be used for the case
44where we don't know the letters (and so need to decide it).
45
46(cherry picked from commit 0ca8d56f2085715f27ee536c6c344bc47af49cdd)
47
48Upstream-Status: Backport [https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=5ceea2ac106d6dd1aa8175670b15a801316cf1c9]
49
50Signed-off-by: Markus Volk <f_l_k@t-online.de>
51---
52 libstdc++-v3/src/c++20/tzdb.cc | 265 +++++++++++-------
53 .../std/time/time_zone/sys_info_abbrev.cc | 106 +++++++
54 libstdc++-v3/testsuite/std/time/tzdb/1.cc | 6 +-
55 3 files changed, 274 insertions(+), 103 deletions(-)
56 create mode 100644 libstdc++-v3/testsuite/std/time/time_zone/sys_info_abbrev.cc
57
58diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
59index c7c7cc9deee6..7e8cce7ce8cf 100644
60--- a/libstdc++-v3/src/c++20/tzdb.cc
61+++ b/libstdc++-v3/src/c++20/tzdb.cc
62@@ -342,51 +342,103 @@ namespace std::chrono
63 friend istream& operator>>(istream&, on_day&);
64 };
65
66- // Wrapper for chrono::year that reads a year, or one of the keywords
67- // "minimum" or "maximum", or an unambiguous prefix of a keyword.
68- struct minmax_year
69+ // Wrapper for two chrono::year values, which reads the FROM and TO
70+ // fields of a Rule line. The FROM field is a year and TO is a year or
71+ // one of the keywords "maximum" or "only" (or an abbreviation of those).
72+ // For backwards compatibility, the keyword "minimum" is recognized
73+ // for FROM and interpreted as 1900.
74+ struct years_from_to
75 {
76- year& y;
77+ year& from;
78+ year& to;
79
80- friend istream& operator>>(istream& in, minmax_year&& y)
81+ friend istream& operator>>(istream& in, years_from_to&& yy)
82 {
83- if (ws(in).peek() == 'm') // keywords "minimum" or "maximum"
84+ string s;
85+ auto c = ws(in).peek();
86+ if (c == 'm') [[unlikely]] // keyword "minimum"
87 {
88- string s;
89- in >> s; // extract the rest of the word, but only look at s[1]
90- if (s[1] == 'a')
91- y.y = year::max();
92- else if (s[1] == 'i')
93- y.y = year::min();
94- else
95- in.setstate(ios::failbit);
96+ in >> s; // extract the rest of the word
97+ yy.from = year(1900);
98+ }
99+ else if (int num = 0; in >> num) [[likely]]
100+ yy.from = year{num};
101+
102+ c = ws(in).peek();
103+ if (c == 'm') // keyword "maximum"
104+ {
105+ in >> s; // extract the rest of the word
106+ yy.to = year::max();
107+ }
108+ else if (c == 'o') // keyword "only"
109+ {
110+ in >> s; // extract the rest of the word
111+ yy.to = yy.from;
112 }
113 else if (int num = 0; in >> num)
114- y.y = year{num};
115+ yy.to = year{num};
116+
117 return in;
118 }
119 };
120
121- // As above for minmax_year, but also supports the keyword "only",
122- // meaning that the TO year is the same as the FROM year.
123- struct minmax_year2
124+ bool
125+ select_std_or_dst_abbrev(string& abbrev, minutes save)
126 {
127- minmax_year to;
128- year from;
129+ if (size_t pos = abbrev.find('/'); pos != string::npos)
130+ {
131+ // Select one of "STD/DST" for standard or daylight.
132+ if (save == 0min)
133+ abbrev.erase(pos);
134+ else
135+ abbrev.erase(0, pos + 1);
136+ return true;
137+ }
138+ return false;
139+ }
140
141- friend istream& operator>>(istream& in, minmax_year2&& y)
142- {
143- if (ws(in).peek() == 'o') // keyword "only"
144- {
145- string s;
146- in >> s; // extract the whole keyword
147- y.to.y = y.from;
148- }
149- else
150- in >> std::move(y.to);
151- return in;
152- }
153- };
154+ // Set the sys_info::abbrev string by expanding any placeholders.
155+ void
156+ format_abbrev_str(sys_info& info, string_view letters = {})
157+ {
158+ if (size_t pos = info.abbrev.find('%'); pos != string::npos)
159+ {
160+ if (info.abbrev[pos + 1] == 's')
161+ {
162+ // Expand "%s" to the variable part, given by Rule::letters.
163+ if (letters == "-")
164+ info.abbrev.erase(pos, 2);
165+ else
166+ info.abbrev.replace(pos, 2, letters);
167+ }
168+ else if (info.abbrev[pos + 1] == 'z')
169+ {
170+ // Expand "%z" to the UT offset as +/-hh, +/-hhmm, or +/-hhmmss.
171+ hh_mm_ss<seconds> t(info.offset);
172+ string z(1, "+-"[t.is_negative()]);
173+ long val = t.hours().count();
174+ int digits = 2;
175+ if (int m = t.minutes().count())
176+ {
177+ digits = 4;
178+ val *= 100;
179+ val += m;
180+ if (int s = t.seconds().count())
181+ {
182+ digits = 6;
183+ val *= 100;
184+ val += s;
185+ }
186+ }
187+ auto sval = std::to_string(val);
188+ z += string(digits - sval.size(), '0');
189+ z += sval;
190+ info.abbrev.replace(pos, 2, z);
191+ }
192+ }
193+ else
194+ select_std_or_dst_abbrev(info.abbrev, info.save);
195+ }
196
197 // A time zone information record.
198 // Zone NAME STDOFF RULES FORMAT [UNTIL]
199@@ -462,6 +514,7 @@ namespace std::chrono
200 info.offset = offset();
201 info.save = minutes(m_save);
202 info.abbrev = format();
203+ format_abbrev_str(info); // expand %z
204 return true;
205 }
206
207@@ -469,12 +522,9 @@ namespace std::chrono
208 friend class time_zone;
209
210 void
211- set_abbrev(const string& abbrev)
212+ set_abbrev(string abbrev)
213 {
214- // In practice, the FORMAT field never needs expanding here.
215- if (abbrev.find_first_of("/%") != abbrev.npos)
216- __throw_runtime_error("std::chrono::time_zone: invalid data");
217- m_buf = abbrev;
218+ m_buf = std::move(abbrev);
219 m_pos = 0;
220 m_expanded = true;
221 }
222@@ -544,9 +594,7 @@ namespace std::chrono
223
224 // Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
225
226- in >> quoted(rule.name)
227- >> minmax_year{rule.from}
228- >> minmax_year2{rule.to, rule.from};
229+ in >> quoted(rule.name) >> years_from_to{rule.from, rule.to};
230
231 if (char type; in >> type && type != '-')
232 in.setstate(ios::failbit);
233@@ -557,7 +605,7 @@ namespace std::chrono
234 if (save_time.indicator != at_time::Wall)
235 {
236 // We don't actually store the save_time.indicator, because we
237- // assume that it's always deducable from the actual offset value.
238+ // assume that it's always deducible from the offset value.
239 auto expected = save_time.time == 0s
240 ? at_time::Standard
241 : at_time::Daylight;
242@@ -567,8 +615,6 @@ namespace std::chrono
243 rule.save = save_time.time;
244
245 in >> rule.letters;
246- if (rule.letters == "-")
247- rule.letters.clear();
248 return in;
249 }
250
251@@ -719,58 +765,6 @@ namespace std::chrono
252 #endif // TZDB_DISABLED
253 };
254
255-#ifndef TZDB_DISABLED
256- namespace
257- {
258- bool
259- select_std_or_dst_abbrev(string& abbrev, minutes save)
260- {
261- if (size_t pos = abbrev.find('/'); pos != string::npos)
262- {
263- // Select one of "STD/DST" for standard or daylight.
264- if (save == 0min)
265- abbrev.erase(pos);
266- else
267- abbrev.erase(0, pos + 1);
268- return true;
269- }
270- return false;
271- }
272-
273- // Set the sys_info::abbrev string by expanding any placeholders.
274- void
275- format_abbrev_str(sys_info& info, string_view letters = {})
276- {
277- if (size_t pos = info.abbrev.find("%s"); pos != string::npos)
278- {
279- // Expand "%s" to the variable part, given by Rule::letters.
280- info.abbrev.replace(pos, 2, letters);
281- }
282- else if (size_t pos = info.abbrev.find("%z"); pos != string::npos)
283- {
284- // Expand "%z" to the UT offset as +/-hh, +/-hhmm, or +/-hhmmss.
285- hh_mm_ss<seconds> t(info.offset);
286- string z(1, "+-"[t.is_negative()]);
287- long val = t.hours().count();
288- if (minutes m = t.minutes(); m != m.zero())
289- {
290- val *= 100;
291- val += m.count();
292- if (seconds s = t.seconds(); s != s.zero())
293- {
294- val *= 100;
295- val += s.count();
296- }
297- }
298- z += std::to_string(val);
299- info.abbrev.replace(pos, 2, z);
300- }
301- else
302- select_std_or_dst_abbrev(info.abbrev, info.save);
303- }
304- }
305-#endif // TZDB_DISABLED
306-
307 // Implementation of std::chrono::time_zone::get_info(const sys_time<D>&)
308 sys_info
309 time_zone::_M_get_sys_info(sys_seconds tp) const
310@@ -839,12 +833,72 @@ namespace std::chrono
311 info.abbrev = ri.format();
312
313 string_view letters;
314- if (i != infos.begin())
315+ if (i != infos.begin() && i[-1].expanded())
316+ letters = i[-1].next_letters();
317+
318+ if (letters.empty())
319 {
320- if (i[-1].expanded())
321- letters = i[-1].next_letters();
322- // XXX else need to find Rule active before this time and use it
323- // to know the initial offset, save, and letters.
324+ sys_seconds t = info.begin - seconds(1);
325+ const year_month_day date(chrono::floor<days>(t));
326+
327+ // Try to find a Rule active before this time, to get initial
328+ // SAVE and LETTERS values. There may not be a Rule for the period
329+ // before the first DST transition, so find the earliest DST->STD
330+ // transition and use the LETTERS from that.
331+ const Rule* active_rule = nullptr;
332+ sys_seconds active_rule_start = sys_seconds::min();
333+ const Rule* first_std = nullptr;
334+ for (const auto& rule : rules)
335+ {
336+ if (rule.save == minutes(0))
337+ {
338+ if (!first_std)
339+ first_std = &rule;
340+ else if (rule.from < first_std->from)
341+ first_std = &rule;
342+ else if (rule.from == first_std->from)
343+ {
344+ if (rule.start_time(rule.from, {})
345+ < first_std->start_time(first_std->from, {}))
346+ first_std = &rule;
347+ }
348+ }
349+
350+ year y = date.year();
351+
352+ if (y > rule.to) // rule no longer applies at time t
353+ continue;
354+ if (y < rule.from) // rule doesn't apply yet at time t
355+ continue;
356+
357+ sys_seconds rule_start;
358+
359+ seconds offset{}; // appropriate for at_time::Universal
360+ if (rule.when.indicator == at_time::Wall)
361+ offset = info.offset;
362+ else if (rule.when.indicator == at_time::Standard)
363+ offset = ri.offset();
364+
365+ // Time the rule takes effect this year:
366+ rule_start = rule.start_time(y, offset);
367+
368+ if (rule_start >= t && rule.from < y)
369+ {
370+ // Try this rule in the previous year.
371+ rule_start = rule.start_time(--y, offset);
372+ }
373+
374+ if (active_rule_start < rule_start && rule_start < t)
375+ {
376+ active_rule_start = rule_start;
377+ active_rule = &rule;
378+ }
379+ }
380+
381+ if (active_rule)
382+ letters = active_rule->letters;
383+ else if (first_std)
384+ letters = first_std->letters;
385 }
386
387 const Rule* curr_rule = nullptr;
388@@ -2069,9 +2123,11 @@ namespace std::chrono
389 istringstream in2(std::move(rules));
390 in2 >> rules_time;
391 inf.m_save = duration_cast<minutes>(rules_time.time);
392+ // If the FORMAT is "STD/DST" then we can choose the right one
393+ // now, so that we store a shorter string.
394 select_std_or_dst_abbrev(fmt, inf.m_save);
395 }
396- inf.set_abbrev(fmt);
397+ inf.set_abbrev(std::move(fmt));
398 }
399
400 // YEAR [MONTH [DAY [TIME]]]
401@@ -2082,7 +2138,12 @@ namespace std::chrono
402 abbrev_month m{January};
403 int d = 1;
404 at_time t{};
405+ // XXX DAY should support ON format, e.g. lastSun or Sun>=8
406 in >> m >> d >> t;
407+ // XXX UNTIL field should be interpreted
408+ // "using the rules in effect just before the transition"
409+ // so might need to store as year_month_day and hh_mm_ss and only
410+ // convert to a sys_time once we know the offset in effect.
411 inf.m_until = sys_days(year(y)/m.m/day(d)) + seconds(t.time);
412 }
413 else
414diff --git a/libstdc++-v3/testsuite/std/time/time_zone/sys_info_abbrev.cc b/libstdc++-v3/testsuite/std/time/time_zone/sys_info_abbrev.cc
415new file mode 100644
416index 000000000000..f1a8fff02f58
417--- /dev/null
418+++ b/libstdc++-v3/testsuite/std/time/time_zone/sys_info_abbrev.cc
419@@ -0,0 +1,106 @@
420+// { dg-do run { target c++20 } }
421+// { dg-require-effective-target tzdb }
422+// { dg-require-effective-target cxx11_abi }
423+// { dg-xfail-run-if "no weak override on AIX" { powerpc-ibm-aix* } }
424+
425+#include <chrono>
426+#include <fstream>
427+#include <testsuite_hooks.h>
428+
429+static bool override_used = false;
430+
431+namespace __gnu_cxx
432+{
433+ const char* zoneinfo_dir_override() {
434+ override_used = true;
435+ return "./";
436+ }
437+}
438+
439+using namespace std::chrono;
440+
441+void
442+test_format()
443+{
444+ std::ofstream("tzdata.zi") << R"(# version test_1
445+Zone Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u
446+ -1 - %z 1975
447+ 0 - GMT
448+Zon Some/Zone 1:2:3 - %z 1900
449+ 1:23:45 - %z 1950
450+Zo Another/Zone 1:2:3 - AZ0 1901
451+ 1 Roolz A%sZ 2000
452+ 1 Roolz SAZ/DAZ 2005
453+ 1 Roolz %z
454+Rule Roolz 1950 max - April 1 2 1 D
455+Rul Roolz 1950 max - Oct 1 1 0 S
456+Z Strange/Zone 1 - X%sX 1980
457+ 1 - FOO/BAR 1990
458+ 2:00 - %zzz 1995
459+ 0:9 - %zzz 1996
460+ 0:8:7 - %zzz 1997
461+ 0:6:5.5 - %zzz 1998
462+)";
463+
464+ const auto& db = reload_tzdb();
465+ VERIFY( override_used ); // If this fails then XFAIL for the target.
466+ VERIFY( db.version == "test_1" );
467+
468+ // Test formatting %z as
469+ auto tz = locate_zone("Africa/Bissau");
470+ auto inf = tz->get_info(sys_days(1974y/1/1));
471+ VERIFY( inf.abbrev == "-01" );
472+
473+ tz = locate_zone("Some/Zone");
474+ inf = tz->get_info(sys_days(1899y/1/1));
475+ VERIFY( inf.abbrev == "+010203" );
476+ inf = tz->get_info(sys_days(1955y/1/1));
477+ VERIFY( inf.abbrev == "+012345" );
478+
479+ tz = locate_zone("Another/Zone");
480+ // Test formatting %s as the LETTER/S field from the active Rule.
481+ inf = tz->get_info(sys_days(1910y/January/1));
482+ VERIFY( inf.abbrev == "ASZ" );
483+ inf = tz->get_info(sys_days(1950y/January/1));
484+ VERIFY( inf.abbrev == "ASZ" );
485+ inf = tz->get_info(sys_days(1950y/June/1));
486+ VERIFY( inf.abbrev == "ADZ" );
487+ inf = tz->get_info(sys_days(1999y/January/1));
488+ VERIFY( inf.abbrev == "ASZ" );
489+ inf = tz->get_info(sys_days(1999y/July/1));
490+ VERIFY( inf.abbrev == "ADZ" );
491+ // Test formatting STD/DST according to the active Rule.
492+ inf = tz->get_info(sys_days(2000y/January/2));
493+ VERIFY( inf.abbrev == "SAZ" );
494+ inf = tz->get_info(sys_days(2001y/January/1));
495+ VERIFY( inf.abbrev == "SAZ" );
496+ inf = tz->get_info(sys_days(2001y/July/1));
497+ VERIFY( inf.abbrev == "DAZ" );
498+ // Test formatting %z as the offset determined by the active Rule.
499+ inf = tz->get_info(sys_days(2005y/January/2));
500+ VERIFY( inf.abbrev == "+01" );
501+ inf = tz->get_info(sys_days(2006y/January/1));
502+ VERIFY( inf.abbrev == "+01" );
503+ inf = tz->get_info(sys_days(2006y/July/1));
504+ VERIFY( inf.abbrev == "+02" );
505+
506+ // Test formatting %z, %s and S/D for a Zone with no associated Rules.
507+ tz = locate_zone("Strange/Zone");
508+ inf = tz->get_info(sys_days(1979y/January/1));
509+ VERIFY( inf.abbrev == "XX" ); // No Rule means nothing to use for %s.
510+ inf = tz->get_info(sys_days(1981y/July/1));
511+ VERIFY( inf.abbrev == "FOO" ); // Always standard time means first string.
512+ inf = tz->get_info(sys_days(1994y/July/1));
513+ VERIFY( inf.abbrev == "+02zz" );
514+ inf = tz->get_info(sys_days(1995y/July/1));
515+ VERIFY( inf.abbrev == "+0009zz" );
516+ inf = tz->get_info(sys_days(1996y/July/1));
517+ VERIFY( inf.abbrev == "+000807zz" );
518+ inf = tz->get_info(sys_days(1997y/July/1));
519+ VERIFY( inf.abbrev == "+000606zz" );
520+}
521+
522+int main()
523+{
524+ test_format();
525+}
526diff --git a/libstdc++-v3/testsuite/std/time/tzdb/1.cc b/libstdc++-v3/testsuite/std/time/tzdb/1.cc
527index 796f3a8b4256..7a31c1c20ba7 100644
528--- a/libstdc++-v3/testsuite/std/time/tzdb/1.cc
529+++ b/libstdc++-v3/testsuite/std/time/tzdb/1.cc
530@@ -39,11 +39,15 @@ test_locate()
531 const tzdb& db = get_tzdb();
532 const time_zone* tz = db.locate_zone("GMT");
533 VERIFY( tz != nullptr );
534- VERIFY( tz->name() == "Etc/GMT" );
535 VERIFY( tz == std::chrono::locate_zone("GMT") );
536 VERIFY( tz == db.locate_zone("Etc/GMT") );
537 VERIFY( tz == db.locate_zone("Etc/GMT+0") );
538
539+ // Since 2022f GMT is now a Zone and Etc/GMT a link instead of vice versa,
540+ // but only when using the vanguard format. As of 2024a, the main and
541+ // rearguard formats still have Etc/GMT as a Zone and GMT as a link.
542+ VERIFY( tz->name() == "GMT" || tz->name() == "Etc/GMT" );
543+
544 VERIFY( db.locate_zone(db.current_zone()->name()) == db.current_zone() );
545 }
546
547--
5482.43.5
549