timezone.inc 7.9 KB

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