1#################################################################
2#
3# win32tzlist.pl -- compare Windows timezone information
4#
5# Copyright (c) 2008-2021, PostgreSQL Global Development Group
6#
7# src/tools/win32tzlist.pl
8#################################################################
9
10#
11# This script compares the timezone information in the Windows registry
12# with that in src/bin/initdb/findtimezone.c.  A list of changes will be
13# written to stdout - no attempt is made to automatically edit the file.
14#
15# Run the script from the top-level PG source directory.
16#
17
18use strict;
19use warnings;
20
21use Win32::Registry;
22
23my $tzfile = 'src/bin/initdb/findtimezone.c';
24
25#
26# Fetch all timezones in the registry
27#
28my $basekey;
29$HKEY_LOCAL_MACHINE->Open(
30	"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", $basekey)
31  or die $!;
32
33my @subkeys;
34$basekey->GetKeys(\@subkeys);
35
36my @system_zones;
37
38foreach my $keyname (@subkeys)
39{
40	my $subkey;
41	my %vals;
42
43	$basekey->Open($keyname, $subkey) or die $!;
44	$subkey->GetValues(\%vals) or die $!;
45	$subkey->Close();
46
47	die "Incomplete timezone data for $keyname!\n"
48	  unless ($vals{Std} && $vals{Dlt} && $vals{Display});
49	push @system_zones,
50	  {
51		'std'     => $vals{Std}->[2],
52		'dlt'     => $vals{Dlt}->[2],
53		'display' => clean_displayname($vals{Display}->[2]),
54	  };
55}
56
57$basekey->Close();
58
59#
60# Fetch all timezones currently in the file
61#
62my @file_zones;
63my $pgtz;
64open(my $tzfh, '<', $tzfile) or die "Could not open $tzfile!\n";
65{
66	local $/ = undef;
67	$pgtz = <$tzfh>;
68}
69close($tzfh);
70
71# Attempt to locate and extract the complete win32_tzmap struct
72$pgtz =~ /win32_tzmap\[\] =\s+{\s+\/\*[^\/]+\*\/\s+(.+?)};/gs
73  or die "Could not locate struct win32_tzmap in $tzfile!";
74$pgtz = $1;
75
76# Extract each individual record from the struct
77while ($pgtz =~
78	m/{\s+\/\*(.+?)\*\/\s+"([^"]+)",\s+"([^"]+)",\s+"([^"]+)",?\s+},/gs)
79{
80	push @file_zones,
81	  {
82		'display' => clean_displayname($1),
83		'std'     => $2,
84		'dlt'     => $3,
85		'match'   => $4,
86	  };
87}
88
89#
90# Look for anything that has changed
91#
92my @add;
93
94for my $sys (@system_zones)
95{
96	my $match = 0;
97	for my $file (@file_zones)
98	{
99		if ($sys->{std} eq $file->{std})
100		{
101			$match = 1;
102			if ($sys->{dlt} ne $file->{dlt})
103			{
104				print
105				  "Timezone $sys->{std}, changed name of daylight zone!\n";
106			}
107			if ($sys->{display} ne $file->{display})
108			{
109				print
110				  "Timezone $sys->{std} changed displayname ('$sys->{display}' from '$file->{display}')!\n";
111			}
112			last;
113		}
114	}
115	unless ($match)
116	{
117		push @add, $sys;
118	}
119}
120
121if (@add)
122{
123	print "\n\nOther than that, add the following timezones:\n";
124	for my $z (@add)
125	{
126		print
127		  "\t{\n\t\t/* $z->{display} */\n\t\t\"$z->{std}\", \"$z->{dlt}\",\n\t\t\"FIXME\"\n\t},\n";
128	}
129}
130
131sub clean_displayname
132{
133	my $dn = shift;
134
135	$dn =~ s/\*//gs;
136	$dn =~ s/\s+/ /gs;
137	$dn =~ s/^\s+//gs;
138	$dn =~ s/\s+$//gs;
139	return $dn;
140}
141