| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 | {  Support for timezone info in /usr/share/timezone}type  ttzhead=packed record    tzh_identifier : array[0..3] of AnsiChar;    tzh_version : AnsiChar;    tzh_reserved : array[0..14] 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 : int64;    change     : longint;  end;var  num_transitions,  num_leaps,  num_types    : longint;  transitions  : PInt64 = nil;  type_idxs    : pbyte = Nil;  types        : pttinfo = Nil;  zone_names   : PAnsiChar = Nil;  leaps        : pleap = Nil;function find_transition(timer:int64;timerIsUTC:Boolean;var trans_start,trans_end:int64):pttinfo;var  i,L,R,CompareRes : longint;  found : boolean;  function DoCompare: longint;  var    timerUTC: int64;  begin    if not timerIsUTC then      timerUTC:=timer-types[type_idxs[i-1]].offset    else      timerUTC:=timer;    if timerUTC<transitions[i-1] then      Exit(-1)    else    if timerUTC>=transitions[i] then      Exit(1)    else      Exit(0);  end;var  timerLoUTC, timerHiUTC: int64;begin  if (num_transitions>0) and not timerIsUTC then   begin     timerLoUTC:=timer-types[type_idxs[0]].offset;     timerHiUTC:=timer-types[type_idxs[num_transitions-1]].offset;   end  else   begin     timerLoUTC:=timer;     timerHiUTC:=timer;   end;  if (num_transitions=0) or (timerLoUTC<transitions[0]) then   { timer is before the first transition }   begin     i:=0;     while (i<num_types) and (types[i].isdst) do      inc(i);     if (i=num_types) then      i:=0;     { unknown transition boundaries }     trans_start:=low(trans_start);     trans_end:=high(trans_end);   end  else  if (num_transitions>0) and (timerHiUTC>=transitions[num_transitions-1]) then   { timer is after the last transition }   begin     i:=type_idxs[num_transitions-1];     trans_start:=transitions[num_transitions-1];     trans_end:=high(trans_end);   end  else   { timer inbetween }   begin      // Use binary search.      L := 1;      R := num_transitions-1;      found := false;      while not found and (L<=R) do      begin        I := L + (R - L) div 2;        CompareRes := DoCompare;        if (CompareRes>0) then          L := I+1        else begin          R := I-1;          if (CompareRes=0) then             found:=true; // break cycle        end;      end;     if not found then       Exit(nil);     trans_start:=transitions[i-1];     trans_end:=transitions[i];     i:=type_idxs[i-1];   end;  find_transition:=@types[i];end;procedure DoGetLocalTimezone(info:pttinfo;const trans_start,trans_end:int64;var ATZInfo:TTZInfo);begin  ATZInfo.validsince:=trans_start;  ATZInfo.validuntil:=trans_end;  ATZInfo.Daylight:=info^.isdst;  ATZInfo.Seconds:=info^.offset;end;procedure DoGetLocalTimezoneEx(timer:int64;info:pttinfo;var ATZInfoEx:TTZInfoEx);var  i : longint;  names: array[Boolean] of PAnsiChar;begin  names[true]:=nil;  names[false]:=nil;  ATZInfoEx.leap_hit:=0;  ATZInfoEx.leap_correct:=0;  i:=0;  while (i<num_types) do   begin     names[types[i].isdst]:=@zone_names[types[i].idx];     inc(i);   end;  names[info^.isdst]:=@zone_names[info^.idx];  ATZInfoEx.name[true]:=names[true];  ATZInfoEx.name[false]:=names[false];  i:=num_leaps;  repeat    if i=0 then     exit;    dec(i);  until (timer>leaps[i].transition);  ATZInfoEx.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     ATZInfoEx.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(ATZInfoEx.leap_hit);        dec(i);      end;   end;end;function GetLocalTimezone(timer:int64;timerIsUTC:Boolean;var ATZInfo:TTZInfo):Boolean;var  info: pttinfo;  trans_start,trans_end,timerUTC: int64;begin  { check if time is in current global Tzinfo }  ATZInfo:=CurrentTZinfo[InterlockedExchangeAdd(CurrentTZindex, 0)];  if not timerIsUTC then    timerUTC:=timer-ATZInfo.seconds  else    timerUTC:=timer;  if (ATZInfo.validsince<=timerUTC) and (timerUTC<ATZInfo.validuntil) then    Exit(True);  LockTZInfo;  info:=find_transition(timer,timerIsUTC,trans_start,trans_end);  GetLocalTimezone:=assigned(info);  if GetLocalTimezone then    DoGetLocalTimezone(info,trans_start,trans_end,ATZInfo);  UnlockTZInfo;end;function GetLocalTimezone(timer:int64;timerIsUTC:Boolean;var ATZInfo:TTZInfo;var ATZInfoEx:TTZInfoEx):Boolean;var  info: pttinfo;  trans_start,trans_end,timerUTC: int64;begin  { check if time is in current global Tzinfo }  ATZInfo:=CurrentTZinfo[InterlockedExchangeAdd(CurrentTZindex, 0)];  if not timerIsUTC then    timerUTC:=timer-ATZInfo.seconds  else    timerUTC:=timer;  if (ATZInfo.validsince<=timerUTC) and (timerUTC<ATZInfo.validuntil) then    begin    ATZInfoEx:=TZInfoEx;    Exit(True);    end;  { not current - search through all }  LockTZInfo;  info:=find_transition(timer,timerIsUTC,trans_start,trans_end);  GetLocalTimezone:=assigned(info);  if GetLocalTimezone then    begin    DoGetLocalTimezone(info,trans_start,trans_end,ATZInfo);    DoGetLocalTimezoneEx(timer,info,ATZInfoEx);    end;  UnlockTZInfo;end;procedure RefreshTZInfo;var  NewTZInfo: TTZInfo;  NewTZInfoEx: TTZInfoEx;begin  LockTZInfo;  if GetLocalTimezone(fptime,true,NewTZInfo,NewTZInfoEx) then    SetTZInfo(NewTZInfo,NewTZInfoEx);  UnlockTZInfo;end;Const  DefaultTimeZoneDir = '/usr/share/zoneinfo';function TimeZoneDir : ShortString;begin  // Observe TZDIR environment variable.  TimeZoneDir:=fpgetenv('TZDIR');  if TimeZoneDir='' then    TimeZoneDir:=DefaultTimeZoneDir;  if TimeZoneDir[length(TimeZoneDir)]<>'/' then    TimeZoneDir:=TimeZoneDir+'/';end;function ReadTimezoneFile(fn:string) : Boolean;  function decode(const l:longint):longint;  begin    {$IFDEF ENDIAN_LITTLE}    decode:=SwapEndian(l);    {$ELSE}    decode:=l;    {$ENDIF}  end;  function decode(const l:int64):int64;  begin    {$IFDEF ENDIAN_LITTLE}    decode:=SwapEndian(l);    {$ELSE}    decode:=l;    {$ENDIF}  end;const  bufsize = 2048;var  buf    : array[0..bufsize-1] of byte;  bufptr : pbyte;  bufbytes : tsSize;  bufoverflow : boolean;  f      : longint;  tzhead : ttzhead;  function readfilebuf : TsSize;  begin    bufptr := @buf[0];    bufbytes:=fpread(f, buf, bufsize);    readfilebuf:=bufbytes;  end;  Procedure checkbufptr(asize : integer);    var    a : tssize;      begin    a:=bufptr-@buf+asize;    if (a>bufbytes) then      bufoverflow:=true;  end;  function readbufbyte: byte;  begin    if bufptr > @buf[bufsize-1] then      readfilebuf;    checkbufptr(1);    readbufbyte := bufptr^;    inc(bufptr);  end;  function readbuf(dest:pointer; count: integer): integer;  var    numbytes: integer;  begin    readbuf := 0;    repeat      numbytes := (@buf[bufsize-1] + 1) - bufptr;      if numbytes > count then        numbytes := count;      if numbytes > 0 then      begin        checkbufptr(numbytes);        if assigned(dest) then          move(bufptr^, dest^, numbytes);        inc(bufptr, numbytes);        dec(count, numbytes);        inc(readbuf, numbytes);        inc(dest, numbytes);      end;      if count > 0 then        readfilebuf      else        break;    until false;  end;  function readheader: boolean;  var    i      : longint;  begin    i:=readbuf(@tzhead,sizeof(tzhead));    if i<>sizeof(tzhead) then      exit(False);    tzhead.tzh_timecnt:=decode(tzhead.tzh_timecnt);    tzhead.tzh_typecnt:=decode(tzhead.tzh_typecnt);    tzhead.tzh_charcnt:=decode(tzhead.tzh_charcnt);    tzhead.tzh_leapcnt:=decode(tzhead.tzh_leapcnt);    tzhead.tzh_ttisstdcnt:=decode(tzhead.tzh_ttisstdcnt);    tzhead.tzh_ttisgmtcnt:=decode(tzhead.tzh_ttisgmtcnt);    readheader:=(tzhead.tzh_identifier[0]='T') and (tzhead.tzh_identifier[1]='Z')      and (tzhead.tzh_identifier[2]='i') and (tzhead.tzh_identifier[3]='f');  end;  procedure AllocFields;  begin    num_transitions:=tzhead.tzh_timecnt;    num_types:=tzhead.tzh_typecnt;    num_leaps:=tzhead.tzh_leapcnt;    reallocmem(transitions,num_transitions*sizeof(int64));    reallocmem(type_idxs,num_transitions);    reallocmem(types,num_types*sizeof(tttinfo));    reallocmem(zone_names,tzhead.tzh_charcnt);    reallocmem(leaps,num_leaps*sizeof(tleap));  end;  function readdata: boolean;  var    i      : longint;    longval: longint;    version: longint;  begin    if tzhead.tzh_version='2' then      begin        version:=2;        // skip version 0        readbuf(nil,           tzhead.tzh_timecnt*4  // transitions          +tzhead.tzh_timecnt    // type_idxs          +tzhead.tzh_typecnt*6  // types          +tzhead.tzh_charcnt    // zone_names          +tzhead.tzh_leapcnt*8  // leaps          +tzhead.tzh_ttisstdcnt // isstd          +tzhead.tzh_ttisgmtcnt // isgmt          );        readheader; // read version 2 header        if tzhead.tzh_version<>'2' then          Exit(False);      end    else      version:=0;    AllocFields;    if version=2 then      begin // read 64bit values        readbuf(transitions,num_transitions*sizeof(int64));        for i:=0 to num_transitions-1 do          transitions[i]:=decode(transitions[i]);      end    else      begin // read 32bit values        for i:=0 to num_transitions-1 do         begin           readbuf(@longval,sizeof(longval));           transitions[i]:=decode(longval);         end;      end;    readbuf(type_idxs,num_transitions);    for i:=0 to num_types-1 do     begin       readbuf(@types[i].offset,sizeof(LongInt));       types[i].offset:=decode(types[i].offset);       readbuf(@types[i].isdst,1);       readbuf(@types[i].idx,1);       types[i].isstd:=0;       types[i].isgmt:=0;     end;    readbuf(zone_names,tzhead.tzh_charcnt);    if version=2 then      begin // read 64bit values        for i:=0 to num_leaps-1 do         begin           readbuf(@leaps[i].transition,sizeof(int64));           readbuf(@leaps[i].change,sizeof(longint));           leaps[i].transition:=decode(leaps[i].transition);           leaps[i].change:=decode(leaps[i].change);         end;      end    else      begin        for i:=0 to num_leaps-1 do         begin           readbuf(@longval,sizeof(longval));           leaps[i].transition:=decode(longval);           readbuf(@longval,sizeof(longval));           leaps[i].change:=decode(longval);         end;      end;    for i:=0 to tzhead.tzh_ttisstdcnt-1 do     types[i].isstd:=byte(readbufbyte<>0);    for i:=0 to tzhead.tzh_ttisgmtcnt-1 do     types[i].isgmt:=byte(readbufbyte<>0);    readdata:=true;  end;  procedure ClearCurrentTZinfo;  var    i:integer;  begin    for i:=low(CurrentTZinfo) to high(CurrentTZinfo) do      CurrentTZinfo[i] := Default(TTZInfo);  end;begin  if fn='' then   fn:='localtime';  if fn[1]<>'/' then    fn:='/usr/share/zoneinfo/'+fn;  f:=fpopen(fn,Open_RdOnly);  if f<0 then   exit(False);  bufoverflow:=false;  bufptr := @buf[bufsize-1]+1;  tzhead:=default(ttzhead);  LockTZInfo;  ReadTimezoneFile:=(readheader() and readdata()) and not BufOverflow;  ClearCurrentTZinfo;  UnlockTZInfo;  fpclose(f);end;Const  // Debian system; contains location of timezone file.  TimeZoneLocationFile = '/etc/timezone';  // SuSE has link in /usr/lib/zoneinfo/localtime to /etc/localtime  // RedHat uses /etc/localtime  TimeZoneFile = '/etc/localtime';                      // POSIX  AltTimeZoneFile = '/usr/lib/zoneinfo/localtime';      // Other  iOSTimeZoneFile = '/var/db/timezone/localtime';      // iOS{$ifdef BSD}  BSDTimeZonefile = DefaultTimeZoneDir;      // BSD usually is POSIX                                                // compliant though{$ENDIF}{$ifndef FPC_HAS_GETTIMEZONEFILE}function GetTimezoneFile:string;var  f,len : longint;  fn,s : shortstring;  info : stat;begin  GetTimezoneFile:='';  // Observe TZ variable.  fn:=fpgetenv('TZ');  if (fn<>'') then    if (fn[1]=':') then      begin      Delete(fn,1,1);      if (fn<>'') then        begin        if (fn[1]<>'/') then          Exit(TimeZoneDir+fn);        Exit(fn);        end;      end;  if (fn='') then    fn:=TimeZoneLocationFile;  f:=fpopen(TimeZoneLocationFile,Open_RdOnly);  if f>0 then    begin    len:=fpread(f,s[1],high(s));    s[0]:=chr(len);    len:=pos(#10,s);    if len<>0 then     s[0]:=chr(len-1);    fpclose(f);    GetTimezoneFile:=s;    end  // Try SuSE  else if fpstat(TimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then    GetTimeZoneFile:=TimeZoneFile  // Try RedHat  else If fpstat(AltTimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then      GetTimeZoneFile:=AltTimeZoneFile{$ifdef BSD}//  else//       If fpstat(BSDTimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then// GetTimeZoneFile:=BSDTimeZoneFile{$ENDIF}{$if (defined(darwin) and defined(arm)) or defined(iphonesim)}  else If fpstat(iOSTimeZoneFile,info)>=0 then      GetTimeZoneFile:=iOSTimeZoneFile{$endif}end;{$endif ndef FPC_HAS_GETTIMEZONEFILE}procedure InitLocalTime;begin  ReadTimezoneFile(GetTimezoneFile);  RefreshTZInfo;end;procedure DoneLocalTime;begin  if assigned(transitions) then   freemem(transitions);  transitions:=nil;  if assigned(type_idxs) then   freemem(type_idxs);  type_idxs:=nil;  if assigned(types) then   freemem(types);  types:=nil;  if assigned(zone_names) then   freemem(zone_names);  zone_names:=Nil;  if assigned(leaps) then   freemem(leaps);  leaps:=nil;  num_transitions:=0;  num_leaps:=0;  num_types:=0;end;Procedure ReReadLocalTime;begin  LockTZInfo;  DoneLocalTime;  InitLocalTime;  UnlockTZInfo;end;
 |