#################################################################
#
# win32tzlist.pl -- compare Windows timezone information
#
# Copyright (c) 2008-2025, PostgreSQL Global Development Group
#
# src/tools/win32tzlist.pl
#################################################################

#
# This script compares the timezone information in the Windows registry
# with that in src/bin/initdb/findtimezone.c.  A list of changes will be
# written to stdout - no attempt is made to automatically edit the file.
#
# Run the script from the top-level PG source directory.
#

use strict;
use warnings FATAL => 'all';

use Config;

our $HKEY_LOCAL_MACHINE;

BEGIN
{
	if ($Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys')
	{
		require Win32::Registry;
		Win32::Registry->import;
	}
}

my $tzfile = 'src/bin/initdb/findtimezone.c';

#
# Fetch all timezones in the registry
#
my $basekey;
$HKEY_LOCAL_MACHINE->Open(
	"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", $basekey)
  or die $!;

my @subkeys;
$basekey->GetKeys(\@subkeys);

my @system_zones;

foreach my $keyname (@subkeys)
{
	my $subkey;
	my %vals;

	$basekey->Open($keyname, $subkey) or die $!;
	$subkey->GetValues(\%vals) or die $!;
	$subkey->Close();

	die "Incomplete timezone data for $keyname!\n"
	  unless ($vals{Std} && $vals{Dlt} && $vals{Display});
	push @system_zones,
	  {
		'std' => $vals{Std}->[2],
		'dlt' => $vals{Dlt}->[2],
		'display' => clean_displayname($vals{Display}->[2]),
	  };
}

$basekey->Close();

#
# Fetch all timezones currently in the file
#
my @file_zones;
my $pgtz;
open(my $tzfh, '<', $tzfile) or die "Could not open $tzfile!\n";
{
	local $/ = undef;
	$pgtz = <$tzfh>;
}
close($tzfh);

# Attempt to locate and extract the complete win32_tzmap struct
$pgtz =~ /win32_tzmap\[\] =\s+{\s+\/\*[^\/]+\*\/\s+(.+?)};/gs
  or die "Could not locate struct win32_tzmap in $tzfile!";
$pgtz = $1;

# Extract each individual record from the struct
while ($pgtz =~
	m/{\s+\/\*(.+?)\*\/\s+"([^"]+)",\s+"([^"]+)",\s+"([^"]+)",?\s+},/gs)
{
	push @file_zones,
	  {
		'display' => clean_displayname($1),
		'std' => $2,
		'dlt' => $3,
		'match' => $4,
	  };
}

#
# Look for anything that has changed
#
my @add;

for my $sys (@system_zones)
{
	my $match = 0;
	for my $file (@file_zones)
	{
		if ($sys->{std} eq $file->{std})
		{
			$match = 1;
			if ($sys->{dlt} ne $file->{dlt})
			{
				print
				  "Timezone $sys->{std}, changed name of daylight zone!\n";
			}
			if ($sys->{display} ne $file->{display})
			{
				print
				  "Timezone $sys->{std} changed displayname ('$sys->{display}' from '$file->{display}')!\n";
			}
			last;
		}
	}
	unless ($match)
	{
		push @add, $sys;
	}
}

if (@add)
{
	print "\n\nOther than that, add the following timezones:\n";
	for my $z (@add)
	{
		print
		  "\t{\n\t\t/* $z->{display} */\n\t\t\"$z->{std}\", \"$z->{dlt}\",\n\t\t\"FIXME\"\n\t},\n";
	}
}

sub clean_displayname
{
	my $dn = shift;

	$dn =~ s/\*//gs;
	$dn =~ s/\s+/ /gs;
	$dn =~ s/^\s+//gs;
	$dn =~ s/\s+$//gs;
	return $dn;
}
