Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General improvements #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
ebin/
_build/
rebar3
doc/*
!doc/style.css
!doc/overview.edoc
45 changes: 40 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,44 @@
all: include/tz_index.hrl
./rebar3 compile
REBAR := ./rebar3
REBAR_URL := https://s3.amazonaws.com/rebar3/rebar3
ERL ?= erl

check:
./rebar3 eunit


all: include/tz_index.hrl compile

include/tz_index.hrl: src/ibuild.erl include/tz_database.hrl
cd include && ln -s ../src/ibuild.erl && escript ibuild.erl; EV=$$?; rm ibuild.erl; exit $$EV
cd include && cp ../src/ibuild.erl ibuild.erl && escript ibuild.erl; EV=$$?; rm ibuild.erl; exit $$EV

compile: $(REBAR)
$(REBAR) compile

check: $(REBAR)
$(REBAR) eunit


clean: $(REBAR) clean_doc
$(REBAR) clean

clean_doc:
@rm -f doc/*.html
@rm -f doc/erlang.png
@rm -f doc/edoc-info

xref: $(REBAR)
$(REBAR) as test xref

dialyzer: $(REBAR)
$(REBAR) as check dialyzer

doc: $(REBAR) doc/overview.edoc
$(REBAR) edoc

doc_private: $(REBAR)
$(REBAR) as doc_private edoc


./rebar3:
$(ERL) -noshell -s inets -s ssl \
-eval '{ok, saved_to_file} = httpc:request(get, {"$(REBAR_URL)", []}, [], [{stream, "./rebar3"}])' \
-s init stop
chmod +x ./rebar3
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
[![Hex.pm version][hexpm version]][hexpm]
[![Hex.pm Downloads][hexpm downloads]][hexpm]
[![Hex.pm Documentation][hexdocs documentation]][hexdocs]
[![License][license]](https://www.apache.org/licenses/LICENSE-2.0)

#### Public exports

* utc_to_local(DateTime, Timezone) - converts UTC time to local according to specified Timezone
Expand All @@ -6,9 +11,14 @@
* tz_name(DateTime, Timezone) - returns a timezone name (E.g. MSK, MSD, etc)
* tz_shift(DateTime, Timezone) - returns time difference between local datetime and GMT
* tz_shift(DateTime, TimezoneFrom, TimezoneTo) - returns time difference between local datetime and required timezone

Where
```
DateTime = {date(), time()}
TimeZone(To, From) = String(). E.g. “Europe/Moscow”, “America/NewYork”. Or abbreviations "MSK", "MSD", etc. Note:
TimeZone(To, From) = String().
```
E.g. “Europe/Moscow”, “America/NewYork”. Or abbreviations "MSK", "MSD", etc. Note:

abbreviation is just used to find appropriate timezone name. If you want to convert "MSK" -> "PDT", but source timezone
is not in daylight saving, it will be corrected by library and "MSK" -> "PST" conversion will be made.

Expand Down Expand Up @@ -51,3 +61,11 @@ Calculates time difference between two local timezones
>localtime:tz_shift({{2013, 01, 22}, {18, 17, 00}}, "America/New York", "Europe/Moscow").
>
>{'+',9,0}

<!-- Badges -->
[hexpm]: https://hex.pm/packages/erlang_localtime
[hexpm version]: https://img.shields.io/hexpm/v/erlang_localtime.svg?style=flat-curcle "Hex version"
[hexpm downloads]: https://img.shields.io/hexpm/dt/erlang_localtime.svg?style=flat-curcle
[hexdocs documentation]: https://img.shields.io/badge/hex-docs-purple.svg?style=flat-curcle
[hexdocs]: https://hexdocs.pm/erlang_localtime
[license]: https://img.shields.io/badge/License-Apache_2.0-blue.svg?logo=apache&logoColor=red "Apache-2.0"
7 changes: 7 additions & 0 deletions doc/overview.edoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@author Dmitry S. Melnikov <[email protected]>
@author Jesse Gumm <[email protected]>
@author Heinz N. Gies
@title erlang_localtime
@doc Erlang library for conversion from one local time to another
@copyright Apache-2.0, 2010 Dmitry S. Melnikov
@since 2010
2 changes: 1 addition & 1 deletion include/tz_index.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
"US/Alaska"],
["GYT","America/Guyana"],
["PMST","America/Miquelon"],
["UTC","Etc/UTC","Antarctica/Troll",
["UTC","Antarctica/Troll","Etc/UTC",
"Etc/Universal","Etc/Zulu","UTC","Universal",
"Zulu"],
["WIT","Asia/Jayapura"],
Expand Down
34 changes: 34 additions & 0 deletions rebar.config
Original file line number Diff line number Diff line change
@@ -1 +1,35 @@
{erl_opts, [debug_info]}.

{profiles, [
{test, [
{xref_checks, [
undefined_function_calls,
locals_not_used,
deprecated_function_calls
]},
{xref_ignores, [
]},
{dialyzer, [
{warnings, [
no_return
]}
]}
]},
{doc_private, [
{edoc_opts, [
{private, true}
]}
]},
{check, [
{dialyzer, [
{warnings, [
no_return,
no_opaque
]}
]},

{erl_opts, [
debug_info
]}
]}
]}.
Binary file removed rebar3
Binary file not shown.
1 change: 1 addition & 0 deletions src/ibuild.erl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
%% @private
%% @author Dmitry S. Melnikov ([email protected])
%% @copyright 2010 Dmitry S. Melnikov

Expand Down
158 changes: 116 additions & 42 deletions src/localtime.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,22 @@
,fmt_shift/1
]).

% utc_to_local(UtcDateTime, Timezone) -> LocalDateTime | [LocalDateTime, DstLocalDateTime] | {error, ErrDescr}
% UtcDateTime = DateTime()
% Timezone = String()
% LocalDateTime = DateTime()
% DstLocalDateTime = DateTime()
% ErrDescr = atom(), unknown_tz
-export_type[timezone/0].

-type timezone() :: string(). % TimeZone

%% @doc Converts <abbr title="Coordinated Universal Time">UTC</abbr> time
%% to local according to specified Timezone.<br/>
%% `TimeList' is `[LocalDateTime, DstLocalDateTime]'.

-spec utc_to_local(UtcDateTime, Timezone) -> Result when
UtcDateTime :: calendar:datetime(),
Timezone :: timezone(),
Result :: LocalDateTime | TimeList | Error,
TimeList :: [calendar:datetime()],
Error :: {error, ErrDescr},
LocalDateTime :: calendar:datetime(),
ErrDescr :: atom() | unknown_tz.
utc_to_local(UtcDateTime, Timezone) ->
case lists:keyfind(get_timezone(Timezone), 1, ?tz_database) of
false ->
Expand All @@ -47,12 +57,17 @@ utc_to_local(UtcDateTime, Timezone) ->
end
end.

% local_to_utc(LocalDateTime, Timezone) -> UtcDateTime | [UtcDateTime, DstUtcDateTime] | time_not_exists | {error, ErrDescr}
% LocalDateTime = DateTime()
% Timezone = String()
% UtcDateTime = DateTime()
% DstUtcDateTime = DateTime()
% ErrDescr = atom(), unknown_tz
%% @doc Converts local time to <abbr title="Coordinated Universal Time">UTC</abbr>.<br/>
%% `TimeList' is `[UtcDateTime, DstUtcDateTime]'.

-spec local_to_utc(LocalDateTime, Timezone) -> Result when
LocalDateTime :: calendar:datetime(),
Timezone :: timezone(),
Result :: UtcDateTime | TimeList | time_not_exists | Error,
UtcDateTime :: calendar:datetime(),
TimeList :: [calendar:datetime()],
Error :: {error, ErrDescr},
ErrDescr :: atom() | unknown_tz.
local_to_utc(LocalDateTime, Timezone) ->
case lists:keyfind(get_timezone(Timezone), 1, ?tz_database) of
false ->
Expand All @@ -73,11 +88,14 @@ local_to_utc(LocalDateTime, Timezone) ->
end
end.

% local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) -> LocalDateTime | ambiguous | time_not_exists | {error, ErrDescr}
% LocalDateTime = DateTime()
% TimezoneFrom = String()
% TimezoneTo = String()
% ErrDescr = atom(), unknown_tz
%% @doc Converts local time to local.
-spec local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) -> Result when
LocalDateTime :: calendar:datetime(),
TimezoneFrom :: timezone(),
TimezoneTo :: timezone(),
Result :: LocalDateTime | ambiguous | time_not_exists | Error,
Error :: {error, ErrDescr},
ErrDescr :: atom() | unknown_tz.
local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) ->
case local_to_utc(LocalDateTime, TimezoneFrom) of
UtcDateTime = {{_,_,_},{_,_,_}} ->
Expand All @@ -94,11 +112,13 @@ local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo) ->
Other
end.

% local_to_local_dst(LocalDateTime, TimezoneFrom, TimezoneTo) -> LocalDateTime | ambiguous | time_not_exists | {error, ErrDescr}
% LocalDateTime = DateTime()
% TimezoneFrom = String()
% TimezoneTo = String()
% ErrDescr = atom(), unknown_tz
-spec local_to_local_dst(LocalDateTime, TimezoneFrom, TimezoneTo) -> Result when
LocalDateTime :: calendar:datetime(),
TimezoneFrom :: timezone(),
TimezoneTo :: timezone(),
Result :: LocalDateTime | ambiguous | time_not_exists | Error,
Error :: {error, ErrDescr},
ErrDescr :: atom() | unknown_tz.
local_to_local_dst(LocalDateTime, TimezoneFrom, TimezoneTo) ->
case local_to_utc(LocalDateTime, TimezoneFrom) of
UtcDateTime = {{_,_,_},{_,_,_}} ->
Expand All @@ -115,15 +135,22 @@ local_to_local_dst(LocalDateTime, TimezoneFrom, TimezoneTo) ->
Other
end.

% tz_name(DateTime(), Timezone) -> {Abbr, Name} | {{StdAbbr, StdName}, {DstAbbr, DstName}} | unable_to_detect | {error, ErrDesc}
% Timezone = String()
% Abbr = String()
% Name = String()
% StdAbbr = String()
% StdName = String()
% DstAbbr = String()
% DstName = String()
% ErrDesc = atom(), unknown_tz
%% @doc
%% @returns a timezone name (E.g. MSK, MSD, etc).
-spec tz_name(DateTime, Timezone) -> Result when
DateTime :: calendar:datetime(),
Timezone :: timezone(),
Result :: AbbrTuple | StdTuple | unable_to_detect | Error,
AbbrTuple :: {Abbr, Name},
StdTuple :: {{StdAbbr, StdName}, {DstAbbr, DstName}},
Abbr :: string(),
Name :: string(),
StdAbbr :: string(),
StdName :: string(),
DstAbbr :: string(),
DstName :: string(),
Error :: {error, ErrDesc},
ErrDesc :: atom() | unknown_tz.
tz_name(_UtcDateTime, "UTC") ->
{"UTC", "UTC"};
tz_name(LocalDateTime, Timezone) ->
Expand All @@ -145,15 +172,21 @@ tz_name(LocalDateTime, Timezone) ->
end
end.

% tz_shift(LocalDateTime, Timezone) -> Shift | {Shift, DstSift} | unable_to_detect | {error, ErrDesc}
% returns time shift from GMT
% LocalDateTime = DateTime()
% Timezone = String()
% Shift = DstShift = {Sign, Hours, Minutes}
% Sign = term(), '+', '-'
% Hours = Minutes = Integer(),
% {Shift, DstShift} - returns, when shift is ambiguous
% ErrDesc = atom(), unknown_tz
%% @doc Shifts local datetime.
%% @returns time difference between local datetime and GMT.
%% Returns `{Shift, DstShift}' when shift is ambiguous.
-spec tz_shift(LocalDateTime, Timezone) -> Result when
LocalDateTime :: calendar:datetime(),
Timezone :: timezone(),
Result :: 0 | Shift | ShiftTuple | unable_to_detect | Error,
ShiftTuple :: {Shift, DstShift},
Shift :: DstShift,
DstShift :: {Sign, Hours, Minutes},
Sign :: term() | '+' | '-',
Hours :: integer(),
Minutes :: integer(),
Error :: {error, ErrDesc},
ErrDesc :: atom() | unknown_tz.
tz_shift(_UtcDateTime, "UTC") ->
0;
tz_shift(LocalDateTime, Timezone) ->
Expand All @@ -175,10 +208,25 @@ tz_shift(LocalDateTime, Timezone) ->
end
end.

% the same as tz_shift/2, but calculates time difference between two local timezones
%% @doc The same as {@link tz_shift/2}, but calculates time difference between two local timezones.
%% @throws term()

-spec tz_shift(LocalDateTime, TimezoneFrom, TimezoneTo) -> Result when
LocalDateTime :: calendar:datetime(),
TimezoneFrom :: timezone(),
TimezoneTo :: timezone(),
Result :: 0 | Shift | ShiftTuple | unable_to_detect | Error,
ShiftTuple :: {Shift, DstShift},
Shift :: DstShift,
DstShift :: {Sign, Hours, Minutes},
Sign :: term() | '+' | '-',
Hours :: integer(),
Minutes :: integer(),
Error :: {error, ErrDesc},
ErrDesc :: atom() | unknown_tz.
tz_shift(LocalDateTime, TimezoneFrom, TimezoneTo) ->
F = fun() ->
FromShift = fmt_shift(tz_shift(LocalDateTime, TimezoneFrom)),
FromShift = fmt_shift(tz_shift(LocalDateTime, TimezoneFrom)),
DateTimeTo = localtime:local_to_local(LocalDateTime, TimezoneFrom, TimezoneTo),
ToShift = fmt_shift(tz_shift(DateTimeTo, TimezoneTo)),
fmt_min(ToShift-FromShift)
Expand All @@ -189,28 +237,54 @@ tz_shift(LocalDateTime, TimezoneFrom, TimezoneTo) ->
Err
end.

-spec adjust_datetime(DateTime, Minutes) -> Result when
DateTime :: calendar:datetime(),
Minutes :: integer(),
Result :: calendar:datetime().
adjust_datetime(DateTime, Minutes) ->
Seconds = calendar:datetime_to_gregorian_seconds(DateTime) + Minutes * 60,
calendar:gregorian_seconds_to_datetime(Seconds).

%% @doc Get TimeZone

-spec get_timezone(TimeZone) -> Result when
TimeZone :: timezone(),
Result :: timezone().
get_timezone(TimeZone) ->
get_timezone_inner(TimeZone).

-spec list_timezones() -> Result when
Result :: [timezone()].
list_timezones() ->
dict:fetch_keys(?tz_index).

% =======================================================================
% privates
% =======================================================================

-spec invert_shift(Minutes) -> Result when
Minutes :: integer(),
Result :: integer().
invert_shift(Minutes) ->
-Minutes.

-spec fmt_min(Shift) -> Result when
Shift :: integer(),
Result :: {Sign, Hours, Minutes},
Sign :: '+' | '-',
Hours :: integer(),
Minutes :: integer().
fmt_min(Shift) when Shift < 0 ->
{'-', abs(Shift) div 60, abs(Shift) rem 60};
fmt_min(Shift) ->
{'+', Shift div 60, Shift rem 60}.

-spec fmt_shift(any() | Shift | 0) -> Return when
Shift :: {Sign, Hours, Minutes},
Sign :: '+' | '-',
Hours :: integer(),
Minutes :: integer(),
Return :: integer().
fmt_shift({'+', H, M}) ->
H * 60 + M;
fmt_shift({'-', H, M}) ->
Expand Down
Loading