diff --git a/.github/workflows/tests-workflow.yml b/.github/workflows/tests-workflow.yml deleted file mode 100644 index 70a9145..0000000 --- a/.github/workflows/tests-workflow.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: qdate tests and dialyzer -on: push - -jobs: - linux: - name: OTP ${{ matrix.otp_version }} - runs-on: ${{ matrix.os }} - continue-on-error: true - - strategy: - matrix: - include: - - os: ubuntu-22.04 - otp_version: '27.x' - rebar3_version: "3.24.0" - - os: ubuntu-22.04 - otp_version: '27.x' - rebar3_version: "3.23.0" - - os: ubuntu-22.04 - otp_version: '26.x' - rebar3_version: "3.22.1" - - os: ubuntu-22.04 - otp_version: '25.x' - rebar3_version: "3.22.1" - - os: ubuntu-22.04 - otp_version: '24.x' - rebar3_version: "3.22.1" - - os: ubuntu-20.04 - otp_version: '23.x' - rebar3_version: "3.19.0" - - steps: - - name: Install OTP ${{matrix.otp_version}} - uses: erlef/setup-beam@v1 - with: - version-type: loose - otp-version: ${{ matrix.otp_version}} - rebar3-version: ${{ matrix.rebar3_version}} - - - name: Checkout qdate - uses: actions/checkout@v4 - - - name: Run Tests - run: make test - - - name: Run Dialyzer - run: make dialyzer diff --git a/.gitignore b/.gitignore index 6ff84d8..47010b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,8 @@ *~ *.beam *.sw? -*.iml deps/ ebin/ .eunit/ -.idea/ _build -doc/ +rebar.lock diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6a6becb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: erlang +script: "make test" +otp_release: + - 17.4 + - 17.1 + - 17.0 + - R16B + - R15B02 + - R15B01 + - R15B +before_script: "sudo apt-get --yes --force-yes install libpam0g-dev" diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index f44c11c..54a2812 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,50 +1,8 @@ -## 0.7.3 +## 0.5.0 (in development) -* Remove the `?else` macro. - -## 0.7.2 - -* Update the error message when qdate is not started with some better instructions. -* Some minor updates for hex.pm -* Removed the `erlnow()` warning -* Fix some typos in the documentation -* Update the makefile to use [rebar3.mk](https://rebar3.mk) -* Skipped 0.7.1 only because there was a partially tagged 0.7.1 for a while, - but was never published to hex. Just to ensure upgrades are easier, I just skipped - a proper 0.7.1 release - -## 0.7.0 - -* Re-introduce the qdate server for storing qdate timezones, formats, and parsers, - rather than overloading the `application` env vars (since the `application` - module only wants keys to be atoms). -* Convert to using `qdate_localtime` 1.2.0 (which passes dialyzer checks) -* `qdate` is passing dialyzer again - -## 0.6.0 - -* Add `age` and `age_days` functions -* Add option to preserve millisecond accuracy in date parsing and formatting (@Leonardb) - - -## 0.5.0 - -* 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, week, month, or year) -+ Add `end_X` functions to return the last second of each time period (this is - just the opposite of `beginning_X`) -* Add `between/[2,3,5]` functions for computing whether a date/time is between - two others. -* Update to rebar3 and add hex compatibility. (@Licenser) +* Update to rebar3 and add hex compatability. (@Licenser) * Properly add dependent apps to .app.src (@Licenser) -* Add an optional "relative date/time parser". -* Fix: Ensure `get_timezone()` returns the default timezone (from config) if it - hasn't been set by `get_timezone()` -* Fix UTC/GMT bug (@loudferret) -* Fix Erlang 21 Stacktrace changes (@tnt-dev) -* Set a better rebar2 version of erlware commons (@tnt-dev) +* Remove R14 from travis testing. ## 0.4.2 diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/Makefile b/Makefile index de4d762..e94d5d0 100644 --- a/Makefile +++ b/Makefile @@ -1,41 +1,21 @@ +REBAR = $(shell pwd)/rebar3 + all: compile -# Check if rebar3.mk exists, and if not, download it -ifeq ("$(wildcard rebar3.mk)","") -$(shell curl -O https://raw.githubusercontent.com/choptastic/rebar3.mk/master/rebar3.mk) -endif - -# rebar3.mk adds a new rebar3 rule to your Makefile -# (see https://github.com/choptastic/rebar3.mk) for full info -include rebar3.mk - -compile: rebar3 +compile: $(REBAR) compile -update: rebar3 +update: $(REBAR) update -test: - EUNIT=1 $(REBAR) compile - EUNIT=1 $(REBAR) eunit +test: compile + $(REBAR) eunit -dialyzer: compile - DIALYZER=1 $(REBAR) dialyzer - -dev: - mkdir -p _checkouts - cd _checkouts; git clone https://github.com/choptastic/qdate_localtime - - -run: rebar3 +run: $(REBAR) shell -push_tags: - git push --tag - -pull_tags: - git pull --tag - -publish: rebar3 pull_tags - $(REBAR) hex publish +publish: + $(REBAR) as pkg upgrade + $(REBAR) as pkg hex publish + $(REBAR) upgrade diff --git a/README.md b/README.markdown similarity index 73% rename from README.md rename to README.markdown index a462a9b..5fcad6f 100644 --- a/README.md +++ b/README.markdown @@ -1,6 +1,6 @@ # qdate - Erlang Date and Timezone Library -[![qdate tests and dialyzer](https://github.com/choptastic/qdate/actions/workflows/tests-workflow.yml/badge.svg)](https://github.com/choptastic/qdate/actions/workflows/tests-workflow.yml) +[![Build Status](https://travis-ci.org/choptastic/qdate.png?branch=master)](https://travis-ci.org/choptastic/qdate) ## Purpose @@ -98,16 +98,14 @@ will infer the timezone in the following order. `set_timezone/1` only applies to that *specific* process. If none is specified. + If no timezone is specified for the process, `qdate` looks at the `qdate` - application variable `default_timezone`. `default_timezone` can be either a - hard-specified timezone, or a `{Module, Function}` tuple. The tuple format - should return either a timezone or the atom `undefined`. + application variable `default_timezone`. + If no timezone is specified by either of the above, `qdate` assumes "GMT" for all dates. + A timezone value of `auto` will act as if no timezone is specified. #### Disambiguating Ambiguous Timezone Conversions -Sometimes, when you're converting a datetime from one timezone to another, there +Sometimes, when youre converting a datetime from one timezone to another, there are potentially two different results if the conversion happens to land on in a timezone that's in the middle of a Daylight Saving conversion. For example, converting "11-Nov-2013 1:00:am" in "America/New York" to "GMT" could be both @@ -192,72 +190,24 @@ ok `qdate` provides a few convenience functions for performing date comparisons. - + `compare(A, B) -> -1|0|1` - Like C's `strcmp`, returns: + + `compare(A, B)` - Like C's `strcmp`, returns: + `0`: `A` and `B` are exactly the same. + `-1`: `A` is less than (before) `B`. + `1`: `A` is greater than (after) `B`. - + `compare(A, Operator, B) -> true|false` - Operator is an infix comparison operator, and - the function will return a boolean. Will return `true` if: + + `compare(A, Operator, B)` - Operator is an infix comparison operator, and + the function will return true if: + `'='`, or `'=='` - `A` is the same time as `B` + `'/='`, or `'=/='` or `'!='` - `A` is not the same time as `B` + `'<'` - `A` is before `B` + `'>'` - `A` is after `B` + `'=<'` or `'<='` - `A` is before or equal to `B` + `'>='` or `'=>'` - `A` is after or equal to `B` - + `between(A, Date, B) -> true|false` - The provided `Date` is (inclusively) - between `A` and `B`. That is, `A =< Date =< B`. - + `between(A, B) -> true|false` - shortcut for `between(A, now(), B)` - + `between(A, Op1, Date, Op2, B) -> true|false` - the fully verbose option of - comparing between. `Op1` and `Op2` are custom operators. For example, if - you wanted to do an exclusive `between`, you can do: - `between(A, '<', Date, '<', B)` **Note 1:** `Operator` must be an atom. **Note 2:** These functions will properly compare times with different timezones (for example: `compare("12am CST",'==',"1am EST")` will properly return true) -### Sorting - -`qdate` also provides a convenience functions for sorting lists of dates/times: - - + `sort(List)` - Sort the list in ascending order of earliest to latest. - + `sort(Op, List)` - Sort the list where `Op` is one of the following: - + `'<'` or `'=<'` or `'<='` - Sort ascending - + `'>'` or `'>='` or `'=>'` - Sort descending - + `sort(Op, List, Opts)` - Sort the list according to the `Op`, with options provided in `Opts`. `Opts` is a proplist of the following options: - + `{non_dates, NonDates}` - Tells it how to handle non-dates. `NonDates` can be any of the following: - + `back` **(default)** - put any non-dates at the end (the back) of the list - + `front` - put any non-dates at the beginning of the list - + `crash` - if there are any non-dates, crash. - -Example: - -```erlang - 1> Dates = ["non date string", <<"garbage">>, - 1466200861, "2011-01-01", "7pm", - {{1999,6,21},{5,30,0}}, non_date_atom, {some_tuple,123}]. - 2> qdate:sort('>=', Dates, [{non_dates, front}]). - [<<"garbage">>,"non date string", - {some_tuple,123}, - non_date_atom,1466200861,"2011-01-01", - {{1999,6,21},{5,30,0}}, - "7pm"] -``` - -**Note 1:** This sorting is optimized to be much faster than using a home-grown -sort using the `compare` functions, as this normalizes the items in the list -before comparing (so it's only really comparing integers, which is quite fast). - -**Note 2:** This is one of the few qdate functions that don't have the "Date" -as the last argument. This follows the pattern in Erlang/OTP to put options as -the last argument (for example, `re:run/3`) - -**Note 3:** You'll notice that qdate's sorting retains the original terms (in -the example above, we compared a datetime tuple, unix timestamp, and two -strings (along with a number of non-dates, which were just prepended to the -front of the list). - ### Timezone Functions + `set_timezone(Key, TZ)` - Set the timezone to TZ for the key `Key` @@ -498,31 +448,8 @@ the crash. **Another Note:** Custom parsers are expected to return either: + A `datetime()` tuple. (ie {{2012,12,21},{14,45,23}}). - + An integer, which represents the Unix timestamp. + The atom `undefined` if this parser is not a match for the supplied value -#### Included Parser: Relative Times - -`qdate` ships with an optional relative time parser. To speed up performance -(since this parser uses regular expressions), this parser is disabled by -default. But if you wish to use it, make sure you call -`qdate:register_parser(parse_relative, fun qdate:parse_relative/1)`. - -Doing this allows you to parse relative time strings of the following formats: - - + "1 hour ago" - + "-15 minutes" - + "in 45 days" - + "+2 years" - -And doing so allows you to construct slightly more readable comparison calls -for sometimes common comparisons. For example, the following two calls are identical: - -```erlang -qdate:between("-15 minutes", Date, "+15 minutes"). - -qdate:between(qdate:add_minutes(-15), Date, qdate:add_minutes(15)). -``` ### Registering Custom Formats @@ -536,7 +463,7 @@ qdate:between(qdate:add_minutes(-15), Date, qdate:add_minutes(15)). %% But, you don't have to: if that's a common format you use in your %% application, you can register your format with the `qdate` server, and then -%% easily refer to that format by its key. +%% easiy refer to that format by its key. %% So let's take that format and register it 16> qdate:register_format(longdate, "l, F jS, Y g:i A T"). @@ -692,76 +619,25 @@ ok %% that timezone to our intended timezone. ``` -## Beginning or Ending of time periods (hours, days, years, weeks, etc) - -qdate can determine beginnings and endings of time periods, like "beginning of the 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. - -#### Beginning of Week - -qdate can also do a special "beginning" case, particularly the "beginning of -the week" calculation. This has three forms, specifically: - - + `beginning_week()` - Returns first day of the current week. - + `beginning_week(Date)` - Assumes the beginning of the week is Monday - (chosen because Erlang's calendar:day_of_the_week uses 1=Monday and - 7=Sunday). - + `beginning_week(DayOfWeek, Date)` - Calculates the beginning of the week - based on the provided `DayOfWeek`. Valid values for DayOfWeek are the - integers 1-7 or the atom versions of the days of the week. Specifically: - - * Monday: `1 | monday | mon` - * Tuesday: `2 | tuesday | tue` - * Wednesday: `3 | wednesday | wed` - * Thursday: `4 | thursday | thu` - * Friday: `5 | friday | fri` - * Saturday: `6 | saturday | sat` - * Sunday: `7 | sunday | sun` - -These all return 12am on the day that is the first day of the week of the -provided date. - -(My apologies to non-English speakers. I'm a lazy American who only speaks -English, hence the Anglocentric day names). - -### End of time period - -There are also the related `end_X` functions available, using the same -conventions, except return the last second of that time period. - -So `end_month("2016-01-05")` will return the unix timestamp representing -"2016-01-31 11:59:59pm" - - ## Date Arithmetic +(not fully tested yet, but will have full tests for 0.4.0) + The current implementation of qdate's date arithmetic returns Unixtimes. There are 8 main functions for date arithmetic: - + `add_seconds(Seconds, Date)` - + `add_minutes(Minutes, Date)` - + `add_hours(Hours, Date)` - + `add_days(Days, Date)` - + `add_weeks(Weeks, Date)` - + `add_months(Months, Date)` - + `add_years(Years, Date)` - + `add_date(DateToAdd, Date)` - `DateToAdd` is a shortcut way of adding - 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 - minutes, and not make any changes to seconds. + + `add_seconds(Seconds, Date)` + + `add_minutes(Minutes, Date)` + + `add_hours(Hours, Date)` + + `add_days(Days, Date)` + + `add_weeks(Weeks, Date)` + + `add_months(Months, Date)` + + `add_years(Years, Date)` + + `add_date(DateToAdd, Date)` - `DateToAdd` is a shortcut way of adding + 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 + minutes, and not make any changes to seconds. For the date arithmetic functions, `Date`, like all `qdate` functions, can be any format. @@ -778,89 +654,6 @@ There are 7 other arithmetic functions that take a single argument, and these do + `add_months(Months)` + `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*. - - -## Age Comparison - -There are two main comparisons right now, age in years, and age in days. - - + `age(Date)` - Number of years since `Date` - + `age(FromDate, ToDate)` - Number of years between `FromDate` to `ToDate` - + `age_days(Date)` - Number of full days since `Date` (for example from `2pm` yesterday to `1:59pm` today is still 0. - + `age_days(FromDate, ToDate)` - Number of full days between `FromDate` and `ToDate`. - -## Configuration Sample - -There is a sample configuration file can be found in the root of the qdate -directory. Or you can just [look at it -here](https://github.com/choptastic/qdate/blob/master/qdate.config). - ## Thanks A few shoutouts to [Dale Harvey](http://github.com/daleharvey) and the @@ -887,6 +680,7 @@ See [CHANGELOG.markdown](https://github.com/choptastic/qdate/blob/master/CHANGEL + Make `qdate` backend-agnostic (allow specifying either ec_date or dh_date as the backend) + Add `-spec` and `-type` info for dialyzer ++ Provide a sample qdate.config for users to see + Research the viability of [ezic](https://github.com/drfloob/ezic) for a timezone backend replacement for `erlang_localtime`. diff --git a/qdate.config b/qdate.config deleted file mode 100644 index a2dd0e2..0000000 --- a/qdate.config +++ /dev/null @@ -1,20 +0,0 @@ -%% vim: ts=4 sw=4 et ft=erlang -[{qdate, [ - %% default_timezone can be one of two things: - %% 1) An actual timezone. Either short-form like "GMT", "UTC", or a - %% longer-form (but more likely to pick the correct daylight saving - %% config timezone like "America/Chicago" - %% 2) A 2-tuple of {Module, Function}, which will be called as - %% Module:Function() to determine the timezone (say you wanted to - %% determine timezone based on some kind of environmental conditions) - {default_timezone, "GMT"}, - - %% See readme section here: - %% https://github.com/choptastic/qdate#about-backwards-compatibility-with-ec_date-and-deterministic-parsing - {deterministic_parsing, {zero, zero}}, - - %% Preserve Milliseconds in parsing - %% Changes the return for qdate:to_date to include any Ms value - %% and also changes the return of to_string to include the Ms value - {preserve_ms, false} -]}]. diff --git a/rebar.config b/rebar.config index 9dc3672..34c0671 100644 --- a/rebar.config +++ b/rebar.config @@ -2,23 +2,25 @@ %% vim:ts=4 sw=4 et ft=erlang {cover_enabled, true}. -{dialyzer, [ - {exclude_apps, []}, - {warnings, []} -]}. - +%% For rebar2 compat {deps, [ - erlware_commons, - {qdate_localtime, "~> 1.2.0"} + %% 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"}}}, + + %% We'll temporarily still use choptastic/erlang_localtime until + %% https://github.com/dmitryme/erlang_localtime/pull/24 gets merged. Then we + %% can switch to the mainline repo + {erlang_localtime, ".*", {git, "git://github.com/choptastic/erlang_localtime.git", {branch, master}}} ]}. -{project_plugins, [rebar3_ex_doc]}. - -{hex, [{doc, ex_doc}]}. - -{ex_doc, [ - {source_url, <<"https://github.com/choptastic/qdate">>}, - {extras, [<<"README.md">>, <<"LICENSE.md">>]}, - {main, <<"readme">>}]}. - +%% for rebar3 +{profiles, + [{pkg, + [{deps, + [ + erlware_commons, + erlang_localtime + ]}]}]}. diff --git a/rebar.config.script b/rebar.config.script deleted file mode 100644 index 61542d6..0000000 --- a/rebar.config.script +++ /dev/null @@ -1,15 +0,0 @@ -%% -*- mode: erlang -*- -%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- -%% ex: ts=4 sw=4 sts ft=erlang et - -case erlang:function_exported(rebar3, main, 1) of - true -> % rebar3 - CONFIG; - false -> % rebar 2.x or older - %% Rebuild deps, possibly including those that have been moved to - %% profiles - [{deps, [ - {erlware_commons, "", {git, "https://github.com/erlware/erlware_commons", {tag, "v1.5.0"}}}, %% this is the version of erlware_commons that works until erlware tags a new version - {qdate_localtime, "", {git, "https://github.com/choptastic/qdate_localtime", {tag, "1.1.0"}}} - ]} | lists:keydelete(deps, 1, CONFIG)] -end. diff --git a/rebar.lock b/rebar.lock deleted file mode 100644 index 30b2fae..0000000 --- a/rebar.lock +++ /dev/null @@ -1,14 +0,0 @@ -{"1.2.0", -[{<<"cf">>,{pkg,<<"cf">>,<<"0.3.1">>},1}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.6.0">>},0}, - {<<"qdate_localtime">>,{pkg,<<"qdate_localtime">>,<<"1.2.1">>},0}]}. -[ -{pkg_hash,[ - {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>}, - {<<"erlware_commons">>, <<"E0DA62F91E65DEFAE28BB450DDC3C24E85ECB99B926F857CDC6ED8E7DD7076B4">>}, - {<<"qdate_localtime">>, <<"72E1034DC6B7FEE8F588281EDDD0BD0DC5260005D758052F50634D265D382C18">>}]}, -{pkg_hash_ext,[ - {<<"cf">>, <<"315E8D447D3A4B02BCDBFA397AD03BBB988A6E0AA6F44D3ADD0F4E3C3BF97672">>}, - {<<"erlware_commons">>, <<"B57C299C39A2992A8C413F9D2C1B8A20061310FB4432B0EEE5E4C4DF7AAA0AA3">>}, - {<<"qdate_localtime">>, <<"1109958D205C65C595C8C5694CB83EBAF2DBE770CF902E4DCE8AFB2C4123764D">>}]} -]. diff --git a/rebar3 b/rebar3 new file mode 100755 index 0000000..8a36476 Binary files /dev/null and b/rebar3 differ diff --git a/src/qdate.app.src b/src/qdate.app.src index 2479739..1c522ce 100644 --- a/src/qdate.app.src +++ b/src/qdate.app.src @@ -1,17 +1,17 @@ {application, qdate, [ {description, "Simple Date and Timezone handling for Erlang"}, - {vsn, git}, + {vsn, "0.4.2"}, {registered, []}, {applications, [ kernel, - stdlib, - qdate_localtime, - erlware_commons + erlang_localtime, + erlware_commons, + stdlib ]}, - {modules, [qdate, qdate_srv, qdate_sup, qdate_app]}, + {modules, [qdate, qdate_srv]}, {env, []}, + {contributors, ["Jesse Gumm"]}, {licenses, ["MIT"]}, - {mod, {qdate_app, []}}, {links, [{"Github", "https://github.com/choptastic/qdate"}]} ]}. diff --git a/src/qdate.erl b/src/qdate.erl index 7027b27..d80529d 100644 --- a/src/qdate.erl +++ b/src/qdate.erl @@ -1,11 +1,9 @@ % vim: ts=4 sw=4 et -% Copyright (c) 2013-2023 Jesse Gumm +% Copyright (c) 2013 Jesse Gumm % See LICENSE for licensing information. % -module(qdate). -%-compile(export_all). - -export([ start/0, stop/0 @@ -26,50 +24,9 @@ unixtime/0 ]). --export([ - beginning_minute/1, - beginning_minute/0, - beginning_hour/1, - beginning_hour/0, - beginning_day/1, - beginning_day/0, - beginning_week/0, - beginning_week/1, - beginning_week/2, - beginning_month/1, - beginning_month/0, - beginning_year/1, - beginning_year/0 -]). - --export([ - end_minute/1, - end_minute/0, - end_hour/1, - end_hour/0, - end_day/1, - end_day/0, - end_week/0, - end_week/1, - end_week/2, - end_month/1, - end_month/0, - end_year/1, - end_year/0 -]). - -export([ compare/2, - compare/3, - between/2, - between/3, - between/5 -]). - --export([ - sort/1, - sort/2, - sort/3 + compare/3 ]). -export([ @@ -87,29 +44,9 @@ add_months/1, add_years/2, add_years/1, - add_unit/2, - add_unit/3, 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([ - age/1, - age/2, - age_days/1, - age_days/2 -]). - -export([ register_parser/2, register_parser/1, @@ -129,9 +66,6 @@ clear_timezone/1 ]). --export([ - parse_relative/1 -]). %% Exported for API compatibility with ec_date -export([ @@ -140,32 +74,28 @@ parse/1 ]). --type qdate() :: any(). --type datetime() :: {{integer(), integer(), integer()}, {integer(), integer(), integer()}} | - {{integer(), integer(), integer()}, {integer(), integer(), integer(), integer()}}. --type erlnow() :: {integer(), integer(), integer()}. --type binary_or_string() :: binary() | string(). --type disambiguate() :: prefer_standard | prefer_daylight | both. - -%% erlang:get_stacktrace/0 is deprecated in OTP 21 --ifndef(OTP_RELEASE). --define(WITH_STACKTRACE(T, R, S), T:R -> S = erlang:get_stacktrace(), ). --else. --define(WITH_STACKTRACE(T, R, S), T:R:S ->). --endif. - %% This the value in gregorian seconds for jan 1st 1970, 12am %% It's used to convert to and from unixtime, since unixtime starts %% 1970-01-01 12:00am -define(UNIXTIME_BASE,62167219200). +%% This is the timezone only if the qdate application variable +%% "default_timezone" isn't set or is set to undefined. +%% It's recommended that your app sets the var in a config, or at least using +%% +%% application:set_env(qdate, default_timezone, "GMT"). +%% +-define(DEFAULT_TZ, case application:get_env(qdate, default_timezone) of + undefined -> "GMT"; + {ok, TZ} -> TZ + end). -define(DETERMINE_TZ, determine_timezone()). -define(DEFAULT_DISAMBIG, prefer_standard). - +-define(else, true). start() -> - application:ensure_all_started(qdate). + application:load(qdate). stop() -> ok. @@ -179,7 +109,6 @@ to_string(Format, Date) -> to_string(Format, ToTZ, Date) -> to_string(Format, ToTZ, ?DEFAULT_DISAMBIG, Date). --spec to_string(Format :: any(), ToTZ :: any(), Disambiguate :: disambiguate(), Date :: qdate()) -> binary_or_string() | {ambiguous, binary_or_string() , binary_or_string()}. to_string(FormatKey, ToTZ, Disambiguate, Date) when is_atom(FormatKey) orelse is_tuple(FormatKey) -> Format = case qdate_srv:get_format(FormatKey) of undefined -> throw({undefined_format_key,FormatKey}); @@ -246,13 +175,7 @@ to_string_worker([$r | RestFormat], ToTZ, Disamb, Date) -> to_string_worker(NewFormat, ToTZ, Disamb, Date) ++ to_string_worker(RestFormat, ToTZ, Disamb, Date); to_string_worker([$c | RestFormat], ToTZ, Disamb, Date) -> Format1 = "Y-m-d", - Format2 = case Date of - {_, {_,_,_,_}} -> - %% Have milliseconds - "H:i:s.fP"; - _ -> - "H:i:sP" - end, + Format2 = "H:i:sP", to_string_worker(Format1, ToTZ, Disamb, Date) ++ "T" ++ to_string_worker(Format2, ToTZ, Disamb, Date) @@ -303,7 +226,6 @@ to_date(RawDate) -> to_date(ToTZ, RawDate) -> to_date(ToTZ, ?DEFAULT_DISAMBIG, RawDate). --spec to_date(ToTZ :: any(), Disambiguate :: disambiguate(), RawDate :: any()) -> {ambiguous, datetime(), datetime()} | datetime(). to_date(ToTZ, Disambiguate, RawDate) when is_binary(RawDate) -> to_date(ToTZ, Disambiguate, binary_to_list(RawDate)); to_date(ToTZ, Disambiguate, RawDate) when is_binary(ToTZ) -> @@ -317,14 +239,10 @@ to_date(ToTZ, Disambiguate, RawDate) -> {ParsedDate,ExtractedTZ}; {ParsedDate,ParsedTZ} -> {ParsedDate,ParsedTZ} - end, - PreserveMs = preserve_ms(), + end, try raw_to_date(RawDate3) of D={{_,_,_},{_,_,_}} -> date_tz_to_tz(D, Disambiguate, FromTZ, ToTZ); - {{Year, Month, Date},{Hour,Minute,Second,Millis}} when PreserveMs -> - {ODate, {OHour,OMinute,OSecond}} = date_tz_to_tz({{Year, Month, Date},{Hour,Minute,Second}}, Disambiguate, FromTZ, ToTZ), - {ODate, {OHour,OMinute,OSecond, Millis}}; {{Year, Month, Date},{Hour,Minute,Second,_Millis}} -> date_tz_to_tz({{Year, Month, Date},{Hour,Minute,Second}}, Disambiguate, FromTZ, ToTZ) catch @@ -361,10 +279,9 @@ get_deterministic_datetime() -> to_unixtime(Date) -> to_unixtime(?DEFAULT_DISAMBIG, Date). --spec to_unixtime(Disamb :: disambiguate(), qdate()) -> {ambiguous, integer(), integer()} | integer(). to_unixtime(_, Unixtime) when is_integer(Unixtime) -> Unixtime; -to_unixtime(_, {MegaSecs,Secs,_}) when is_integer(MegaSecs), is_integer(Secs) -> +to_unixtime(_, {MegaSecs,Secs,_}) -> MegaSecs*1000000 + Secs; to_unixtime(Disamb, ToParse) -> %% We want to treat all unixtimes as GMT @@ -383,12 +300,11 @@ unixtime() -> to_now(Date) -> to_now(?DEFAULT_DISAMBIG, Date). --spec to_now(Disamb :: disambiguate(), qdate()) -> erlnow() | {ambiguous, erlnow(), erlnow()}. to_now(_, Now = {_,_,_}) -> Now; to_now(Disamb, ToParse) -> case to_unixtime(Disamb, ToParse) of - {ambiguous, Standard, Daylight} when is_integer(Standard), is_integer(Daylight) -> + {ambiguous, Standard, Daylight} -> {ambiguous, unixtime_to_now(Standard), unixtime_to_now(Daylight)}; @@ -397,144 +313,10 @@ to_now(Disamb, ToParse) -> end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Beginning/Truncation %%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -beginning_minute() -> - beginning_minute({date(),time()}). - -beginning_minute(Date) -> - {{Y,M,D},{H,I,_}} = to_date(Date), - {{Y,M,D},{H,I,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(unixtime()). - -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}}. - -beginning_week() -> - beginning_week({date(), time()}). - -%% 1 = Monday, 7 = Sunday -beginning_week(Date) -> - beginning_week(1, Date). - -beginning_week(BeginningDayOfWeek, Date) when is_atom(BeginningDayOfWeek) -> - DOW = weekday_map(BeginningDayOfWeek), - beginning_week(DOW, Date); -beginning_week(BeginningDayOfWeek, Date0) when - BeginningDayOfWeek >= 1, - BeginningDayOfWeek =< 7, - is_integer(BeginningDayOfWeek) -> - {DateOnly, _} = Date = to_date(Date0), - CurDOW = calendar:day_of_the_week(DateOnly), - if - CurDOW==BeginningDayOfWeek -> - {DateOnly, {0,0,0}}; - CurDOW > BeginningDayOfWeek-> - Diff = CurDOW - BeginningDayOfWeek, - beginning_day(add_days(-Diff, Date)); - CurDOW < BeginningDayOfWeek -> - Diff = 7 - (BeginningDayOfWeek - CurDOW), - beginning_day(add_days(-Diff, Date)) - end. - -weekday_map(mon) -> 1; -weekday_map(tue) -> 2; -weekday_map(wed) -> 3; -weekday_map(thu) -> 4; -weekday_map(fri) -> 5; -weekday_map(sat) -> 6; -weekday_map(sun) -> 7; - -weekday_map(monday) -> 1; -weekday_map(tuesday) -> 2; -weekday_map(wednesday) -> 3; -weekday_map(thursday) -> 4; -weekday_map(friday) -> 5; -weekday_map(saturday) -> 6; -weekday_map(sunday) -> 7. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%% End of Period (day/hour, etc) %%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -end_minute() -> - end_minute({date(),time()}). - -end_minute(Date) -> - {{Y,M,D},{H,I,_}} = to_date(Date), - {{Y,M,D},{H,I,59}}. - -end_hour() -> - end_hour({date(),time()}). - -end_hour(Date) -> - {{Y,M,D},{H,_,_}} = to_date(Date), - {{Y,M,D},{H,59,59}}. - -end_day() -> - end_day({date(),time()}). - -end_day(Date) -> - {{Y,M,D},_} = to_date(Date), - {{Y,M,D},{23,59,59}}. - -end_month() -> - end_month({date(), time()}). - -end_month(Date) -> - Beginning = beginning_month(Date), - add_seconds(-1, add_months(1, Beginning)). - -end_year() -> - end_year({date(),time()}). - -end_year(Date) -> - {{Y,_,_},_} = to_date(Date), - {{Y,12,31},{23,59,59}}. - -end_week() -> - end_week({date(), time()}). - -end_week(Date) -> - end_week(1, Date). - -end_week(BeginningDayOfWeek, Date) -> - Beginning = beginning_week(BeginningDayOfWeek, Date), - PlusWeek = add_weeks(1, Beginning), - add_seconds(-1, PlusWeek). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comparisons %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec compare(A :: qdate(), B :: qdate()) -> integer(). compare(A, B) -> NowA = to_now(A), NowB = to_now(B), @@ -544,7 +326,6 @@ compare(A, B) -> NowA > NowB -> 1 end. --spec compare(A :: qdate(), Op :: atom(), B :: qdate()) -> boolean(). compare(A, Op, B) -> Comp = compare(A, B), case Op of @@ -555,93 +336,15 @@ compare(A, Op, B) -> '=/=' -> Comp =/= 0; '/=' -> Comp =/= 0; - 'before'-> Comp =:= -1; '<' -> Comp =:= -1; '<=' -> Comp =:= -1 orelse Comp =:= 0; '=<' -> Comp =:= -1 orelse Comp =:= 0; - 'after' -> Comp =:= 1; '>' -> Comp =:= 1; '>=' -> Comp =:= 1 orelse Comp =:= 0; '=>' -> Comp =:= 1 orelse Comp =:= 0 end. -between(A, B) -> - between(A, unixtime(), B). - -between(A, Date, B) -> - between(A, '=<', Date, '=<', B). - -between(A, Op1, Date, Op2, B) -> - compare(A, Op1, Date) andalso compare(Date, Op2, B). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Sorting %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -sort(List) -> - sort('=<', List). - -sort(Op, List) -> - sort(Op, List, [{non_dates, back}]). - -sort(Op, List, Opts) -> - NonDateOpt = proplists:get_value(non_dates, Opts, back), - WithNorm = add_sort_normalization(List, NonDateOpt), - SortFun = make_sort_fun(Op, NonDateOpt), - Sorted = lists:sort(SortFun, WithNorm), - strip_sort_normalization(Sorted). - -%% Normalization pre-processes the dates (converting them to unixtimes for easy -%% comparison, and also tags non-dates (dates that crashed during parsing) as such -add_sort_normalization(List, NonDateOpt) -> - lists:map(fun(Date) -> - Sortable = try to_unixtime(Date) - catch _:_ when NonDateOpt=/=crash -> - {non_date, Date} - end, - {Sortable, Date} - end, List). - -%% Remove the normalization tag to return the original term -strip_sort_normalization(List) -> - [Date || {_, Date} <- List]. - --spec make_sort_fun(Op :: atom(), NonDateOpt :: front | back) -> fun(). -make_sort_fun(Op, NonDateOpt) -> - DateComp = sort_op_comp_fun(Op), - - fun({{non_date, A}, _}, {{non_date, B},_}) -> - DateComp(A,B); - ({{non_date, _}, _}, _) when NonDateOpt == front -> - true; - ({{non_date, _}, _}, _) when NonDateOpt == back -> - false; - (_, {{non_date, _}, _}) when NonDateOpt == front -> - false; - (_, {{non_date, _}, _}) when NonDateOpt == back -> - true; - (A, B) -> - DateComp(A, B) - end. - -sort_op_comp_fun(Op) -> - fun(A, B) -> - case Op of - 'before'-> A < B; - '<' -> A < B; - '<=' -> A =< B; - '=<' -> A =< B; - - 'after' -> A > B; - '>' -> A > B; - '>=' -> A >= B; - '=>' -> A >= B - end - end. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Date Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -702,48 +405,6 @@ add_years(Years, Date) -> add_years(Years) -> add_years(Years, os:timestamp()). --type unit() :: second | seconds | - minute | minutes | - hour | hours | - day | days | - week | weeks | - month | months | - year | years. - --spec add_unit(Unit :: unit(), Value :: integer(), Date :: qdate()) -> qdate(). -add_unit(second, Value, Date) -> - add_unit(seconds, Value, Date); -add_unit(seconds, Value, Date) -> - add_seconds(Value, Date); -add_unit(minute, Value, Date) -> - add_unit(minutes, Value, Date); -add_unit(minutes, Value, Date) -> - add_minutes(Value, Date); -add_unit(hour, Value, Date) -> - add_unit(hours, Value, Date); -add_unit(hours, Value, Date) -> - add_hours(Value, Date); -add_unit(day, Value, Date) -> - add_unit(days, Value, Date); -add_unit(days, Value, Date) -> - add_days(Value, Date); -add_unit(week, Value, Date) -> - add_unit(weeks, Value, Date); -add_unit(weeks, Value, Date) -> - add_weeks(Value, Date); -add_unit(month, Value, Date) -> - add_unit(months, Value, Date); -add_unit(months, Value, Date) -> - add_months(Value, Date); -add_unit(year, Value, Date) -> - add_unit(years, Value, Date); -add_unit(years, Value, Date) -> - add_years(Value, Date). - -add_unit(Unit, Value) -> - add_unit(Unit, Value, os:timestamp()). - - add_date({{AddY, AddM, AddD}, {AddH, AddI, AddS}}, Date) -> {{Y, M, D}, {H, I, S}} = to_date(Date), Date1 = fix_maybe_improper_date({{Y+AddY, M+AddM, D+AddD}, {H, I, S}}), @@ -759,22 +420,12 @@ fix_maybe_improper_date({Date0, Time}) -> Date = fmid(Date0), {Date, Time}. - -%% Originally, this function didn't recurse. Here's the story. Some numbers, -%% like M = 12 (December) or M = -11 (January) would trigger an overflow or -%% underflow, resulting in fix_year_month returning something nonsensical like -%% {2018, 13}. I added some extra clauses to special treat those "overflow but -%% shouldn't" situations, but realized it was just cleaner to recurse, calling -%% fix_year_month on the calculated result, knowing that the numbers will -%% normalize on their own. So for all the clauses of fix_year_month, we recurse -%% as a sanity check, eventually only returning the result of the "Everything -%% Looks good" clause at the bottom. fix_year_month({Y, M}) when M > 12 -> YearsOver = M div 12, - fix_year_month({Y + YearsOver, M-(YearsOver*12)}); + {Y + YearsOver, M-(YearsOver*12)}; fix_year_month({Y, M}) when M < 1 -> YearsUnder = (abs(M-1) div 12) + 1, - fix_year_month({Y - YearsUnder, M+(YearsUnder*12)}); + {Y - YearsUnder, M+(YearsUnder*12)}; fix_year_month({Y, M}) -> {Y, M}. @@ -809,170 +460,6 @@ fmid({Y, M, D}) when D < 1 -> fmid(Date) -> Date. -age(Birth) -> - age(Birth, os:timestamp()). - -age(Birth, Now) -> - %% B=Birth - {{BY, BM, BD}, _} = to_date(Birth), - %% C=Current - {{CY, CM, CD}, _} = to_date(Now), - if - (CM > BM); - (CM == BM andalso CD >= BD) - -> CY - BY; - true -> - (CY - BY) - 1 - end. - -age_days(Birth) -> - age_days(Birth, os:timestamp()). - -age_days(Birth, Now) -> - case {to_date(Birth), to_date(Now)} of - {{SameDay, _}, {SameDay, _}} -> - 0; - {{BirthDate, BirthTime}, {NowDate, NowTime}} -> - BirthDays = calendar:date_to_gregorian_days(BirthDate), - NowDays = calendar:date_to_gregorian_days(NowDate), - DiffDays = NowDays - BirthDays, - if - NowTime >= BirthTime -> - DiffDays; - true -> - DiffDays-1 - end - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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). - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%% Relative Date Parsing %%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -parse_relative({relative, Date, Relation}) when is_atom(Relation) -> - parse_relative({relative, Date, atom_to_list(Relation)}); -parse_relative({relative, Date, Relation}) when is_list(Relation); is_binary(Relation) -> - case parse_actual_relation(Relation) of - undefined -> undefined; - {OpStr, NumStr, UnitStr} -> - {Num, Unit} = normalize_relative_matches(OpStr, NumStr, UnitStr), - add_unit(Unit, Num, Date) - end; -parse_relative(now) -> - unixtime(); -parse_relative("now") -> - unixtime(); -parse_relative(<<"now">>) -> - unixtime(); -parse_relative(Relation) when is_list(Relation); is_binary(Relation) -> - parse_relative({relative, unixtime(), Relation}); -parse_relative(_) -> - undefined. - - -%% I would do this function recursively, but the return order of arguments -%% inconsistent, so I just leave it like this. It's a little nasty to have the -%% nested case expressions, but I can deal with it. -parse_actual_relation(Relation) -> - PrefixRE = "^(\\-|\\+|in)\\s?(\\d+) (second|minute|hour|day|week|month|year)s?$", - SuffixRE = "^(\\d+) (second|minute|hour|day|week|month|year)s?\\s?(ago|from now)?$", - case re:run(Relation, PrefixRE, [{capture, all_but_first, list}]) of - nomatch -> - case re:run(Relation, SuffixRE, [{capture, all_but_first, list}]) of - nomatch -> undefined; - {match, [NumStr, UnitStr, OpStr]} -> - {OpStr, NumStr, UnitStr} - end; - {match, [OpStr, NumStr, UnitStr]} -> - {OpStr, NumStr, UnitStr} - end. - -normalize_relative_matches(OpStr, NumStr, UnitStr) -> - Op = normalize_relative_op(OpStr), - Num = list_to_integer(Op ++ NumStr), - Unit = list_to_existing_atom(UnitStr), - {Num, Unit}. - -normalize_relative_op(Op) -> - case Op of - "+" -> "+"; - "-" -> "-"; - "ago" -> "-"; - "from now" -> "+"; - "in" -> "+" - end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Timezone Stuff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -984,7 +471,6 @@ get_timezone_shift(TZ, Disambiguate, Date) -> {error,T} -> {error,T}; {Sh, _} when Disambiguate==prefer_standard -> Sh; {_, Sh} when Disambiguate==prefer_daylight -> Sh; - 0 -> {'+', 0, 0}; Sh -> Sh end. @@ -1002,8 +488,6 @@ extract_timezone(DateString) when is_list(DateString) -> end; extract_timezone(Date={{_,_,_},{_,_,_}}) -> {Date, ?DETERMINE_TZ}; -extract_timezone(Rel={relative, _, _}) -> - {Rel, "GMT"}; extract_timezone(Now={_,_,_}) -> {Now, "GMT"}; extract_timezone({MiscDate,TZ}) -> @@ -1039,33 +523,15 @@ extract_timezone_helper(RevDate, [TZ | TZs]) when length(RevDate) >= length(TZ) extract_timezone_helper(RevDate, [_TZ | TZs]) -> extract_timezone_helper(RevDate, TZs). -preserve_ms() -> - application:get_env(qdate, preserve_ms, false). - -%% This is the timezone only if the qdate application variable -%% "default_timezone" isn't set or is set to undefined. -%% It's recommended that your app sets the var in a config, or at least using -%% -%% application:set_env(qdate, default_timezone, "GMT"). -%% -default_timezone() -> - case application:get_env(qdate, default_timezone) of - undefined -> "GMT"; - {ok, {Mod, Fun}} -> Mod:Fun(); - {ok, TZ} -> TZ - end. - determine_timezone() -> case qdate_srv:get_timezone() of - undefined -> default_timezone(); + undefined -> ?DEFAULT_TZ; TZ -> TZ end. %% If FromTZ is an integer, then it's an integer that represents the number of minutes %% relative to GMT. So we convert the date to GMT based on that number, then we can %% do the other timezone conversion. --spec date_tz_to_tz(Date :: datetime(), Disambiguate :: disambiguate(), FromTZ :: any(), ToTZ :: any()) -> - datetime() | {ambiguous, datetime(), datetime()}. date_tz_to_tz(Date, Disambiguate, FromTZ, ToTZ) when is_integer(FromTZ) -> NewDate = localtime:adjust_datetime(Date, FromTZ), date_tz_to_tz(NewDate, Disambiguate, "GMT", ToTZ); @@ -1080,14 +546,13 @@ date_tz_to_tz(Date, Disambiguate, FromTZ, ToTZ) -> date_tz_to_tz_both(Date, FromTZ, ToTZ) end. --spec date_tz_to_tz_both(Date :: datetime(), FromTZ :: string(), ToTZ :: string()) -> datetime() | {ambiguous, datetime(), datetime()}. date_tz_to_tz_both(Date, FromTZ, ToTZ) -> Standard = localtime:local_to_local(Date, FromTZ, ToTZ), Daylight = localtime:local_to_local_dst(Date, FromTZ, ToTZ), if Standard=:=Daylight -> Standard; - true -> + ?else -> {ambiguous, Standard, Daylight} end. @@ -1103,7 +568,7 @@ set_timezone(Key,TZ) -> qdate_srv:set_timezone(Key, TZ). get_timezone() -> - ?DETERMINE_TZ. + qdate_srv:get_timezone(). get_timezone(Key) -> qdate_srv:get_timezone(Key). @@ -1154,8 +619,6 @@ try_parsers(_RawDate,[]) -> undefined; try_parsers(RawDate,[{ParserKey,Parser}|Parsers]) -> try Parser(RawDate) of - Timestamp when is_integer(Timestamp) -> - {Timestamp, "GMT"}; {{_,_,_},{_,_,_}} = DateTime -> {DateTime,undefined}; {DateTime={{_,_,_},{_,_,_}},Timezone} -> @@ -1165,8 +628,8 @@ try_parsers(RawDate,[{ParserKey,Parser}|Parsers]) -> Other -> throw({invalid_parser_return_value,[{parser_key,ParserKey},{return,Other}]}) catch - ?WITH_STACKTRACE(Error, Reason, Stacktrace) - throw({error_in_parser,[{error,{Error,Reason}},{parser_key,ParserKey}, {stacktrace, Stacktrace}]}) + Error:Reason -> + throw({error_in_parser,[{error,{Error,Reason}},{parser_key,ParserKey}]}) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1185,7 +648,7 @@ deregister_format(Key) -> unixtime_to_now(T) when is_integer(T) -> - MegaSec = flooring(T/1000000), + MegaSec = floor(T/1000000), Secs = T - MegaSec*1000000, {MegaSec,Secs,0}. @@ -1193,10 +656,10 @@ unixtime_to_date(T) -> Now = unixtime_to_now(T), calendar:now_to_datetime(Now). -flooring(N) when N >= 0 -> - erlang:trunc(N); -flooring(N) when N < 0 -> - Int = erlang:trunc(N), +floor(N) when N >= 0 -> + trunc(N); +floor(N) when N < 0 -> + Int = trunc(N), if Int==N -> Int; true -> Int-1 @@ -1206,8 +669,6 @@ flooring(N) when N < 0 -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TESTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --ifdef(EUNIT). - -include_lib("eunit/include/eunit.hrl"). %% emulates as if a forum-type website has a Site tz, and a user-specified tz @@ -1235,30 +696,6 @@ tz_test_() -> end }. -tz_preserve_ms_true_test_() -> - { - setup, - fun start_test/0, - fun stop_test/1, - fun(SetupData) -> - {inorder,[ - preserve_ms_true_tests(SetupData) - ]} - end - }. - -tz_preserve_ms_false_test_() -> - { - setup, - fun start_test/0, - fun stop_test/1, - fun(SetupData) -> - {inorder,[ - preserve_ms_false_tests(SetupData) - ]} - end - }. - test_deterministic_parser(_) -> {inorder, [ ?_assertEqual(ok, application:set_env(qdate, deterministic_parsing, {now, now})), @@ -1317,20 +754,6 @@ tz_tests(_) -> ?_assertEqual({{2013,3,6},{18,0,0}}, to_date("GMT","3/7/2013 12:00am +0600")), ?_assertEqual({{2013,3,6},{12,0,0}}, to_date("CST","3/7/2013 12:00am +0600")), - %% These next two test check to make sure that the tz database properly - %% interprets GMT+/-X timezones (an earlier issue with - %% erlang_localtime's tz database had it incrementing/decrementing the - %% minute field rather than hours. - %% - %% It also ensures that GMT+/-X handling is interpreted the way you'd - %% intuitively expect, rather than the POSIX way, which is, quite - %% frankly, broken. - ?_assertEqual({{2013,3,7},{10,0,0}}, to_date("GMT-0","3/7/2013 10:00am GMT")), - ?_assertEqual({{2013,3,7},{10,0,0}}, to_date("GMT+0","3/7/2013 10:00am GMT")), - ?_assertEqual({{2013,3,7},{9,0,0}}, to_date("GMT-1","3/7/2013 10:00am GMT")), - ?_assertEqual({{2013,3,7},{11,0,0}}, to_date("GMT+1","3/7/2013 10:00am GMT")), - - %% parsing, then reformatting the same time with a different timezone using the php "r" (rfc2822) ?_assertEqual("Thu, 07 Mar 2013 12:15:00 -0600", to_string("r","CST",to_string("r","EST",{{2013,3,7},{13,15,0}}))), @@ -1346,13 +769,8 @@ tz_tests(_) -> ?_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(ok, set_timezone("GMT")), - ?_assertEqual({{1970, 1, 1}, {1, 0, 0}}, to_date("CET", "1970-01-01T00:00:00Z")), - ?_assertEqual(ok, set_timezone("UTC")), - ?_assertEqual(1521945120, to_unixtime("2018-3-25T2:32:00")), - ?_assertEqual(true, between("-1 seconds", os:timestamp(), "+1 seconds")), - ?_assertEqual(true, between("60 hours ago", unixtime(), "in 15 days")), - ?_assertEqual(false, between("+1 seconds", qdate:to_string("n/j/Y g:ia"), "+2 seconds")), - ?_assertEqual(false, between("5 seconds ago","1 second ago")) + ?_assertEqual({{1970, 1, 1}, {1, 0, 0}}, to_date("CET", "1970-01-01T00:00:00Z")) + ]}. @@ -1418,81 +836,22 @@ arith_tests(_) -> ?_assertEqual({{2015,1,31},{23,59,59}}, to_date(add_months(1, {{2014,12,31},{23,59,59}}))), ?_assertEqual({{2015,2,28},{0,0,0}}, to_date(add_months(2, {{2014,12,31},{0,0,0}}))), ?_assertEqual({{2016,2,28},{0,0,0}}, to_date(add_years(1, {{2015,2,28},{0,0,0}}))), - - ?_assertEqual({{2017,2,1},{0,0,0}}, to_date(add_months(-11, {{2018,1,1},{0,0,0}}))), - ?_assertEqual({{2017,1,1},{0,0,0}}, to_date(add_months(-12, {{2018,1,1},{0,0,0}}))), - ?_assertEqual({{2016,12,1},{0,0,0}}, to_date(add_months(-13, {{2018,1,1},{0,0,0}}))), - - ?_assertEqual({{2018,12,1},{0,0,0}}, to_date(add_months(11, {{2018,1,1},{0,0,0}}))), - ?_assertEqual({{2019,1,1},{0,0,0}}, to_date(add_months(12, {{2018,1,1},{0,0,0}}))), - ?_assertEqual({{2019,2,1},{0,0,0}}, to_date(add_months(13, {{2018,1,1},{0,0,0}}))), - - ?_assertEqual({{2018,1,1},{0,0,0}}, to_date(add_months(-11, {{2018,12,1},{0,0,0}}))), - ?_assertEqual({{2017,12,1},{0,0,0}}, to_date(add_months(-12, {{2018,12,1},{0,0,0}}))), - ?_assertEqual({{2017,11,1},{0,0,0}}, to_date(add_months(-13, {{2018,12,1},{0,0,0}}))), - - ?_assertEqual({{2019,11,1},{0,0,0}}, to_date(add_months(11, {{2018,12,1},{0,0,0}}))), - ?_assertEqual({{2019,12,1},{0,0,0}}, to_date(add_months(12, {{2018,12,1},{0,0,0}}))), - ?_assertEqual({{2020,1,1},{0,0,0}}, to_date(add_months(13, {{2018,12,1},{0,0,0}}))), - - ?_assertEqual({{2014,2,28},{0,0,0}}, to_date(add_months(-24, {{2016,2,29},{0,0,0}}))), - ?_assertEqual({{2018,12,15},{0,0,0}}, to_date(add_months(24, {{2016,12,15},{0,0,0}}))), ?_assertEqual({{2012,2,29},{0,0,0}}, to_date(add_months(-48, {{2016,2,29},{0,0,0}}))), ?_assertEqual({{2016,2,29},{0,0,0}}, to_date(add_months(-1, {{2016,3,31},{0,0,0}}))), ?_assertEqual({{2017,2,28},{0,0,0}}, to_date(add_years(1, {{2016,2,29},{0,0,0}}))), ?_assertEqual({{2015,3,1},{0,0,0}}, to_date(add_days(1, {{2015,2,28},{0,0,0}}))), - ?_assertEqual({{2015,3,3},{0,0,0}}, to_date(add_days(3, {{2015,2,28},{0,0,0}}))), - ?_assertEqual({{2017,1,2},{0,0,0}}, beginning_week({{2017,1,2},{0,0,0}})), - ?_assertEqual({{2017,1,2},{0,0,0}}, beginning_week({{2017,1,3},{0,0,0}})), - ?_assertEqual({{2017,1,3},{0,0,0}}, beginning_week(2, {{2017,1,4},{0,0,0}})), - ?_assertEqual({{2016,12,29},{0,0,0}}, beginning_week(4, {{2017,1,4},{0,0,0}})), - ?_assertEqual({{2016,12,31},{0,0,0}}, beginning_week(6, {{2017,1,6},{0,0,0}})), - ?_assertEqual({{2017,1,1},{0,0,0}}, beginning_week(7, {{2017,1,6},{0,0,0}})), + ?_assertEqual({{2015,3,3},{0,0,0}}, to_date(add_days(3, {{2015,2,28},{0,0,0}}))) + ]}. - ?_assertEqual(0, age("1981-01-15", "1981-12-31")), - ?_assertEqual(39, age("1981-01-15", "2020-01-15")), - ?_assertEqual(39, age("1981-01-15", "2020-01-15 12am")), - ?_assertEqual(38, age("1981-01-15", "2020-01-14")), - ?_assertEqual(38, age("1981-01-15", "2020-01-14 11:59pm")), - - %% checking pre-unix-epoch - ?_assertEqual(100, age("1901-01-01","2001-01-01")), - ?_assertEqual(20, age("1900-01-01", "1920-01-01")), - ?_assertEqual(0, age_days("2020-11-20 12am","2020-11-20 11:59:59pm")), - ?_assertEqual(1, age_days("2020-11-19 11:59:59pm","2020-11-20 11:59:59pm")), - ?_assertEqual(7, age_days("2020-11-20","2020-11-27")), - ?_assertEqual(7, age_days("2020-11-27","2020-12-04")) - - ]}. - -preserve_ms_true_tests(_) -> - application:set_env(qdate, preserve_ms, true), - {inorder, [ - ?_assertEqual({{2021,5,8},{23,0,16,472000}}, qdate:to_date(<<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)), - ?_assertEqual(<<"2021-05-08T23:00:16.472000+00:00">>, qdate:to_string(<<"Y-m-d\\TH:i:s.fP">>, <<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)), - ?_assertEqual(<<"2021-05-08T23:00:16+00:00">>, qdate:to_string(<<"Y-m-d\\TH:i:sP">>, <<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)), - ?_assertEqual(<<"2021-05-08T23:00:16.472000+00:00">>, qdate:to_string(<<"c">>, <<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)) - ]}. - -preserve_ms_false_tests(_) -> - application:set_env(qdate, preserve_ms, false), - {inorder, [ - ?_assertEqual({{2021,5,8},{23,0,16}}, qdate:to_date(<<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)), - ?_assertEqual(<<"2021-05-08T23:00:16.0+00:00">>, qdate:to_string(<<"Y-m-d\\TH:i:s.fP">>, <<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)), - ?_assertEqual(<<"2021-05-08T23:00:16+00:00">>, qdate:to_string(<<"Y-m-d\\TH:i:sP">>, <<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)), - ?_assertEqual(<<"2021-05-08T23:00:16+00:00">>, qdate:to_string(<<"c">>, <<"UTC">>,<<"2021-5-09 0:0:16.472+01:00">>)) - ]}. - start_test() -> - qdate:start(), + application:start(qdate), set_timezone(?SELF_TZ), set_timezone(?SITE_KEY,?SITE_TZ), set_timezone(?USER_KEY,?USER_TZ), register_parser(compressed,fun compressed_parser/1), register_parser(microsoft_date,fun microsoft_parser/1), - register_parser(parse_relative, fun parse_relative/1), register_format(shortdate,"n/j/Y"), register_format(longdate,"n/j/Y g:ia"). @@ -1514,7 +873,7 @@ compressed_parser(_) -> microsoft_parser(FloatDate) when is_float(FloatDate) -> try - DaysSince1900 = flooring(FloatDate), + DaysSince1900 = floor(FloatDate), Days0to1900 = calendar:date_to_gregorian_days(1900,1,1), GregorianDays = Days0to1900 + DaysSince1900, Date = calendar:gregorian_days_to_date(GregorianDays), @@ -1531,5 +890,3 @@ microsoft_parser(_) -> stop_test(_) -> ok. - --endif. diff --git a/src/qdate_app.erl b/src/qdate_app.erl deleted file mode 100644 index 36a8289..0000000 --- a/src/qdate_app.erl +++ /dev/null @@ -1,15 +0,0 @@ --module(qdate_app). - --behaviour(application). - -%% Application callbacks --export([start/2, stop/1]). - -start(_StartType, _StartArgs) -> - qdate_sup:start_link(). - - -stop(_State) -> - ok. - - diff --git a/src/qdate_srv.erl b/src/qdate_srv.erl index 731b8b1..6aeecc0 100644 --- a/src/qdate_srv.erl +++ b/src/qdate_srv.erl @@ -1,9 +1,12 @@ % vim: ts=4 sw=4 et -% Copyright (c) 2013-2021 Jesse Gumm +% Copyright (c) 2013-2015 Jesse Gumm % See LICENSE for licensing information. +% +% NOTE: You'll probably notice that this isn't *actually* a server. It *used* +% to be a server, but is now instead just where we interact with the qdate +% application environment. Anyway, sorry for the confusion. -module(qdate_srv). --behaviour(gen_server). -export([ set_timezone/1, @@ -25,19 +28,6 @@ get_formats/0 ]). - -%% API --export([start_link/0]). - -%% gen_server callbacks --export([init/1, - handle_call/3, - handle_cast/2, - handle_info/2, - terminate/2, - code_change/3]). - - %% Simple wrappers for unique keys -define(BASETAG, qdate_var). -define(KEY(Name), {?BASETAG, Name}). @@ -49,42 +39,6 @@ -define(FORMATTAG, qdate_format). -define(FORMATKEY(Name), {?FORMATTAG, Name}). --define(SERVER, ?MODULE). --define(TABLE, ?MODULE). - --record(state, {}). - -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). - -init([]) -> - error_logger:info_msg("Creating qdate ETS Table: ~p",[?TABLE]), - ?TABLE = ets:new(?TABLE, [public, {read_concurrency, true}, named_table]), - {ok, #state{}}. - -handle_call({set, Key, Val}, _From, State) -> - ets:insert(?TABLE, {Key, Val}), - {reply, ok, State}; -handle_call({unset, Key}, _From, State) -> - ets:delete(?TABLE, Key), - {reply, ok, State}; -handle_call(_, _From, State) -> - {reply, invalid_request, State}. - -handle_cast(_Msg, State) -> - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - - %% PUBLIC API FUNCTIONS set_timezone(TZ) -> @@ -137,41 +91,26 @@ get_formats() -> %% App Vars set_env(Key, Val) -> - gen_server:call(?SERVER, {set, ?KEY(Key), Val}). + application:set_env(qdate, ?KEY(Key), Val). get_env(Key) -> get_env(Key, undefined). get_env(Key, Default) -> - case ets:lookup(?TABLE, ?KEY(Key)) of - [{__Key, Val}] -> Val; - [] -> Default + %% Soon, this can just be replaced with application:get_env/3 + %% which was introduced in R16B. + case application:get_env(qdate, ?KEY(Key)) of + undefined -> Default; + {ok, Val} -> Val end. unset_env(Key) -> - gen_server:call(?SERVER, {unset, ?KEY(Key)}). + application:unset_env(qdate, ?KEY(Key)). get_all_env(FilterTag) -> - try ets:tab2list(?TABLE) of - All -> - [{Key, V} || {{?BASETAG, {Tag, Key}}, V} <- All, Tag==FilterTag] - catch - error:badarg:S -> - Msg = maybe_start_msg(), - logger:error(Msg), - - error({qdate_get_all_env_failed, #{ - original_error => {error, badarg, S} - }}) - end. - -maybe_start_msg() -> - "Attempting to read qdate environment failed (qdate_srv:get_all_env/1).\n" - "qdate may not have been started properly.\n" - "Please ensure qdate is started properly by either:\n" - "* Putting qdate in the 'applications' key in your .app.src or .app file, or\n" - "* Starting it manually with application:ensure_all_started(qdate) or qdate:start().". - + All = application:get_all_env(qdate), + %% Maybe this is a little nasty. + [{Key, V} || {{?BASETAG, {Tag, Key}}, V} <- All, Tag==FilterTag]. %% ProcDic Vars @@ -184,9 +123,3 @@ put_pd(Key, Val) -> unset_pd(Key) -> put_pd(Key, undefined). - - - - - - diff --git a/src/qdate_sup.erl b/src/qdate_sup.erl deleted file mode 100644 index 492dd8e..0000000 --- a/src/qdate_sup.erl +++ /dev/null @@ -1,32 +0,0 @@ --module(qdate_sup). - --behaviour(supervisor). - -%% API --export([start_link/0]). - -%% Supervisor callbacks --export([init/1]). - --define(SERVER, ?MODULE). - -start_link() -> - supervisor:start_link({local, ?SERVER}, ?MODULE, []). - -init([]) -> - - SupFlags = #{}, - - ChildSpec = #{ - id=>qdate_srv, - start=>{qdate_srv, start_link, []} - }, - - {ok, {SupFlags, [ChildSpec]}}. - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== - - -