@@ -6,11 +6,11 @@ defmodule Timex.Interval do
66
77 iex> use Timex
88 ...> Interval.new(from: ~D[2016-03-03], until: [days: 3])
9- %#{ __MODULE__ } {from: ~N[2016-03-03 00:00:00], left_open: true , right_open: false , step: [days: 1], until: ~N[2016-03-06 00:00:00]}
9+ %#{ __MODULE__ } {from: ~N[2016-03-03 00:00:00], left_open: false , right_open: true , step: [days: 1], until: ~N[2016-03-06 00:00:00]}
1010
1111 iex> use Timex
1212 ...> Interval.new(from: ~D[2016-03-03], until: ~N[2016-03-10 01:23:45])
13- %Timex.Interval{from: ~N[2016-03-03 00:00:00], left_open: true , right_open: false , step: [days: 1], until: ~N[2016-03-10 01:23:45]}
13+ %Timex.Interval{from: ~N[2016-03-03 00:00:00], left_open: false , right_open: true , step: [days: 1], until: ~N[2016-03-10 01:23:45]}
1414
1515 iex> use Timex
1616 ...> ~N[2016-03-04 12:34:56] in Interval.new(from: ~D[2016-03-03], until: [days: 3])
@@ -36,30 +36,31 @@ defmodule Timex.Interval do
3636 """
3737 defexception message: "Unable to format interval!"
3838
39- def exception ( [ message: message ] ) do
39+ def exception ( message: message ) do
4040 % FormatError { message: message }
4141 end
4242 end
4343
4444 @ type t :: % __MODULE__ { }
45- @ type valid_step_unit :: :microseconds
46- | :milliseconds
47- | :seconds
48- | :minutes
49- | :hours
50- | :days
51- | :weeks
52- | :months
53- | :years
45+ @ type valid_step_unit ::
46+ :microseconds
47+ | :milliseconds
48+ | :seconds
49+ | :minutes
50+ | :hours
51+ | :days
52+ | :weeks
53+ | :months
54+ | :years
5455 @ type valid_interval_step :: { valid_step_unit , integer }
5556 @ type valid_interval_steps :: [ valid_interval_step ]
5657
5758 @ enforce_keys [ :from , :until ]
58- defstruct from: nil ,
59- until: nil ,
60- left_open: false ,
59+ defstruct from: nil ,
60+ until: nil ,
61+ left_open: false ,
6162 right_open: true ,
62- step: [ days: 1 ]
63+ step: [ days: 1 ]
6364
6465 @ valid_step_units [
6566 :microseconds ,
@@ -76,7 +77,7 @@ defmodule Timex.Interval do
7677 @ doc """
7778 Create a new Interval struct.
7879
79- **Note:** By default intervals are left open , i.e. they include the `from` date/time,
80+ **Note:** By default intervals are left closed , i.e. they include the `from` date/time,
8081 and exclude the `until` date/time. Put another way, `from <= x < until`. This behavior
8182 matches that of other popular date/time libraries, such as Joda Time, as well as the SQL
8283 behavior of the `overlaps` keyword.
@@ -94,9 +95,9 @@ defmodule Timex.Interval do
9495 can see more detail on the theory [on Wikipedia](https://en.wikipedia.org/wiki/Interval_(mathematics)),
9596 but it can be more intuitively thought of like so:
9697
97- - An "open" bound is inclusive , and a "closed" bound is exclusive
98- - So a left-open interval includes the `from` value, and a left-closed interval does not.
99- - Likewise, a right-open interval includes the `until` value, and a right-closed interval does not.
98+ - An "open" bound is exclusive , and a "closed" bound is inclusive
99+ - So a left-closed interval includes the `from` value, and a left-open interval does not.
100+ - Likewise, a right-closed interval includes the `until` value, and a right-open interval does not.
100101 - An open interval is both left and right open, conversely, a closed interval is both left and right closed.
101102
102103 **Note:** `until` shifts delegate to `Timex.shift`, so the options provided should match its valid options.
@@ -106,58 +107,70 @@ defmodule Timex.Interval do
106107 iex> use Timex
107108 ...> Interval.new(from: ~D[2014-09-22], until: ~D[2014-09-29])
108109 ...> |> Interval.format!("%Y-%m-%d", :strftime)
109- "( 2014-09-22, 2014-09-29] "
110+ "[ 2014-09-22, 2014-09-29) "
110111
111112 iex> use Timex
112113 ...> Interval.new(from: ~D[2014-09-22], until: [days: 7])
113114 ...> |> Interval.format!("%Y-%m-%d", :strftime)
114- "( 2014-09-22, 2014-09-29] "
115+ "[ 2014-09-22, 2014-09-29) "
115116
116117 iex> use Timex
117- ...> Interval.new(from: ~D[2014-09-22], until: [days: 7], left_open: false , right_open: true )
118+ ...> Interval.new(from: ~D[2014-09-22], until: [days: 7], left_open: true , right_open: false )
118119 ...> |> Interval.format!("%Y-%m-%d", :strftime)
119- "[ 2014-09-22, 2014-09-29) "
120+ "( 2014-09-22, 2014-09-29] "
120121
121122 iex> use Timex
122- ...> Interval.new(from: ~N[2014-09-22T15:30:00], until: [minutes: 20], left_open : false)
123+ ...> Interval.new(from: ~N[2014-09-22T15:30:00], until: [minutes: 20], right_open : false)
123124 ...> |> Interval.format!("%H:%M", :strftime)
124125 "[15:30, 15:50]"
125126
126127 """
127- @ spec new ( Keyword . t ) :: t
128- | { :error , :invalid_until }
129- | { :error , :invalid_step }
128+ @ spec new ( Keyword . t ( ) ) ::
129+ t
130+ | { :error , :invalid_until }
131+ | { :error , :invalid_step }
130132 def new ( options \\ [ ] ) do
131133 from =
132134 case Keyword . get ( options , :from ) do
133135 nil ->
134136 Timex.Protocol.NaiveDateTime . now ( )
137+
135138 % NaiveDateTime { } = d ->
136139 d
140+
137141 d ->
138142 Timex . to_naive_datetime ( d )
139143 end
140- left_open = Keyword . get ( options , :left_open , true )
141- right_open = Keyword . get ( options , :right_open , false )
142- step = Keyword . get ( options , :step , [ days: 1 ] )
144+
145+ left_open = Keyword . get ( options , :left_open , false )
146+ right_open = Keyword . get ( options , :right_open , true )
147+ step = Keyword . get ( options , :step , days: 1 )
148+
143149 until =
144- case Keyword . get ( options , :until , [ days: 1 ] ) do
150+ case Keyword . get ( options , :until , days: 1 ) do
145151 { :error , _ } = err ->
146152 err
153+
147154 x when is_list ( x ) ->
148155 Timex . shift ( from , x )
156+
149157 % NaiveDateTime { } = d ->
150158 d
159+
151160 d ->
152161 Timex . to_naive_datetime ( d )
153162 end
163+
154164 cond do
155165 invalid_step? ( step ) ->
156166 { :error , :invalid_step }
167+
157168 invalid_until? ( until ) ->
158169 { :error , :invalid_until }
170+
159171 Timex . compare ( until , from ) <= 0 ->
160172 { :error , :invalid_until }
173+
161174 :else ->
162175 % __MODULE__ {
163176 from: from ,
@@ -173,9 +186,11 @@ defmodule Timex.Interval do
173186 defp invalid_until? ( _ ) , do: false
174187
175188 defp invalid_step? ( [ ] ) , do: false
189+
176190 defp invalid_step? ( [ { unit , n } | rest ] ) when unit in @ valid_step_units and is_integer ( n ) do
177191 invalid_step? ( rest )
178192 end
193+
179194 defp invalid_step? ( _ ) , do: true
180195
181196 @ doc """
@@ -199,8 +214,9 @@ defmodule Timex.Interval do
199214
200215 """
201216 def duration ( % __MODULE__ { until: until , from: from } , :duration ) do
202- Timex . diff ( until , from , :microseconds ) |> Duration . from_microseconds
217+ Timex . diff ( until , from , :microseconds ) |> Duration . from_microseconds ( )
203218 end
219+
204220 def duration ( % __MODULE__ { until: until , from: from } , unit ) do
205221 Timex . diff ( until , from , unit )
206222 end
@@ -213,17 +229,22 @@ defmodule Timex.Interval do
213229 ## Examples
214230
215231 iex> use Timex
216- ...> Interval.new(from: ~D[2014-09-22], until: [days: 3])
232+ ...> Interval.new(from: ~D[2014-09-22], until: [days: 3], right_open: true )
217233 ...> |> Interval.with_step([days: 1]) |> Enum.map(&Timex.format!(&1, "%Y-%m-%d", :strftime))
218234 ["2014-09-22", "2014-09-23", "2014-09-24"]
219235
236+ iex> use Timex
237+ ...> Interval.new(from: ~D[2014-09-22], until: [days: 3], right_open: false)
238+ ...> |> Interval.with_step([days: 1]) |> Enum.map(&Timex.format!(&1, "%Y-%m-%d", :strftime))
239+ ["2014-09-22", "2014-09-23", "2014-09-24", "2014-09-25"]
240+
220241 iex> use Timex
221242 ...> Interval.new(from: ~D[2014-09-22], until: [days: 3], right_open: false)
222243 ...> |> Interval.with_step([days: 2]) |> Enum.map(&Timex.format!(&1, "%Y-%m-%d", :strftime))
223244 ["2014-09-22", "2014-09-24"]
224245
225246 iex> use Timex
226- ...> Interval.new(from: ~D[2014-09-22], until: [days: 3], right_open: true )
247+ ...> Interval.new(from: ~D[2014-09-22], until: [days: 3], right_open: false )
227248 ...> |> Interval.with_step([days: 3]) |> Enum.map(&Timex.format!(&1, "%Y-%m-%d", :strftime))
228249 ["2014-09-22", "2014-09-25"]
229250
@@ -245,21 +266,23 @@ defmodule Timex.Interval do
245266 iex> use Timex
246267 ...> Interval.new(from: ~D[2014-09-22], until: [days: 3])
247268 ...> |> Interval.format!("%Y-%m-%d %H:%M", :strftime)
248- "( 2014-09-22 00:00, 2014-09-25 00:00] "
269+ "[ 2014-09-22 00:00, 2014-09-25 00:00) "
249270
250271 iex> use Timex
251272 ...> Interval.new(from: ~D[2014-09-22], until: [days: 3])
252273 ...> |> Interval.format!("%Y-%m-%d", :strftime)
253- "( 2014-09-22, 2014-09-25] "
274+ "[ 2014-09-22, 2014-09-25) "
254275 """
255276 def format ( % __MODULE__ { } = interval , format , formatter \\ nil ) do
256277 case Timex . format ( interval . from , format , formatter ) do
257278 { :error , _ } = err ->
258279 err
280+
259281 { :ok , from } ->
260282 case Timex . format ( interval . until , format , formatter ) do
261283 { :error , _ } = err ->
262284 err
285+
263286 { :ok , until } ->
264287 lopen = if interval . left_open , do: "(" , else: "["
265288 ropen = if interval . right_open , do: ")" , else: "]"
@@ -275,8 +298,9 @@ defmodule Timex.Interval do
275298 case format ( interval , format , formatter ) do
276299 { :ok , str } ->
277300 str
301+
278302 { :error , e } ->
279- raise FormatError , message: "#{ inspect e } "
303+ raise FormatError , message: "#{ inspect ( e ) } "
280304 end
281305 end
282306
@@ -310,21 +334,27 @@ defmodule Timex.Interval do
310334
311335 ## Examples
312336
313- iex> #{ __MODULE__ } .overlaps?(#{ __MODULE__ } .new(from: ~D[2016-03-04], until: [days: 1]), #{ __MODULE__ } .new(from: ~D[2016-03-03], until: [days: 3]))
337+ iex> #{ __MODULE__ } .overlaps?(#{ __MODULE__ } .new(from: ~D[2016-03-04], until: [days: 1]), #{
338+ __MODULE__
339+ } .new(from: ~D[2016-03-03], until: [days: 3]))
314340 true
315341
316- iex> #{ __MODULE__ } .overlaps?(#{ __MODULE__ } .new(from: ~D[2016-03-07], until: [days: 1]), #{ __MODULE__ } .new(from: ~D[2016-03-03], until: [days: 3]))
342+ iex> #{ __MODULE__ } .overlaps?(#{ __MODULE__ } .new(from: ~D[2016-03-07], until: [days: 1]), #{
343+ __MODULE__
344+ } .new(from: ~D[2016-03-03], until: [days: 3]))
317345 false
318346 """
319- @ spec overlaps? ( __MODULE__ . t , __MODULE__ . t ) :: boolean ( )
347+ @ spec overlaps? ( __MODULE__ . t ( ) , __MODULE__ . t ( ) ) :: boolean ( )
320348 def overlaps? ( % __MODULE__ { } = a , % __MODULE__ { } = b ) do
321349 cond do
322350 Timex . compare ( max ( a ) , min ( b ) ) < 0 ->
323351 # a is completely before b
324352 false
353+
325354 Timex . compare ( max ( b ) , min ( a ) ) < 0 ->
326355 # b is completely before a
327356 false
357+
328358 :else ->
329359 # a and b have overlapping elements
330360 true
@@ -334,11 +364,13 @@ defmodule Timex.Interval do
334364 @ doc false
335365 def min ( interval )
336366
337- def min ( % __MODULE__ { from: from , left_open: true } ) , do: from
367+ def min ( % __MODULE__ { from: from , left_open: false } ) , do: from
368+
338369 def min ( % __MODULE__ { from: from , step: step } ) do
339370 case Timex . shift ( from , step ) do
340371 { :error , { :unknown_shift_unit , unit } } ->
341- raise FormatError , message: "Invalid step unit for interval: #{ inspect unit } "
372+ raise FormatError , message: "Invalid step unit for interval: #{ inspect ( unit ) } "
373+
342374 d ->
343375 d
344376 end
@@ -347,8 +379,8 @@ defmodule Timex.Interval do
347379 @ doc false
348380 def max ( interval )
349381
350- def max ( % __MODULE__ { until: until , right_open: true } ) , do: until
351- def max ( % __MODULE__ { until: until } ) , do: Timex . shift ( until , [ microseconds: - 1 ] )
382+ def max ( % __MODULE__ { until: until , right_open: false } ) , do: until
383+ def max ( % __MODULE__ { until: until } ) , do: Timex . shift ( until , microseconds: - 1 )
352384
353385 defimpl Enumerable do
354386 alias Timex.Interval
@@ -357,9 +389,10 @@ defmodule Timex.Interval do
357389 do_reduce ( { Interval . min ( i ) , until , open? , step } , acc , fun )
358390 end
359391
360- defp do_reduce ( _state , { :halt , acc } , _fun ) ,
392+ defp do_reduce ( _state , { :halt , acc } , _fun ) ,
361393 do: { :halted , acc }
362- defp do_reduce ( state , { :suspend , acc } , fun ) ,
394+
395+ defp do_reduce ( state , { :suspend , acc } , fun ) ,
363396 do: { :suspended , acc , & do_reduce ( state , & 1 , fun ) }
364397
365398 defp do_reduce ( { current_date , end_date , right_open , step } , { :cont , acc } , fun ) do
@@ -368,42 +401,51 @@ defmodule Timex.Interval do
368401 else
369402 case Timex . shift ( current_date , step ) do
370403 { :error , { :unknown_shift_unit , unit } } ->
371- raise FormatError , message: "Invalid step unit for interval: #{ inspect unit } "
404+ raise FormatError , message: "Invalid step unit for interval: #{ inspect ( unit ) } "
405+
372406 { :error , err } ->
373- raise FormatError , message: "Failed to shift to next element in interval: #{ inspect err } "
407+ raise FormatError ,
408+ message: "Failed to shift to next element in interval: #{ inspect ( err ) } "
409+
374410 next_date ->
375411 do_reduce ( { next_date , end_date , right_open , step } , fun . ( current_date , acc ) , fun )
376412 end
377413 end
378414 end
379415
380- defp has_interval_ended? ( current_date , end_date , true ) ,
381- do: Timex . compare ( current_date , end_date ) > 0
382- defp has_interval_ended? ( current_date , end_date , false ) ,
416+ defp has_interval_ended? ( current_date , end_date , _right_open = true ) ,
383417 do: Timex . compare ( current_date , end_date ) >= 0
384418
419+ defp has_interval_ended? ( current_date , end_date , _right_open = false ) ,
420+ do: Timex . compare ( current_date , end_date ) > 0
421+
385422 def member? ( % Interval { } = interval , value ) do
386423 result =
387424 cond do
388425 before? ( interval , value ) ->
389426 false
427+
390428 after? ( interval , value ) ->
391429 false
430+
392431 :else ->
393432 true
394433 end
434+
395435 { :ok , result }
396436 end
397437
398438 defp before? ( % Interval { from: from , left_open: true } , value ) ,
399- do: Timex . compare ( value , from ) < 0
400- defp before? ( % Interval { from: from , left_open: false } , value ) ,
401439 do: Timex . compare ( value , from ) <= 0
402440
441+ defp before? ( % Interval { from: from , left_open: false } , value ) ,
442+ do: Timex . compare ( value , from ) < 0
443+
403444 defp after? ( % Interval { until: until , right_open: true } , value ) ,
404- do: Timex . compare ( until , value ) < 0
445+ do: Timex . compare ( value , until ) >= 0
446+
405447 defp after? ( % Interval { until: until , right_open: false } , value ) ,
406- do: Timex . compare ( until , value ) <= 0
448+ do: Timex . compare ( value , until ) > 0
407449
408450 def count ( _interval ) do
409451 { :error , __MODULE__ }
0 commit comments