Coder Social home page Coder Social logo

pavkam / tzdb Goto Github PK

View Code? Open in Web Editor NEW
81.0 21.0 28.0 7.91 MB

Delphi/FPC Time Zone Database

Home Page: https://www.iana.org/time-zones

License: BSD 3-Clause "New" or "Revised" License

Pascal 99.13% Shell 0.87%
tzdb delphi iana pascal fpc time-zone-table time-zones

tzdb's Introduction

TZDB - IANA Time Zone Database for Delphi/FreePascal

Test TZDB/CLDR Bump Release

Introduction

TZDB is an offline, in-process compiled database for IANA's (https://www.iana.org/time-zones) TZDB project.

The source code is compatible with Delphi XE+ and FreePascal 3+, though some components are only available for Delphi.

The current version of TZDB is compiled with 2024a version of IANA TZDB and the latest Windows alias translation table (from CLDR project).

API Documentation and Code Examples.

Manually updating to latest TZDB from IANA

This project follows the IANA releases quite closely, usually with 1-2 weeks delay. If you are inclined to update the library manually use the shell script located in the repository: update-compile.sh.

The shell script runs under MacOS, Linux or Windows WSL. You will need Free Pascal compiler installed though.

Using the Library

To use TZDB you only require one file: TZDB.pas. Download it and simply add it to your uses clause. This unit contains the whole pre-compiled TZ database and all the code required to interpret it.

All the other files in the project are optional. After you download the files to your local project, simply include the TZDB unit in the uses clause.

Simplest example looks like:

uses TZDB;

begin
  LTimeZone := TBundledTimeZone.GetTimeZone('Africa/Cairo');
  WriteLn(LTimeZone.ToUniversalTime(Now));
end.

A large number of methods are provided on the TBundledTimeZone class that allow date/time manipulation.

Things of Interest

There are a large number of misconceptions when it comes to time zones in general. And TZDB tries to deal with them in specific ways. The following list should shed some light on issue one might encounter during the use of this library:

  • TBundledTimeZone.Create accepts both normal timezone IDs as well as Windows aliases such as European Standard Time. The method will throw an ETimeZoneInvalid if the given ID is unknown.
  • All methods that take date/times might throw anEUnknownTimeZoneYear exception. This can happen if one supplies a date/time that is not covered by the database. Example would be a date in 1800s when such data is not available. One needs to catch such exceptions pro-actively.
  • All methods that take local time accept an optional parameter called AForceDaylight that defaults true. This is due to local times potentially being ambiguous in some time periods (between daylight time and standard time, there is an hour (s) that appear twice). This argument allows the code to assign the hour to either the daylight period or the standard period.
  • Methods might throw ELocalTimeInvalid exception if the give local time is in the invalid period (between standard time and daylight time there is the missing hour(s)). Use GetLocalTimeType to check the type of the local time before trying to operate on it.
  • There is no guarantee that a time zone supports daylight time. Use HasDaylightTime to detect whether this is the case.
  • Some time zones include multiple daylight periods so do not assume there is only one. This might throw methods such as AmbiguousTimeStart or StandardTimeStart off.
  • Do not rely on DisplayName, Abbreviation and especially UtcOffset properties of the TBundledTimeZone class. These are provided for information only and will change during the year.

Time Zone Visualizer

The Time Zone Visualizer is a development tool we use to display time zone details. It is currently only supported on Windows:

Screen shot

tzdb's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tzdb's Issues

Obsolete data

There is a new release of TZ data updated at 2020.
I'm new to GIT so I write this problem here.
Please move this issue somewhere else more appropriate in the repository if it is necessary.
Please publish some kind of HOW-TO document on updating the TZ database ny oneself.
Thanks.

Delphi XE2

I am having problems building for Delphi Xe2.
Your samples appear to be very dated and even TTimeZone has been replaced with TBundledTimeZone, I guess.

After adapting the sample exercise from your readme.md
var
LTimeZone: TBundledTimeZone;
begin
LTimeZone := TBundledTimeZone.GetTimeZone('Africa/Cairo');
WriteLn(LTimeZone.ToUniversalTime(Now));

I receive an invalid floating point exception here.
{ Extract the last millisecond in the end to mark the end of the period. }
LEnd := IncMilliSecond(LEnd, -1);

Any idea what I am doing wrong?

