timezone.inc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. {
  2. Support for timezone info in /usr/share/timezone
  3. }
  4. type
  5. ttzhead=packed record
  6. tzh_reserved : array[0..19] of byte;
  7. tzh_ttisgmtcnt,
  8. tzh_ttisstdcnt,
  9. tzh_leapcnt,
  10. tzh_timecnt,
  11. tzh_typecnt,
  12. tzh_charcnt : longint;
  13. end;
  14. pttinfo=^tttinfo;
  15. tttinfo=packed record
  16. offset : longint;
  17. isdst : boolean;
  18. idx : byte;
  19. isstd : byte;
  20. isgmt : byte;
  21. end;
  22. pleap=^tleap;
  23. tleap=record
  24. transition : longint;
  25. change : longint;
  26. end;
  27. var
  28. num_transitions,
  29. num_leaps,
  30. num_types : longint;
  31. transitions : plongint = nil;
  32. type_idxs : pbyte = Nil;
  33. types : pttinfo = Nil;
  34. zone_names : pchar = Nil;
  35. leaps : pleap = Nil;
  36. function find_transition(timer:longint;var trans_start,trans_end:longint):pttinfo;
  37. var
  38. i : longint;
  39. begin
  40. if (num_transitions=0) or (timer<transitions[0]) then
  41. begin
  42. i:=0;
  43. while (i<num_types) and (types[i].isdst) do
  44. inc(i);
  45. if (i=num_types) then
  46. i:=0;
  47. { unknown transition boundaries }
  48. trans_start:=low(trans_start);
  49. trans_end:=high(trans_end);
  50. end
  51. else
  52. begin
  53. i:=1;
  54. while i<=num_transitions-1 do
  55. begin
  56. if (timer<transitions[i]) then
  57. break;
  58. inc(i);
  59. end;
  60. trans_start:=transitions[i-1];
  61. trans_end:=transitions[i];
  62. i:=type_idxs[i-1];
  63. end;
  64. find_transition:=@types[i];
  65. end;
  66. function GetLocalTimezone(timer:cint;var ATZInfo:TTZInfo;FullInfo:Boolean):Boolean;
  67. var
  68. info : pttinfo;
  69. i,trans_start,trans_end : longint;
  70. begin
  71. { reset }
  72. ATZInfo.Daylight:=false;
  73. ATZInfo.Seconds:=0;
  74. ATZInfo.Name[false]:=nil;
  75. ATZInfo.Name[true]:=nil;
  76. ATZInfo.validsince:=0;
  77. ATZInfo.validuntil:=0;
  78. ATZInfo.leap_correct:=0;
  79. ATZInfo.leap_hit:=0;
  80. { get info }
  81. info:=find_transition(timer,trans_start,trans_end);
  82. GetLocalTimezone:=assigned(info);
  83. if not GetLocalTimezone then
  84. exit;
  85. ATZInfo.validsince:=trans_start;
  86. ATZInfo.validuntil:=trans_end;
  87. ATZInfo.Daylight:=info^.isdst;
  88. ATZInfo.Seconds:=info^.offset;
  89. if not FullInfo then
  90. Exit;
  91. i:=0;
  92. while (i<num_types) do
  93. begin
  94. ATZInfo.name[types[i].isdst]:=@zone_names[types[i].idx];
  95. inc(i);
  96. end;
  97. ATZInfo.name[info^.isdst]:=@zone_names[info^.idx];
  98. i:=num_leaps;
  99. repeat
  100. if i=0 then
  101. exit;
  102. dec(i);
  103. until (timer>leaps[i].transition);
  104. ATZInfo.leap_correct:=leaps[i].change;
  105. if (timer=leaps[i].transition) and
  106. (((i=0) and (leaps[i].change>0)) or
  107. (leaps[i].change>leaps[i-1].change)) then
  108. begin
  109. ATZInfo.leap_hit:=1;
  110. while (i>0) and
  111. (leaps[i].transition=leaps[i-1].transition+1) and
  112. (leaps[i].change=leaps[i-1].change+1) do
  113. begin
  114. inc(ATZInfo.leap_hit);
  115. dec(i);
  116. end;
  117. end;
  118. end;
  119. procedure GetLocalTimezone(timer:cint);
  120. begin
  121. GetLocalTimezone(timer,Tzinfo,true);
  122. end;
  123. Const
  124. DefaultTimeZoneDir = '/usr/share/zoneinfo';
  125. function TimeZoneDir : ShortString;
  126. begin
  127. // Observe TZDIR environment variable.
  128. TimeZoneDir:=fpgetenv('TZDIR');
  129. if TimeZoneDir='' then
  130. TimeZoneDir:=DefaultTimeZoneDir;
  131. if TimeZoneDir[length(TimeZoneDir)]<>'/' then
  132. TimeZoneDir:=TimeZoneDir+'/';
  133. end;
  134. procedure ReadTimezoneFile(fn:shortstring);
  135. procedure decode(var l:longint);
  136. var
  137. k : longint;
  138. p : pbyte;
  139. begin
  140. p:=pbyte(@l);
  141. if (p[0] and (1 shl 7))<>0 then
  142. k:=not 0
  143. else
  144. k:=0;
  145. k:=(k shl 8) or p[0];
  146. k:=(k shl 8) or p[1];
  147. k:=(k shl 8) or p[2];
  148. k:=(k shl 8) or p[3];
  149. l:=k;
  150. end;
  151. const
  152. bufsize = 2048;
  153. var
  154. buf : array[0..bufsize-1] of byte;
  155. bufptr : pbyte;
  156. f : longint;
  157. procedure readfilebuf;
  158. begin
  159. bufptr := @buf[0];
  160. fpread(f, buf, bufsize);
  161. end;
  162. function readbufbyte: byte;
  163. begin
  164. if bufptr > @buf[bufsize-1] then
  165. readfilebuf;
  166. readbufbyte := bufptr^;
  167. inc(bufptr);
  168. end;
  169. function readbuf(var dest; count: integer): integer;
  170. var
  171. numbytes: integer;
  172. begin
  173. readbuf := 0;
  174. repeat
  175. numbytes := (@buf[bufsize-1] + 1) - bufptr;
  176. if numbytes > count then
  177. numbytes := count;
  178. if numbytes > 0 then
  179. begin
  180. move(bufptr^, dest, numbytes);
  181. inc(bufptr, numbytes);
  182. dec(count, numbytes);
  183. inc(readbuf, numbytes);
  184. end;
  185. if count > 0 then
  186. readfilebuf
  187. else
  188. break;
  189. until false;
  190. end;
  191. var
  192. tzdir : shortstring;
  193. tzhead : ttzhead;
  194. i : longint;
  195. chars : longint;
  196. begin
  197. if fn='' then
  198. fn:='localtime';
  199. if fn[1]<>'/' then
  200. fn:=TimeZoneDir+fn;
  201. f:=fpopen(fn,Open_RdOnly);
  202. if f<0 then
  203. exit;
  204. bufptr := @buf[bufsize-1]+1;
  205. i:=readbuf(tzhead,sizeof(tzhead));
  206. if i<>sizeof(tzhead) then
  207. exit;
  208. decode(tzhead.tzh_timecnt);
  209. decode(tzhead.tzh_typecnt);
  210. decode(tzhead.tzh_charcnt);
  211. decode(tzhead.tzh_leapcnt);
  212. decode(tzhead.tzh_ttisstdcnt);
  213. decode(tzhead.tzh_ttisgmtcnt);
  214. num_transitions:=tzhead.tzh_timecnt;
  215. num_types:=tzhead.tzh_typecnt;
  216. chars:=tzhead.tzh_charcnt;
  217. num_leaps:=tzhead.tzh_leapcnt;
  218. reallocmem(transitions,num_transitions*sizeof(longint));
  219. reallocmem(type_idxs,num_transitions);
  220. reallocmem(types,num_types*sizeof(tttinfo));
  221. reallocmem(zone_names,chars);
  222. reallocmem(leaps,num_leaps*sizeof(tleap));
  223. readbuf(transitions^,num_transitions*4);
  224. readbuf(type_idxs^,num_transitions);
  225. for i:=0 to num_transitions-1 do
  226. decode(transitions[i]);
  227. for i:=0 to num_types-1 do
  228. begin
  229. readbuf(types[i].offset,4);
  230. readbuf(types[i].isdst,1);
  231. readbuf(types[i].idx,1);
  232. decode(types[i].offset);
  233. types[i].isstd:=0;
  234. types[i].isgmt:=0;
  235. end;
  236. readbuf(zone_names^,chars);
  237. for i:=0 to num_leaps-1 do
  238. begin
  239. readbuf(leaps[i].transition,4);
  240. readbuf(leaps[i].change,4);
  241. decode(leaps[i].transition);
  242. decode(leaps[i].change);
  243. end;
  244. for i:=0 to tzhead.tzh_ttisstdcnt-1 do
  245. types[i].isstd:=byte(readbufbyte<>0);
  246. for i:=0 to tzhead.tzh_ttisgmtcnt-1 do
  247. types[i].isgmt:=byte(readbufbyte<>0);
  248. fpclose(f);
  249. end;
  250. Const
  251. // Debian system; contains location of timezone file.
  252. TimeZoneLocationFile = '/etc/timezone';
  253. // SuSE has link in /usr/lib/zoneinfo/localtime to /etc/localtime
  254. // RedHat uses /etc/localtime
  255. TimeZoneFile = '/etc/localtime'; // POSIX
  256. AltTimeZoneFile = '/usr/lib/zoneinfo/localtime'; // Other
  257. iOSTimeZoneFile = '/var/db/timezone/localtime'; // iOS
  258. {$ifdef BSD}
  259. BSDTimeZonefile = DefaultTimeZoneDir; // BSD usually is POSIX
  260. // compliant though
  261. {$ENDIF}
  262. {$ifndef FPC_HAS_GETTIMEZONEFILE}
  263. function GetTimezoneFile:shortstring;
  264. var
  265. f,len : longint;
  266. fn,s : shortstring;
  267. info : stat;
  268. begin
  269. GetTimezoneFile:='';
  270. // Observe TZ variable.
  271. fn:=fpgetenv('TZ');
  272. if (fn<>'') then
  273. if (fn[1]=':') then
  274. begin
  275. Delete(fn,1,1);
  276. if (fn<>'') then
  277. begin
  278. if (fn[1]<>'/') then
  279. Exit(TimeZoneDir+fn);
  280. Exit(fn);
  281. end;
  282. end;
  283. if (fn='') then
  284. fn:=TimeZoneLocationFile;
  285. f:=fpopen(TimeZoneLocationFile,Open_RdOnly);
  286. if f>0 then
  287. begin
  288. len:=fpread(f,s[1],high(s));
  289. s[0]:=chr(len);
  290. len:=pos(#10,s);
  291. if len<>0 then
  292. s[0]:=chr(len-1);
  293. fpclose(f);
  294. GetTimezoneFile:=s;
  295. end
  296. // Try SuSE
  297. else if fpstat(TimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then
  298. GetTimeZoneFile:=TimeZoneFile
  299. // Try RedHat
  300. else If fpstat(AltTimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then
  301. GetTimeZoneFile:=AltTimeZoneFile
  302. {$ifdef BSD}
  303. // else
  304. // If fpstat(BSDTimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then
  305. // GetTimeZoneFile:=BSDTimeZoneFile
  306. {$ENDIF}
  307. {$if (defined(darwin) and defined(arm)) or defined(iphonesim)}
  308. else If fpstat(iOSTimeZoneFile,info)>=0 then
  309. GetTimeZoneFile:=iOSTimeZoneFile
  310. {$endif}
  311. end;
  312. {$endif ndef FPC_HAS_GETTIMEZONEFILE}
  313. procedure InitLocalTime;
  314. begin
  315. ReloadTzinfo:=@InitLocalTime;
  316. ReadTimezoneFile(GetTimezoneFile);
  317. GetLocalTimezone(fptime);
  318. end;
  319. procedure DoneLocalTime;
  320. begin
  321. if assigned(transitions) then
  322. freemem(transitions);
  323. transitions:=nil;
  324. if assigned(type_idxs) then
  325. freemem(type_idxs);
  326. type_idxs:=nil;
  327. if assigned(types) then
  328. freemem(types);
  329. types:=nil;
  330. if assigned(zone_names) then
  331. freemem(zone_names);
  332. zone_names:=Nil;
  333. if assigned(leaps) then
  334. freemem(leaps);
  335. leaps:=nil;
  336. num_transitions:=0;
  337. num_leaps:=0;
  338. num_types:=0;
  339. end;
  340. Procedure ReReadLocalTime;
  341. begin
  342. DoneLocalTime;
  343. InitLocalTime;
  344. end;