mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-25 17:19:15 +02:00
429 lines
8.6 KiB
PHP
429 lines
8.6 KiB
PHP
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2002 by the Free Pascal development team.
|
|
|
|
Timezone extraction routines
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
|
|
const
|
|
TZ_MAGIC = 'TZif';
|
|
|
|
type
|
|
plongint=^longint;
|
|
pbyte=^byte;
|
|
|
|
ttzhead=packed record
|
|
tzh_magic : array[0..3] of char;
|
|
tzh_reserved : array[1..16] of byte;
|
|
tzh_ttisgmtcnt,
|
|
tzh_ttisstdcnt,
|
|
tzh_leapcnt,
|
|
tzh_timecnt,
|
|
tzh_typecnt,
|
|
tzh_charcnt : longint;
|
|
end;
|
|
|
|
pttinfo=^tttinfo;
|
|
tttinfo=packed record
|
|
offset : longint;
|
|
isdst : boolean;
|
|
idx : byte;
|
|
isstd : byte;
|
|
isgmt : byte;
|
|
end;
|
|
|
|
pleap=^tleap;
|
|
tleap=record
|
|
transition : longint;
|
|
change : longint;
|
|
end;
|
|
|
|
var
|
|
num_transitions,
|
|
num_leaps,
|
|
num_types : longint;
|
|
|
|
transitions : plongint;
|
|
type_idxs : pbyte;
|
|
types : pttinfo;
|
|
zone_names : pchar;
|
|
leaps : pleap;
|
|
|
|
function find_transition(timer:time_t):pttinfo;
|
|
var
|
|
i : longint;
|
|
begin
|
|
if (num_transitions=0) or (timer<time_t(transitions[0])) then
|
|
begin
|
|
i:=0;
|
|
while (i<num_types) and (types[i].isdst) do
|
|
inc(i);
|
|
if (i=num_types) then
|
|
i:=0;
|
|
end
|
|
else
|
|
begin
|
|
for i:=1 to num_transitions do
|
|
if (timer<transitions[i]) then
|
|
break;
|
|
i:=type_idxs[i-1];
|
|
end;
|
|
find_transition:=@types[i];
|
|
end;
|
|
|
|
|
|
procedure GetLocalTimezone(timer:time_t;var leap_correct,leap_hit:longint);
|
|
var
|
|
info : pttinfo;
|
|
i : longint;
|
|
begin
|
|
{ reset }
|
|
TZDaylight:=false;
|
|
TZSeconds:=0;
|
|
TZName[false]:=nil;
|
|
TZName[true]:=nil;
|
|
leap_correct:=0;
|
|
leap_hit:=0;
|
|
{ get info }
|
|
info:=find_transition(timer);
|
|
if not assigned(info) then
|
|
exit;
|
|
TZDaylight:=info^.isdst;
|
|
TZSeconds:=info^.offset;
|
|
i:=0;
|
|
while (i<num_types) do
|
|
begin
|
|
tzname[types[i].isdst]:=@zone_names[types[i].idx];
|
|
inc(i);
|
|
end;
|
|
tzname[info^.isdst]:=@zone_names[info^.idx];
|
|
i:=num_leaps;
|
|
repeat
|
|
if i=0 then
|
|
exit;
|
|
dec(i);
|
|
until (timer>leaps[i].transition);
|
|
leap_correct:=leaps[i].change;
|
|
if (timer=leaps[i].transition) and
|
|
(((i=0) and (leaps[i].change>0)) or
|
|
(leaps[i].change>leaps[i-1].change)) then
|
|
begin
|
|
leap_hit:=1;
|
|
while (i>0) and
|
|
(leaps[i].transition=leaps[i-1].transition+1) and
|
|
(leaps[i].change=leaps[i-1].change+1) do
|
|
begin
|
|
inc(leap_hit);
|
|
dec(i);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure GetLocalTimezone(timer:longint);
|
|
var
|
|
lc,lh : longint;
|
|
begin
|
|
GetLocalTimezone(timer,lc,lh);
|
|
end;
|
|
|
|
|
|
procedure ReadTimezoneFile(fn:string);
|
|
|
|
procedure decode(var l:longint);
|
|
var
|
|
k : longint;
|
|
p : pbyte;
|
|
begin
|
|
p:=pbyte(@l);
|
|
if (p[0] and (1 shl 7))<>0 then
|
|
k:=not 0
|
|
else
|
|
k:=0;
|
|
k:=(k shl 8) or p[0];
|
|
k:=(k shl 8) or p[1];
|
|
k:=(k shl 8) or p[2];
|
|
k:=(k shl 8) or p[3];
|
|
l:=k;
|
|
end;
|
|
|
|
var
|
|
f : File;
|
|
tzdir : string;
|
|
tzhead : ttzhead;
|
|
i : longint;
|
|
chars : longint;
|
|
buf : pbyte;
|
|
_result : longint;
|
|
label lose;
|
|
begin
|
|
if fn = '' then
|
|
exit;
|
|
{$IFOPT I+}
|
|
{$DEFINE IOCHECK_ON}
|
|
{$ENDIF}
|
|
{$I-}
|
|
Assign(F, fn);
|
|
Reset(F,1);
|
|
If IOResult <> 0 then
|
|
exit;
|
|
{$IFDEF IOCHECK_ON}
|
|
{$I+}
|
|
{$ENDIF}
|
|
{$UNDEF IOCHECK_ON}
|
|
BlockRead(f,tzhead,sizeof(tzhead),i);
|
|
if i<>sizeof(tzhead) then
|
|
goto lose;
|
|
if tzhead.tzh_magic<>TZ_MAGIC then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
decode(tzhead.tzh_timecnt);
|
|
decode(tzhead.tzh_typecnt);
|
|
decode(tzhead.tzh_charcnt);
|
|
decode(tzhead.tzh_leapcnt);
|
|
decode(tzhead.tzh_ttisstdcnt);
|
|
decode(tzhead.tzh_ttisgmtcnt);
|
|
|
|
num_transitions:=tzhead.tzh_timecnt;
|
|
num_types:=tzhead.tzh_typecnt;
|
|
chars:=tzhead.tzh_charcnt;
|
|
|
|
reallocmem(transitions,num_transitions*sizeof(longint));
|
|
reallocmem(type_idxs,num_transitions);
|
|
reallocmem(types,num_types*sizeof(tttinfo));
|
|
reallocmem(zone_names,chars);
|
|
reallocmem(leaps,num_leaps*sizeof(tleap));
|
|
|
|
BlockRead(f,transitions^,num_transitions*4,_result);
|
|
if _result <> num_transitions*4 then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
BlockRead(f,type_idxs^,num_transitions,_result);
|
|
if _result <> num_transitions then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
{* Check for bogus indices in the data file, so we can hereafter
|
|
safely use type_idxs[T] as indices into `types' and never crash. *}
|
|
for i := 0 to num_transitions-1 do
|
|
if (type_idxs[i] >= num_types) then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
|
|
|
|
for i:=0 to num_transitions-1 do
|
|
decode(transitions[i]);
|
|
|
|
for i:=0 to num_types-1 do
|
|
begin
|
|
blockread(f,types[i].offset,4,_result);
|
|
if _result <> 4 then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
blockread(f,types[i].isdst,1,_result);
|
|
if _result <> 1 then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
blockread(f,types[i].idx,1,_result);
|
|
if _result <> 1 then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
decode(types[i].offset);
|
|
types[i].isstd:=0;
|
|
types[i].isgmt:=0;
|
|
end;
|
|
|
|
blockread(f,zone_names^,chars,_result);
|
|
if _result<>chars then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
|
|
|
|
for i:=0 to num_leaps-1 do
|
|
begin
|
|
blockread(f,leaps[i].transition,4);
|
|
if _result <> 4 then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
blockread(f,leaps[i].change,4);
|
|
begin
|
|
goto lose;
|
|
end;
|
|
decode(leaps[i].transition);
|
|
decode(leaps[i].change);
|
|
end;
|
|
|
|
getmem(buf,tzhead.tzh_ttisstdcnt);
|
|
blockread(f,buf^,tzhead.tzh_ttisstdcnt,_result);
|
|
if _result<>tzhead.tzh_ttisstdcnt then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
for i:=0 to tzhead.tzh_ttisstdcnt-1 do
|
|
types[i].isstd:=byte(buf[i]<>0);
|
|
freemem(buf);
|
|
|
|
getmem(buf,tzhead.tzh_ttisgmtcnt);
|
|
blockread(f,buf^,tzhead.tzh_ttisgmtcnt);
|
|
if _result<>tzhead.tzh_ttisgmtcnt then
|
|
begin
|
|
goto lose;
|
|
end;
|
|
for i:=0 to tzhead.tzh_ttisgmtcnt-1 do
|
|
types[i].isgmt:=byte(buf[i]<>0);
|
|
freemem(buf);
|
|
close(f);
|
|
exit;
|
|
lose:
|
|
close(f);
|
|
end;
|
|
|
|
|
|
{ help function to extract TZ variable data }
|
|
function extractnumberend(tzstr: string; offset : integer): integer;
|
|
var
|
|
j: integer;
|
|
begin
|
|
j:=0;
|
|
extractnumberend := 0;
|
|
repeat
|
|
if (offset+j) > length(tzstr) then
|
|
begin
|
|
exit;
|
|
end;
|
|
inc(j);
|
|
until not (tzstr[offset+j] in ['0'..'9']);
|
|
extractnumberend := offset+j;
|
|
end;
|
|
|
|
function getoffsetseconds(tzstr: string): longint;
|
|
{ extract GMT timezone information }
|
|
{ Returns the number of minutes to }
|
|
{ add or subtract to the GMT time }
|
|
{ to get the local time. }
|
|
{ Format of TZ variable (POSIX) }
|
|
{ std offset dst }
|
|
{ std = characters of timezone }
|
|
{ offset = hh[:mm] to add to GMT }
|
|
{ dst = daylight savings time }
|
|
{ CURRENTLY DOES NOT TAKE CARE }
|
|
{ OF SUMMER TIME DIFFERENCIAL }
|
|
var
|
|
s: string;
|
|
i, j: integer;
|
|
code : integer;
|
|
hours : longint;
|
|
minutes : longint;
|
|
negative : boolean;
|
|
begin
|
|
hours:=0;
|
|
minutes:=0;
|
|
getoffsetseconds := 0;
|
|
negative := FALSE;
|
|
i:=-1;
|
|
{ get to offset field }
|
|
repeat
|
|
if i > length(tzstr) then
|
|
begin
|
|
exit;
|
|
end;
|
|
inc(i);
|
|
until (tzstr[i] = '-') or (tzstr[i] in ['0'..'9']);
|
|
if tzstr[i] = '-' then
|
|
begin
|
|
Inc(i);
|
|
negative := TRUE;
|
|
end;
|
|
j:=extractnumberend(tzstr,i);
|
|
s:=copy(tzstr,i,j-i);
|
|
val(s,hours,code);
|
|
if code <> 0 then
|
|
begin
|
|
exit;
|
|
end;
|
|
if tzstr[j] = ':' then
|
|
begin
|
|
i:=j;
|
|
Inc(i);
|
|
j:=extractnumberend(tzstr,i);
|
|
s:=copy(tzstr,i,j-i);
|
|
val(s,minutes,code);
|
|
if code <> 0 then
|
|
begin
|
|
exit;
|
|
end;
|
|
end;
|
|
if negative then
|
|
begin
|
|
minutes := -minutes;
|
|
hours := -hours;
|
|
end;
|
|
getoffsetseconds := minutes*60 + hours*3600;
|
|
end;
|
|
|
|
|
|
procedure InitLocalTime;
|
|
var
|
|
tloc: time_t;
|
|
s : string;
|
|
begin
|
|
TZSeconds:=0;
|
|
{ try to get the POSIX version }
|
|
{ of the local time offset }
|
|
{ if '', then it does not exist }
|
|
{ if ': ..', then non-POSIX }
|
|
s:=GetTimezoneString;
|
|
if (s<>'') and (s[1]<>':') then
|
|
begin
|
|
TZSeconds := getoffsetseconds(s);
|
|
end
|
|
else
|
|
begin
|
|
s:=GetTimeZoneFile;
|
|
{ only read if there is something to read }
|
|
if s<>'' then
|
|
begin
|
|
ReadTimezoneFile(s);
|
|
tloc:=sys_time(tloc);
|
|
GetLocalTimezone(tloc);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure DoneLocalTime;
|
|
begin
|
|
if assigned(transitions) then
|
|
freemem(transitions);
|
|
if assigned(type_idxs) then
|
|
freemem(type_idxs);
|
|
if assigned(types) then
|
|
freemem(types);
|
|
if assigned(zone_names) then
|
|
freemem(zone_names);
|
|
if assigned(leaps) then
|
|
freemem(leaps);
|
|
num_transitions:=0;
|
|
num_leaps:=0;
|
|
num_types:=0;
|
|
end;
|
|
|
|
|
|
|