Merge branch 'master' of github.com:choptastic/qdate

Conflicts:
	rebar.config
This commit is contained in:
Jesse Gumm 2016-01-31 09:10:19 -06:00
commit 7910de376e
8 changed files with 259 additions and 18 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ deps/
ebin/ ebin/
.eunit/ .eunit/
_build _build
rebar.lock

View file

@ -8,4 +8,4 @@ otp_release:
- R15B02 - R15B02
- R15B01 - R15B01
- R15B - R15B
before_script: "sudo apt-get --yes --force-yes install libpam0g-dev" before_script: "sudo apt-get --yes --force-yes install libpam-runtime"

View file

@ -1,9 +1,18 @@
## 0.5.0 (in development) ## 0.5.0 (in development)
* Add `range_X` functions for getting a list of dates/times within a range
(such as `range_day/3` to get a range of days between a start and end date.
* Add `beginning_X` functions to return the beginning of the provided precision
(minute, hour, day, month, or year)
* Update to rebar3 and add hex compatability. (@Licenser) * Update to rebar3 and add hex compatability. (@Licenser)
* Properly add dependent apps to .app.src (@Licenser) * Properly add dependent apps to .app.src (@Licenser)
* Remove R14 from travis testing. * Remove R14 from travis testing.
## 0.4.2
* Add partial support for `ec_date`'s 4-tuple subsecond accuracy time format.
* Fix `erlware_commons` dependency to a rebar2-compatible version.
## 0.4.1 ## 0.4.1
* Remove unnecessary `io:format` call. * Remove unnecessary `io:format` call.

View file

@ -5,6 +5,9 @@ all: compile
compile: compile:
$(REBAR) compile $(REBAR) compile
update:
$(REBAR) update
test: compile test: compile
$(REBAR) eunit $(REBAR) eunit

View file

@ -619,25 +619,41 @@ ok
%% that timezone to our intended timezone. %% that timezone to our intended timezone.
``` ```
## Date Arithmetic ## Date Truncation (Beginning of X)
(not fully tested yet, but will have full tests for 0.4.0) Sometimes you need to truncate a time (say, the beginning of the current
month).
This is abstracted to `beginning_X` functions, which return a date/time format
with the dates and times truncated to the specified level.
+ `beginning_minute(Date)`
+ `beginning_hour(Date)`
+ `beginning_day(Date)`
+ `beginning_month(Date)`
+ `beginning_year(Date)`
There are also 0-arity versions of the above, in which `Date` is assumed to be
"right now". For example, calling `qdate:beginning_month()` would return
midnight on the first day of the current month.
## Date Arithmetic
The current implementation of qdate's date arithmetic returns Unixtimes. The current implementation of qdate's date arithmetic returns Unixtimes.
There are 8 main functions for date arithmetic: There are 8 main functions for date arithmetic:
+ `add_seconds(Seconds, Date)` + `add_seconds(Seconds, Date)`
+ `add_minutes(Minutes, Date)` + `add_minutes(Minutes, Date)`
+ `add_hours(Hours, Date)` + `add_hours(Hours, Date)`
+ `add_days(Days, Date)` + `add_days(Days, Date)`
+ `add_weeks(Weeks, Date)` + `add_weeks(Weeks, Date)`
+ `add_months(Months, Date)` + `add_months(Months, Date)`
+ `add_years(Years, Date)` + `add_years(Years, Date)`
+ `add_date(DateToAdd, Date)` - `DateToAdd` is a shortcut way of adding + `add_date(DateToAdd, Date)` - `DateToAdd` is a shortcut way of adding
numerous options. For example. `qdate:add_date({{1, 2, -3}, {-500, 20, 0}})` numerous options. For example. `qdate:add_date({{1, 2, -3}, {-500, 20, 0}})`
will add 1 year, add 2 months, subtract 3 days, subtract 500 hours, add 20 will add 1 year, add 2 months, subtract 3 days, subtract 500 hours, add 20
minutes, and not make any changes to seconds. minutes, and not make any changes to seconds.
For the date arithmetic functions, `Date`, like all `qdate` functions, can be any For the date arithmetic functions, `Date`, like all `qdate` functions, can be any
format. format.
@ -654,6 +670,74 @@ There are 7 other arithmetic functions that take a single argument, and these do
+ `add_months(Months)` + `add_months(Months)`
+ `add_years(Years)` + `add_years(Years)`
## Date and Time Ranges
qdate provides a number of `range` functions that give applicable dates/times
within a start and end time. For example, "All days from 2015-01-01 to today",
"every 3rd month from 2000-01-01 to 2009-12-31", or "every 15 minutes from
midnight to 11:59pm on 2015-04-15".
The functions are as follows:
+ `range_seconds(Interval, Start, End)`
+ `range_minutes(Interval, Start, End)`
+ `range_hours(Interval, Start, End)`
+ `range_days(Interval, Start, End)`
+ `range_weeks(Interval, Start, End)`
+ `range_months(Interval, Start, End)`
+ `range_years(Interval, Start, End)`
Where `Interval` is the number of seconds/days/years/etc.
So for example:
```erlang
%% Get every 15th minute from "2015-04-15 12:00am to 2015-04-15 11:59am"
> qdate:range_minutes(15, "2015-04-15 12:00am", "2015-04-15 11:59am").
[1429056000,1429056900,1429057800,1429058700,1429059600,
1429060500,1429061400,1429062300,1429063200,1429064100,
1429065000,1429065900,1429066800,1429067700,1429068600,
1429069500,1429070400,1429071300,1429072200,1429073100,
1429074000,1429074900,1429075800,1429076700,1429077600,
1429078500,1429079400,1429080300,1429081200|...]
%% Get every day of April, 2014
> qdate:range_days(1, "2014-04-01", "2014-04-30").
[1396310400,1396396800,1396483200,1396569600,1396656000,
1396742400,1396828800,1396915200,1397001600,1397088000,
1397174400,1397260800,1397347200,1397433600,1397520000,
1397606400,1397692800,1397779200,1397865600,1397952000,
1398038400,1398124800,1398211200,1398297600,1398384000,
1398470400,1398556800,1398643200,1398729600|...]
```
Note, that the return value (just like qdate's arithmetic functions) is a list
of integers. These integers are unix timestamps and can be easily formatted
with qdate:
```erlang
> Mins = qdate:range_minutes(15, "2015-04-15 12:00am", "2015-04-15 11:59am"),
> [qdate:to_string("Y-m-d h:ia", M) || M <- Mins].
["2015-04-15 00:00am","2015-04-15 00:15am",
"2015-04-15 00:30am","2015-04-15 00:45am",
"2015-04-15 01:00am","2015-04-15 01:15am",
"2015-04-15 01:30am","2015-04-15 01:45am",
"2015-04-15 02:00am","2015-04-15 02:15am",
"2015-04-15 02:30am","2015-04-15 02:45am",
"2015-04-15 03:00am","2015-04-15 03:15am",
"2015-04-15 03:30am","2015-04-15 03:45am",
"2015-04-15 04:00am","2015-04-15 04:15am",
"2015-04-15 04:30am","2015-04-15 04:45am",
"2015-04-15 05:00am","2015-04-15 05:15am",
"2015-04-15 05:30am","2015-04-15 05:45am",
"2015-04-15 06:00am","2015-04-15 06:15am",
"2015-04-15 06:30am","2015-04-15 06:45am",
[...]|...]
```
Also note that the range functions are *inclusive*.
## Thanks ## Thanks
A few shoutouts to [Dale Harvey](http://github.com/daleharvey) and the A few shoutouts to [Dale Harvey](http://github.com/daleharvey) and the
@ -683,6 +767,7 @@ See [CHANGELOG.markdown](https://github.com/choptastic/qdate/blob/master/CHANGEL
+ Provide a sample qdate.config for users to see + Provide a sample qdate.config for users to see
+ Research the viability of [ezic](https://github.com/drfloob/ezic) for a + Research the viability of [ezic](https://github.com/drfloob/ezic) for a
timezone backend replacement for `erlang_localtime`. timezone backend replacement for `erlang_localtime`.
+ Add age calculation stuff: `age_years(Date)`, `age_minutes(Date)`, etc.
## Conclusion ## Conclusion

View file

@ -5,7 +5,11 @@
%% For rebar2 compat %% For rebar2 compat
{deps, {deps,
[ [
%% This uses an older erlware_commons version so retain compatibility with
%% rebar2. v0.16.1 introduced a 'cf' dependency, which seems to cause
%% breakage.
{erlware_commons, ".*", {git, "git://github.com/erlware/erlware_commons.git", {tag, "v0.15.0"}}}, {erlware_commons, ".*", {git, "git://github.com/erlware/erlware_commons.git", {tag, "v0.15.0"}}},
{erlang_localtime, ".*", {git, "git://github.com/choptastic/erlang_localtime.git", {branch, master}}} {erlang_localtime, ".*", {git, "git://github.com/choptastic/erlang_localtime.git", {branch, master}}}
]}. ]}.

View file

@ -1,7 +1,7 @@
{application, qdate, {application, qdate,
[ [
{description, "Simple Date and Timezone handling for Erlang"}, {description, "Simple Date and Timezone handling for Erlang"},
{vsn, "0.4.1"}, {vsn, "0.4.2"},
{registered, []}, {registered, []},
{applications, [ {applications, [
kernel, kernel,

View file

@ -1,5 +1,5 @@
% vim: ts=4 sw=4 et % vim: ts=4 sw=4 et
% Copyright (c) 2013 Jesse Gumm % Copyright (c) 2013-2015 Jesse Gumm
% See LICENSE for licensing information. % See LICENSE for licensing information.
% %
-module(qdate). -module(qdate).
@ -24,6 +24,20 @@
unixtime/0 unixtime/0
]). ]).
-export([
beginning_minute/1,
beginning_minute/0,
beginning_hour/1,
beginning_hour/0,
beginning_day/1,
beginning_day/0,
%beginning_week/2, %% needs to be /2 because we also need to define what day is considered "beginning of week", since some calendars do sunday and some do monday. We'll hold off on implementation here
beginning_month/1,
beginning_month/0,
beginning_year/1,
beginning_year/0
]).
-export([ -export([
compare/2, compare/2,
compare/3 compare/3
@ -47,6 +61,17 @@
add_date/2 add_date/2
]). ]).
-export([
range/4,
range_seconds/3,
range_minutes/3,
range_hours/3,
range_days/3,
range_weeks/3,
range_months/3,
range_years/3
]).
-export([ -export([
register_parser/2, register_parser/2,
register_parser/1, register_parser/1,
@ -242,7 +267,9 @@ to_date(ToTZ, Disambiguate, RawDate) ->
end, end,
try raw_to_date(RawDate3) of try raw_to_date(RawDate3) of
D={{_,_,_},{_,_,_}} -> D={{_,_,_},{_,_,_}} ->
date_tz_to_tz(D, Disambiguate, FromTZ, ToTZ) date_tz_to_tz(D, Disambiguate, FromTZ, ToTZ);
{{Year, Month, Date},{Hour,Minute,Second,_Millis}} ->
date_tz_to_tz({{Year, Month, Date},{Hour,Minute,Second}}, Disambiguate, FromTZ, ToTZ)
catch catch
_:_ -> _:_ ->
case raw_to_date(RawDate) of case raw_to_date(RawDate) of
@ -311,6 +338,45 @@ to_now(Disamb, ToParse) ->
end. end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Beginning/Truncation %%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
beginning_minute() ->
beginning_minute({date(),time()}).
beginning_minute(Date) ->
{{Y,M,D},{H,M,_}} = to_date(Date),
{{Y,M,D},{H,M,0}}.
beginning_hour() ->
beginning_hour({date(),time()}).
beginning_hour(Date) ->
{{Y,M,D},{H,_,_}} = to_date(Date),
{{Y,M,D},{H,0,0}}.
beginning_day() ->
beginning_day({date(),time()}).
beginning_day(Date) ->
{{Y,M,D},{_,_,_}} = to_date(Date),
{{Y,M,D},{0,0,0}}.
beginning_month() ->
beginning_month({date(),time()}).
beginning_month(Date) ->
{{Y,M,_},{_,_,_}} = to_date(Date),
{{Y,M,1},{0,0,0}}.
beginning_year() ->
beginning_year({date(),time()}).
beginning_year(Date) ->
{{Y,_,_},{_,_,_}} = to_date(Date),
{{Y,1,1},{0,0,0}}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comparisons %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comparisons %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -458,6 +524,77 @@ fmid({Y, M, D}) when D < 1 ->
fmid(Date) -> fmid(Date) ->
Date. Date.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Ranges %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
range(seconds, Interval, Start, Finish) ->
range_inner(fun add_seconds/2, Interval, Start, Finish);
range(minutes, Interval, Start, Finish) ->
range_inner(fun add_minutes/2, Interval, Start, Finish);
range(hours, Interval, Start, Finish) ->
range_inner(fun add_hours/2, Interval, Start, Finish);
range(days, Interval, Start, Finish) ->
range_inner(fun add_days/2, Interval, Start, Finish);
range(weeks, Interval, Start, Finish) ->
range_inner(fun add_weeks/2, Interval, Start, Finish);
range(months, Interval, Start, Finish) ->
range_inner(fun add_months/2, Interval, Start, Finish);
range(years, Interval, Start, Finish) ->
range_inner(fun add_years/2, Interval, Start, Finish).
range_inner(IntervalFun, Interval, Start, Finish) when Interval > 0 ->
%% If Interval>0, then we're ascending, and we want to compare start/end
%% dates normally
CompareFun = fun(S, F) -> compare(S, F) end,
range_normalizer(IntervalFun, Interval, CompareFun, Start, Finish);
range_inner(IntervalFun, Interval, Start, Finish) when Interval < 0 ->
%% If Interval<0, then we're descending, and we want to compare start/end
%% dates backwards (we want to end when the Start Date is Lower than
%% Finish)
CompareFun = fun(S, F) -> compare(F, S) end,
range_normalizer(IntervalFun, Interval, CompareFun, Start, Finish);
range_inner(_, Interval, _, _) when Interval==0 ->
throw(interval_cannot_be_zero).
range_normalizer(IntervalFun, Interval, CompareFun, Start0, Finish0) ->
%% Convert dates to unixtime for speed's sake
Start = to_unixtime(Start0),
Finish = to_unixtime(Finish0),
%% Prepare the incrementer, so we just need to pass the date to the incrementer.
Incrementer = fun(D) -> IntervalFun(Interval, D) end,
range_worker(Incrementer, CompareFun, Start, Finish).
range_worker(Incrementer, CompareFun, Start, Finish) ->
case CompareFun(Start, Finish) of
0 -> [Finish]; %% Equal, so we add our Finish value
1 -> []; %% Start is after Finish, so we add nothing
-1 -> %% Start is before Finish, so we include it, and recurse
NextDay = Incrementer(Start),
[Start | range_worker(Incrementer, CompareFun, NextDay, Finish)]
end.
range_seconds(Interval, Start, Finish) ->
range(seconds, Interval, Start, Finish).
range_minutes(Interval, Start, Finish) ->
range(minutes, Interval, Start, Finish).
range_hours(Interval, Start, Finish) ->
range(hours, Interval, Start, Finish).
range_days(Interval, Start, Finish) ->
range(days, Interval, Start, Finish).
range_weeks(Interval, Start, Finish) ->
range(weeks, Interval, Start, Finish).
range_months(Interval, Start, Finish) ->
range(months, Interval, Start, Finish).
range_years(Interval, Start, Finish) ->
range(years, Interval, Start, Finish).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Timezone Stuff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Timezone Stuff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -780,7 +917,9 @@ tz_tests(_) ->
?_assertEqual(ok, set_timezone("EST")), ?_assertEqual(ok, set_timezone("EST")),
?_assertEqual(555555555,to_unixtime("1987-08-10 00:59:15 GMT")), ?_assertEqual(555555555,to_unixtime("1987-08-10 00:59:15 GMT")),
?_assertEqual({555,555555,0},to_now("1987-08-10 00:59:15 GMT")), ?_assertEqual({555,555555,0},to_now("1987-08-10 00:59:15 GMT")),
?_assertEqual(ok, set_timezone("GMT")) ?_assertEqual(ok, set_timezone("GMT")),
?_assertEqual({{1970, 1, 1}, {1, 0, 0}}, to_date("CET", "1970-01-01T00:00:00Z"))
]}. ]}.