Support for breakdown with rule-less years.

If the previous year has no rules, nothing is brought forward and the start of the year has no carried rule. This results in missing segments.

Same for a period that has no rules at all.

I needed to convert some date times to one timezone to another, and I found your TZDB project.

I needed to convert some date times to one timezone to another, and I found your TZDB project.

It works like a charm, so It have saved me a lot of time. Many thanks 😊

But I’m missing one thing. I have tried to implement what I need, inheriting my own class from TBundledTimeZone, but all the data I need is Private or under the implementation section, so I can’t access it without modifying the original sources.

What I am missing is a function to get the year/date when a Standard or Daylight rule start for a given year.

Let me explain… I have seen that the function DaylightTimeStart(Year) returns the day and time of the Daylight segment for the given year, but I need to know when the rule applied to get that Daylight segment started to be applied.

Browsing TZDB.pas, I have seen that that info is in CFamily_X_Arr:

{ Date-bound rules for EU family }

CFamily_1_Arr: array[0 .. 5] of TYearBoundRule = (

(FStart: 1977; FEnd: 1980; FRule: @CRules[50]),

(FStart: 1977; FEnd: 1977; FRule: @CRules[51]),

(FStart: 1978; FEnd: 1978; FRule: @CRules[52]),

(FStart: 1979; FEnd: 1995; FRule: @CRules[51]),

(FStart: 1981; FEnd: 9999; FRule: @CRules[53]),

(FStart: 1996; FEnd: 9999; FRule: @CRules[54])

);

This info is important to properly define a timezone in a iCalendar (.ics) file.

Would it be possible to include the function I need (something like DaylightRuleStartYear/Date - StandardRuleStartYear/Date) in next distributions?

I think it could be helpful for many people too.

Issue in ToLocalTime for Stockholm time zone.

Bug in TZDB 2020a
Crash in TBundledTimeZone.ToLocalTime(const ADateTime: TDateTime)
When using the date/time '2018-12-31 22:18:01' (which causes a switch to next year 2019).
Time Zone: "Stockholm"

Incompatible types 'TTimeZone' and 'TBundledTimeZone'

Hello,

When i tried to execute the example:

var
LAmsterdam: TTimeZone;
begin
LAmsterdam := TBundledTimeZone.GetTimeZone('Europe/Amsterdam');
...
...
end;

I got the error saying that LAmsterdam is a TTimeZone not compatible with TBundledTimeZone.

Do you know how to fix this?

C++Builder error compiling - constant expression expected - TZDB requires at least Delphi XE to compile!

I am using RadStudio 10.3.3 (C++ Builder) to compile TZDB.pas and use, but I get an error on the following:

40 {$IF DECLARED(CompilerVersion)}
41 {$IF CompilerVersion >= 22} // Delphi XE +
42 {$DEFINE TZDB_SUPPORTED_COMPILER}
43 {$DEFINE DELPHI}
44 {$IFEND}
45 {$ENDIF}

The errors:
[Pascal Error] TZDB.pas(41,32): E2026 Constant Expression expected
[Pascal Error] TZDB.pas(50,4): E1054 TZDB requires at least Delphi XE or FreePascal 3.0 to compile!
[Pascal Error] TZDB.pas(69,5): F2613 Unit 'FGL' not found.

I don't know if this is a problem in RadStudio 10.3.3 or if it is a problem with the C++ Builder side, but I can simply comment out the lines of code 40,41,44,45 and compile no problem. But I don't like doing this because whenever I update the source it will be a problem.

Any ideas?

Please use fully qualified unitname

Any chance that in TZDB.pas all Delphi units are fully qualified - SysUtils -> System.SysUtils?

There are only 17 places :) to make the file "more" correct - for us people who do not want any aliases.

Thanks - and great work.

Footnotes on 2019b addition of "Fri<=1" Rule

TZdb.pas Extract.txt
My last word on this - promise!

My speculation that the new Rule should be "Fri<=29" with Mar (rather than Apr) has been knocked out. IANA 2019b is correct according to Knesset documents.**

But as TZdb can now handle rules like the one I proposed, I thought I should test the code against this (we only tested "Fri<=1" with Apr the other day).

I editted Asia file, and changed the line thusly:
### pmh Rule Zion 2005 2012 - Apr Fri<=1 2:00 1:00 D
Rule Zion 2005 2012 - Mar Fri<=29 2:00 1:00 D

