timezone.inc 9.4 KB

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