timezone.inc 7.8 KB

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