|
12 | 12 |
|
13 | 13 | import org.elasticsearch.common.time.DateUtils; |
14 | 14 | import org.elasticsearch.core.Nullable; |
| 15 | +import org.elasticsearch.core.Tuple; |
15 | 16 | import org.elasticsearch.xpack.esql.core.expression.Expression; |
16 | 17 | import org.elasticsearch.xpack.esql.core.tree.Source; |
17 | 18 | import org.elasticsearch.xpack.esql.core.type.DataType; |
|
22 | 23 |
|
23 | 24 | import java.time.Duration; |
24 | 25 | import java.time.Instant; |
| 26 | +import java.time.LocalDateTime; |
25 | 27 | import java.time.Period; |
26 | 28 | import java.time.ZoneId; |
27 | 29 | import java.util.ArrayList; |
28 | 30 | import java.util.List; |
29 | 31 | import java.util.function.Supplier; |
| 32 | +import java.util.stream.Stream; |
30 | 33 |
|
31 | 34 | import static org.elasticsearch.test.ReadableMatchers.matchesDateMillis; |
32 | 35 | import static org.elasticsearch.test.ReadableMatchers.matchesDateNanos; |
@@ -150,52 +153,84 @@ public static List<DurationTestCaseData> makeTruncDurationTestCases() { |
150 | 153 | } |
151 | 154 |
|
152 | 155 | public static List<PeriodTestCaseData> makeTruncPeriodTestCases() { |
153 | | - String ts = "2023-02-17T10:25:33.38Z"; |
154 | | - return List.of( |
155 | | - /// |
156 | | - /// UTC |
157 | | - /// |
158 | | - new PeriodTestCaseData(Period.ofDays(1), ts, "UTC", "2023-02-17T00:00:00.00Z"), |
159 | | - new PeriodTestCaseData(Period.ofMonths(1), ts, "UTC", "2023-02-01T00:00:00.00Z"), |
160 | | - new PeriodTestCaseData(Period.ofYears(1), ts, "UTC", "2023-01-01T00:00:00.00Z"), |
161 | | - new PeriodTestCaseData(Period.ofDays(10), ts, "UTC", "2023-02-12T00:00:00.00Z"), |
162 | | - // 7 days period should return weekly rounding |
163 | | - new PeriodTestCaseData(Period.ofDays(7), ts, "UTC", "2023-02-13T00:00:00.00Z"), |
164 | | - // 3 months period should return quarterly |
165 | | - new PeriodTestCaseData(Period.ofMonths(3), ts, "UTC", "2023-01-01T00:00:00.00Z"), |
166 | | - // arbitrary period of months and years |
167 | | - new PeriodTestCaseData(Period.ofMonths(7), ts, "UTC", "2022-11-01T00:00:00.00Z"), |
168 | | - new PeriodTestCaseData(Period.ofYears(5), ts, "UTC", "2021-01-01T00:00:00.00Z"), |
169 | | - |
170 | | - /// |
171 | | - /// Timezones |
172 | | - /// |
173 | | - new PeriodTestCaseData(Period.ofDays(1), "2024-03-01T00:30:00Z", "-03", "2024-02-29T03:00:00Z"), |
| 156 | + List<PeriodTestCaseData> cases = new ArrayList<>(); |
| 157 | + |
| 158 | + // Add generic cases for either UTC, fixed timezones and timezones with minutes. |
| 159 | + // Note that we can't do this with variable timezones, as date formats accept only offsets. |
| 160 | + // |
| 161 | + // For every unit, we test 2 cases: 1 unit, and multiple units. |
| 162 | + // Then, for every case, we check 4 boundaries (↑Bucket1, ↓Bucket2, ↑Bucket2, ↓Bucket3) to ensure the exact size of the buckets. |
| 163 | + final List<String> timezones = List.of("Z", "-08:00", "+04:00", "+11:45", "Europe/Madrid", "America/New_York"); |
| 164 | + Stream.of( |
| 165 | + // Days |
| 166 | + new PeriodTestCaseData(Period.ofDays(1), "2023-02-16T23:59:59.99", "", "2023-02-16T00:00:00"), |
| 167 | + new PeriodTestCaseData(Period.ofDays(1), "2023-02-17T00:00:00", "", "2023-02-17T00:00:00"), |
| 168 | + new PeriodTestCaseData(Period.ofDays(1), "2023-02-17T23:59:59.99", "", "2023-02-17T00:00:00"), |
| 169 | + new PeriodTestCaseData(Period.ofDays(1), "2023-02-18T00:00:00", "", "2023-02-18T00:00:00"), |
| 170 | + new PeriodTestCaseData(Period.ofDays(10), "2023-02-11T23:59:59.99", "", "2023-02-02T00:00:00"), |
| 171 | + new PeriodTestCaseData(Period.ofDays(10), "2023-02-12T00:00:00", "", "2023-02-12T00:00:00"), |
| 172 | + new PeriodTestCaseData(Period.ofDays(10), "2023-02-21T23:59:59.99", "", "2023-02-12T00:00:00"), |
| 173 | + new PeriodTestCaseData(Period.ofDays(10), "2023-02-22T00:00:00", "", "2023-02-22T00:00:00"), |
| 174 | + // Weeks |
| 175 | + new PeriodTestCaseData(Period.ofDays(7), "2023-02-05T23:59:59.99", "", "2023-01-30T00:00:00"), |
| 176 | + new PeriodTestCaseData(Period.ofDays(7), "2023-02-06T00:00:00", "", "2023-02-06T00:00:00"), |
| 177 | + new PeriodTestCaseData(Period.ofDays(7), "2023-02-12T23:59:59.99", "", "2023-02-06T00:00:00"), |
| 178 | + new PeriodTestCaseData(Period.ofDays(7), "2023-02-13T00:00:00", "", "2023-02-13T00:00:00"), |
| 179 | + new PeriodTestCaseData(Period.ofDays(21), "2023-01-25T23:59:59.99", "", "2023-01-05T00:00:00"), |
| 180 | + new PeriodTestCaseData(Period.ofDays(21), "2023-01-26T00:00:00", "", "2023-01-26T00:00:00"), |
| 181 | + new PeriodTestCaseData(Period.ofDays(21), "2023-02-15T23:59:59.99", "", "2023-01-26T00:00:00"), |
| 182 | + new PeriodTestCaseData(Period.ofDays(21), "2023-02-16T00:00:00", "", "2023-02-16T00:00:00"), |
| 183 | + // Months |
| 184 | + new PeriodTestCaseData(Period.ofMonths(1), "2024-02-29T23:59:59.99", "", "2024-02-01T00:00:00"), |
| 185 | + new PeriodTestCaseData(Period.ofMonths(1), "2024-03-01T00:00:00", "", "2024-03-01T00:00:00"), |
| 186 | + new PeriodTestCaseData(Period.ofMonths(1), "2024-03-31T23:59:59.99", "", "2024-03-01T00:00:00"), |
| 187 | + new PeriodTestCaseData(Period.ofMonths(1), "2024-04-01T00:00:00", "", "2024-04-01T00:00:00"), |
| 188 | + new PeriodTestCaseData(Period.ofMonths(7), "2022-10-31T23:59:59.99", "", "2022-04-01T00:00:00"), |
| 189 | + new PeriodTestCaseData(Period.ofMonths(7), "2022-11-01T00:00:00", "", "2022-11-01T00:00:00"), |
| 190 | + new PeriodTestCaseData(Period.ofMonths(7), "2023-05-31T23:59:59.99", "", "2022-11-01T00:00:00"), |
| 191 | + new PeriodTestCaseData(Period.ofMonths(7), "2023-06-01T00:00:00", "", "2023-06-01T00:00:00"), |
| 192 | + // Quarters |
| 193 | + new PeriodTestCaseData(Period.ofMonths(3), "2023-12-31T23:59:59.99", "", "2023-10-01T00:00:00"), |
| 194 | + new PeriodTestCaseData(Period.ofMonths(3), "2024-01-01T00:00:00", "", "2024-01-01T00:00:00"), |
| 195 | + new PeriodTestCaseData(Period.ofMonths(3), "2024-03-31T23:59:59.99", "", "2024-01-01T00:00:00"), |
| 196 | + new PeriodTestCaseData(Period.ofMonths(3), "2024-04-01T00:00:00", "", "2024-04-01T00:00:00"), |
| 197 | + new PeriodTestCaseData(Period.ofMonths(6), "2023-12-31T23:59:59.99", "", "2023-07-01T00:00:00"), |
| 198 | + new PeriodTestCaseData(Period.ofMonths(6), "2024-01-01T00:00:00", "", "2024-01-01T00:00:00"), |
| 199 | + new PeriodTestCaseData(Period.ofMonths(6), "2024-06-30T23:59:59.99", "", "2024-01-01T00:00:00"), |
| 200 | + new PeriodTestCaseData(Period.ofMonths(6), "2024-07-01T00:00:00", "", "2024-07-01T00:00:00"), |
| 201 | + // Years |
| 202 | + new PeriodTestCaseData(Period.ofYears(1), "2022-12-31T23:59:59.99", "", "2022-01-01T00:00:00"), |
| 203 | + new PeriodTestCaseData(Period.ofYears(1), "2023-01-01T00:00:00", "", "2023-01-01T00:00:00"), |
| 204 | + new PeriodTestCaseData(Period.ofYears(1), "2023-12-31T23:59:59.99", "", "2023-01-01T00:00:00"), |
| 205 | + new PeriodTestCaseData(Period.ofYears(1), "2024-01-01T00:00:00", "", "2024-01-01T00:00:00"), |
| 206 | + new PeriodTestCaseData(Period.ofYears(5), "2020-12-31T23:59:59.99", "", "2016-01-01T00:00:00"), |
| 207 | + new PeriodTestCaseData(Period.ofYears(5), "2021-01-01T00:00:00", "", "2021-01-01T00:00:00"), |
| 208 | + new PeriodTestCaseData(Period.ofYears(5), "2025-12-31T23:59:59.99", "", "2021-01-01T00:00:00"), |
| 209 | + new PeriodTestCaseData(Period.ofYears(5), "2026-01-01T00:00:00", "", "2026-01-01T00:00:00") |
| 210 | + ).forEach(c -> timezones.forEach(timezone -> { |
| 211 | + // Convert the timezone to the offset in each local time. |
| 212 | + // This is required as date strings can't have a zone name as its zone. |
| 213 | + var inputOffset = timezone.startsWith("+") || timezone.startsWith("-") |
| 214 | + ? timezone |
| 215 | + : LocalDateTime.parse(c.inputDate()).atZone(ZoneId.of(timezone)).getOffset().getId(); |
| 216 | + var expectedOffset = timezone.startsWith("+") || timezone.startsWith("-") |
| 217 | + ? timezone |
| 218 | + : LocalDateTime.parse(c.expectedDate()).atZone(ZoneId.of(timezone)).getOffset().getId(); |
| 219 | + cases.add( |
| 220 | + new PeriodTestCaseData(c.period(), c.inputDate() + inputOffset, timezone, c.expectedDate() + expectedOffset) |
| 221 | + ); |
| 222 | + })); |
174 | 223 |
|
| 224 | + // Special cases |
| 225 | + cases.addAll(List.of( |
175 | 226 | /// |
176 | | - /// Timezone with DST (e.g. New York: -5 to -4 at 2025-03-09T02:00:00-05, and -4 to -5 at 2025-11-02T02:00:00-04) |
| 227 | + /// DST boundaries (e.g. New York: -5 to -4 at 2025-03-09T02:00:00-05, and -4 to -5 at 2025-11-02T02:00:00-04) |
177 | 228 | /// |
| 229 | + // Days |
178 | 230 | new PeriodTestCaseData(Period.ofDays(1), "2025-03-09T06:00:00-04:00", "America/New_York", "2025-03-09T00:00:00-05:00"), |
179 | | - new PeriodTestCaseData(Period.ofMonths(1), "2025-03-09T06:00:00-04:00", "America/New_York", "2025-03-01T00:00:00-05:00"), |
180 | | - new PeriodTestCaseData(Period.ofYears(1), "2025-03-09T06:00:00-04:00", "America/New_York", "2025-01-01T00:00:00-05:00"), |
181 | | - new PeriodTestCaseData(Period.ofDays(10), "2025-03-09T06:00:00-04:00", "America/New_York", "2025-03-03T00:00:00-05:00"), |
182 | | - new PeriodTestCaseData(Period.ofDays(1), "2025-11-02T05:00:00-05:00", "America/New_York", "2025-11-02T00:00:00-04:00"), |
183 | | - new PeriodTestCaseData(Period.ofDays(7), "2025-11-02T05:00:00-05:00", "America/New_York", "2025-10-27T00:00:00-04:00"), |
184 | | - new PeriodTestCaseData(Period.ofMonths(3), "2025-11-02T05:00:00-05:00", "America/New_York", "2025-10-01T00:00:00-04:00"), |
185 | | - new PeriodTestCaseData(Period.ofDays(1), "2025-10-26T02:00:00+02:00", "Europe/Rome", "2025-10-26T00:00:00+02:00"), |
186 | | - new PeriodTestCaseData(Period.ofMonths(3), "2025-11-02T05:00:00-05:00", "Europe/Rome", "2025-10-01T00:00:00-04:00"), |
187 | | - |
188 | | - /// |
189 | | - /// Partial hours timezones |
190 | | - /// |
191 | | - new PeriodTestCaseData(Period.ofDays(1), "2025-03-09T07:08:09-05:45", "-05:45", "2025-03-09T00:00:00-05:45"), |
192 | | - new PeriodTestCaseData(Period.ofMonths(1), "2025-03-09T07:08:09-05:45", "-05:45", "2025-03-01T00:00:00-05:45"), |
193 | | - new PeriodTestCaseData(Period.ofYears(1), "2025-03-09T07:08:09-05:45", "-05:45", "2025-01-01T00:00:00-05:45"), |
194 | | - new PeriodTestCaseData(Period.ofDays(10), "2025-03-09T07:08:09-05:45", "-05:45", "2025-03-03T00:00:00-05:45"), |
195 | | - new PeriodTestCaseData(Period.ofDays(1), "2025-03-09T07:08:09-05:45", "-05:45", "2025-03-09T00:00:00-05:45"), |
196 | | - new PeriodTestCaseData(Period.ofDays(7), "2025-03-09T07:08:09-05:45", "-05:45", "2025-03-03T00:00:00-05:45"), |
197 | | - new PeriodTestCaseData(Period.ofMonths(3), "2025-03-09T07:08:09-05:45", "-05:45", "2025-01-01T00:00:00-05:45") |
198 | | - ); |
| 231 | + new PeriodTestCaseData(Period.ofDays(1), "2025-11-02T05:00:00-05:00", "America/New_York", "2025-11-02T00:00:00-04:00") |
| 232 | + )); |
| 233 | + return cases; |
199 | 234 | } |
200 | 235 |
|
201 | 236 | private static List<TestCaseSupplier> ofDatePeriod(PeriodTestCaseData data) { |
|
0 commit comments