Enumerating Windows TimeZone information

Hi experts,

I'm needing to be able to enumerate the available time zone lists so that I can use the bias details to fill a lpTimeZone structure ... and I haven't a clue how to go about it.

I suppose it is possible to walk through the different registry entries in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones and then decrypt the different binary storage values, but this seems *way* too inefficient.

I've tried searching msdn for variations of EnumTimeZones and different connotations to that effect and come up with a big fat zero.

What I am ultimately trying to do is give a user a list of time zones, as can be found in Adjust Date / Time in windows, so that they can choose a different locality to display a secondary clock that is different from their own.

For example, I'm in UK and want to have a dual clock that shows my current time alongside say current time in Tokyo, JP, and take into account daylight savings and such things.

In my investigations of how i might go about this, I've found the API call, SystemTimeToTzSpecificLocalTime, which I'm sure is the way i need to proceed to get this job done. But this function needs a fully built lpTimeZone structure to be able to pass back the alternative systemtime.

Therefore, if i can enumerate windows built in timezone structures, i can pass them in as required.

I'm guessing what I need to end up with from you people is a way to generate an array of fully built timezone structures from windows own time rules.

Another problem is that I'm not at all proficient in the use of the Win API

Any help is greatly appreciated - hence the generous points on offer

Many thanks in advance

Darren
elkiorsAsked:
Who is Participating?
 
Ferruccio AccalaiConnect With a Mentor Senior developer, analyst and customer assistance Commented:
well.....Win32 SDK fil is a little bit summary.....BTW look at this example.i think you can find most of what you need in it:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,registry;

type

  TRegTZI = packed record
    Bias: Longint;
    StandardBias: Longint;
    DaylightBias: Longint;
    StandardDate: TSystemTime;
    DaylightDate: TSystemTime;
  end;
  TForm1 = class(TForm)
    Button2: TButton;
    Memo1: TMemo;
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure SHowDateTime(reg: TRegistry;memoinfo: Tmemo;standardname,DaylightName:STring);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function GetTZFromRegistry(reg: TRegistry):string;
var
  regTZI: TRegTZI;
  d: TDAteTime;
begin
  Result := '';
  if assigned(reg) then begin
    with reg do begin
        if GetDataSize('TZI') = SizeOf(regTZI) then begin
        ReadBinaryData('TZI',regTZI,SizeOf(regTZI));
        d := now + regtzi.Bias/1440;
        Result := DateTimeToSTr(d);
      end;
    end;
  end;
end; { GetTZFromRegistry}

PROCEDURE TForm1.ShowDateTime(reg: TRegistry;memoinfo: Tmemo;standardname,DaylightName:STring);
  var
  regTZI: TRegTZI;
  d: TDAteTime;
  FUNCTION DayInMonth(CONST w: WORD): STRING;
  BEGIN
    CASE w OF
      1: RESULT := '1st';
      2: RESULT := '2nd';
      3: RESULT := '3rd';
      4: RESULT := '4th';
      5: RESULT := 'Last';
      ELSE RESULT := 'Unknown'
    END
  END {DayInMonth};

BEGIN
  if assigned(reg) then begin
    with reg do begin
      if GetDataSize('TZI') = SizeOf(regTZI) then begin
        ReadBinaryData('TZI',regTZI,SizeOf(regTZI));
        with regtzi do begin
              IF   StandardDate.wYear = 0
              THEN MemoInfo.Lines.Add(' ' + StandardName + ' Starts ' +
                   DayInMonth(StandardDate.wDay) + ' ' +
                   LongDayNames[1+StandardDate.wDayofWeek] + ' of ' +
                   LongMonthNames[StandardDate.wMonth])
              ELSE MemoInfo.Lines.Add(' ' + StandardName + ' Starts ' +
                   Format('%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d',
                   [StandardDate.wMonth, StandardDate.wDay,
                   StandardDate.wYear,
                   StandardDate.wHour, StandardDate.wMinute,
                   StandardDate.wSecond]));
              IF   DaylightDate.wYear = 0
              THEN MemoInfo.Lines.Add(' ' + DaylightName + ' Starts ' +
                   DayInMonth(DaylightDate.wDay) + ' ' +
                   LongDayNames[1+DaylightDate.wDayofWeek] + ' of ' +
                   LongMonthNames[DaylightDate.wMonth])
              ELSE MemoInfo.Lines.Add(' ' + DaylightName + ' Starts ' +
                   Format('%2.2d/%2.2d/%4.4 %2.2d:%2.2d:%2.2d',
                   [DaylightDate.wMonth, DaylightDate.wDay,
                   DaylightDate.wYear,
                   DaylightDate.wHour, DaylightDate.wMinute,
                   DaylightDate.wSecond]));
         end;
      end;
  end;
  end;
END {ShowDateTime};
procedure TForm1.Button2Click(Sender: TObject);
var
  reg : TRegistry;
  ts : TStringList;
  i : integer;
begin
  reg := TRegistry.Create;
  reg.RootKey := HKEY_LOCAL_MACHINE;
  reg.OpenKey(
'SOFTWARE\Microsoft\Windows nt\CurrentVersion\Time Zones',
              false);
  if reg.HasSubKeys then begin
    ts := TStringList.Create;
    ts.sorted := True;
    reg.GetKeyNames(ts);
    reg.CloseKey;

    for i := 0 to ts.Count -1 do begin
      reg.OpenKey(
  'SOFTWARE\Microsoft\Windows nt\CurrentVersion\Time Zones\' +
        ts.Strings[i],
      false);
      Memo1.Lines.Add(ts.Strings[i]);
      Memo1.Lines.Add(reg.ReadString('Display'));
      ShowDatetime(reg, memo1,reg.ReadString('Std'),reg.ReadString('Dlt'));
      memo1.lines.add(GetTZFromRegistry(reg));
      Memo1.Lines.Add('----------------------');
      reg.CloseKey;
    end;
    ts.Free;
  end else
  reg.CloseKey;
  reg.free;
end;

end.

F68 ;-)
0
 
Greg RowlandSoftware Designer, SysDBA, WebMaster OwnerCommented:
Win32 SDK Help file, Find zone.

I believe everything you could want is documented there.
0
 
elkiorsAuthor Commented:
Thanks Ferruccio68 that's pretty much all that I needed, it seems there isn't a standard enumeration function in the api afterall - at least now i know i'm not going mad.

However, I did notice that your code brings up quite a few "Unknown Day of Dec" entries - does this imply that there is something wrong with the parsing of the reg data or does this indicate that the particular timezone doesn't employ any daylight saving rules ? It's confusing for me.

My fault, not yours ;)

If you can clear just that one little quirk up for me so that I can begin to understand your code and my options for manipulating the reg data then the points are yours ;)

thanks in advance

Darren
0
Cloud Class® Course: SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
Well, in fact there's not a direct enumeration...btw you can check the tzi infos for the assigned tzi using windows.GetTimeZoneInformation.

About that 'Unknowns' note that the DayLight system is not applied in all Countries in the World, so it means that in that zone there's not a DayLight value (not time changes during the Year)...

For more code example you can also look here

http://17slon.com/gp/gp/files/gptimezone.htm

F68 ;-)
0
 
elkiorsAuthor Commented:
Thanx F68,

that clears things up for me nicely.

I now have more than enough info to be able to work through it all a bit more and get to grips with.

Thanks again.

Darren
0
 
Greg RowlandSoftware Designer, SysDBA, WebMaster OwnerCommented:
Great job F68
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.