Current section
7 Versions
Jump to
Current section
7 Versions
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…