I recompiled with TZcompile, and tested with TZvisualizer.

For the years the rule affects I got these Start DST dates:
Friday, 4 March 2005
Friday, 3 March 2006
Friday, 2 March 2007
Friday, 7 March 2008
Friday, 6 March 2009
Friday, 5 March 2010
Friday, 4 March 2011
Friday, 2 March 2012

A bit of digging around showed me that the compiler had correctly created the entries in TZdb.inc, but that it was the new function in TZdb.pas (EncodeDateMonthFirstDayOfWeekBefore) that was not working for the larger numbers.

I amended TZdb.pas (extract attached) and retested:
Friday, 25 March 2005
Friday, 24 March 2006
Friday, 23 March 2007
Friday, 28 March 2008
Friday, 27 March 2009
Friday, 26 March 2010
Friday, 25 March 2011
Friday, 23 March 2012

These dates conform to the rule (dd is <= 29, mm is March and weekday is Fri). The changed code was regression tested for rule "Fri<=1 Apr" and produced results the same as the original.

Should I "pull" the source and apply my version of the function?

Cheers, Paul

** I got a very frosty, unhelpful response from IANA. Apparently I contacted the wrong person. While I was hunting around for an alternative place to pose my question, I came across another source (TimeAndDate.com) that confirmed all the original dates for Start of DST from 2005-12.

Turns out Wikipedia is wrong. But getting that corrected is a battle I don't want to front up for!

pmh

Incorrect handling of Pacific/Apia zone in 2010 and 2011

In 2010 there is a rule that starts the DST but the library identifies it as Standard.
In 2011 there is a complicated combination of 2 rules and a period switch at the end of the year.

Need to investigate whether 24:00 is treated accordingly.

Sao Paulo TZ missing daylight time in October (FPC)

America/Sao_Paulo:
Period Start (Local) End (Local) Abbrv. Name Bias
Daylight 2019-01-01 00:00:00.000 2019-02-16 22:59:59.000 GMT-02 -02 -2h0m
Ambiguous 2019-02-16 23:00:00.000 2019-02-16 23:59:59.000 -2h0m
Standard 2019-02-17 00:00:00.000 2019-12-31 23:59:59.998 GMT-03 -03 -3h0m

A problem with Argentina /Buenos aires time regime

I'm trying to create a birth chart for Diego Armando Maradona 30 October 1960 at 07:05
The data on IANA says that at the day/time of birth it was active the daylight saving time (+ 1 hour), so the correct time is ADT h3w (GMT-3)
The program of visualization says that in 1960 there was not any daylight saving time active.
These are the data of the time zone active onto IANA:
Buenos Aires DLST From 01/10/1946 00:00 to 01/10/1963 00:00
Please double check the data because it seems to be a problem of reliability of your precious project.

New Zealand - ETimeZoneInvalid

Hello,

New Zealand could not be identified as a timezone using a code line below with current version of the sources.

PickupDateTime := TBundledTimeZone.GetTimeZone(FData.bookings[I].pickup_date_time_zone).ToLocalTime(FData.bookings[I].pickup_date_time);

I could not clearly understand reason of the source as it is using 2020d version of the time zone database.

Stack trace is like below:
image

Any help is appreciated.

Thanks & Regards,
Ertan

2019b database rule for Zion causes compilation failure

G'day all,

We've been trying to install the 2019b database with v1.8.2018c of Alex's excellent toolset, but TZcompile just kept shutting down. Eventually we tracked the root of the problem to the TzStrToDay function in Tzcompile\TZSchema.pas, which objects to the "Fri<=1" in the Asia file.

Doing a BCompare on "Asia" for 2019a and 2019b shows this is new code. There do not seem to be any other "Day<=nn" tests in the IANA source.

As a workaround we've set the test to "1" for now. Any suggestions on best way to fix the code? Is "Day<=nn" going to become a "thing"? Do we need to enhance TzStrToDay?

Cheers, Paul

TZcompile.log

Screenshot 2019-07-06 15 51 21

Sao Paulo TZ has weirdly incorrect Display Name (FPC)

