Current section

7 Versions

Jump to

Compare versions

7 files changed
+541 additions
-62 deletions
  @@ -98,7 +98,9 @@ will infer the timezone in the following order.
98 98 `set_timezone/1` only applies to that *specific* process. If none is
99 99 specified.
100 100 + If no timezone is specified for the process, `qdate` looks at the `qdate`
101 - application variable `default_timezone`.
101 + application variable `default_timezone`. `default_timezone` can be either a
102 + hard-specified timezone, or a `{Module, Function}` tuple. The tuple format
103 + should return either a timezone or the atom `undefined`.
102 104 + If no timezone is specified by either of the above, `qdate` assumes "GMT"
103 105 for all dates.
104 106 + A timezone value of `auto` will act as if no timezone is specified.
  @@ -190,24 +192,72 @@ ok
190 192
191 193 `qdate` provides a few convenience functions for performing date comparisons.
192 194
193 - + `compare(A, B)` - Like C's `strcmp`, returns:
195 + + `compare(A, B) -> -1|0|1` - Like C's `strcmp`, returns:
194 196 + `0`: `A` and `B` are exactly the same.
195 197 + `-1`: `A` is less than (before) `B`.
196 198 + `1`: `A` is greater than (after) `B`.
197 - + `compare(A, Operator, B)` - Operator is an infix comparison operator, and
198 - the function will return true if:
199 + + `compare(A, Operator, B) -> true|false` - Operator is an infix comparison operator, and
200 + the function will return a boolean. Will return `true` if:
199 201 + `'='`, or `'=='` - `A` is the same time as `B`
200 202 + `'/='`, or `'=/='` or `'!='` - `A` is not the same time as `B`
201 203 + `'<'` - `A` is before `B`
202 204 + `'>'` - `A` is after `B`
203 205 + `'=<'` or `'<='` - `A` is before or equal to `B`
204 206 + `'>='` or `'=>'` - `A` is after or equal to `B`
207 + + `between(A, Date, B) -> true|false` - The provided `Date` is (inclusively)
208 + between `A` and `B`. That is, `A =< Date =< B`.
209 + + `between(A, B) -> true|false` - shortcut for `between(A, now(), B)`
210 + + `between(A, Op1, Date, Op2, B) -> true|false` - the fully verbose option of
211 + comparing between. `Op1` and `Op2` are custom operators. For example, if
212 + you wanted to do an exclusive `between`, you can do:
213 + `between(A, '<', Date, '<', B)`
205 214
206 215 **Note 1:** `Operator` must be an atom.
207 216
208 217 **Note 2:** These functions will properly compare times with different timezones
209 218 (for example: `compare("12am CST",'==',"1am EST")` will properly return true)
210 219
220 + ### Sorting
221 +
222 + `qdate` also provides a convenience functions for sorting lists of dates/times:
223 +
224 + + `sort(List)` - Sort the list in ascending order of earliest to latest.
225 + + `sort(Op, List)` - Sort the list where `Op` is one of the following:
226 + + `'<'` or `'=<'` or `'<='` - Sort ascending
227 + + `'>'` or `'>='` or `'=>'` - Sort descending
228 + + `sort(Op, List, Opts)` - Sort the list according to the `Op`, with options provided in `Opts`. `Opts` is a proplist of the following options:
229 + + `{non_dates, NonDates}` - Tells it how to handle non-dates. `NonDates` can be any of the following:
230 + + `back` **(default)** - put any non-dates at the end (the back) of the list
231 + + `front` - put any non-dates at the beginning of the list
232 + + `crash` - if there are any non-dates, crash.
233 +
234 + Example:
235 +
236 + ```erlang
237 + 1> Dates = ["non date string", <<"garbage">>,
238 + 1466200861, "2011-01-01", "7pm",
239 + {{1999,6,21},{5,30,0}}, non_date_atom, {some_tuple,123}].
240 + 2> qdate:sort('>=', Dates, [{non_dates, front}]).
241 + [<<"garbage">>,"non date string",
242 + {some_tuple,123},
243 + non_date_atom,1466200861,"2011-01-01",
244 + {{1999,6,21},{5,30,0}},
245 + "7pm"]
246 + ```
247 +
248 + **Note 1:** This sorting is optimized to be much faster than using a home-grown
249 + sort using the `compare` functions, as this normalizes the items in the list
250 + before comparing (so it's only really comparing integers, which is quite fast).
251 +
252 + **Note 2:** This is one of the few qdate functions that don't have the "Date"
253 + as the last argument. This follows the pattern in Erlang/OTP to put options as
254 + the last argument (for example, `re:run/3`)
255 +
256 + **Note 3:** You'll notice that qdate's sorting retains the original terms (in
257 + the example above, we compared a datetime tuple, unix timestamp, and two
258 + strings (along with a number of non-dates, which were just prepended to the
259 + front of the list).
260 +
211 261 ### Timezone Functions
212 262
213 263 + `set_timezone(Key, TZ)` - Set the timezone to TZ for the key `Key`
  @@ -448,8 +498,31 @@ the crash.
448 498
449 499 **Another Note:** Custom parsers are expected to return either:
450 500 + A `datetime()` tuple. (ie {{2012,12,21},{14,45,23}}).
501 + + An integer, which represents the Unix timestamp.
451 502 + The atom `undefined` if this parser is not a match for the supplied value
452 503
504 + #### Included Parser: Relative Times
505 +
506 + `qdate` ships with an optional relative time parser. To speed up performance
507 + (since this parser uses regular expressions), this parser is disabled by
508 + default. But if you wish to use it, make sure you call
509 + `qdate:register_parser(parse_relative, fun qdate:parse_relative/1)`.
510 +
511 + Doing this allows you to parse relative time strings of the following formats:
512 +
513 + + "1 hour ago"
514 + + "-15 minutes"
515 + + "in 45 days"
516 + + "+2 years"
517 +
518 + And doing so allows you to construct slightly more readable comparison calls
519 + for sometimes common comparisons. For example, the following two calls are identical:
520 +
521 + ```erlang
522 + qdate:between("-15 minutes", Date, "+15 minutes").
523 +
524 + qdate:between(qdate:add_minutes(-15), Date, qdate:add_minutes(15)).
525 + ```
453 526
454 527 ### Registering Custom Formats
455 528
  @@ -619,10 +692,9 @@ ok
619 692 %% that timezone to our intended timezone.
620 693 ```
621 694
622 - ## Date Truncation (Beginning of X)
695 + ## Beginning or Ending of time periods (hours, days, years, weeks, etc)
623 696
624 - Sometimes you need to truncate a time (say, the beginning of the current
625 - month).
697 + qdate can determine beginnings and endings of time periods, like "beginning of the month"
626 698
627 699 This is abstracted to `beginning_X` functions, which return a date/time format
628 700 with the dates and times truncated to the specified level.
  @@ -637,6 +709,42 @@ There are also 0-arity versions of the above, in which `Date` is assumed to be
637 709 "right now". For example, calling `qdate:beginning_month()` would return
638 710 midnight on the first day of the current month.
639 711
712 + #### Beginning of Week
713 +
714 + qdate can also do a special "beginning" case, particularly the "beginning of
715 + the week" calculation. This has three forms, specifically:
716 +
717 + + `beginning_week()` - Returns first day of the current week.
718 + + `beginning_week(Date)` - Assumes the beginning of the week is Monday
719 + (chosen because Erlang's calendar:day_of_the_week uses 1=Monday and
720 + 7=Sunday).
721 + + `beginning_week(DayOfWeek, Date)` - Calculates the beginning of the week
722 + based on the provided `DayOfWeek`. Valid values for DayOfWeek are the
723 + integers 1-7 or the atom verions of the days of the week. Specifically:
724 +
725 + * Monday: `1 | monday | mon`
726 + * Tuesday: `2 | tuesday | tue`
727 + * Wednesday: `3 | wednesday | wed`
728 + * Thursday: `4 | thursday | thu`
729 + * Friday: `5 | friday | fri`
730 + * Saturday: `6 | saturday | sat`
731 + * Sunday: `7 | sunday | sun`
732 +
733 + These all return 12am on the day that is the first day of the week of the
734 + provided date.
735 +
736 + (My apologies to non-English speakers. I'm a lazy American who only speaks
737 + English, hence the Anglocentric day names).
738 +
739 + ### End of time period
740 +
741 + There are also the related `end_X` functions available, using the same
742 + conventions, except return the last second of that time period.
743 +
744 + So `end_month("2016-01-05")` will return the unix timestamp representing
745 + "2016-01-31 11:59:59pm"
746 +
747 +
640 748 ## Date Arithmetic
641 749
642 750 The current implementation of qdate's date arithmetic returns Unixtimes.
  @@ -737,6 +845,11 @@ with qdate:
737 845
738 846 Also note that the range functions are *inclusive*.
739 847
848 + ## Configuration Sample
849 +
850 + There is a sample configuration file can be found in the root of the qdate
851 + directory. Or you can just [look at it
852 + here](https://github.com/choptastic/qdate/blob/master/qdate.config).
740 853
741 854 ## Thanks
742 855
  @@ -764,7 +877,6 @@ See [CHANGELOG.markdown](https://github.com/choptastic/qdate/blob/master/CHANGEL
764 877 + Make `qdate` backend-agnostic (allow specifying either ec_date or dh_date as
765 878 the backend)
766 879 + Add `-spec` and `-type` info for dialyzer
767 - + Provide a sample qdate.config for users to see
768 880 + Research the viability of [ezic](https://github.com/drfloob/ezic) for a
769 881 timezone backend replacement for `erlang_localtime`.
770 882 + Add age calculation stuff: `age_years(Date)`, `age_minutes(Date)`, etc.
  @@ -1,21 +1,14 @@
1 1 {<<"name">>,<<"qdate">>}.
2 - {<<"version">>,<<"0.4.3">>}.
2 + {<<"version">>,<<"0.5.0">>}.
3 + {<<"requirements">>,#{}}.
3 4 {<<"app">>,<<"qdate">>}.
4 - {<<"requirements">>,
5 - [{<<"erlang_localtime">>,
6 - [{<<"app">>,<<"erlang_localtime">>},
7 - {<<"optional">>,false},
8 - {<<"requirement">>,<<"1.0.0">>}]},
9 - {<<"erlware_commons">>,
10 - [{<<"app">>,<<"erlware_commons">>},
11 - {<<"optional">>,false},
12 - {<<"requirement">>,<<"0.20.0">>}]}]}.
13 - {<<"maintainers">>,[<<"Jesse Gumm">>,<<"Heinz Gies">>]}.
5 + {<<"maintainers">>,[<<"Jesse Gumm">>]}.
14 6 {<<"precompiled">>,false}.
15 7 {<<"description">>,<<"Simple Date and Timezone handling for Erlang">>}.
16 8 {<<"files">>,
17 - [<<"src/qdate.app.src">>,<<"src/qdate.erl">>,<<"src/qdate_srv.erl">>,
18 - <<"rebar.config">>,<<"rebar.lock">>,<<"README.markdown">>,<<"LICENSE">>]}.
9 + [<<"src/qdate.app.src">>,<<"LICENSE">>,<<"README.markdown">>,
10 + <<"rebar.config">>,<<"rebar.config.script">>,<<"rebar.lock">>,
11 + <<"src/qdate.erl">>,<<"src/qdate_srv.erl">>]}.
19 12 {<<"licenses">>,[<<"MIT">>]}.
20 13 {<<"links">>,[{<<"Github">>,<<"https://github.com/choptastic/qdate">>}]}.
21 14 {<<"build_tools">>,[<<"rebar3">>]}.
  @@ -4,6 +4,6 @@
4 4
5 5 {deps,
6 6 [
7 - {erlware_commons, "~>0.20.0"},
8 - {erlang_localtime, "~>1.0.0"}
7 + erlware_commons,
8 + qdate_localtime
9 9 ]}.
  @@ -0,0 +1,15 @@
1 + %% -*- mode: erlang -*-
2 + %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
3 + %% ex: ts=4 sw=4 sts ft=erlang et
4 +
5 + case erlang:function_exported(rebar3, main, 1) of
6 + true -> % rebar3
7 + CONFIG;
8 + false -> % rebar 2.x or older
9 + %% Rebuild deps, possibly including those that have been moved to
10 + %% profiles
11 + [{deps, [
12 + {erlware_commons, "", {git, "git://github.com/erlware/erlware_commons", {tag, "v1.3.1"}}}, %% this is the version of erlware_commons that works until erlware tags a new version
13 + {qdate_localtime, "", {git, "git://github.com/choptastic/qdate_localtime", {tag, "1.1.0"}}}
14 + ]} | lists:keydelete(deps, 1, CONFIG)]
15 + end.
  @@ -1,3 +1,10 @@
1 - [{<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},1},
2 - {<<"erlang_localtime">>,{pkg,<<"erlang_localtime">>,<<"1.0.0">>},0},
3 - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.20.0">>},0}].
1 + {"1.1.0",
2 + [{<<"cf">>,{pkg,<<"cf">>,<<"0.3.1">>},1},
3 + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.3.1">>},0},
4 + {<<"qdate_localtime">>,{pkg,<<"qdate_localtime">>,<<"1.1.0">>},0}]}.
5 + [
6 + {pkg_hash,[
7 + {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>},
8 + {<<"erlware_commons">>, <<"0CE192AD69BC6FD0880246D852D0ECE17631E234878011D1586E053641ED4C04">>},
9 + {<<"qdate_localtime">>, <<"5F6C3ACF10ECC5A7E2EFA3DCD2C863102B962188DBD9E086EC01D29FE029DA29">>}]}
10 + ].
Loading more files…