1#################################################################
2#
3# win32tzlist.pl -- compare Windows timezone information
4#
5# Copyright (c) 2008-2018, 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;
63open(my $tzfh, '<', $tzfile) or die "Could not open $tzfile!\n";
64my $t = $/;
65undef $/;
66my $pgtz = <$tzfh>;
67close($tzfh);
68$/ = $t;
69
70# Attempt to locate and extract the complete win32_tzmap struct
71$pgtz =~ /win32_tzmap\[\] =\s+{\s+\/\*[^\/]+\*\/\s+(.+?)};/gs
72  or die "Could not locate struct win32_tzmap in $tzfile!";
73$pgtz = $1;
74
75# Extract each individual record from the struct
76while ($pgtz =~
77	m/{\s+\/\*(.+?)\*\/\s+"([^"]+)",\s+"([^"]+)",\s+"([^"]+)",?\s+},/gs)
78{
79	push @file_zones,
80	  {
81		'display' => clean_displayname($1),
82		'std'     => $2,
83		'dlt'     => $3,
84		'match'   => $4,
85	  };
86}
87
88#
89# Look for anything that has changed
90#
91my @add;
92
93for my $sys (@system_zones)
94{
95	my $match = 0;
96	for my $file (@file_zones)
97	{
98		if ($sys->{std} eq $file->{std})
99		{
100			$match = 1;
101			if ($sys->{dlt} ne $file->{dlt})
102			{
103				print
104				  "Timezone $sys->{std}, changed name of daylight zone!\n";
105			}
106			if ($sys->{display} ne $file->{display})
107			{
108				print
109				  "Timezone $sys->{std} changed displayname ('$sys->{display}' from '$file->{display}')!\n";
110			}
111			last;
112		}
113	}
114	unless ($match)
115	{
116		push @add, $sys;
117	}
118}
119
120if (@add)
121{
122	print "\n\nOther than that, add the following timezones:\n";
123	for my $z (@add)
124	{
125		print
126		  "\t{\n\t\t/* $z->{display} */\n\t\t\"$z->{std}\", \"$z->{dlt}\",\n\t\t\"FIXME\"\n\t},\n";
127	}
128}
129
130sub clean_displayname
131{
132	my $dn = shift;
133
134	$dn =~ s/\*//gs;
135	$dn =~ s/\s+/ /gs;
136	$dn =~ s/^\s+//gs;
137	$dn =~ s/\s+$//gs;
138	return $dn;
139}
140