@@ -6093,6 +6093,150 @@ defmodule Kernel do
6093
6093
Macro . compile_apply ( mod , fun , [ code , options , __CALLER__ | args ] , __CALLER__ )
6094
6094
end
6095
6095
6096
+ hour_in_ms = 1000 * 60 * 60
6097
+ day_in_ms = 24 * hour_in_ms
6098
+ week_in_ms = 7 * day_in_ms
6099
+
6100
+ @ doc """
6101
+ Constructs a millisecond timeout from the given components, duration, or timeout.
6102
+
6103
+ This function is useful for constructing timeouts to use in functions that
6104
+ expect `t:timeout/0` values (such as `Process.send_after/4` and many others).
6105
+
6106
+ ## Argument
6107
+
6108
+ The `duration` argument can be one of a `Duration`, a `t:timeout/0`, or a list
6109
+ of components. Each of these is described below.
6110
+
6111
+ ### Passing `Duration`s
6112
+
6113
+ `t:Duration.t/0` structs can be converted to timeouts. The given duration must have
6114
+ `year` and `month` fields set to `0`, since those cannot be reliably converted to
6115
+ milliseconds (due to the varying number of days in a month and year).
6116
+
6117
+ Microseconds in durations are converted to milliseconds (through `System.convert_time_unit/3`).
6118
+
6119
+ ### Passing components
6120
+
6121
+ The `duration` argument can also be keyword list which can contain the following
6122
+ keys, each appearing at most once with a non-negative integer value:
6123
+
6124
+ * `:week` - the number of weeks (a week is always 7 days)
6125
+ * `:day` - the number of days (a day is always 24 hours)
6126
+ * `:hour` - the number of hours
6127
+ * `:minute` - the number of minutes
6128
+ * `:second` - the number of seconds
6129
+ * `:millisecond` - the number of milliseconds
6130
+
6131
+ The timeout is calculated as the sum of the components, each multiplied by
6132
+ the corresponding factor.
6133
+
6134
+ ### Passing timeouts
6135
+
6136
+ You can also pass timeouts directly to this functions, that is, milliseconds or
6137
+ the atom `:infinity`. In this case, this function just returns the given argument.
6138
+
6139
+ ## Examples
6140
+
6141
+ With a keyword list:
6142
+
6143
+ iex> to_timeout(hour: 1, minute: 30)
6144
+ 5400000
6145
+
6146
+ With a duration:
6147
+
6148
+ iex> to_timeout(%Duration{hour: 1, minute: 30})
6149
+ 5400000
6150
+
6151
+ With a timeout:
6152
+
6153
+ iex> to_timeout(5400000)
6154
+ 5400000
6155
+ iex> to_timeout(:infinity)
6156
+ :infinity
6157
+
6158
+ """
6159
+ @ doc since: "1.17.0"
6160
+ @ spec to_timeout ( [ component , ... ] | timeout ( ) | Duration . t ( ) ) :: non_neg_integer ( )
6161
+ when component: [ { unit , non_neg_integer ( ) } , ... ] ,
6162
+ unit: :week | :day | :hour | :minute | :second | :millisecond
6163
+ def to_timeout ( duration )
6164
+
6165
+ def to_timeout ( :infinity ) , do: :infinity
6166
+ def to_timeout ( timeout ) when is_integer ( timeout ) and timeout >= 0 , do: timeout
6167
+
6168
+ def to_timeout ( % { __struct__: Duration } = duration ) do
6169
+ case duration do
6170
+ % { year: year } when year != 0 ->
6171
+ raise ArgumentError ,
6172
+ "duration with a non-zero year cannot be reliably converted to timeouts"
6173
+
6174
+ % { month: month } when month != 0 ->
6175
+ raise ArgumentError ,
6176
+ "duration with a non-zero month cannot be reliably converted to timeouts"
6177
+
6178
+ _other ->
6179
+ { microsecond , _precision } = duration . microsecond
6180
+ millisecond = :erlang . convert_time_unit ( microsecond , :microsecond , :millisecond )
6181
+
6182
+ duration . week * unquote ( week_in_ms ) +
6183
+ duration . day * unquote ( day_in_ms ) +
6184
+ duration . hour * unquote ( hour_in_ms ) +
6185
+ duration . minute * 60_000 +
6186
+ duration . second * 1000 +
6187
+ millisecond
6188
+ end
6189
+ end
6190
+
6191
+ def to_timeout ( components ) when is_list ( components ) do
6192
+ reducer = fn
6193
+ { key , value } , { acc , seen_keys } when is_integer ( value ) and value >= 0 ->
6194
+ case :lists . member ( key , seen_keys ) do
6195
+ true ->
6196
+ raise ArgumentError , "timeout component #{ inspect ( key ) } is duplicated"
6197
+
6198
+ false ->
6199
+ :ok
6200
+ end
6201
+
6202
+ factor =
6203
+ case key do
6204
+ :week ->
6205
+ unquote ( week_in_ms )
6206
+
6207
+ :day ->
6208
+ unquote ( day_in_ms )
6209
+
6210
+ :hour ->
6211
+ unquote ( hour_in_ms )
6212
+
6213
+ :minute ->
6214
+ 60_000
6215
+
6216
+ :second ->
6217
+ 1000
6218
+
6219
+ :millisecond ->
6220
+ 1
6221
+
6222
+ other ->
6223
+ raise ArgumentError , """
6224
+ timeout component #{ inspect ( other ) } is not a valid timeout component, valid \
6225
+ values are: :week, :day, :hour, :minute, :second, :millisecond\
6226
+ """
6227
+ end
6228
+
6229
+ { acc + value * factor , [ key | seen_keys ] }
6230
+
6231
+ { key , value } , { _acc , _seen_keys } ->
6232
+ raise ArgumentError ,
6233
+ "timeout component #{ inspect ( key ) } must be a non-negative " <>
6234
+ "integer, got: #{ inspect ( value ) } "
6235
+ end
6236
+
6237
+ elem ( :lists . foldl ( reducer , { 0 , _seen_keys = [ ] } , components ) , 0 )
6238
+ end
6239
+
6096
6240
## Sigils
6097
6241
6098
6242
@ doc ~S"""
0 commit comments