America/Sao_Paulo:
Period Start (Local) End (Local) Abbrv. Name Bias
Daylight 2019-01-01 00:00:00.000 2019-02-16 22:59:59.000 GMT-02 -02 -2h0m
Ambiguous 2019-02-16 23:00:00.000 2019-02-16 23:59:59.000 -2h0m
Standard 2019-02-17 00:00:00.000 2019-12-31 23:59:59.998 GMT-03 -03 -3h0m

2024a version

Hello,

Will it be possible to have a new version with 2024a data?

Thanks & Regards,
Ertan

Unhandled exception EInvalidOp compiling recent IANA sources

G'day Alex,

We try to apply each IANA DB version as they are published. I've written a utility that takes the download and performs all the steps we need to get it compile in the right places. This includes running TZcompile against the "latest DB".

Many times the compile step will fail with a message like:
Screenshot 2019-07-07 14 31 16

We've identified three Links that are problematic:

  • Chongqing
  • South_Pole
  • Asmara (or Asmera)

The problem is with the data in Backward and Backzone files. We commented out the lines in these files, and the TZcompile completes AOK.

The two files change infrequently, but when they do we lose our mods and the TZcompile exception is raised again.

Annoying but not a show stopper. I apologise for not drilling into the code yet to work out a more permanent solution...

Below are some scans showing the duplicate Links.

Cheers, Paul

Screenshot 2019-07-07 21 41 23
Screenshot 2019-07-07 21 53 35
Screenshot 2019-07-07 21 41 56

New Issue from (Jean F.)

I'm working on the development of a software that uses the TZDB for Delphi to deal with the timezones' management, and I'd like to have your opinion about a problem that we are facing.

In a procedure, in order to decompose the periods of a timezone, we have to know the timezone's year of a UTC DateTime.

To do so, we are using these commands :
thisBundledTimezone := TBundledTimeZone.GetTimeZone('America/Los_Angeles'); timeZoneYear := YearOf(IncMinute(aUTCDateTime, Round(thisBundledTimezone.UtcOffset.TotalMinutes)));

This is fine if the program is running on Windows, but it crashes on Linux.

The error occurs when we're trying to get the UtcOffset. The TBundledTimeZone can't read the
FPeriods[I] in the function GetPeriodAndRule.

The thing is that FPeriods is a TList that is not typed (with TCompiledPeriod) and Delphi can't use it this way with Linux.

We can by the way see a warning when compiling a project that includes TZDB.pas, telling that TList is deprecated.

We have solved the problem by modifying the TZDB.pas unit, but that's quite a lot of changes in the declarations.

For your information, the Decomposer.pas unit doesn't have any problem with Linux because it is already working with typed TLists (function Decompose).

Test for invalid local times is incorrect

Consider this test function.

procedure TForm1.Button1Click(Sender: TObject);
var
tz: TBundledTimeZone;
d: TDateTime;
begin
tz := TBundledTimeZone.Create('America/Vancouver');
d := tz.ToUniversalTime(StrToDateTime('3/11/2012 1:06:32 AM'));
d := tz.ToUniversalTime(StrToDateTime('3/11/2012 2:06:32 AM'));
tz.Free;
end;

=== What is the expected output? What do you see instead? ===

The first line will produce an ELocalTimeInvalid exception; the second will not. This is the opposite of the correct behaviour. At 2 AM local time, the clock rolls forward to 3 AM, so local times >= 2 AM and < 3AM should be invalid.

=== What version of the product are you using? On what operating system? ===

Delphi 7
Windows 7
TZDB 1.8

=== Please provide any additional information below. ===

I was able to correct the problem by changing the following lines 681-683 in TZDB.pas in
function TCompiledRule.GetLocalTimeType(const ADateTime: TDateTime): TLocalTimeType :

< if (FNext <> nil) and (FNext.FOffset > FOffset) and
< (CompareDateTime(ADateTime, IncSecond(FNext.FStartsOn, FOffset - FNext.FOffset)) >= 0) then
< Result := lttInvalid

if (FPrev <> nil) and (FOffset > FPrev.FOffset) and
(CompareDateTime(ADateTime, FStartsOn) >= 0) and
(CompareDateTime(ADateTime, IncSecond(FStartsOn, FOffset - FPrev.FOffset)) < 0) then
Result := lttInvalid

I am not sure if this is the correct fix, but it does appear to correctly address the boundary cases and makes intuitive sense. By the time this edge case is tested, the contents of FPrev / Self / FNext have already advanced, for which the current code fails to account.

