Quellcode durchsuchen

* read int64 transition table from linux timezone files if available

git-svn-id: trunk@47324 -
ondrej vor 4 Jahren
Ursprung
Commit
be4c907117
3 geänderte Dateien mit 174 neuen und 102 gelöschten Zeilen
  1. 158 99
      rtl/unix/timezone.inc
  2. 2 2
      rtl/unix/unix.pp
  3. 14 1
      tests/test/units/unix/ttimezone1.pp

+ 158 - 99
rtl/unix/timezone.inc

@@ -6,7 +6,9 @@
 type
 
   ttzhead=packed record
-    tzh_reserved : array[0..19] of byte;
+    tzh_identifier : array[0..3] of AnsiChar;
+    tzh_version : AnsiChar;
+    tzh_reserved : array[0..14] of byte;
     tzh_ttisgmtcnt,
     tzh_ttisstdcnt,
     tzh_leapcnt,
@@ -26,8 +28,8 @@ type
 
   pleap=^tleap;
   tleap=record
-    transition : longint;
-    change     : longint;
+    transition : int64;
+    change     : int64;
   end;
 
 var
@@ -35,19 +37,20 @@ var
   num_leaps,
   num_types    : longint;
 
-  transitions  : plongint = nil;
+  transitions  : PInt64 = nil;
   type_idxs    : pbyte = Nil;
   types        : pttinfo = Nil;
   zone_names   : pchar = Nil;
   leaps        : pleap = Nil;
 
-function find_transition(timer:longint;timerIsUTC:Boolean;var trans_start,trans_end:longint):pttinfo;
+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: LongInt;
+    timerUTC: int64;
   begin
     if not timerIsUTC then
       timerUTC:=timer-types[type_idxs[i-1]].offset
@@ -78,7 +81,8 @@ begin
       // Use binary search.
       L := 1;
       R := num_transitions-1;
-      while (L<=R) do
+      found := false;
+      while not found and (L<=R) do
       begin
         I := L + (R - L) div 2;
         CompareRes := DoCompare;
@@ -87,10 +91,11 @@ begin
         else begin
           R := I-1;
           if (CompareRes=0) then
-             L:=I; // break cycle
+             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];
@@ -148,11 +153,10 @@ begin
    end;
 end;
 
-function GetLocalTimezone(timer:cint;timerIsUTC:Boolean;var ATZInfo:TTZInfo):Boolean;
+function GetLocalTimezone(timer:int64;timerIsUTC:Boolean;var ATZInfo:TTZInfo):Boolean;
 var
   info: pttinfo;
-  trans_start,trans_end: longint;
-  timerUTC: cint;
+  trans_start,trans_end,timerUTC: int64;
 begin
   { check if time is in current global Tzinfo }
   ATZInfo:=CurrentTZinfo[InterlockedExchangeAdd(CurrentTZindex, 0)];
@@ -171,11 +175,10 @@ begin
   UnlockTZInfo;
 end;
 
-function GetLocalTimezone(timer:cint;timerIsUTC:Boolean;var ATZInfo:TTZInfo;var ATZInfoEx:TTZInfoEx):Boolean;
+function GetLocalTimezone(timer:int64;timerIsUTC:Boolean;var ATZInfo:TTZInfo;var ATZInfoEx:TTZInfoEx):Boolean;
 var
   info: pttinfo;
-  trans_start,trans_end: longint;
-  timerUTC: cint;
+  trans_start,trans_end,timerUTC: int64;
 begin
   { check if time is in current global Tzinfo }
   ATZInfo:=CurrentTZinfo[InterlockedExchangeAdd(CurrentTZindex, 0)];
@@ -228,21 +231,22 @@ end;
 
 function ReadTimezoneFile(fn:string) : Boolean;
 
-  procedure decode(var l:longint);
-  var
-    k : longint;
-    p : pbyte;
+  function decode(const l:longint):longint;
   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;
+    {$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
@@ -251,6 +255,7 @@ var
   buf    : array[0..bufsize-1] of byte;
   bufptr : pbyte;
   f      : longint;
+  tzhead : ttzhead;
 
   procedure readfilebuf;
   begin
@@ -266,7 +271,7 @@ var
     inc(bufptr);
   end;
 
-  function readbuf(var dest; count: integer): integer;
+  function readbuf(dest:pointer; count: integer): integer;
   var
     numbytes: integer;
   begin
@@ -277,10 +282,12 @@ var
         numbytes := count;
       if numbytes > 0 then
       begin
-        move(bufptr^, dest, 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
@@ -289,82 +296,134 @@ var
     until false;
   end;
 
-var
-  tzdir  : shortstring;
-  tzhead : ttzhead;
-  i      : longint;
-  chars  : longint;
-begin
-  LockTZInfo;
-  if fn='' then
-   fn:='localtime';
-  if fn[1]<>'/' then
-    fn:=TimeZoneDir+fn;
-  f:=fpopen(fn,Open_RdOnly);
-  if f<0 then
+  function readheader: boolean;
+  var
+    i      : longint;
   begin
-   UnlockTZInfo;
-   exit(False);
+    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;
-  bufptr := @buf[bufsize-1]+1;
-  i:=readbuf(tzhead,sizeof(tzhead));
-  if i<>sizeof(tzhead) then
+
+  procedure AllocFields;
   begin
-   UnlockTZInfo;
-   exit(False);
+    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;
-  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;
-  num_leaps:=tzhead.tzh_leapcnt;
-  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));
-
-  readbuf(transitions^,num_transitions*4);
-  readbuf(type_idxs^,num_transitions);
-
-  for i:=0 to num_transitions-1 do
-   decode(transitions[i]);
-
-  for i:=0 to num_types-1 do
-   begin
-     readbuf(types[i].offset,4);
-     readbuf(types[i].isdst,1);
-     readbuf(types[i].idx,1);
-     decode(types[i].offset);
-     types[i].isstd:=0;
-     types[i].isgmt:=0;
-   end;
 
