@@ -8,10 +8,10 @@ defmodule Predicator.Duration do
88 ## Examples
99
1010 iex> Predicator.Duration.new(days: 3, hours: 8)
11- %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0}
11+ %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0, milliseconds: 0 }
1212
1313 iex> Predicator.Duration.from_units([{"3", "d"}, {"8", "h"}])
14- {:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0}}
14+ {:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0, milliseconds: 0 }}
1515
1616 iex> Predicator.Duration.to_seconds(%{days: 1, hours: 2, minutes: 30})
1717 95400
@@ -27,10 +27,10 @@ defmodule Predicator.Duration do
2727 ## Examples
2828
2929 iex> Predicator.Duration.new(days: 2, hours: 3)
30- %{years: 0, months: 0, weeks: 0, days: 2, hours: 3, minutes: 0, seconds: 0}
30+ %{years: 0, months: 0, weeks: 0, days: 2, hours: 3, minutes: 0, seconds: 0, milliseconds: 0 }
3131
3232 iex> Predicator.Duration.new()
33- %{years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0}
33+ %{years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }
3434 """
3535 @ spec new ( keyword ( ) ) :: Types . duration ( )
3636 def new ( opts \\ [ ] ) do
@@ -41,7 +41,8 @@ defmodule Predicator.Duration do
4141 days: Keyword . get ( opts , :days , 0 ) ,
4242 hours: Keyword . get ( opts , :hours , 0 ) ,
4343 minutes: Keyword . get ( opts , :minutes , 0 ) ,
44- seconds: Keyword . get ( opts , :seconds , 0 )
44+ seconds: Keyword . get ( opts , :seconds , 0 ) ,
45+ milliseconds: Keyword . get ( opts , :milliseconds , 0 )
4546 }
4647 end
4748
@@ -53,7 +54,7 @@ defmodule Predicator.Duration do
5354 ## Examples
5455
5556 iex> Predicator.Duration.from_units([{"3", "d"}, {"8", "h"}])
56- {:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0}}
57+ {:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0, milliseconds: 0 }}
5758
5859 iex> Predicator.Duration.from_units([{"invalid", "d"}])
5960 {:error, "Invalid duration value: invalid"}
@@ -90,7 +91,7 @@ defmodule Predicator.Duration do
9091
9192 iex> duration = Predicator.Duration.new(days: 1)
9293 iex> Predicator.Duration.add_unit(duration, "h", 3)
93- %{years: 0, months: 0, weeks: 0, days: 1, hours: 3, minutes: 0, seconds: 0}
94+ %{years: 0, months: 0, weeks: 0, days: 1, hours: 3, minutes: 0, seconds: 0, milliseconds: 0 }
9495 """
9596 @ spec add_unit ( Types . duration ( ) , binary ( ) , non_neg_integer ( ) ) :: Types . duration ( )
9697 def add_unit ( duration , "y" , value ) , do: % { duration | years: duration . years + value }
@@ -101,6 +102,9 @@ defmodule Predicator.Duration do
101102 def add_unit ( duration , "m" , value ) , do: % { duration | minutes: duration . minutes + value }
102103 def add_unit ( duration , "s" , value ) , do: % { duration | seconds: duration . seconds + value }
103104
105+ def add_unit ( duration , "ms" , value ) ,
106+ do: % { duration | milliseconds: duration . milliseconds + value }
107+
104108 def add_unit ( _duration , unit , _value ) do
105109 throw ( { :error , "Unknown duration unit: #{ unit } " } )
106110 end
@@ -131,6 +135,33 @@ defmodule Predicator.Duration do
131135 Map . get ( duration , :years , 0 ) * 31_536_000
132136 end
133137
138+ @ doc """
139+ Converts a duration to total milliseconds (approximate for months and years).
140+
141+ Uses approximate conversions:
142+ - 1 month = 30 days
143+ - 1 year = 365 days
144+
145+ ## Examples
146+
147+ iex> Predicator.Duration.to_milliseconds(%{seconds: 1, milliseconds: 500})
148+ 1500
149+
150+ iex> Predicator.Duration.to_milliseconds(%{minutes: 1, seconds: 30, milliseconds: 250})
151+ 90250
152+ """
153+ @ spec to_milliseconds ( Types . duration ( ) ) :: integer ( )
154+ def to_milliseconds ( duration ) do
155+ Map . get ( duration , :milliseconds , 0 ) +
156+ Map . get ( duration , :seconds , 0 ) * 1_000 +
157+ Map . get ( duration , :minutes , 0 ) * 60_000 +
158+ Map . get ( duration , :hours , 0 ) * 3_600_000 +
159+ Map . get ( duration , :days , 0 ) * 86_400_000 +
160+ Map . get ( duration , :weeks , 0 ) * 604_800_000 +
161+ Map . get ( duration , :months , 0 ) * 2_592_000_000 +
162+ Map . get ( duration , :years , 0 ) * 31_536_000_000
163+ end
164+
134165 @ doc """
135166 Adds a duration to a Date, returning a Date.
136167
@@ -171,6 +202,11 @@ defmodule Predicator.Duration do
171202 ~U[2024-01-17T14:00:00Z]
172203 """
173204 @ spec add_to_datetime ( DateTime . t ( ) , Types . duration ( ) ) :: DateTime . t ( )
205+ def add_to_datetime ( datetime , % { milliseconds: ms } = duration ) when ms > 0 do
206+ total_ms = to_milliseconds ( duration )
207+ DateTime . add ( datetime , total_ms , :millisecond )
208+ end
209+
174210 def add_to_datetime ( datetime , duration ) do
175211 total_seconds = to_seconds ( duration )
176212 DateTime . add ( datetime , total_seconds , :second )
@@ -216,6 +252,11 @@ defmodule Predicator.Duration do
216252 ~U[2024-01-15T10:30:00Z]
217253 """
218254 @ spec subtract_from_datetime ( DateTime . t ( ) , Types . duration ( ) ) :: DateTime . t ( )
255+ def subtract_from_datetime ( datetime , % { milliseconds: ms } = duration ) when ms > 0 do
256+ total_ms = to_milliseconds ( duration )
257+ DateTime . add ( datetime , - total_ms , :millisecond )
258+ end
259+
219260 def subtract_from_datetime ( datetime , duration ) do
220261 total_seconds = to_seconds ( duration )
221262 DateTime . add ( datetime , - total_seconds , :second )
@@ -243,7 +284,8 @@ defmodule Predicator.Duration do
243284 { :days , "d" } ,
244285 { :hours , "h" } ,
245286 { :minutes , "m" } ,
246- { :seconds , "s" }
287+ { :seconds , "s" } ,
288+ { :milliseconds , "ms" }
247289 ]
248290
249291 parts =
0 commit comments