How can I compare to minutes to something like local time

I have a list of list containing start_time and end_time. something like this

[[3600, 4800], [3600, 4800], [3600, 4800]]

We are storing start_time and end_time as an integer and in minutes. Now I need to how can I somehow convert this to a DateTime. So that I can compare it with local_time or system time.

But in order to do that, I’m struggling with the next step. Do I need to convert this to a second? Or first add them and convert it to a second and add some format to it. What should I do?

You can convert these to Time and compare them with Time.utc_now. Not sure what exactly the minutes represent in your example since 3600 minutes is 60 hours and you can’t convert to datetime unless you have some reference date to add the minutes to.

iex(1)> minute_to_time = fn minutes ->
...(1)>   hours = rem(div(minutes, 60), 24)
...(1)>   minutes = rem(minutes, 60)
...(1)>   Time.new!(hours, minutes, _seconds = 0)
...(1)> end
#Function<44.40011524/1 in :erl_eval.expr/5>
iex(2)> minute_to_time.(3600)
~T[12:00:00]
iex(3)> minute_to_time.(4800)
~T[08:00:00]
iex(4)> minute_ranges = [[3600, 4800], [3600, 4800], [3600, 4800]]
[[3600, 4800], [3600, 4800], [3600, 4800]]
iex(5)> Enum.map(minute_ranges, fn [from, to] -> [minute_to_time.(from), minute_to_time.(to)] end)
[
  [~T[12:00:00], ~T[08:00:00]],
  [~T[12:00:00], ~T[08:00:00]],
  [~T[12:00:00], ~T[08:00:00]]
]

What do 3600 and 4800 represent in this case? Minutes since 1st of Jan 1970, or something else?

This is interesting. Thanks

If we are storing a minute do we need to calculate that far?
Can we not just get the minute for a particular day?

Why are you answering a question with a question? :grinning_face_with_smiling_eyes:

I am trying to understand what does your data represent exactly so I can try and help you.

So it’s elapsed minutes since current week’s Monday midnight, correct?

Yes something like that

Then you’d need to get the date for beginning of the week convert it to a datetime with zero time Time.new!(0,0,0), and add minutes * 60 seconds to it. You might also need to use a timezone other than UTC for calculations to be correct (comparing to DateTime.utc_now probably would work fine anyways).

1 Like

So do you mean with start_time and end_time there should be a date also?

I might not fully understand the problem, but based on minutes being relative to beginning of the week, yes, each minute in your dataset would need to be using a reference date to get a correct datetime.

iex(1)> minutes = 3600
3600
iex(2)> bow = Date.beginning_of_week(Date.utc_today)
~D[2021-09-13]
iex(3)> reference = DateTime.new!(bow, Time.new!(0,0,0))
~U[2021-09-13 00:00:00Z]
iex(4)> DateTime.add(reference, minutes * 60)
~U[2021-09-15 12:00:00Z]

Yes it makes sense.

I have one more question. How can I check if this ~U[2021-09-15 11:44:25.320726Z]
lies between this [~U[2021-09-15 12:00:00Z], ~U[2021-09-16 08:00:00Z]]. Is there any in built function for this?

I know there is Time.diff/2.

1 Like

You’d use DateTime.compare twice.

iex(5)> dt = ~U[2021-09-15 11:44:25.320726Z]
~U[2021-09-15 11:44:25.320726Z]
iex(6)> interval =  [~U[2021-09-15 12:00:00Z], ~U[2021-09-16 08:00:00Z]]
[~U[2021-09-15 12:00:00Z], ~U[2021-09-16 08:00:00Z]]
iex(7)> [from, to] = interval
[~U[2021-09-15 12:00:00Z], ~U[2021-09-16 08:00:00Z]]
iex(8)> DateTime.compare(dt, from)
:lt
iex(9)> DateTime.compare(dt, to)
:lt
iex(10)> between = fn date, [from, to] ->
...(10)>   after_from? = DateTime.compare(date, from) == :gt
...(10)>   before_to? = DateTime.compare(date, to) == :lt
...(10)>   after_from? and before_to?
...(10)> end
#Function<43.40011524/2 in :erl_eval.expr/5>
iex(11)> between.(dt, interval)
false

Another way would be to store start_time as datetime, and a duration (in minutes)…

This allow easy conversion, from start, stop and duration.

1 Like

Yeah this is also good. I was trying with Time.diff/2.

I don’t think Time.diff would work for your use-case as you’ve described here.

1 Like

I maintain the little library Tox. Maybe Tox could be helpful for you.

iex(1)> interval = fn [s, e] ->
...(1)>   b = DateTime.utc_now() |> Tox.DateTime.beginning_of_week()
...(1)>   s = Tox.DateTime.shift(b, minute: s)
...(1)>   e = Tox.DateTime.shift(b, minute: e)
...(1)>   Tox.Interval.new!(s, e)
...(1)> end
#Function<44.40011524/1 in :erl_eval.expr/5>
iex(2)> i = interval.([3600, 4800])
#Tox.Interval<[2021-09-15T12:00:00.000000Z/2021-09-16T08:00:00.000000Z[>
iex(3)> Tox.Interval.contains?(i, ~U[2021-09-15 12:00:00Z])
true
iex(4)> Tox.Interval.contains?(i, ~U[2021-09-16 12:00:00Z])
false
2 Likes

This is really nice. Thanks :slight_smile:

1 Like