1package DateTime::TimeZone::Local::Win32;
2
3use strict;
4use warnings;
5
6use base 'DateTime::TimeZone::Local';
7
8use Win32::TieRegistry ( 'KEY_READ', Delimiter => q{/} );
9
10
11sub Methods { return qw( FromEnv FromRegistry ) }
12
13sub EnvVars { return 'TZ' }
14
15{
16    # This list comes (mostly) in the zipball for the Chronos project
17    # - a Smalltalk datetime library. Thanks, Chronos!
18    my %WinToOlson =
19        ( 'Afghanistan'                     => 'Asia/Kabul',
20          'Afghanistan Standard Time'       => 'Asia/Kabul',
21          'Alaskan'                         => 'America/Anchorage',
22          'Alaskan Standard Time'           => 'America/Anchorage',
23          'Arab'                            => 'Asia/Riyadh',
24          'Arab Standard Time'              => 'Asia/Riyadh',
25          'Arabian'                         => 'Asia/Muscat',
26          'Arabian Standard Time'           => 'Asia/Muscat',
27          'Arabic Standard Time'            => 'Asia/Baghdad',
28          'Argentina Standard Time'         => 'America/Argentina/Buenos_Aires',
29          'Armenian Standard Time'          => 'Asia/Yerevan',
30          'Atlantic'                        => 'America/Halifax',
31          'Atlantic Standard Time'          => 'America/Halifax',
32          'AUS Central'                     => 'Australia/Darwin',
33          'AUS Central Standard Time'       => 'Australia/Darwin',
34          'AUS Eastern'                     => 'Australia/Sydney',
35          'AUS Eastern Standard Time'       => 'Australia/Sydney',
36          'Azerbaijan Standard Time'        => 'Asia/Baku',
37          'Azores'                          => 'Atlantic/Azores',
38          'Azores Standard Time'            => 'Atlantic/Azores',
39          'Bangkok'                         => 'Asia/Bangkok',
40          'Bangkok Standard Time'           => 'Asia/Bangkok',
41          'Beijing'                         => 'Asia/Shanghai',
42          'Canada Central'                  => 'America/Regina',
43          'Canada Central Standard Time'    => 'America/Regina',
44          'Cape Verde Standard Time'        => 'Atlantic/Cape_Verde',
45          'Caucasus'                        => 'Asia/Yerevan',
46          'Caucasus Standard Time'          => 'Asia/Yerevan',
47          'Cen. Australia'                  => 'Australia/Adelaide',
48          'Cen. Australia Standard Time'    => 'Australia/Adelaide',
49          'Central'                         => 'America/Chicago',
50          'Central America Standard Time'   => 'America/Regina',
51          'Central Asia'                    => 'Asia/Dhaka',
52          'Central Asia Standard Time'      => 'Asia/Dhaka',
53          'Central Brazilian Standard Time' => 'America/Manaus',
54          'Central Europe'                  => 'Europe/Prague',
55          'Central Europe Standard Time'    => 'Europe/Prague',
56          'Central European'                => 'Europe/Belgrade',
57          'Central European Standard Time'  => 'Europe/Belgrade',
58          'Central Pacific'                 => 'Pacific/Guadalcanal',
59          'Central Pacific Standard Time'   => 'Pacific/Guadalcanal',
60          'Central Standard Time'           => 'America/Chicago',
61          'Central Standard Time (Mexico)'  => 'America/Mexico_City',
62          'China'                           => 'Asia/Shanghai',
63          'China Standard Time'             => 'Asia/Shanghai',
64          'Dateline'                        => 'Pacific/Majuro',
65          'Dateline Standard Time'          => 'Pacific/Majuro',
66          'E. Africa'                       => 'Africa/Nairobi',
67          'E. Africa Standard Time'         => 'Africa/Nairobi',
68          'E. Australia'                    => 'Australia/Brisbane',
69          'E. Australia Standard Time'      => 'Australia/Brisbane',
70          'E. Europe'                       => 'Europe/Minsk',
71          'E. Europe Standard Time'         => 'Europe/Minsk',
72          'E. South America'                => 'America/Sao_Paulo',
73          'E. South America Standard Time'  => 'America/Sao_Paulo',
74          'Eastern'                         => 'America/New_York',
75          'Eastern Standard Time'           => 'America/New_York',
76          'Egypt'                           => 'Africa/Cairo',
77          'Egypt Standard Time'             => 'Africa/Cairo',
78          'Ekaterinburg'                    => 'Asia/Yekaterinburg',
79          'Ekaterinburg Standard Time'      => 'Asia/Yekaterinburg',
80          'Fiji'                            => 'Pacific/Fiji',
81          'Fiji Standard Time'              => 'Pacific/Fiji',
82          'FLE'                             => 'Europe/Helsinki',
83          'FLE Standard Time'               => 'Europe/Helsinki',
84          'Georgian Standard Time'          => 'Asia/Tbilisi',
85          'GFT'                             => 'Europe/Athens',
86          'GFT Standard Time'               => 'Europe/Athens',
87          'GMT'                             => 'Europe/London',
88          'GMT Standard Time'               => 'Europe/London',
89          'Greenland Standard Time'         => 'America/Godthab',
90          'Greenwich'                       => 'GMT',
91          'Greenwich Standard Time'         => 'GMT',
92          'GTB'                             => 'Europe/Athens',
93          'GTB Standard Time'               => 'Europe/Athens',
94          'Hawaiian'                        => 'Pacific/Honolulu',
95          'Hawaiian Standard Time'          => 'Pacific/Honolulu',
96          'India'                           => 'Asia/Calcutta',
97          'India Standard Time'             => 'Asia/Calcutta',
98          'Iran'                            => 'Asia/Tehran',
99          'Iran Standard Time'              => 'Asia/Tehran',
100          'Israel'                          => 'Asia/Jerusalem',
101          'Israel Standard Time'            => 'Asia/Jerusalem',
102          'Jordan Standard Time'            => 'Asia/Amman',
103          'Kamchatka Standard Time'         => 'Asia/Kamchatka',
104          'Korea'                           => 'Asia/Seoul',
105          'Korea Standard Time'             => 'Asia/Seoul',
106          'Mauritius Standard Time'         => 'Indian/Mauritius',
107          'Mexico'                          => 'America/Mexico_City',
108          'Mexico Standard Time'            => 'America/Mexico_City',
109          'Mexico Standard Time 2'          => 'America/Chihuahua',
110          'Mid-Atlantic'                    => 'Atlantic/South_Georgia',
111          'Mid-Atlantic Standard Time'      => 'Atlantic/South_Georgia',
112          'Middle East Standard Time'       => 'Asia/Beirut',
113          'Montevideo Standard Time'        => 'America/Montevideo',
114          'Morocco Standard Time'           => 'Africa/Casablanca',
115          'Mountain'                        => 'America/Denver',
116          'Mountain Standard Time'          => 'America/Denver',
117          'Mountain Standard Time (Mexico)' => 'America/Chihuahua',
118          'Myanmar Standard Time'           => 'Asia/Rangoon',
119          'N. Central Asia Standard Time'   => 'Asia/Novosibirsk',
120          'Namibia Standard Time'           => 'Africa/Windhoek',
121          'Nepal Standard Time'             => 'Asia/Katmandu',
122          'New Zealand'                     => 'Pacific/Auckland',
123          'New Zealand Standard Time'       => 'Pacific/Auckland',
124          'Newfoundland'                    => 'America/St_Johns',
125          'Newfoundland Standard Time'      => 'America/St_Johns',
126          'North Asia East Standard Time'   => 'Asia/Irkutsk',
127          'North Asia Standard Time'        => 'Asia/Krasnoyarsk',
128          'Pacific'                         => 'America/Los_Angeles',
129          'Pacific SA'                      => 'America/Santiago',
130          'Pacific SA Standard Time'        => 'America/Santiago',
131          'Pacific Standard Time'           => 'America/Los_Angeles',
132          'Pacific Standard Time (Mexico)'  => 'America/Tijuana',
133          'Pakistan Standard Time'          => 'Asia/Karachi',
134          'Paraguay Standard Time'          => 'America/Asuncion',
135          'Prague Bratislava'               => 'Europe/Prague',
136          'Romance'                         => 'Europe/Paris',
137          'Romance Standard Time'           => 'Europe/Paris',
138          'Russian'                         => 'Europe/Moscow',
139          'Russian Standard Time'           => 'Europe/Moscow',
140          'SA Eastern'                      => 'America/Buenos_Aires',
141          'SA Eastern Standard Time'        => 'America/Buenos_Aires',
142          'SA Pacific'                      => 'America/Bogota',
143          'SA Pacific Standard Time'        => 'America/Bogota',
144          'SA Western'                      => 'America/Caracas',
145          'SA Western Standard Time'        => 'America/Caracas',
146          'Samoa'                           => 'Pacific/Apia',
147          'Samoa Standard Time'             => 'Pacific/Apia',
148          'Saudi Arabia'                    => 'Asia/Riyadh',
149          'Saudi Arabia Standard Time'      => 'Asia/Riyadh',
150          'SE Asia'                         => 'Asia/Bangkok',
151          'SE Asia Standard Time'           => 'Asia/Bangkok',
152          'Singapore'                       => 'Asia/Singapore',
153          'Singapore Standard Time'         => 'Asia/Singapore',
154          'South Africa'                    => 'Africa/Harare',
155          'South Africa Standard Time'      => 'Africa/Harare',
156          'Sri Lanka'                       => 'Asia/Colombo',
157          'Sri Lanka Standard Time'         => 'Asia/Colombo',
158          'Sydney Standard Time'            => 'Australia/Sydney',
159          'Taipei'                          => 'Asia/Taipei',
160          'Taipei Standard Time'            => 'Asia/Taipei',
161          'Tasmania'                        => 'Australia/Hobart',
162          'Tasmania Standard Time'          => 'Australia/Hobart',
163          'Tokyo'                           => 'Asia/Tokyo',
164          'Tokyo Standard Time'             => 'Asia/Tokyo',
165          'Tonga Standard Time'             => 'Pacific/Tongatapu',
166          'Ulaanbaatar Standard Time'       => 'Asia/Ulaanbaatar',
167          'US Eastern'                      => 'America/Indianapolis',
168          'US Eastern Standard Time'        => 'America/Indianapolis',
169          'US Mountain'                     => 'America/Phoenix',
170          'US Mountain Standard Time'       => 'America/Phoenix',
171          'UTC'                             => 'UTC',
172          'Venezuela Standard Time'         => 'America/Caracas',
173          'Vladivostok'                     => 'Asia/Vladivostok',
174          'Vladivostok Standard Time'       => 'Asia/Vladivostok',
175          'W. Australia'                    => 'Australia/Perth',
176          'W. Australia Standard Time'      => 'Australia/Perth',
177          'W. Central Africa Standard Time' => 'Africa/Luanda',
178          'W. Europe'                       => 'Europe/Berlin',
179          'W. Europe Standard Time'         => 'Europe/Berlin',
180          'Warsaw'                          => 'Europe/Warsaw',
181          'West Asia'                       => 'Asia/Karachi',
182          'West Asia Standard Time'         => 'Asia/Karachi',
183          'West Pacific'                    => 'Pacific/Guam',
184          'West Pacific Standard Time'      => 'Pacific/Guam',
185          'Western Brazilian Standard Time' => 'America/Rio_Branco',
186          'Yakutsk'                         => 'Asia/Yakutsk',
187          'Yakutsk Standard Time'           => 'Asia/Yakutsk',
188        );
189
190    sub FromRegistry
191    {
192        my $class = shift;
193
194        my $win_name = $class->_FindWindowsTZName();
195
196        # On Windows 2008 Server, there is additional junk after a
197        # null character.
198        $win_name =~ s/\0.*$//
199            if defined $win_name;
200
201        return unless defined $win_name;
202
203        my $olson = $WinToOlson{$win_name};
204
205        return unless $olson;
206
207        return unless $class->_IsValidName($olson);
208
209        local $@;
210        local $SIG{__DIE__};
211        return eval { DateTime::TimeZone->new( name => $olson ) };
212    }
213}
214
215sub _FindWindowsTZName
216{
217    my $class = shift;
218
219    my $LMachine = $Registry->Open( 'LMachine/', { Access => KEY_READ } );
220
221    my $TimeZoneInfo =
222        $LMachine->{'SYSTEM/CurrentControlSet/Control/TimeZoneInformation/'};
223
224    # Windows Vista, Windows 2008 Server
225    if ( defined $TimeZoneInfo->{'/TimeZoneKeyName'} &&
226         $TimeZoneInfo->{'/TimeZoneKeyName'} ne '' )
227    {
228        return $TimeZoneInfo->{'/TimeZoneKeyName'};
229    }
230    else
231    {
232        my $AllTimeZones =
233            $LMachine->{'SOFTWARE/Microsoft/Windows NT/CurrentVersion/Time Zones/'}
234            # Windows NT, Windows 2000, Windows XP, Windows 2003 Server
235            ? $LMachine->{'SOFTWARE/Microsoft/Windows NT/CurrentVersion/Time Zones/'}
236            # Windows 95, Windows 98, Windows Millenium Edition
237            : $LMachine->{'SOFTWARE/Microsoft/Windows/CurrentVersion/Time Zones/'};
238
239        foreach my $zone ( $AllTimeZones->SubKeyNames() )
240        {
241            if ( $AllTimeZones->{ $zone . '/Std' } eq $TimeZoneInfo->{'/StandardName'} )
242            {
243                return $zone;
244            }
245        }
246    }
247
248    return;
249}
250
251
2521;
253
254__END__
255
256=head1 NAME
257
258DateTime::TimeZone::Local::Win32 - Determine the local system's time zone on Windows
259
260=head1 SYNOPSIS
261
262  my $tz = DateTime::TimeZone->new( name => 'local' );
263
264  my $tz = DateTime::TimeZone::Local->TimeZone();
265
266=head1 DESCRIPTION
267
268This module provides methods for determining the local time zone on a
269Windows platform.
270
271=head1 HOW THE TIME ZONE IS DETERMINED
272
273This class tries the following methods of determining the local time
274zone:
275
276=over 4
277
278=item * $ENV{TZ}
279
280It checks C<< $ENV{TZ} >> for a valid time zone name.
281
282=item * Windows Registry
283
284When using the registry, we look for the Windows time zone and use a
285mapping to translate this to an Olson time zone name.
286
287=over 8
288
289=item * Windows Vista and 2008
290
291We look in "SYSTEM/CurrentControlSet/Control/TimeZoneInformation/" for
292a node named "/TimeZoneKeyName". If this exists, we use this key to
293look up the Olson time zone name in our mapping.
294
295=item * Windows NT, Windows 2000, Windows XP, Windows 2003 Server
296
297We look in "SOFTWARE/Microsoft/Windows NT/CurrentVersion/Time Zones/"
298and loop through all of its sub keys.
299
300For each sub key, we compare the value of the key with "/Std" appended
301to the end to the value of
302"SYSTEM/CurrentControlSet/Control/TimeZoneInformation/StandardName". This
303gives us the I<English> name of the Windows time zone, which we use to
304look up the Olson time zone name.
305
306=item * Windows 95, Windows 98, Windows Millenium Edition
307
308The algorithm is the same as for NT, but we loop through the sub keys
309of "SOFTWARE/Microsoft/Windows/CurrentVersion/Time Zones/"
310
311=back
312
313=back
314
315=head1 AUTHOR
316
317Dave Rolsky, <autarch@urth.org>
318
319=head1 COPYRIGHT & LICENSE
320
321Copyright (c) 2003-2008 David Rolsky.  All rights reserved.  This
322program is free software; you can redistribute it and/or modify it
323under the same terms as Perl itself.
324
325The full text of the license can be found in the LICENSE file included
326with this module.
327
328=cut
329