-  readbuf(zone_names^,chars);
+  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;
 
-  for i:=0 to num_leaps-1 do
-   begin
-     readbuf(leaps[i].transition,4);
-     readbuf(leaps[i].change,4);
-     decode(leaps[i].transition);
-     decode(leaps[i].change);
-   end;
+    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(int64));
+           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_ttisstdcnt-1 do
+     types[i].isstd:=byte(readbufbyte<>0);
 
-  for i:=0 to tzhead.tzh_ttisgmtcnt-1 do
-   types[i].isgmt:=byte(readbufbyte<>0);
+    for i:=0 to tzhead.tzh_ttisgmtcnt-1 do
+     types[i].isgmt:=byte(readbufbyte<>0);
 
-  fpclose(f);
-  ReadTimezoneFile:=True;
+    readdata:=true;
+  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);
+  bufptr := @buf[bufsize-1]+1;
+  tzhead:=default(ttzhead);
+  LockTZInfo;
+  ReadTimezoneFile:=(readheader() and readdata());
   UnlockTZInfo;
+  fpclose(f);
 end;
 
 Const
@@ -398,9 +457,9 @@ begin
       Delete(fn,1,1);
       if (fn<>'') then
         begin
-        if (fn[1]<>'/') then 
+        if (fn[1]<>'/') then
           Exit(TimeZoneDir+fn);
-        Exit(fn);  
+        Exit(fn);
         end;
       end;
   if (fn='') then

+ 2 - 2
rtl/unix/unix.pp

@@ -88,8 +88,8 @@ type
                        // it doesn't (yet) work for.
 
 { timezone support }
-function GetLocalTimezone(timer:cint;timerIsUTC:Boolean;var ATZInfo:TTZInfo;var ATZInfoEx:TTZInfoEx):Boolean;
-function GetLocalTimezone(timer:cint;timerIsUTC:Boolean;var ATZInfo:TTZInfo):Boolean;
+function GetLocalTimezone(timer:int64;timerIsUTC:Boolean;var ATZInfo:TTZInfo;var ATZInfoEx:TTZInfoEx):Boolean;
+function GetLocalTimezone(timer:int64;timerIsUTC:Boolean;var ATZInfo:TTZInfo):Boolean;
 procedure RefreshTZInfo;
 function  ReadTimezoneFile(fn:string) : Boolean;
 function  GetTimezoneFile:string;

+ 14 - 1
tests/test/units/unix/ttimezone1.pp

@@ -2,6 +2,16 @@
 uses
   BaseUnix,unix;
 
+function LocalTimeIsDefined(Year, Month, Day, Hour, Minute, Second: word; const UTC: Boolean): Boolean;
+var
+  UnixTime: Int64;
+  lTZInfo: TTZInfo;
+begin
+  lTZInfo:=Default(TTZInfo);
+  UnixTime:=UniversalToEpoch(Year, Month, Day, Hour, Minute, Second);
+  Result := GetLocalTimezone(UnixTime,UTC,lTZInfo);
+end;
+
 function GetOffset(Year, Month, Day, Hour, Minute, Second: word; const UTC: Boolean): Integer;
 var
   UnixTime: Int64;
@@ -28,9 +38,12 @@ begin
   end;
 
   if GetOffset(2019, 03, 31, 1, 59, 0, False)<>1 then Halt(11);
+  // 2019-03-31 02:00-03:00 CET is not defined
+  if LocalTimeIsDefined(2019, 03, 31, 2, 30, 0, False) then Halt(19);
   if GetOffset(2019, 03, 31, 3, 0, 0, False)<>2 then Halt(12);
 
-  if GetOffset(2019, 10, 27, 2, 59, 0, False)<>2 then Halt(13);
+  if GetOffset(2019, 10, 27, 1, 59, 0, False)<>2 then Halt(13);
+  // 2019-10-27 02:00-03:00 CET is ambiguos, therefore do not check
   if GetOffset(2019, 10, 27, 3, 0, 0, False)<>1 then Halt(14);
 
   if GetOffset(2019, 03, 31, 0, 59, 0, True)<>1 then Halt(15);