File size: 3,921 Bytes
c9c49ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
defmodule Plausible.Stats.DateTimeRangeTest do
  use Plausible.DataCase, async: true

  alias Plausible.Stats.DateTimeRange

  describe "new!/2" do
    test "creates a range when datetimes are in correct order" do
      first = DateTime.new!(~D[2023-01-01], ~T[10:00:00], "UTC")
      last = DateTime.new!(~D[2023-01-02], ~T[10:00:00], "UTC")

      range = DateTimeRange.new!(first, last)

      assert range.first == first
      assert range.last == last
    end

    test "swaps datetimes when in reverse order" do
      first = DateTime.new!(~D[2023-02-01], ~T[10:00:00], "UTC")
      last = DateTime.new!(~D[2023-01-01], ~T[10:00:00], "UTC")

      range = DateTimeRange.new!(first, last)

      assert range.first == last
      assert range.last == first
    end

    test "truncates microseconds" do
      first = DateTime.new!(~D[2023-01-01], ~T[10:00:00.123], "UTC")
      last = DateTime.new!(~D[2023-01-02], ~T[10:00:00.456], "UTC")

      range = DateTimeRange.new!(first, last)

      assert range.first == DateTime.truncate(first, :second)
      assert range.last == DateTime.truncate(last, :second)
    end
  end

  describe "new!/3 with dates and timezone" do
    test "creates range with start and end of day" do
      first_date = ~D[2023-01-01]
      last_date = ~D[2023-01-02]

      range = DateTimeRange.new!(first_date, last_date, "UTC")

      assert range.first == DateTime.new!(first_date, ~T[00:00:00], "UTC")
      assert range.last == DateTime.new!(last_date, ~T[23:59:59], "UTC")
    end

    test "creates a range from a mix of date and datetime" do
      first = DateTime.new!(~D[2023-02-15], ~T[12:30:00], "UTC")
      last = ~D[2023-02-18]

      range = DateTimeRange.new!(first, last, "UTC")

      assert range.first == first
      assert range.last == DateTime.new!(last, ~T[23:59:59], "UTC")
    end

    test "creates a range from a mix of datetime and date" do
      first = ~D[2023-02-15]
      last = DateTime.new!(~D[2023-02-18], ~T[12:30:00], "UTC")

      range = DateTimeRange.new!(first, last, "UTC")

      assert range.first == DateTime.new!(first, ~T[00:00:00], "UTC")
      assert range.last == last
    end

    test "handles timezone gaps (spring forward)" do
      # https://stackoverflow.com/questions/18489927/a-day-without-midnight
      range = DateTimeRange.new!(~D[2020-03-29], ~D[2020-03-29], "Asia/Beirut")

      assert range.first == DateTime.new!(~D[2020-03-29], ~T[01:00:00], "Asia/Beirut")
      assert range.last == DateTime.new!(~D[2020-03-29], ~T[23:59:59], "Asia/Beirut")
    end
  end

  describe "to_timezone/2" do
    test "converts range to specified timezone" do
      first = DateTime.new!(~D[2023-01-01], ~T[10:00:00], "UTC")
      last = DateTime.new!(~D[2023-01-02], ~T[10:00:00], "UTC")
      range = DateTimeRange.new!(first, last)

      converted = DateTimeRange.to_timezone(range, "America/New_York")

      assert converted.first == DateTime.shift_zone!(first, "America/New_York")
      assert converted.last == DateTime.shift_zone!(last, "America/New_York")
    end
  end

  describe "to_date_range/2" do
    test "converts datetime range to date range" do
      first = DateTime.new!(~D[2023-01-01], ~T[10:00:00], "UTC")
      last = DateTime.new!(~D[2023-01-05], ~T[10:00:00], "UTC")
      range = DateTimeRange.new!(first, last)

      date_range = DateTimeRange.to_date_range(range, "UTC")

      assert date_range.first == ~D[2023-01-01]
      assert date_range.last == ~D[2023-01-05]
    end

    test "handles timezone conversions that cross date boundaries" do
      first = DateTime.new!(~D[2023-01-01], ~T[23:00:00], "UTC")
      last = DateTime.new!(~D[2023-01-02], ~T[04:59:59], "UTC")
      range = DateTimeRange.new!(first, last)

      date_range = DateTimeRange.to_date_range(range, "America/New_York")

      assert date_range.first == ~D[2023-01-01]
      assert date_range.last == ~D[2023-01-01]
    end
  end
end