# Time range with reversed bounds

**URL:** <https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693>\
**Category:** General Usage\
**Tags:** dates, range, datetime\
**Created:** [June 6, 2025, 9:58am UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693 "2025-06-06T09:58:54Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ju\_ska](https://avatars.discourse-cdn.com/v4/letter/j/ecd19e/32.png) [@Ju\_ska](https://discourse.julialang.org/u/Ju_ska)\
**Post date:** [June 6, 2025, 9:58am UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693/1 "2025-06-06T09:58:54Z")

</div>

Hi,

I have:

```julia
using Dates
lower_bound = Dates.Time(23,59,00)
upper_bound = Dates.Time(00,01,00)

```

I do:

```julia
julia> collect(upper_bound:Dates.Minute(1):lower_bound)
1439-element Vector{Time}:
 00:01:00
 00:02:00
 00:03:00
 ⋮
 23:57:00
 23:58:00
 23:59:00

```

But if I do:

```julia
julia> collect(lower_bound:Dates.Minute(1):upper_bound)
Time[]

```

I expected to have this result:

```julia
23:59:00
00:00:00
00:01:00

```

How could I get this result nicely (e.g. without having to manipulate days)?

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [June 6, 2025, 11:48am UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693/2 "2025-06-06T11:48:04Z")

</div>

This is actually interesting.

I think your third code block is wrong (it’s the same as the second), but I think what you’re getting at is the fact that we have:

```julia
julia> collect(3:1:5)
3-element Vector{Int64}:
 3
 4
 5

julia> collect(5:-1:3)
3-element Vector{Int64}:
 5
 4
 3

```

so one would expect

```julia
julia> collect(Time(0,1):Minute(-1):Time(23, 59))
Time[]

```

to work given that we have

```julia
julia> [Time(0,1) + i*Minute(-1) for i ∈ 0:2]
3-element Vector{Time}:
 00:01:00
 00:00:00
 23:59:00

```

Someone who’s more intimately familiar with the workings of the `Dates` std lib might comment, but this seems issue-worthy to me?

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [June 6, 2025, 12:07pm UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693/3 "2025-06-06T12:07:13Z")

</div>

I am not _intimately_ familiar with `Dates`, but the problem is that `Time` arithmetic, on one hand, works modulo 24 hours,

```julia
julia> Time("23:59", "HH:MM") + Minute(2)
00:01:00 # one minute after midnight

```

but the ordering that is defined for `Time` is valid only within one day. So

```julia
x + Minute(1) > x

```

does not necessarily hold for `x::Time`. For example,

```julia
julia> Time("23:59", "HH:MM") + Minute(1) > Time("23:59", "HH:MM")
false

```

Therefore, if you create a range `r` such that the start time, `first(r)`, is before midnight and the end time, `last(r)`, is after midnight, that range is considered empty, because `first(r) > last(r)`.

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [June 6, 2025, 12:19pm UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693/4 "2025-06-06T12:19:15Z")

</div>

Ah sure, that makes sense, same as

```julia
julia> [typemax(Int)-1 + i for i ∈ 0:3]
4-element Vector{Int64}:
  9223372036854775806
  9223372036854775807
 -9223372036854775808
 -9223372036854775807

julia> collect(typemax(Int)-1:(typemin(Int)+1))
Int64[]

```

So OP would need a proper ordering of those times like:

```julia
julia> Time.(DateTime(0, 1, 1, 23,59):Minute(1):DateTime(0,1,2,0,1))
3-element Vector{Time}:
 23:59:00
 00:00:00
 00:01:00

julia> Time.(DateTime(0, 1, 2, 0, 1):Minute(-1):DateTime(0,1,1,23,59))
3-element Vector{Time}:
 00:01:00
 00:00:00
 23:59:00

```

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [June 6, 2025, 3:54pm UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693/5 "2025-06-06T15:54:47Z")

</div>

This is tricky. It seems like we could use a nice utility for this. The best I could do with builtins was

```julia-repl
julia> Iterators.takewhile(!=(Dates.Time(00,01,00) + Dates.Minute(1)),
           Iterators.countfrom(Dates.Time(23,59,00), Dates.Minute(1))) |> collect
3-element Vector{Time}:
 23:59:00
 00:00:00
 00:01:00

```

But this is obviously very brittle. It relies on the endpoint being hit exactly and has to add 1 to it so that it doesn’t stop one too early.

If you knew how many steps you wanted, rather than the stop point, this would be easier

```julia
julia> Iterators.take(Iterators.countfrom(Dates.Time(23,59,00), Dates.Minute(1)), 3) |> collect
3-element Vector{Time}:
 23:59:00
 00:00:00
 00:01:00

```

One can always [implement their own iterator](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-iteration) for this. Here’s what I threw together. It covers this basic case but perhaps not everything you need. It probably has some strange behavior in some corner cases that you may need to fix (for example, increments larger than 1 Day will probably cause trouble).

```julia
struct TimeIterator{I<:Dates.TimePeriod}
    start::Dates.Time
    stop::Dates.Time
    step::I
end

Base.IteratorSize(::Type{<:TimeIterator}) = Base.SizeUnknown()
Base.eltype(::Type{<:TimeIterator}) = Dates.Time

function Base.iterate(t::TimeIterator, state=(t.start, t.start <= t.stop, false))
    t.step > zero(t.step) || throw(DomainError(t.step, "needs extra work to support nonpositive steps")) # TODO: should check in constructor instead
    tnow, homestretch, stopnow = state
    if stopnow || (homestretch && !(tnow <= t.stop)) # time to stop
        return nothing
    end
    tnext = tnow + t.step
    stopnext = false
    if tnext < tnow # detect when the time wraps
        if homestretch # we were already on the home stretch -- don't let us wrap again!
            stopnext = true
        end
        homestretch = true # use inequality-based stopping condition now
    end
    return (tnow, (tnext, homestretch, stopnext))
end

```

```julia-repl
julia> TimeIterator(Dates.Time(23,57,00), Dates.Time(23,59,00), Dates.Minute(1)) |> collect
3-element Vector{Time}:
 23:57:00
 23:58:00
 23:59:00

julia> TimeIterator(Dates.Time(23,57,00), Dates.Time(00,01,00), Dates.Minute(1)) |> collect
5-element Vector{Time}:
 23:57:00
 23:58:00
 23:59:00
 00:00:00
 00:01:00

```

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [June 6, 2025, 4:51pm UTC](https://discourse.julialang.org/t/time-range-with-reversed-bounds/129693/6 "2025-06-06T16:51:12Z")

</div>

Using the various Iterator tools:

```julia
using Iterators
using IterTools
using Dates

maprepeateduntil(mapfn, condfn, stepfn, initval) = 
  Iterators.map(mapfn, 
    Iterators.takewhile(
      let done=false
        x->(t = done; done = condfn(x); !t)
      end, 
      IterTools.iterated(stepfn, initval)
    )
  )

iteratetimes(lb, ub, step) = 
  maprepeateduntil(
    Time, ==(ub)∘Time, 
    Base.Fix1(+, step), 
    DateTime(Date(now())+lb)
  )

```

with these in place:

```julia
julia> iteratetimes(lower_bound, upper_bound, Minute(1)) |> collect
3-element Vector{Time}:
 23:59:00
 00:00:00
 00:01:00

```

and

```julia
julia> iteratetimes(upper_bound, lower_bound, Minute(1)) |> collect |> 
  x->(display.(extrema(x)); length(x))
00:01:00
23:59:00
1439

```
