diff --git a/README.md b/README.md index e593a9b..d974f61 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,27 @@ ezic ==== -ezic is a set of erlang utilities for the Olsen timezone database files. - -see the [tz database](http://www.twinsun.com/tz/tz-link.htm) page for more information. +ezic is a set of erlang utilities for the Olsen timezone database files. See the [tz database](http://www.twinsun.com/tz/tz-link.htm) page for more information. -Usage + +API +--- + + * `ezic:localtime(TimeZone) -> datetime()` + * `ezic:utc_to_time(universal_datetime(), TimeZone) -> local_datetime()` + * `ezic:utc_from_time(local_datetime(), TimeZone) -> universal_datetime()` + * `ezic:zone_convert(from_datetime(), TimeZoneFrom, TimeZoneTo) -> to_datetime()` + + + +Admin API +------- + + * `ezic:load("/path/to/tzdata") -> ok` + + + +Setup ----- # download the timezone data files @@ -22,25 +38,26 @@ Usage 2> ezic:localtime("Australia/Adelaide"). -API ---- - - * `ezic:load(Folder)` - * `ezic:localtime(TimeZone) -> datetime()` - * `ezic:time(datetime(), TimeZone) -> datetime()` - * `ezic:time2time(datetime(), TimeZoneFrom, TimeZoneTo) -> datetime()` Purpose ------- -The [recommended way](http://www.erlang.org/pipermail/erlang-questions/2006-December/024291.html) of handling timezones in erlang involves setting the system environment variable `TZ` to the desired timezone, then calling erlang time functions. This technique is probably sufficient for many purposes, but it has a few key issues: +The [recommended way](http://www.erlang.org/pipermail/erlang-questions/2006-December/024291.html) of handling timezones in erlang involves setting the system environment variable `TZ` to the desired timezone, then calling erlang time functions. This technique has a few key issues: * it only works on *nix systems, - * it depends on your system's timezone data (preventing customization), and + * it depends on your system's timezone data (preventing custom timezone hackery), and * you can create race conditions if you aren't careful -A pure erlang solution avoids all of these pitfalls, hence this project. +A self-containted erlang solution would avoid these pitfalls, but to be fair, it introduces a few others: + + * you (the admin) are responsible for updating the timezone database yourself + * ezic has not yet been battle tested + +I think both of these issues will disappear over time, so I opted to write ezic. + + + License ------- diff --git a/src/ezic.erl b/src/ezic.erl index 950d967..1c4e526 100644 --- a/src/ezic.erl +++ b/src/ezic.erl @@ -1,7 +1,8 @@ -module(ezic). -include("include/ezic.hrl"). --export([localtime/1, time2time/3]). +-export([localtime/1, utc_to_local/2, utc_from_local/2, zone_convert/3]). + -export([load/1, dev_start/0, test/0]). @@ -10,36 +11,37 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% API +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + localtime(TzName) -> - time(erlang:universaltime(), TzName). + utc_to_local(erlang:universaltime(), TzName). -time(Datetime, TzName) -> +utc_to_local(Datetime, TzName) -> + {ok, Zone, Rule}= current(Datetime, TzName), + SecDiff= offset(Zone, Rule), + date_add(Datetime, SecDiff). + - Zones= ezic_db:zones(TzName), -% ?debug("All Zones: ~p", [Zones]), - CZone= ezic_zone:current(Datetime, Zones), -% ?debug("Current Zone: ~p", [CZone]), - - RuleName= CZone#zone.rule, - Rules= ezic_db:rules(RuleName), -% ?debug("All Rules: ~p", [Rules]), - CRule= ezic_rule:current(Datetime, Rules), -% ?debug("Current Rule: ~p", [CRule]), - - OffsetSec= ezic_zone:offset_sec(CZone), - DSTSec= ezic_rule:dst_sec(CRule), - - SecDiff= OffsetSec + DSTSec, - ?gs2dt(?dt2gs(Datetime) + SecDiff). +utc_from_local(Datetime, TzName) -> + {ok, Zone, Rule}= current(Datetime, TzName), + SecDiff= offset(Zone, Rule), + date_subtract(Datetime, SecDiff). - -time2time(_DateTime, _FromTimeZone, _ToTimeZone) -> - void. +zone_convert(Datetime, FromTimeZone, ToTimeZone) -> + UTC= utc_from_local(Datetime, FromTimeZone), + utc_to_local(UTC, ToTimeZone). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% DEV API +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -57,3 +59,40 @@ test() -> io:format("~s: ~p~n", [TZ, localtime(TZ)]) end, ["Asia/Tokyo", "America/New_York", "America/Los_Angeles", "America/Jamaica", "Australia/Adelaide"]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% PRIVATE +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +current(Datetime, TzName) -> + Zones= ezic_db:zones(TzName), +% ?debug("All Zones: ~p", [Zones]), + CZone= ezic_zone:current(Datetime, Zones), +% ?debug("Current Zone: ~p", [CZone]), + + RuleName= CZone#zone.rule, + Rules= ezic_db:rules(RuleName), +% ?debug("All Rules: ~p", [Rules]), + CRule= ezic_rule:current(Datetime, Rules), +% ?debug("Current Rule: ~p", [CRule]), + {ok, CZone, CRule}. + + + + +offset(Zone, Rule) -> + OffsetSec= ezic_zone:offset_sec(Zone), + DSTSec= ezic_rule:dst_sec(Rule), + OffsetSec + DSTSec. + + + +date_add(Datetime, SecDiff) -> + ?gs2dt(?dt2gs(Datetime) + SecDiff). + +date_subtract(Datetime, SecDiff) -> + ?gs2dt(?dt2gs(Datetime) - SecDiff). +