timezone.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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;timerIsUTC:Boolean;var trans_start,trans_end:longint):pttinfo;
  37. var
  38. i,L,R,CompareRes : longint;
  39. function DoCompare: longint;
  40. var
  41. timerUTC: LongInt;
  42. begin
  43. if not timerIsUTC then
  44. timerUTC:=timer-types[type_idxs[i-1]].offset
  45. else
  46. timerUTC:=timer;
  47. if timerUTC<transitions[i-1] then
  48. Exit(-1)
  49. else
  50. if timerUTC>=transitions[i] then
  51. Exit(1)
  52. else
  53. Exit(0);
  54. end;
  55. begin
  56. if (num_transitions=0) or (timer<transitions[0]) then
  57. begin
  58. i:=0;
  59. while (i<num_types) and (types[i].isdst) do
  60. inc(i);
  61. if (i=num_types) then
  62. i:=0;
  63. { unknown transition boundaries }
  64. trans_start:=low(trans_start);
  65. trans_end:=high(trans_end);
  66. end
  67. else
  68. begin
  69. // Use binary search.
  70. L := 1;
  71. R := num_transitions-1;
  72. while (L<=R) do
  73. begin
  74. I := L + (R - L) div 2;
  75. CompareRes := DoCompare;
  76. if (CompareRes>0) then
  77. L := I+1
  78. else begin
  79. R := I-1;
  80. if (CompareRes=0) then
  81. L:=I; // break cycle
  82. end;
  83. end;
  84. trans_start:=transitions[i-1];
  85. trans_end:=transitions[i];
  86. i:=type_idxs[i-1];
  87. end;
  88. find_transition:=@types[i];
  89. end;
  90. procedure DoGetLocalTimezone(info:pttinfo;const trans_start,trans_end:longint;var ATZInfo:TTZInfo);
  91. begin
  92. ATZInfo.validsince:=trans_start;
  93. ATZInfo.validuntil:=trans_end;
  94. ATZInfo.Daylight:=info^.isdst;
  95. ATZInfo.Seconds:=info^.offset;
  96. end;
  97. procedure DoGetLocalTimezoneEx(timer:cint;info:pttinfo;var ATZInfoEx:TTZInfoEx);
  98. var
  99. i : longint;
  100. names: array[Boolean] of pchar;
  101. begin
  102. names[true]:=nil;
  103. names[false]:=nil;
  104. ATZInfoEx.leap_hit:=0;
  105. ATZInfoEx.leap_correct:=0;
  106. i:=0;
  107. while (i<num_types) do
  108. begin
  109. names[types[i].isdst]:=@zone_names[types[i].idx];
  110. inc(i);
  111. end;
  112. names[info^.isdst]:=@zone_names[info^.idx];
  113. ATZInfoEx.name[true]:=names[true];
  114. ATZInfoEx.name[false]:=names[false];
  115. i:=num_leaps;
  116. repeat
  117. if i=0 then
  118. exit;
  119. dec(i);
  120. until (timer>leaps[i].transition);
  121. ATZInfoEx.leap_correct:=leaps[i].change;
  122. if (timer=leaps[i].transition) and
  123. (((i=0) and (leaps[i].change>0)) or
  124. (leaps[i].change>leaps[i-1].change)) then
  125. begin
  126. ATZInfoEx.leap_hit:=1;
  127. while (i>0) and
  128. (leaps[i].transition=leaps[i-1].transition+1) and
  129. (leaps[i].change=leaps[i-1].change+1) do
  130. begin
  131. inc(ATZInfoEx.leap_hit);
  132. dec(i);
  133. end;
  134. end;
  135. end;
  136. function GetLocalTimezone(timer:cint;timerIsUTC:Boolean;var ATZInfo:TTZInfo):Boolean;
  137. var
  138. info: pttinfo;
  139. trans_start,trans_end: longint;
  140. timerUTC: cint;
  141. begin
  142. { check if time is in current global Tzinfo }
  143. ATZInfo:=CurrentTZinfo[InterlockedExchangeAdd(CurrentTZindex, 0)];
  144. if not timerIsUTC then
  145. timerUTC:=timer-ATZInfo.seconds
  146. else
  147. timerUTC:=timer;
  148. if (ATZInfo.validsince<=timerUTC) and (timerUTC<ATZInfo.validuntil) then
  149. Exit(True);
  150. LockTZInfo;
  151. info:=find_transition(timer,timerIsUTC,trans_start,trans_end);
  152. GetLocalTimezone:=assigned(info);
  153. if GetLocalTimezone then
  154. DoGetLocalTimezone(info,trans_start,trans_end,ATZInfo);
  155. UnlockTZInfo;
  156. end;
  157. function GetLocalTimezone(timer:cint;timerIsUTC:Boolean;var ATZInfo:TTZInfo;var ATZInfoEx:TTZInfoEx):Boolean;
  158. var
  159. info: pttinfo;
  160. trans_start,trans_end: longint;
  161. timerUTC: cint;
  162. begin
  163. { check if time is in current global Tzinfo }
  164. ATZInfo:=CurrentTZinfo[InterlockedExchangeAdd(CurrentTZindex, 0)];
  165. if not timerIsUTC then
  166. timerUTC:=timer-ATZInfo.seconds
  167. else
  168. timerUTC:=timer;
  169. if (ATZInfo.validsince<=timerUTC) and (timerUTC<ATZInfo.validuntil) then
  170. begin
  171. ATZInfoEx:=TZInfoEx;
  172. Exit(True);
  173. end;
  174. { not current - search through all }
  175. LockTZInfo;
  176. info:=find_transition(timer,timerIsUTC,trans_start,trans_end);
  177. GetLocalTimezone:=assigned(info);
  178. if GetLocalTimezone then
  179. begin
  180. DoGetLocalTimezone(info,trans_start,trans_end,ATZInfo);
  181. DoGetLocalTimezoneEx(timer,info,ATZInfoEx);
  182. end;
  183. UnlockTZInfo;
  184. end;
  185. procedure RefreshTZInfo;
  186. var
  187. NewTZInfo: TTZInfo;
  188. NewTZInfoEx: TTZInfoEx;
  189. begin
  190. LockTZInfo;
  191. if GetLocalTimezone(fptime,false,NewTZInfo,NewTZInfoEx) then
  192. SetTZInfo(NewTZInfo,NewTZInfoEx);
  193. UnlockTZInfo;
  194. end;
  195. Const
  196. DefaultTimeZoneDir = '/usr/share/zoneinfo';
  197. function TimeZoneDir : ShortString;
  198. begin
  199. // Observe TZDIR environment variable.
  200. TimeZoneDir:=fpgetenv('TZDIR');
  201. if TimeZoneDir='' then
  202. TimeZoneDir:=DefaultTimeZoneDir;
  203. if TimeZoneDir[length(TimeZoneDir)]<>'/' then
  204. TimeZoneDir:=TimeZoneDir+'/';
  205. end;
  206. function ReadTimezoneFile(fn:string) : Boolean;
  207. procedure decode(var l:longint);
  208. var
  209. k : longint;
  210. p : pbyte;
  211. begin
  212. p:=pbyte(@l);
  213. if (p[0] and (1 shl 7))<>0 then
  214. k:=not 0
  215. else
  216. k:=0;
  217. k:=(k shl 8) or p[0];
  218. k:=(k shl 8) or p[1];
  219. k:=(k shl 8) or p[2];
  220. k:=(k shl 8) or p[3];
  221. l:=k;
  222. end;
  223. const
  224. bufsize = 2048;
  225. var
  226. buf : array[0..bufsize-1] of byte;
  227. bufptr : pbyte;
  228. f : longint;
  229. procedure readfilebuf;
  230. begin
  231. bufptr := @buf[0];
  232. fpread(f, buf, bufsize);
  233. end;
  234. function readbufbyte: byte;
  235. begin
  236. if bufptr > @buf[bufsize-1] then
  237. readfilebuf;
  238. readbufbyte := bufptr^;
  239. inc(bufptr);
  240. end;
  241. function readbuf(var dest; count: integer): integer;
  242. var
  243. numbytes: integer;
  244. begin
  245. readbuf := 0;
  246. repeat
  247. numbytes := (@buf[bufsize-1] + 1) - bufptr;
  248. if numbytes > count then
  249. numbytes := count;
  250. if numbytes > 0 then
  251. begin
  252. move(bufptr^, dest, numbytes);
  253. inc(bufptr, numbytes);
  254. dec(count, numbytes);
  255. inc(readbuf, numbytes);
  256. end;
  257. if count > 0 then
  258. readfilebuf
  259. else
  260. break;
  261. until false;
  262. end;
  263. var
  264. tzdir : shortstring;
  265. tzhead : ttzhead;
  266. i : longint;
  267. chars : longint;
  268. begin
  269. LockTZInfo;
  270. if fn='' then
  271. fn:='localtime';
  272. if fn[1]<>'/' then
  273. fn:=TimeZoneDir+fn;
  274. f:=fpopen(fn,Open_RdOnly);
  275. if f<0 then
  276. begin
  277. UnlockTZInfo;
  278. exit(False);
  279. end;
  280. bufptr := @buf[bufsize-1]+1;
  281. i:=readbuf(tzhead,sizeof(tzhead));
  282. if i<>sizeof(tzhead) then
  283. begin
  284. UnlockTZInfo;
  285. exit(False);
  286. end;
  287. decode(tzhead.tzh_timecnt);
  288. decode(tzhead.tzh_typecnt);
  289. decode(tzhead.tzh_charcnt);
  290. decode(tzhead.tzh_leapcnt);
  291. decode(tzhead.tzh_ttisstdcnt);
  292. decode(tzhead.tzh_ttisgmtcnt);
  293. num_transitions:=tzhead.tzh_timecnt;
  294. num_types:=tzhead.tzh_typecnt;
  295. chars:=tzhead.tzh_charcnt;
  296. num_leaps:=tzhead.tzh_leapcnt;
  297. reallocmem(transitions,num_transitions*sizeof(longint));
  298. reallocmem(type_idxs,num_transitions);
  299. reallocmem(types,num_types*sizeof(tttinfo));
  300. reallocmem(zone_names,chars);
  301. reallocmem(leaps,num_leaps*sizeof(tleap));
  302. readbuf(transitions^,num_transitions*4);
  303. readbuf(type_idxs^,num_transitions);
  304. for i:=0 to num_transitions-1 do
  305. decode(transitions[i]);
  306. for i:=0 to num_types-1 do
  307. begin
  308. readbuf(types[i].offset,4);
  309. readbuf(types[i].isdst,1);
  310. readbuf(types[i].idx,1);
  311. decode(types[i].offset);
  312. types[i].isstd:=0;
  313. types[i].isgmt:=0;
  314. end;
  315. readbuf(zone_names^,chars);
  316. for i:=0 to num_leaps-1 do
  317. begin
  318. readbuf(leaps[i].transition,4);
  319. readbuf(leaps[i].change,4);
  320. decode(leaps[i].transition);
  321. decode(leaps[i].change);
  322. end;
  323. for i:=0 to tzhead.tzh_ttisstdcnt-1 do
  324. types[i].isstd:=byte(readbufbyte<>0);
  325. for i:=0 to tzhead.tzh_ttisgmtcnt-1 do
  326. types[i].isgmt:=byte(readbufbyte<>0);
  327. fpclose(f);
  328. ReadTimezoneFile:=True;
  329. UnlockTZInfo;
  330. end;
  331. Const
  332. // Debian system; contains location of timezone file.
  333. TimeZoneLocationFile = '/etc/timezone';
  334. // SuSE has link in /usr/lib/zoneinfo/localtime to /etc/localtime
  335. // RedHat uses /etc/localtime
  336. TimeZoneFile = '/etc/localtime'; // POSIX
  337. AltTimeZoneFile = '/usr/lib/zoneinfo/localtime'; // Other
  338. iOSTimeZoneFile = '/var/db/timezone/localtime'; // iOS
  339. {$ifdef BSD}
  340. BSDTimeZonefile = DefaultTimeZoneDir; // BSD usually is POSIX
  341. // compliant though
  342. {$ENDIF}
  343. {$ifndef FPC_HAS_GETTIMEZONEFILE}
  344. function GetTimezoneFile:shortstring;
  345. var
  346. f,len : longint;
  347. fn,s : shortstring;
  348. info : stat;
  349. begin
  350. GetTimezoneFile:='';
  351. // Observe TZ variable.
  352. fn:=fpgetenv('TZ');
  353. if (fn<>'') then
  354. if (fn[1]=':') then
  355. begin
  356. Delete(fn,1,1);
  357. if (fn<>'') then
  358. begin
  359. if (fn[1]<>'/') then
  360. Exit(TimeZoneDir+fn);
  361. Exit(fn);
  362. end;
  363. end;
  364. if (fn='') then
  365. fn:=TimeZoneLocationFile;
  366. f:=fpopen(TimeZoneLocationFile,Open_RdOnly);
  367. if f>0 then
  368. begin
  369. len:=fpread(f,s[1],high(s));
  370. s[0]:=chr(len);
  371. len:=pos(#10,s);
  372. if len<>0 then
  373. s[0]:=chr(len-1);
  374. fpclose(f);
  375. GetTimezoneFile:=s;
  376. end
  377. // Try SuSE
  378. else if fpstat(TimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then
  379. GetTimeZoneFile:=TimeZoneFile
  380. // Try RedHat
  381. else If fpstat(AltTimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then
  382. GetTimeZoneFile:=AltTimeZoneFile
  383. {$ifdef BSD}
  384. // else
  385. // If fpstat(BSDTimeZoneFile,{$ifdef oldlinuxstat}baseunix.stat(info){$else}info{$endif})>=0 then
  386. // GetTimeZoneFile:=BSDTimeZoneFile
  387. {$ENDIF}
  388. {$if (defined(darwin) and defined(arm)) or defined(iphonesim)}
  389. else If fpstat(iOSTimeZoneFile,info)>=0 then
  390. GetTimeZoneFile:=iOSTimeZoneFile
  391. {$endif}
  392. end;
  393. {$endif ndef FPC_HAS_GETTIMEZONEFILE}
  394. procedure InitLocalTime;
  395. begin
  396. ReadTimezoneFile(GetTimezoneFile);
  397. RefreshTZInfo;
  398. end;
  399. procedure DoneLocalTime;
  400. begin
  401. if assigned(transitions) then
  402. freemem(transitions);
  403. transitions:=nil;
  404. if assigned(type_idxs) then
  405. freemem(type_idxs);
  406. type_idxs:=nil;
  407. if assigned(types) then
  408. freemem(types);
  409. types:=nil;
  410. if assigned(zone_names) then
  411. freemem(zone_names);
  412. zone_names:=Nil;
  413. if assigned(leaps) then
  414. freemem(leaps);
  415. leaps:=nil;
  416. num_transitions:=0;
  417. num_leaps:=0;
  418. num_types:=0;
  419. end;
  420. Procedure ReReadLocalTime;
  421. begin
  422. LockTZInfo;
  423. DoneLocalTime;
  424. InitLocalTime;
  425. UnlockTZInfo;
  426. end;