ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/fbintf/client/3.0/FB30TimeZoneServices.pas
Revision: 328
Committed: Fri Feb 26 09:33:18 2021 UTC (3 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 37269 byte(s)
Log Message:
Add missing files

File Contents

# Content
1 (*
2 * Firebird Interface (fbintf). The fbintf components provide a set of
3 * Pascal language bindings for the Firebird API. Although predominantly
4 * a new development they include source code taken from IBX and may be
5 * considered a derived product. This software thus also includes the copyright
6 * notice and license conditions from IBX.
7 *
8 * Except for those parts dervied from IBX, contents of this file are subject
9 * to the Initial Developer's Public License Version 1.0 (the "License"); you
10 * may not use this file except in compliance with the License. You may obtain a
11 * copy of the License here:
12 *
13 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
14 *
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing rights
18 * and limitations under the License.
19 *
20 * The Initial Developer of the Original Code is Tony Whyman.
21 *
22 * The Original Code is (C) 2020 Tony Whyman, MWA Software
23 * (http://www.mwasoftware.co.uk).
24 *
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 *)
30 unit FB30TimeZoneServices;
31
32 {$IFDEF MSWINDOWS}
33 {$DEFINE WINDOWS}
34 {$ENDIF}
35
36 {$IFDEF FPC}
37 {$mode delphi}
38 {$codepage UTF8}
39 {$ENDIF}
40
41 {Enable the following to use the current date for GMT/Local Time translations instead
42 of the the Firebird default of 2020/1/1}
43
44 { $DEFINE USECURRENTDATEFORGMTTOLOCALTIME}
45
46 interface
47
48 uses
49 Classes, SysUtils, Firebird, IB, IBExternals, FBActivityMonitor, FBClientAPI,
50 FB30ClientAPI, FBAttachment, FB30Attachment, FBTransaction, FBSQLData,
51 {$IFDEF FPC} contnrs; {$ELSE} Generics.Collections; {$ENDIF}
52
53 type
54 { TFB30TimeZoneServices }
55
56 TFB30TimeZoneServices = class(TFBInterfacedObject, ITimeZoneServices, IExTimeZoneServices, ITransactionUser)
57 private type
58 PTimeZoneInfo = ^TTimeZoneInfo;
59 TTimeZoneInfo = record
60 Starts: TDateTime;
61 Ends: TDateTime;
62 ZoneOffset: Smallint;
63 DstOffset: SmallInt;
64 EffectiveOffset: SmallInt;
65 Prev: PTimeZoneInfo;
66 Next: PTimeZoneInfo;
67 end;
68
69 private type
70 ITimeZone = interface
71 function GetTimeZoneID: TFBTimeZoneID;
72 function GetTimeZoneName: AnsiString;
73 function GetTimeZoneData(timestamp: TDateTime; isLocalTime: boolean): TFB30TimeZoneServices.PTimeZoneInfo;
74 function GetFirstTimeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo;
75 function GetLastTimeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo;
76 function AddTimeZoneInfo(Starts_, Ends_: TDateTime; ZoneOffset_: Smallint;
77 DstOffer_: SmallInt; EffectiveOffset_: SmallInt): TFB30TimeZoneServices.PTimeZoneInfo;
78 function CompareTimeRange(timeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo; timestamp: TDateTime; isLocalTime: boolean): integer;
79 end;
80
81 private type
82 ITimeZoneCache = interface
83 function GetTimeZone(aTimeZoneID: TFBTimeZoneID): TFB30TimeZoneServices.ITimeZone; overload;
84 function GetTimeZone(aTimeZone: AnsiString): TFB30TimeZoneServices.ITimeZone; overload;
85 function AddTimeZone(aTimeZoneID: TFBTimeZoneID; aTimeZone: AnsiString): TFB30TimeZoneServices.ITimeZone;
86 end;
87
88 private
89 FAttachment: TFB30Attachment;
90 FTransaction: ITransaction;
91 FFirebird30ClientAPI: TFB30ClientAPI;
92 FUsingRemoteTZDB: boolean;
93 FTimeZoneCache: ITimeZoneCache;
94 FInLoadTimeZoneData: boolean;
95 FTimeTZDate: TDateTime;
96 FTZTextOption: TTZTextOptions;
97 FServerTZName: AnsiString;
98 function ComputeDstOffset(localtime, gmtTimestamp: TDateTime): integer;
99 function GetTransaction: ITransaction;
100 function GetTimeZoneCache: ITimeZoneCache;
101 function GetTimeZoneData(aTimeZone: ITimeZone; timestamp: TDateTime;
102 isLocalTime: boolean): PTimeZoneInfo;
103 function GetDstOffset(timestamp: TDateTime; timezoneID: TFBTimeZoneID;
104 IsLocalTime: boolean): smallint; overload;
105 function GetDstOffset(timestamp: TDateTime; timezone: AnsiString;
106 IsLocalTime: boolean): smallint; overload;
107 function DecodeGMTTimestampTZ(bufptr: PISC_TIMESTAMP_TZ): TDateTime;
108 function LookupTimeZoneName(aTimeZoneID: TFBTimeZoneID): AnsiString;
109 function LookupTimeZoneID(aTimeZone: AnsiString): TFBTimeZoneID;
110 function LookupTimeZone(aTimeZoneID: TFBTimeZoneID): ITimeZone; overload;
111 function LookupTimeZone(aTimeZone: AnsiString): ITimeZone; overload;
112 function LookupTimeZoneInfo(aTimeZoneID: TFBTimeZoneID; timestamp: TDateTime;
113 isLocalTime: boolean): PTimeZoneInfo; overload;
114 function LookupTimeZoneInfo(aTimeZone: AnsiString; timestamp: TDateTime; isLocalTime: boolean): PTimeZoneInfo; overload;
115 public
116 constructor Create(attachment: TFB30Attachment);
117 destructor Destroy; override;
118
119 public
120 {ITransactionUser}
121 procedure TransactionEnding(aTransaction: ITransaction; Force: boolean);
122
123 public
124 {ITimeZoneServices}
125 function TimeZoneID2TimeZoneName(aTimeZoneID: TFBTimeZoneID): AnsiString;
126 function TimeZoneName2TimeZoneID(aTimeZone: AnsiString): TFBTimeZoneID;
127 function LocalTimeToGMT(aLocalTime: TDateTime; aTimeZone: AnsiString): TDateTime; overload;
128 function LocalTimeToGMT(aLocalTime: TDateTime; aTimeZoneID: TFBTimeZoneID): TDateTime; overload;
129 function GMTToLocalTime(aGMTTime: TDateTime; aTimeZone: AnsiString): TDateTime; overload;
130 function GMTToLocalTime(aGMTTime: TDateTime; aTimeZoneID: TFBTimeZoneID): TDateTime; overload;
131 function GetEffectiveOffsetMins(aLocalTime: TDateTime; aTimeZone: AnsiString): integer; overload;
132 function GetEffectiveOffsetMins(aLocalTime: TDateTime; aTimeZoneID: TFBTimeZoneID): integer; overload;
133
134 {Time Zone DB Information}
135 function UsingRemoteTZDB: boolean;
136 procedure SetUseLocalTZDB(useLocalTZDB: boolean);
137 function GetLocalTimeZoneName: AnsiString;
138 function GetLocalTimeZoneID: TFBTimeZoneID;
139 procedure GetTimeZoneInfo(aTimeZone: AnsiString; OnDate: TDateTime;
140 var ZoneOffset, DSTOffset, EffectiveOffset: integer);
141 function GetTimeTZDate: TDateTime;
142 procedure SetTimeTZDate(aDate: TDateTime);
143 function GetTZTextOption: TTZTextOptions;
144 procedure SetTZTextOption(aOptionValue: TTZTextOptions);
145
146 public
147 {IExTimeZoneServices}
148 procedure EncodeTimestampTZ(timestamp: TDateTime; timezoneID: TFBTimeZoneID;
149 bufptr: PByte); overload;
150 procedure EncodeTimestampTZ(timestamp: TDateTime; timezone: AnsiString;
151 bufptr: PByte); overload;
152 procedure EncodeTimeTZ(time: TDateTime; timezoneID: TFBTimeZoneID; OnDate: TDateTime;
153 bufptr: PByte); overload;
154 procedure EncodeTimeTZ(time: TDateTime; timezone: AnsiString; OnDate: TDateTime;
155 bufptr: PByte); overload;
156 procedure DecodeTimestampTZ(bufptr: PByte; var timestamp: TDateTime;
157 var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
158 procedure DecodeTimestampTZ(bufptr: PByte; var timestamp: TDateTime;
159 var dstOffset: smallint; var timezone: AnsiString); overload;
160 procedure DecodeTimestampTZEx(bufptr: PByte; var timestamp: TDateTime;
161 var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
162 procedure DecodeTimestampTZEx(bufptr: PByte; var timestamp: TDateTime;
163 var dstOffset: smallint; var timezone: AnsiString); overload;
164 procedure DecodeTimeTZ(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
165 var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
166 procedure DecodeTimeTZ(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
167 var dstOffset: smallint; var timezone: AnsiString); overload;
168 procedure DecodeTimeTZEx(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
169 var dstOffset: smallint; var timezoneID: TFBTimeZoneID); overload;
170 procedure DecodeTimeTZEx(bufptr: PByte; OnDate: TDateTime; var time: TDateTime;
171 var dstOffset: smallint; var timezone: AnsiString); overload;
172 end;
173
174 implementation
175
176 uses DateUtils, IBUtils, FBMessages;
177
178 type
179 {$IFDEF FPC}
180 TTZHashTable = TFPHashList;
181 {$ELSE}
182 TTZHashTable = TDictionary<AnsiString,integer>;
183 {$ENDIF}
184
185
186 { TTimeZoneCache }
187
188 TTimeZoneCache = class(TFBInterfacedObject,TFB30TimeZoneServices.ITimeZoneCache)
189 private const
190 MaxZoneID = High(TFBTimeZoneID);
191 private
192 FTimeZoneIDIndex: array of TFB30TimeZoneServices.ITimeZone;
193 FTimeZoneNameIndex: TTZHashTable;
194 FLowValue: integer;
195 public
196 constructor Create(aLowValue: integer);
197 destructor Destroy; override;
198 public
199 {ITimeZoneCache}
200 function GetTimeZone(aTimeZoneID: TFBTimeZoneID): TFB30TimeZoneServices.ITimeZone; overload;
201 function GetTimeZone(aTimeZone: AnsiString): TFB30TimeZoneServices.ITimeZone; overload;
202 function AddTimeZone(aTimeZoneID: TFBTimeZoneID; aTimeZone: AnsiString): TFB30TimeZoneServices.ITimeZone;
203 end;
204
205 {The TTimeZone class provides information about a time zone, including its
206 ID, name and a cache of time zone records where each record provides the
207 offset in minutes to GMT and any applicable daylight savings time offset
208 for a timestamp range (in GMT). The time zone records are held as a
209 bidirectional linked list.}
210
211 TTimeZone = class(TFBInterfacedObject,TFB30TimeZoneServices.ITimeZone)
212 private
213 FTimeZoneID: TFBTimeZoneID;
214 FTimeZone: AnsiString;
215 FFirst: TFB30TimeZoneServices.PTimeZoneInfo;
216 FLast: TFB30TimeZoneServices.PTimeZoneInfo;
217 FCurrent: TFB30TimeZoneServices.PTimeZoneInfo;
218 public
219 constructor Create(aTimeZoneID: TFBTimeZoneID; aTimeZone: AnsiString);
220 destructor Destroy; override;
221 public
222 {ITimeZone}
223 function GetTimeZoneID: TFBTimeZoneID;
224 function GetTimeZoneName: AnsiString;
225 function GetTimeZoneData(timestamp: TDateTime; isLocalTime: boolean): TFB30TimeZoneServices.PTimeZoneInfo;
226 function GetFirstTimeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo;
227 function GetLastTimeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo;
228 function AddTimeZoneInfo(Starts_, Ends_: TDateTime; ZoneOffset_: Smallint;
229 DstOffset_: SmallInt; EffectiveOffset_: SmallInt): TFB30TimeZoneServices.PTimeZoneInfo;
230 function CompareTimeRange(timeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo; timestamp: TDateTime; isLocalTime: boolean): integer;
231 end;
232
233 constructor TTimeZone.Create(aTimeZoneID: TFBTimeZoneID; aTimeZone: AnsiString);
234 begin
235 inherited Create;
236 FTimeZoneID := aTimeZoneID;
237 FTimeZone := aTimeZone;
238 end;
239
240 destructor TTimeZone.Destroy;
241 var P: TFB30TimeZoneServices.PTimeZoneInfo;
242 tmp: TFB30TimeZoneServices.PTimeZoneInfo;
243 begin
244 P := FFirst;
245 while p <> nil do
246 begin
247 tmp := P^.Next;
248 dispose(P);
249 P := tmp;
250 end;
251 inherited Destroy;
252 end;
253
254 function TTimeZone.GetTimeZoneID: TFBTimeZoneID;
255 begin
256 Result := FTimeZoneID;
257 end;
258
259 function TTimeZone.GetTimeZoneName: AnsiString;
260 begin
261 Result := FTimeZone;
262 end;
263
264 {Walk the linked list from FCurrent to find the TimeZoneInfo record for the timestamp.
265 Returns a nil interface if not record present.}
266
267 function TTimeZone.GetTimeZoneData(timestamp: TDateTime; isLocalTime: boolean
268 ): TFB30TimeZoneServices.PTimeZoneInfo;
269 var CompareFlag: integer;
270 begin
271 Result := nil;
272 if FCurrent = nil then
273 FCurrent := FFirst;
274
275 while FCurrent <> nil do
276 begin
277 CompareFlag := CompareTimeRange(FCurrent,timestamp,isLocalTime);
278 case CompareFlag of
279 -1:
280 if (FCurrent^.Prev <> nil) and (CompareTimeRange(FCurrent^.Prev,timestamp,isLocalTime) > 0) then
281 FCurrent := nil
282 else
283 FCurrent := FCurrent^.Prev;
284 0:
285 begin
286 Result := FCurrent;
287 Exit;
288 end;
289 1:
290 if (FCurrent^.Next <> nil) and (CompareTimeRange(FCurrent^.Next,timestamp,isLocalTime) < 0) then
291 FCurrent := nil
292 else
293 FCurrent := FCurrent^.Next;
294 end;
295 end;
296 Result := FCurrent;
297 end;
298
299 function TTimeZone.GetFirstTimeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo;
300 begin
301 Result := FFirst;
302 end;
303
304 function TTimeZone.GetLastTimeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo;
305 begin
306 Result := FLast;
307 end;
308
309 {Adds a new Time Zone Info record by inserting it into the list in start
310 time ascending order.}
311
312 function TTimeZone.AddTimeZoneInfo(Starts_, Ends_: TDateTime;
313 ZoneOffset_: Smallint; DstOffset_: SmallInt; EffectiveOffset_: SmallInt
314 ): TFB30TimeZoneServices.PTimeZoneInfo;
315 var P: TFB30TimeZoneServices.PTimeZoneInfo;
316 P1: TFB30TimeZoneServices.PTimeZoneInfo;
317 begin
318 {create and initialise new entry}
319 new(Result);
320 with Result^ do
321 begin
322 Starts := Starts_;
323 Ends := Ends_;
324 ZoneOffset := ZoneOffset_;
325 DstOffset := DstOffset_;
326 EffectiveOffset := EffectiveOffset_;
327 end;
328
329 {empty list? then insert at front}
330 if FFirst = nil then
331 begin
332 FFirst := Result;
333 FCurrent := Result;
334 FLast := Result;
335 Result^.Prev := nil;
336 Result^.Next := nil;
337 end
338 else
339 {Before first entry in list? then insert before}
340 if Result^.Starts < FFirst^.Starts then
341 begin
342 Result^.Next := FFirst;
343 Result^.Prev := nil;
344 FFirst := Result;
345 end
346 else
347 {walk the list to find where we have to insert new entry}
348 begin
349 P := FFirst^.Next;
350 P1 := FFirst;
351 while (P <> nil) and (Result^.Starts > P^.Ends) do
352 begin
353 P1 := P;
354 P := P^.Next;
355 end;
356
357 {ignore duplicate entry}
358 if (P <> nil) and (Result^.Starts = P^.Starts) then
359 begin
360 dispose(Result);
361 Result := P;
362 end
363 else
364 {either at end of list (P=nil) or we insert after P1}
365 begin
366 Result^.Next := P;
367 Result^.Prev := P1;
368 P1^.Next := Result;
369 if P <> nil then {P=nil => at end of list}
370 P^.Prev := Result
371 else
372 FLast := Result;
373 end
374 end;
375 end;
376
377 function TTimeZone.CompareTimeRange(
378 timeZoneInfo: TFB30TimeZoneServices.PTimeZoneInfo; timestamp: TDateTime;
379 isLocalTime: boolean): integer;
380 begin
381 if isLocalTime then {adjust to GMT}
382 timestamp := IncMinute(timestamp,-timeZoneInfo^.EffectiveOffset);
383
384 if timestamp < timeZoneInfo^.Starts then
385 Result := -1
386 else
387 if timestamp > timeZoneInfo^.Ends then
388 Result := 1
389 else
390 Result := 0;
391 end;
392
393 { TTimeZoneCache }
394
395 constructor TTimeZoneCache.Create(aLowValue: integer);
396 begin
397 inherited Create;
398 FTimeZoneNameIndex := TTZHashTable.Create;
399 SetLength(FTimeZoneIDIndex,MaxZoneID - aLowValue + 1);
400 FLowValue := aLowValue;
401 end;
402
403 destructor TTimeZoneCache.Destroy;
404 var i: integer;
405 begin
406 for i := Low(FTimeZoneIDIndex) to high(FTimeZoneIDIndex) do
407 FTimeZoneIDIndex[i] := nil;
408 if FTimeZoneNameIndex <> nil then FTimeZoneNameIndex.Free;
409 inherited Destroy;
410 end;
411
412 function TTimeZoneCache.GetTimeZone(aTimeZoneID: TFBTimeZoneID
413 ): TFB30TimeZoneServices.ITimeZone;
414 begin
415 Result := FTimeZoneIDIndex[aTimeZoneID - FLowValue];
416 end;
417
418 function TTimeZoneCache.GetTimeZone(aTimeZone: AnsiString
419 ): TFB30TimeZoneServices.ITimeZone;
420 {$IFDEF FPC}
421 var index: Pointer;
422 begin
423 index := FTimeZoneNameIndex.Find(aTimeZone);
424 if index = nil then
425 Result := nil
426 else
427 Result := FTimeZoneIDIndex[PtrUInt(index)];
428 end;
429 {$ELSE}
430 var index: integer;
431 begin
432 Result := nil;
433 if FTimeZoneNameIndex.TryGetValue(aTimeZone,index) then
434 Result := FTimeZoneIDIndex[index];
435 end;
436 {$ENDIF}
437
438 function TTimeZoneCache.AddTimeZone(aTimeZoneID: TFBTimeZoneID;
439 aTimeZone: AnsiString): TFB30TimeZoneServices.ITimeZone;
440 {$IFDEF FPC}
441 var index: PtrUInt;
442 {$ELSE}
443 var index: integer;
444 {$ENDIF}
445 begin
446 Result := nil;
447 if aTimeZoneID < FLowValue then
448 IBError(ibxeInvalidTimeZoneID,[aTimeZoneID]);
449
450 index := aTimeZoneID - FLowValue;
451 if FTimeZoneIDIndex[index] = nil then
452 begin
453 Result := TTimeZone.Create(aTimeZoneID,aTimeZone);
454 FTimeZoneIDIndex[index] := Result;
455 {$IFDEF FPC}
456 FTimeZoneNameIndex.Add(aTimeZone,Pointer(index));
457 {$ELSE}
458 FTimeZoneNameIndex.Add(aTimeZone,index);
459 {$ENDIF}
460 end;
461 end;
462
463 { TFB30TimeZoneServices }
464
465 function TFB30TimeZoneServices.ComputeDstOffset(localtime,
466 gmtTimestamp: TDateTime): integer;
467 begin
468 Result := Round(MinuteSpan(localtime,gmtTimestamp));
469 if gmtTimestamp > localtime then
470 Result := -Result;
471 end;
472
473 function TFB30TimeZoneServices.GetTransaction: ITransaction;
474 begin
475 if FTransaction = nil then
476 begin
477 FTransaction := FAttachment.StartTransaction([isc_tpb_read,isc_tpb_wait,isc_tpb_concurrency],taCommit);
478 (FTransaction as TFBTransaction).AddObject(self);
479 end;
480 Result := FTransaction;
481 end;
482
483 function TFB30TimeZoneServices.GetTimeZoneCache: ITimeZoneCache;
484 var Data: IResultSet;
485 begin
486 if FTimeZoneCache = nil then
487 begin
488 Data := FAttachment.OpenCursorAtStart(GetTransaction,
489 'select min(RDB$TIME_ZONE_ID) as LowValue From RDB$TIME_ZONES');
490 FTimeZoneCache := TTimeZoneCache.Create(Data[0].AsInteger);
491 end;
492 Result := FTimeZoneCache;
493 end;
494
495 {This method returns the TimeZoneInfo record for the TimeZone and for which the
496 timestamp is between the start and end time for the record. If a matching record
497 does not exist in the cache then the remote TZ Database is queried for records
498 five years earlier than the timestamp and these are entered into the cache.
499 If one of the returned record is a match for the timestamp then it is returned
500 as the result of the function.
501
502 Note: the function may return nil if there is no matching record in either the
503 cache or remote database. For a localtime, this may be because it is an invalid
504 timestamp when taking daylight savings time adjusts into account.}
505
506 function TFB30TimeZoneServices.GetTimeZoneData(aTimeZone: ITimeZone;
507 timestamp: TDateTime; isLocalTime: boolean): PTimeZoneInfo;
508 var Data: IResultSet;
509 TimeZoneInfo: PTimeZoneInfo;
510 begin
511 Result := aTimeZone.GetTimeZoneData(timestamp,isLocalTime);
512 if Result = nil then
513 begin
514 FInLoadTimeZoneData := true;
515 // writeln('Looking up time zone data for ',aTimeZone.GetTimeZoneName,' at ',DateTimeToStr(timestamp));
516 try
517 {Lookup remote time zone database for ten year range and add result to cache}
518 with FAttachment.Prepare(GetTransaction,'Select * From rdb$time_zone_util.transitions(?,?,?)') do
519 begin
520 SQLParams[0].AsString := aTimeZone.GetTimeZoneName;
521 SQLParams[1].AsDateTime := timestamp;
522 SQLParams[2].AsDateTime := timestamp;
523 Data := OpenCursor;
524 while Data.FetchNext do
525 begin
526 TimeZoneInfo := aTimeZone.AddTimeZoneInfo(Data[0].AsDateTime,Data[1].AsDateTime,
527 Data[2].AsInteger,Data[3].AsInteger,
528 Data[4].AsInteger);
529 if aTimeZone.CompareTimeRange(TimeZoneInfo,timestamp,isLocalTime) = 0 then
530 Result := TimeZoneInfo;
531 end;
532 end;
533 finally
534 FInLoadTimeZoneData:= false;
535 end;
536 end
537 // else
538 // writeln('Cache hit for ',aTimeZone.GetTimeZoneName,' at ',DateTimeToStr(timestamp), ' Row Starts at ',DateTimeToStr(Result^.Starts));
539 end;
540
541 function TFB30TimeZoneServices.GetDstOffset(timestamp: TDateTime;
542 timezoneID: TFBTimeZoneID; IsLocalTime: boolean): smallint;
543 var TimeZoneInfo: PTimeZoneInfo;
544 begin
545 if DateOf(timestamp) = 0 then
546 timestamp := timestamp + GetTimeTZDate;
547 if FInLoadTimeZoneData then
548 Result := 0 {Assume GMT}
549 else
550 if timeZoneID < MaxOffsetTimeZoneID then
551 begin
552 if IsLocalTime then
553 Result := TimeZoneDisplacementDelta - timezoneID
554 else
555 Result := timezoneID - TimeZoneDisplacementDelta
556 end
557 else
558 if not FUsingRemoteTZDB then
559 Result := GetDstOffset(timestamp,TimeZoneID2TimeZoneName(timezoneID),isLocalTime)
560 else
561 begin
562 TimeZoneInfo := LookupTimeZoneInfo(timezoneID,timestamp,isLocalTime);
563 if TimeZoneInfo <> nil then
564 begin
565 Result := TimeZoneInfo^.EffectiveOffset;
566 if IsLocalTime then
567 Result := -Result;
568 end
569 else
570 IBError(ibxeBadTimestampOrNoTimeZoneDBEntry,[DateTimeToStr(timestamp),timezoneID]);
571 end;
572 end;
573
574 function TFB30TimeZoneServices.GetDstOffset(timestamp: TDateTime;
575 timezone: AnsiString; IsLocalTime: boolean): smallint;
576 var gmtTimeStamp: TDateTime;
577 Buffer: ISC_TIMESTAMP_TZ;
578 begin
579 if DateOf(timestamp) = 0 then
580 timestamp := timestamp + GetTimeTZDate;
581 if not FUsingRemoteTZDB then
582 begin
583 EncodeTimestampTZ(timestamp,timezone,@Buffer);
584 gmtTimeStamp := FFirebird30ClientAPI.SQLDecodeDateTime(@Buffer);
585 Result := -ComputeDstOffset(timestamp,gmtTimestamp);
586 end
587 else
588 Result := GetDstOffset(timestamp,TimeZoneName2TimeZoneID(timezone),isLocalTime);
589 end;
590
591 function TFB30TimeZoneServices.DecodeGMTTimestampTZ(bufptr: PISC_TIMESTAMP_TZ
592 ): TDateTime;
593 var Yr, Mn, Dy: word;
594 Hr, Mt, S: word;
595 DMs: cardinal;
596 begin
597 with FFirebird30ClientAPI do
598 begin
599 UtilIntf.DecodeDate(bufptr^.utc_timestamp.timestamp_date,@Yr, @Mn, @Dy);
600 UtilIntf.DecodeTime(bufptr^.utc_timestamp.timestamp_time,@Hr, @Mt, @S, @DMs);
601 Result := EncodeDate(Yr, Mn, Dy) + FBEncodeTime(Hr,Mt,S,DMs);
602 end
603 end;
604
605 function TFB30TimeZoneServices.LookupTimeZoneName(aTimeZoneID: TFBTimeZoneID
606 ): AnsiString;
607 begin
608 Result := LookupTimeZone(aTimeZoneID).GetTimeZoneName;
609 end;
610
611 function TFB30TimeZoneServices.LookupTimeZoneID(aTimeZone: AnsiString
612 ): TFBTimeZoneID;
613 var dstOffset: integer;
614 begin
615 if DecodeTimeZoneOffset(aTimeZone,dstOffset) then
616 Result := dstOffset + TimeZoneDisplacementDelta
617 else
618 Result := LookupTimeZone(aTimeZone).GetTimeZoneID;
619 end;
620
621 function TFB30TimeZoneServices.LookupTimeZone(aTimeZoneID: TFBTimeZoneID
622 ): ITimeZone;
623 var aTimeZone: Ansistring;
624 begin
625 Result := GetTimeZoneCache.GetTimeZone(aTimeZoneID);
626 if Result = nil then
627 begin
628 try
629 aTimeZone := FAttachment.OpenCursorAtStart(GetTransaction,
630 'Select Trim(RDB$TIME_ZONE_NAME) as RDB$TIME_ZONE_NAME From RDB$TIME_ZONES Where RDB$TIME_ZONE_ID = ?',3,
631 [aTimeZoneID])[0].AsString;
632 except
633 IBError(ibxeBadTimeZoneID,[aTimeZoneID,0]);
634 end;
635 Result := GetTimeZoneCache.AddTimeZone(aTimeZoneID,aTimeZone);
636 end;
637 end;
638
639 function TFB30TimeZoneServices.LookupTimeZone(aTimeZone: AnsiString): ITimeZone;
640 var aTimeZoneID: TFBTimeZoneID;
641 begin
642 Result := GetTimeZoneCache.GetTimeZone(aTimeZone);
643 if Result = nil then
644 begin
645 try
646 aTimeZoneID := FAttachment.OpenCursorAtStart(GetTransaction,
647 'Select RDB$TIME_ZONE_ID From RDB$TIME_ZONES Where RDB$TIME_ZONE_Name = ?',3,
648 [aTimeZone])[0].AsInteger;
649 except
650 IBError(ibxeBadTimeZoneName,[aTimeZone]);
651 end;
652 Result := GetTimeZoneCache.AddTimeZone(aTimeZoneID,aTimeZone);
653 end;
654 end;
655
656 function TFB30TimeZoneServices.LookupTimeZoneInfo(aTimeZoneID: TFBTimeZoneID;
657 timestamp: TDateTime; isLocalTime: boolean): PTimeZoneInfo;
658 var TimeZone: ITimeZone;
659 begin
660 TimeZone := LookupTimeZone(aTimeZoneID);
661 if TimeZone <> nil then
662 Result := GetTimeZoneData(TimeZone,timestamp,isLocalTime)
663 else
664 Result := nil;
665 end;
666
667 function TFB30TimeZoneServices.LookupTimeZoneInfo(aTimeZone: AnsiString;
668 timestamp: TDateTime; isLocalTime: boolean): PTimeZoneInfo;
669 var TimeZone: ITimeZone;
670 begin
671 TimeZone := LookupTimeZone(aTimeZone);
672 if TimeZone <> nil then
673 Result := GetTimeZoneData(TimeZone,timestamp,isLocalTime)
674 else
675 Result := nil;
676 end;
677
678 constructor TFB30TimeZoneServices.Create(attachment: TFB30Attachment);
679 begin
680 inherited Create;
681 FAttachment := attachment;
682 FFirebird30ClientAPI := attachment.Firebird30ClientAPI;
683 FUsingRemoteTZDB := true;
684 {$IFDEF USECURRENTDATEFORGMTTOLOCALTIME}
685 FTimeTZDate := Sysutils.Date;
686 {$ELSE}
687 FTimeTZDate := EncodeDate(2020,1,1);
688 {$ENDIF}
689 end;
690
691 destructor TFB30TimeZoneServices.Destroy;
692 begin
693 if FTransaction <> nil then
694 (FTransaction as TFBTransaction).Remove(self);
695 FTransaction := nil;
696 FTimeZoneCache := nil;
697 inherited Destroy;
698 end;
699
700 procedure TFB30TimeZoneServices.TransactionEnding(aTransaction: ITransaction;
701 Force: boolean);
702 begin
703 if (aTransaction as TObject) = (FTransaction as TObject) then
704 begin
705 (FTransaction as TFBTransaction).Remove(self);
706 FTransaction := nil;
707 end;
708 end;
709
710 procedure TFB30TimeZoneServices.EncodeTimestampTZ(timestamp: TDateTime;
711 timezoneID: TFBTimeZoneID; bufptr: PByte);
712 var
713 Yr, Mn, Dy: word;
714 Hr, Mt, S: word;
715 DMs: cardinal;
716 begin
717 if not FUsingRemoteTZDB then
718 EncodeTimestampTZ(timestamp,TimeZoneID2TimeZoneName(timezoneID),bufptr)
719 else
720 with FFirebird30ClientAPI do
721 begin
722 timestamp := LocalTimeToGMT(timestamp,timezoneID);
723 DecodeDate(timestamp, Yr, Mn, Dy);
724 FBDecodeTime(timestamp, Hr, Mt, S, DMs);
725 with PISC_TIMESTAMP_TZ(Bufptr)^ do
726 begin
727 utc_timestamp.timestamp_date := UtilIntf.encodeDate(Yr, Mn, Dy);
728 utc_timestamp.timestamp_time := UtilIntf.encodeTime(Hr, Mt, S, DMs);
729 time_zone := timezoneID;
730 end;
731 end
732 end;
733
734 procedure TFB30TimeZoneServices.EncodeTimestampTZ(timestamp: TDateTime; timezone: AnsiString;
735 bufptr: PByte);
736 var
737 Yr, Mn, Dy: word;
738 Hr, Mt, S: word;
739 DMs: cardinal;
740 begin
741 if FUsingRemoteTZDB then
742 EncodeTimestampTZ(timestamp,TimezoneName2TimeZoneID(timezone),bufptr)
743 else
744 with FFirebird30ClientAPI do
745 begin
746 DecodeDate(timestamp, Yr, Mn, Dy);
747 FBDecodeTime(timestamp, Hr, Mt, S, DMs);
748 if timezone = '' then
749 timezone := GetLocalTimeZoneName;
750 UtilIntf.encodeTimeStampTz(StatusIntf,ISC_TIMESTAMP_TZPtr(bufPtr),Yr, Mn, Dy, Hr, Mt, S, DMs,PAnsiChar(timezone));
751 Check4DataBaseError;
752 end;
753 end;
754
755 {When encoding a time it must first be converted to GMT on a given date.}
756
757 procedure TFB30TimeZoneServices.EncodeTimeTZ(time: TDateTime;
758 timezoneID: TFBTimeZoneID; OnDate: TDateTime; bufptr: PByte);
759 var localtime: TDateTime;
760 buffer: ISC_TIMESTAMP_TZ;
761 begin
762 localtime := DateOf(OnDate) + time;
763 EncodeTimestampTZ(localtime,timezoneID,@buffer);
764 PISC_TIME_TZ(bufptr)^.utc_time := buffer.utc_timestamp.timestamp_time;
765 PISC_TIME_TZ(bufptr)^.time_zone := buffer.time_zone;
766 end;
767
768 procedure TFB30TimeZoneServices.EncodeTimeTZ(time: TDateTime;
769 timezone: AnsiString; OnDate: TDateTime; bufptr: PByte);
770 var localtime: TDateTime;
771 buffer: ISC_TIMESTAMP_TZ;
772 begin
773 localtime := DateOf(OnDate) + time;
774 EncodeTimestampTZ(localtime,timezone,@buffer);
775 PISC_TIME_TZ(bufptr)^.utc_time := buffer.utc_timestamp.timestamp_time;
776 PISC_TIME_TZ(bufptr)^.time_zone := buffer.time_zone;
777 end;
778
779 procedure TFB30TimeZoneServices.DecodeTimestampTZ(bufptr: PByte;
780 var timestamp: TDateTime; var dstOffset: smallint;
781 var timezoneID: TFBTimeZoneID);
782
783 var aTimeZone: AnsiString;
784 gmtTimestamp: TDateTime;
785 begin
786 if not FUsingRemoteTZDB then
787 begin
788 DecodeTimestampTZ(bufptr,timestamp,dstOffset,aTimeZone);
789 timezoneID := PISC_TIMESTAMP_TZ(bufptr)^.time_zone;
790 end
791 else
792 with FFirebird30ClientAPI do
793 begin
794 gmtTimestamp := DecodeGMTTimestampTZ(PISC_TIMESTAMP_TZ(bufptr));
795 timezoneID := PISC_TIMESTAMP_TZ(bufptr)^.time_zone;
796 dstOffset := GetDstOffset(gmtTimestamp,timezoneID,false);
797 timestamp := IncMinute(gmtTimestamp, dstOffset);
798 end;
799 end;
800
801 procedure TFB30TimeZoneServices.DecodeTimestampTZ(bufptr: PByte;
802 var timestamp: TDateTime; var dstOffset: smallint; var timezone: AnsiString);
803 const
804 bufLength = 128;
805
806 var Yr, Mn, Dy: cardinal;
807 Hr, Mt, S: cardinal;
808 DMs: cardinal;
809 tzBuffer: array[ 0.. bufLength] of AnsiChar;
810 gmtTimestamp: TDateTime;
811 timezoneID: TFBTimeZoneID;
812 begin
813 if FUsingRemoteTZDB then
814 begin
815 DecodeTimestampTZ(bufptr,timestamp,dstOffset,timezoneID);
816 timezone := TimeZoneID2TimeZoneName(timeZoneID);
817 end
818 else
819 with FFirebird30ClientAPI do
820 begin
821 UtilIntf.decodeTimeStampTz(StatusIntf,ISC_TIMESTAMP_TZPtr(bufPtr),@Yr,@ Mn, @Dy, @Hr, @Mt, @S, @DMs,bufLength,PAnsiChar(@tzBuffer));
822 Check4DataBaseError;
823 timestamp := EncodeDate(Yr, Mn, Dy) + FBEncodeTime(Hr,Mt,S,DMs);
824 timezone := strpas(PAnsiChar(@tzBuffer));
825 gmtTimestamp := DecodeGMTTimestampTZ(PISC_TIMESTAMP_TZ(bufptr));
826 dstOffset := ComputeDstOffset(timestamp,gmtTimestamp);
827 end;
828 end;
829
830 procedure TFB30TimeZoneServices.DecodeTimestampTZEx(bufptr: PByte;
831 var timestamp: TDateTime; var dstOffset: smallint;
832 var timezoneID: TFBTimeZoneID);
833 var timezone: AnsiString;
834 gmtTimestamp: TDateTime;
835 begin
836 if not FUsingRemoteTZDB then
837 begin
838 DecodeTimestampTZEx(bufptr,timestamp,dstOffset,timezone);
839 timezoneID := PISC_TIMESTAMP_TZ(bufptr)^.time_zone;
840 end
841 else
842 with FFirebird30ClientAPI do
843 begin
844 gmtTimestamp := DecodeGMTTimestampTZ(PISC_TIMESTAMP_TZ(bufptr));
845 timezoneID := PISC_TIMESTAMP_TZ_EX(bufptr)^.time_zone;
846 dstOffset := PISC_TIMESTAMP_TZ_EX(bufptr)^.ext_offset;
847 timestamp := IncMinute(gmtTimestamp, dstOffset);
848 end;
849 end;
850
851 procedure TFB30TimeZoneServices.DecodeTimestampTZEx(bufptr: PByte;
852 var timestamp: TDateTime; var dstOffset: smallint; var timezone: AnsiString);
853
854 const
855 bufLength = 128;
856 var
857 Yr, Mn, Dy: cardinal;
858 Hr, Mt, S, DMs: cardinal;
859 tzBuffer: array[ 0.. bufLength] of AnsiChar;
860 timezoneID: TFBTimeZoneID;
861 begin
862 if FUsingRemoteTZDB then
863 begin
864 DecodeTimestampTZEx(bufptr,timestamp,dstOffset,timezoneID);
865 timezone := TimeZoneID2TimeZoneName(timezoneID);
866 end
867 else
868 with FFirebird30ClientAPI do
869 begin
870 if not HasExtendedTZSupport then
871 IBError(ibxeNotSupported,[]);
872
873 UtilIntf.decodeTimeStampTzEx(StatusIntf,ISC_TIMESTAMP_TZ_EXPtr(bufPtr),@Yr,@ Mn, @Dy, @Hr, @Mt, @S, @DMs,bufLength,PAnsiChar(@tzBuffer));
874 Check4DataBaseError;
875 timestamp := EncodeDate(Yr, Mn, Dy) + FBEncodeTime(Hr,Mt,S,DMs);
876 dstOffset := ISC_TIMESTAMP_TZ_EXPtr(bufPtr)^.ext_offset;
877 timezone := strpas(PAnsiChar(@tzBuffer));
878 end;
879 end;
880
881 procedure TFB30TimeZoneServices.DecodeTimeTZ(bufptr: PByte; OnDate: TDateTime;
882 var time: TDateTime; var dstOffset: smallint; var timezoneID: TFBTimeZoneID);
883
884 var
885 Hr, Mt, S, DMs: cardinal;
886 gmtTime: TDateTime;
887 gmtTimestamp: TDateTime;
888 aTimeZone: AnsiString;
889 begin
890 timezoneID := PISC_TIME_TZ(bufptr)^.time_zone;
891 if FUsingRemoteTZDB then
892 with FFirebird30ClientAPI do
893 begin
894 {decode the GMT time}
895 UtilIntf.decodeTime(PISC_TIME_TZ(bufptr)^.utc_time, @Hr, @Mt, @S, @DMs);
896 gmtTime := FBEncodeTime(Hr, Mt, S, DMs);
897
898 {expand to a timestamp}
899 gmtTimestamp := DateOf(OnDate) + gmtTime;
900
901 dstOffset := GetDstOffset(gmtTimestamp,timezoneID,false);
902 time := TimeOf(IncMinute(gmtTimestamp,dstOffset));
903 end
904 else
905 DecodeTimeTZ(bufptr,OnDate,time,dstOffset,aTimeZone);
906 end;
907
908 procedure TFB30TimeZoneServices.DecodeTimeTZ(bufptr: PByte; OnDate: TDateTime;
909 var time: TDateTime; var dstOffset: smallint; var timezone: AnsiString);
910 var aTimeZoneID: TFBTimeZoneID;
911 Hr, Mt, S, DMs: cardinal;
912 gmtTime: TDateTime;
913 gmtTimestamp: TDateTime;
914 localtimestamp: TDateTime;
915 tmptimestamp: ISC_TIMESTAMP_TZ;
916 begin
917 if FUsingRemoteTZDB then
918 begin
919 DecodeTimeTZ(bufptr,OnDate,time,dstOffset,aTimeZoneID);
920 timezone := TimeZoneID2TimeZoneName(aTimeZoneID);
921 end
922 else
923 with FFirebird30ClientAPI do
924 begin
925 {decode the GMT time}
926 UtilIntf.decodeTime(PISC_TIME_TZ(bufptr)^.utc_time, @Hr, @Mt, @S, @DMs);
927 gmtTime := FBEncodeTime(Hr, Mt, S, DMs);
928
929 {expand to a timestamp}
930 gmtTimestamp := DateOf(OnDate) + gmtTime;
931
932 {re-encode as a timestamp}
933 SQLEncodeDateTime(gmtTimeStamp,@(tmpTimestamp.utc_timestamp));
934 tmpTimestamp.time_zone := PISC_TIME_TZ(bufptr)^.time_zone;
935
936 {Decode to local time using local TZ data}
937 DecodeTimestampTZ(@tmpTimestamp,localtimestamp,dstOffset,timezone);
938 time := TimeOf(localtimestamp);
939 end;
940 end;
941
942 procedure TFB30TimeZoneServices.DecodeTimeTZEx(bufptr: PByte;
943 OnDate: TDateTime; var time: TDateTime; var dstOffset: smallint;
944 var timezone: AnsiString);
945
946 var timezoneID: TFBTimeZoneID;
947 begin
948 DecodeTimeTZEx(bufptr,OnDate,time,dstOffset,timezoneID);
949 timezone := TimeZoneID2TimeZoneName(timezoneID);
950 end;
951
952 procedure TFB30TimeZoneServices.DecodeTimeTZEx(bufptr: PByte;
953 OnDate: TDateTime; var time: TDateTime; var dstOffset: smallint;
954 var timezoneID: TFBTimeZoneID);
955
956 var
957 Hr, Mt, S, DMs: cardinal;
958 gmtTime: TDateTime;
959 gmtTimestamp: TDateTime;
960 begin
961 with FFirebird30ClientAPI do
962 begin
963 {decode the GMT time}
964 UtilIntf.decodeTime(PISC_TIME_TZ_EX(bufptr)^.utc_time, @Hr, @Mt, @S, @DMs);
965 gmtTime := FBEncodeTime(Hr, Mt, S, DMs);
966
967 {expand to a timestamp}
968 gmtTimestamp := DateOf(OnDate) + gmtTime;
969 timezoneID := PISC_TIME_TZ_EX(bufptr)^.time_zone;
970 dstOffset := GetDstOffset(gmtTimestamp,timezoneID,false);
971 {dstOffset := PISC_TIME_TZ_EX(bufptr)^.ext_offset; {--ignored see CORE6328}
972
973 time := TimeOf(IncMinute(gmtTimestamp,dstOffset));
974 end;
975 end;
976
977 function TFB30TimeZoneServices.TimeZoneID2TimeZoneName(
978 aTimeZoneID: TFBTimeZoneID): AnsiString;
979 const
980 bufLength = 128;
981 var Buffer: ISC_TIME_TZ;
982 Hr, Mt, S, DMs: cardinal;
983 tzBuffer: array[ 0.. bufLength] of AnsiChar;
984 begin
985 if aTimeZoneID < MaxOffsetTimeZoneID then {Time Zone ID is for an offset}
986 Result := FormatTimeZoneOffset(aTimeZoneID - TimeZoneDisplacementDelta)
987 else
988 with FFirebird30ClientAPI do
989 if not FUsingRemoteTZDB then
990 begin
991 Buffer.utc_time := 0;
992 Buffer.time_zone := aTimeZoneID;
993 UtilIntf.decodeTimeTz(StatusIntf, @Buffer,@Hr, @Mt, @S, @DMs,bufLength,PAnsiChar(@tzBuffer));
994 Check4DataBaseError;
995 Result := strpas(PAnsiChar(@tzBuffer));
996 end
997 else
998 Result := LookupTimeZoneName(aTimeZoneID);
999 end;
1000
1001 function TFB30TimeZoneServices.TimeZoneName2TimeZoneID(aTimeZone: AnsiString
1002 ): TFBTimeZoneID;
1003 var Buffer: ISC_TIME_TZ;
1004 dstOffset: integer;
1005 begin
1006 with FFirebird30ClientAPI do
1007 if Trim(aTimeZone) = '' then
1008 begin
1009 aTimeZone := GetLocalTimeZoneName;
1010 Result := LookupTimeZoneID(aTimeZone);
1011 end
1012 else
1013 if DecodeTimeZoneOffset(aTimeZone,dstOffset) then
1014 Result := dstOffset + TimeZoneDisplacementDelta
1015 else
1016 if not FUsingRemoteTZDB then
1017 begin
1018 UtilIntf.EncodeTimeTZ(StatusIntf,@Buffer,0,0,0,0,PAnsiChar(aTimeZone));
1019 Result := Buffer.time_zone;
1020 end
1021 else
1022 Result := LookupTimeZoneID(aTimeZone);
1023 end;
1024
1025 function TFB30TimeZoneServices.LocalTimeToGMT(aLocalTime: TDateTime;
1026 aTimeZone: AnsiString): TDateTime;
1027 begin
1028 Result := IncMinute(aLocalTime,GetDSTOffset(aLocalTime,aTimeZone,true));
1029 end;
1030
1031 function TFB30TimeZoneServices.LocalTimeToGMT(aLocalTime: TDateTime;
1032 aTimeZoneID: TFBTimeZoneID): TDateTime;
1033 begin
1034 Result := IncMinute(aLocalTime,GetDSTOffset(aLocalTime,aTimeZoneID,true));
1035 end;
1036
1037 function TFB30TimeZoneServices.GMTToLocalTime(aGMTTime: TDateTime;
1038 aTimeZone: AnsiString): TDateTime;
1039 begin
1040 Result := IncMinute(aGMTTime,GetDSTOffset(aGMTTime,aTimeZone,false));
1041 end;
1042
1043 function TFB30TimeZoneServices.GMTToLocalTime(aGMTTime: TDateTime;
1044 aTimeZoneID: TFBTimeZoneID): TDateTime;
1045 begin
1046 Result := IncMinute(aGMTTime,GetDSTOffset(aGMTTime,aTimeZoneID,false));
1047 end;
1048
1049 function TFB30TimeZoneServices.GetEffectiveOffsetMins(aLocalTime: TDateTime;
1050 aTimeZone: AnsiString): integer;
1051 begin
1052 Result := -GetDSTOffset(aLocalTime,aTimeZone,true);
1053 end;
1054
1055 function TFB30TimeZoneServices.GetEffectiveOffsetMins(aLocalTime: TDateTime;
1056 aTimeZoneID: TFBTimeZoneID): integer;
1057 begin
1058 Result := -GetDSTOffset(aLocalTime,aTimeZoneID,true);
1059 end;
1060
1061 function TFB30TimeZoneServices.UsingRemoteTZDB: boolean;
1062 begin
1063 Result := FUsingRemoteTZDB;
1064 end;
1065
1066 procedure TFB30TimeZoneServices.SetUseLocalTZDB(useLocalTZDB: boolean);
1067 begin
1068 if FFirebird30ClientAPI.HasLocalTZDB then
1069 FUsingRemoteTZDB := not useLocalTZDB
1070 else
1071 FUsingRemoteTZDB := true;
1072 end;
1073
1074 function TFB30TimeZoneServices.GetLocalTimeZoneName: AnsiString;
1075 var aDateTime: TDateTime;
1076 dstOffset: SmallInt;
1077 begin
1078 with FFirebird30ClientAPI do
1079 if TZDataTimeZoneID <> '' then
1080 Result := TZDataTimeZoneID
1081 else
1082 {Use the Server TZ Data Name if possible}
1083 begin
1084 if FServerTZName = '' then
1085 FAttachment.OpenCursorAtStart('Select Current_Timestamp at local from RDB$Database')[0].
1086 GetAsDateTime(aDateTime,dstOffset,FServerTZName);
1087 if FServerTZName <> '' then
1088 Result := FServerTZName
1089 else
1090 {Otherwise use local time offset}
1091 Result := FormatTimeZoneOffset(-LocalTimeOffset);
1092 end;
1093 end;
1094
1095 function TFB30TimeZoneServices.GetLocalTimeZoneID: TFBTimeZoneID;
1096 begin
1097 Result := TimeZoneName2TimeZoneID(GetLocalTimeZoneName);
1098 end;
1099
1100 procedure TFB30TimeZoneServices.GetTimeZoneInfo(aTimeZone: AnsiString;
1101 OnDate: TDateTime; var ZoneOffset, DSTOffset, EffectiveOffset: integer);
1102 var Stmt: IStatement;
1103 TZInfo: IResultSet;
1104 begin
1105 with FAttachment do
1106 Stmt := Prepare(StartTransaction([isc_tpb_read,isc_tpb_wait,isc_tpb_concurrency],taCommit),
1107 'select * from rdb$time_zone_util.transitions(?,?,?)');
1108 Stmt.SQLParams[0].AsString := aTimeZone;
1109 Stmt.SQLParams[1].AsDateTime := OnDate;
1110 Stmt.SQLParams[2].AsDateTime := OnDate;
1111 TZInfo := Stmt.OpenCursor;
1112 if TZInfo.FetchNext then
1113 begin
1114 ZoneOffset := TZInfo.ByName('ZONE_OFFSET').AsInteger;
1115 DSTOffset := TZInfo.ByName('DST_OFFSET').AsInteger;
1116 EffectiveOffset := TZInfo.ByName('EFFECTIVE_OFFSET').AsInteger;
1117 end;
1118 end;
1119
1120 function TFB30TimeZoneServices.GetTimeTZDate: TDateTime;
1121 begin
1122 Result := FTimeTZDate;
1123 end;
1124
1125 procedure TFB30TimeZoneServices.SetTimeTZDate(aDate: TDateTime);
1126 begin
1127 FTimeTZDate := DateOf(aDate);
1128 end;
1129
1130 function TFB30TimeZoneServices.GetTZTextOption: TTZTextOptions;
1131 begin
1132 Result := FTZTextOption;
1133 end;
1134
1135 procedure TFB30TimeZoneServices.SetTZTextOption(aOptionValue: TTZTextOptions);
1136 begin
1137 FTZTextOption := aOptionValue;
1138 end;
1139
1140 end.
1141