(As a side note, I was not able to add breakpoints to tzdb.pas -- despite checking for CRLF line endings, removing DCU files, enabling debug info, etc. Would be interested to know if there is something about tzdb that prevents this.)

Update ztdb to latest version (2016i)

I would like to have the latest version included in the project. I would provide a PR but I don't know the exact steps to generate the .inc file as I am not very familiar with delphi programming.

Compile Year sometimes is unable to fill in the start of the year

In case of Cairo 2014, the first part of the year has no coverage because there is no "previous year rule" that overlaps this year.

Possible solutions:

  1. Find the rule from last year, then the year before ... etc.
  2. Treat the segments that don't have a rule as standard time while in breakdown.

ToISO8601Format: missing "T" date-time separator and documentation error

Hi,

the ToISO8601Format function returns a string where time and date are separated by a space. According to the iso 8601 spec, the date and time should be separated by the letter "T". Could you change that in the CZFormat and CFullFormat in the implementation of the function?

Also, the documentation (in dist/TZDB.pas) says that the ADateTime should be a utc timestamp. However, the function only appends the timezone bias, but does not convert the date/time from utc to local. That means that if you pass a utc TDateTime into the function, it will output the utc date and time but with the local timezone bias, resulting in the wrong time. Could you change the documentation to say that the function accepts a local TDateTime (and not a utc one)? The function then works as expected. I think it actually makes more sense to pass a local TDateTime into the function, as it only formats it to an iso 8601 string. It doesn't have to convert the TDateTime from utc to local, since there's already another function to do that.

Update to version 2020f

I want update library to version 2020f, but when I run script it fails with error:

  TTZDBTest Time:00.058 N:38 E:0 F:1 I:0
    00.003  Test_Africa_Accra_1997  Failed: "DST name differs for 0 period." expected: <GMT> but was: <>
      Exception:   "DST name differs for 0 period." expected: <GMT> but was: <>
      at   $0000000000433E5A
  TTZDB_St_Johns_2018_Test Time:00.002 N:13 E:0 F:0 I:0
  TTZDB_London_2018_Test Time:00.002 N:13 E:0 F:0 I:0
  TTZDB_Canberra_2018_Test Time:00.003 N:13 E:0 F:0 I:0
  TTZDB_NewYork_2018_Test Time:00.002 N:13 E:0 F:0 I:0

Number of run tests: 90
Number of errors:    0
Number of failures:  1

List of failures:
  Failure: 
    Message:           TTZDBTest.Test_Africa_Accra_1997: "DST name differs for 0 period." expected: <GMT> but was: <>
    Exception class:   EAssertionFailedError
        at   $0000000000433E5A

[ERR] Tests failed! Please correct the errors before continuing.

There are also a few errors with aliases (are they critical?):

ERROR: Alias "Africa/Asmera" already points to zone "Africa/Asmara". Cannot reassign to zone "Africa/Nairobi".
ERROR: Alias "Antarctica/South_Pole" already points to zone "Antarctica/McMurdo". Cannot reassign to zone "Pacific/Auckland". 
ERROR: Alias "Asia/Chungking" already points to zone "Asia/Chongqing". Cannot reassign to zone "Asia/Shanghai".
ERROR: Alias "UTC" already points to zone "Etc/UTC". Cannot reassign to zone "Etc/GMT".

and script can't find two files during update (it seems, they was removed as obsolete):

cp: cannot stat './iana_temp/pacificnew': No such file or directory
cp: cannot stat './iana_temp/systemv': No such file or directory

Get rid of TCompiledPeriod and TCompiledRule

The next logical step is to get rid of the compiled period and rules. There is a potential data loss in regards to multi-period years and years that end in a rule right at midnight 31st of December.

Thought I haven't seen any of those if would be nice to have all types of rules working.

Extra comma in generated CRuleFamilies

An extra comma can appear after the last item. I have fixed it in TZSchema.pas by adding a new local integer variable nFamilies. Then before the loop to generate this array add:
nFamilies := FRuleFamilies.Count - LGhosts;
and instead of the test to write the comma:
Dec(nFamilies);
if nFamilies > 0 then Write(LFile, ',');

Update wiki with new code

The wiki needs to be updated to reflect the new code added.
Also add a couple of samples for the new functions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.