@@ -69,6 +69,11 @@ defmodule Date do
69
69
calendar: Calendar . calendar ( )
70
70
}
71
71
72
+ @ typedoc "A duration unit expressed as a tuple."
73
+ @ typedoc since: "1.19.0"
74
+ @ type duration_unit_pair ::
75
+ { :year , integer } | { :month , integer } | { :week , integer } | { :day , integer }
76
+
72
77
@ doc """
73
78
Returns a range of dates.
74
79
@@ -84,6 +89,20 @@ defmodule Date do
84
89
iex> Date.range(~D[1999-01-01], ~D[2000-01-01])
85
90
Date.range(~D[1999-01-01], ~D[2000-01-01])
86
91
92
+ A range may also be built from a `Date` and a `Duration`
93
+ (also expressed as a keyword list of `t:duration_unit_pair/0`):
94
+
95
+ iex> Date.range(~D[1999-01-01], Duration.new!(year: 1))
96
+ Date.range(~D[1999-01-01], ~D[2000-01-01])
97
+ iex> Date.range(~D[1999-01-01], year: 1)
98
+ Date.range(~D[1999-01-01], ~D[2000-01-01])
99
+
100
+ > #### Durations {: .warning}
101
+ >
102
+ > Support for expressing `last` as a [`Duration`](`t:Duration.t/0`) or
103
+ > keyword list of `t:duration_unit_pair/0`s was introduced in
104
+ > v1.19.0.
105
+
87
106
A range of dates implements the `Enumerable` protocol, which means
88
107
functions in the `Enum` module can be used to work with
89
108
ranges:
@@ -100,7 +119,11 @@ defmodule Date do
100
119
101
120
"""
102
121
@ doc since: "1.5.0"
103
- @ spec range ( Calendar . date ( ) , Calendar . date ( ) ) :: Date.Range . t ( )
122
+ @ spec range (
123
+ first :: Calendar . date ( ) ,
124
+ last_or_duration :: Calendar . date ( ) | Duration . t ( ) | [ duration_unit_pair ]
125
+ ) ::
126
+ Date.Range . t ( )
104
127
def range ( % { calendar: calendar } = first , % { calendar: calendar } = last ) do
105
128
{ first_days , _ } = to_iso_days ( first )
106
129
{ last_days , _ } = to_iso_days ( last )
@@ -123,6 +146,16 @@ defmodule Date do
123
146
raise ArgumentError , "both dates must have matching calendars"
124
147
end
125
148
149
+ def range ( % { calendar: _ } = first , % Duration { } = duration ) do
150
+ last = shift ( first , duration )
151
+ range ( first , last )
152
+ end
153
+
154
+ def range ( % { calendar: _ } = first , duration ) when is_list ( duration ) do
155
+ last = shift ( first , duration )
156
+ range ( first , last )
157
+ end
158
+
126
159
@ doc """
127
160
Returns a range of dates with a step.
128
161
@@ -140,8 +173,11 @@ defmodule Date do
140
173
141
174
"""
142
175
@ doc since: "1.12.0"
143
- @ spec range ( Calendar . date ( ) , Calendar . date ( ) , step :: pos_integer | neg_integer ) ::
144
- Date.Range . t ( )
176
+ @ spec range (
177
+ first :: Calendar . date ( ) ,
178
+ last_or_duration :: Calendar . date ( ) | Duration . t ( ) | [ duration_unit_pair ] ,
179
+ step :: pos_integer | neg_integer
180
+ ) :: Date.Range . t ( )
145
181
def range ( % { calendar: calendar } = first , % { calendar: calendar } = last , step )
146
182
when is_integer ( step ) and step != 0 do
147
183
{ first_days , _ } = to_iso_days ( first )
@@ -159,6 +195,24 @@ defmodule Date do
159
195
"non-zero integer, got: #{ inspect ( first ) } , #{ inspect ( last ) } , #{ step } "
160
196
end
161
197
198
+ def range ( % { calendar: _ } = first , % Duration { } = duration , step )
199
+ when is_integer ( step ) and step != 0 do
200
+ last = shift ( first , duration )
201
+ range ( first , last , step )
202
+ end
203
+
204
+ def range ( % { calendar: _ } = first , duration , step )
205
+ when is_list ( duration ) and is_integer ( step ) and step != 0 do
206
+ last = shift ( first , duration )
207
+ range ( first , last , step )
208
+ end
209
+
210
+ def range ( % { calendar: _ } = first , last , step ) do
211
+ raise ArgumentError ,
212
+ "expected a date or duration as second argument and the step must be a " <>
213
+ "non-zero integer, got: #{ inspect ( first ) } , #{ inspect ( last ) } , #{ step } "
214
+ end
215
+
162
216
defp range ( first , first_days , last , last_days , calendar , step ) do
163
217
% Date.Range {
164
218
first: % Date { calendar: calendar , year: first . year , month: first . month , day: first . day } ,
@@ -795,8 +849,7 @@ defmodule Date do
795
849
796
850
"""
797
851
@ doc since: "1.17.0"
798
- @ spec shift ( Calendar . date ( ) , Duration . t ( ) | [ unit_pair ] ) :: t
799
- when unit_pair: { :year , integer } | { :month , integer } | { :week , integer } | { :day , integer }
852
+ @ spec shift ( Calendar . date ( ) , Duration . t ( ) | [ duration_unit_pair ] ) :: t
800
853
def shift ( % { calendar: calendar } = date , duration ) do
801
854
% { year: year , month: month , day: day } = date
802
855
{ year , month , day } = calendar . shift_date ( year , month , day , __duration__! ( duration ) )
0 commit comments