perspective-python 4.2.0__cp311-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. perspective/__init__.py +396 -0
  2. perspective/extension/finos-perspective-nbextension.json +5 -0
  3. perspective/handlers/__init__.py +11 -0
  4. perspective/handlers/aiohttp.py +61 -0
  5. perspective/handlers/starlette.py +55 -0
  6. perspective/handlers/tornado.py +184 -0
  7. perspective/perspective.pyd +0 -0
  8. perspective/templates/exported_widget.html.template +35 -0
  9. perspective/tests/__init__.py +11 -0
  10. perspective/tests/async/test_async_client.py +83 -0
  11. perspective/tests/async/test_websocket_client.py +124 -0
  12. perspective/tests/conftest.py +272 -0
  13. perspective/tests/core/__init__.py +11 -0
  14. perspective/tests/core/test_async.py +351 -0
  15. perspective/tests/multi_threaded/__init__.py +11 -0
  16. perspective/tests/multi_threaded/test_multi_threaded.py +201 -0
  17. perspective/tests/server/__init__.py +11 -0
  18. perspective/tests/server/test_server.py +1016 -0
  19. perspective/tests/server/test_session.py +110 -0
  20. perspective/tests/table/__init__.py +11 -0
  21. perspective/tests/table/arrow/date32.arrow +0 -0
  22. perspective/tests/table/arrow/date64.arrow +0 -0
  23. perspective/tests/table/arrow/dict.arrow +0 -0
  24. perspective/tests/table/arrow/dict_update.arrow +0 -0
  25. perspective/tests/table/arrow/int_float_str.arrow +0 -0
  26. perspective/tests/table/arrow/int_float_str_file.arrow +0 -0
  27. perspective/tests/table/arrow/int_float_str_update.arrow +0 -0
  28. perspective/tests/table/object_sequence.py +402 -0
  29. perspective/tests/table/test_column_paths.py +89 -0
  30. perspective/tests/table/test_delete.py +124 -0
  31. perspective/tests/table/test_exception.py +65 -0
  32. perspective/tests/table/test_leaks.py +54 -0
  33. perspective/tests/table/test_ports.py +178 -0
  34. perspective/tests/table/test_remove.py +102 -0
  35. perspective/tests/table/test_table.py +641 -0
  36. perspective/tests/table/test_table_arrow.py +503 -0
  37. perspective/tests/table/test_table_datetime.py +2409 -0
  38. perspective/tests/table/test_table_infer.py +201 -0
  39. perspective/tests/table/test_table_limit.py +45 -0
  40. perspective/tests/table/test_table_numpy.py +1022 -0
  41. perspective/tests/table/test_table_pandas.py +1018 -0
  42. perspective/tests/table/test_table_polars.py +251 -0
  43. perspective/tests/table/test_table_view_table.py +130 -0
  44. perspective/tests/table/test_to_arrow.py +417 -0
  45. perspective/tests/table/test_to_arrow_lz4.py +32 -0
  46. perspective/tests/table/test_to_format.py +1024 -0
  47. perspective/tests/table/test_to_polars.py +26 -0
  48. perspective/tests/table/test_update.py +545 -0
  49. perspective/tests/table/test_update_arrow.py +980 -0
  50. perspective/tests/table/test_update_pandas.py +211 -0
  51. perspective/tests/table/test_view.py +2261 -0
  52. perspective/tests/table/test_view_expression.py +1940 -0
  53. perspective/tests/test_dependencies.py +53 -0
  54. perspective/tests/viewer/__init__.py +11 -0
  55. perspective/tests/viewer/test_viewer.py +246 -0
  56. perspective/tests/widget/__init__.py +11 -0
  57. perspective/tests/widget/test_widget.py +278 -0
  58. perspective/tests/widget/test_widget_pandas.py +453 -0
  59. perspective/virtual_servers/__init__.py +134 -0
  60. perspective/virtual_servers/clickhouse.py +245 -0
  61. perspective/virtual_servers/duckdb.py +236 -0
  62. perspective/widget/__init__.py +349 -0
  63. perspective/widget/viewer/__init__.py +15 -0
  64. perspective/widget/viewer/validate.py +22 -0
  65. perspective/widget/viewer/viewer.py +343 -0
  66. perspective/widget/viewer/viewer_traitlets.py +101 -0
  67. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/install.json +5 -0
  68. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/package.json +71 -0
  69. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/253.5f5c9e80605aa4106a28.js +2 -0
  70. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/253.5f5c9e80605aa4106a28.js.LICENSE.txt +25 -0
  71. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/523.c030af5d3c4f67ff83f6.js +1 -0
  72. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/remoteEntry.95a8ea1b44d96032833f.js +1 -0
  73. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/style.js +4 -0
  74. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/third-party-licenses.json +16 -0
  75. perspective_python-4.2.0.dist-info/METADATA +27 -0
  76. perspective_python-4.2.0.dist-info/RECORD +79 -0
  77. perspective_python-4.2.0.dist-info/WHEEL +4 -0
  78. perspective_python-4.2.0.dist-info/licenses/LICENSE.md +193 -0
  79. perspective_python-4.2.0.dist-info/licenses/LICENSE_THIRDPARTY_cargo.yml +17395 -0
@@ -0,0 +1,2409 @@
1
+ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ # ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ # ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ # ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ # ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ # ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ # ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ # ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ # ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import os
14
+ import time
15
+ from perspective.tests.conftest import Util
16
+ import pytz
17
+ import numpy as np
18
+ import pandas as pd
19
+ from datetime import date, datetime, timedelta
20
+ from dateutil import tz
21
+ from pytest import mark, raises
22
+ from perspective import PerspectiveError
23
+ import perspective as psp
24
+
25
+ client = psp.Server().new_local_client()
26
+ Table = client.table
27
+
28
+ LOCAL_DATETIMES = [
29
+ datetime(2019, 1, 11, 0, 10, 20),
30
+ datetime(2019, 1, 11, 11, 10, 20),
31
+ datetime(2019, 1, 11, 19, 10, 20),
32
+ ]
33
+
34
+ LOCAL_DATETIMES_TS = [int(d.timestamp() * 1000) for d in LOCAL_DATETIMES]
35
+
36
+ # Test the DST transition for Continental US
37
+ LOCAL_DATETIMES_DST = [
38
+ datetime(2019, 3, 9, 12, 10, 20),
39
+ datetime(2019, 3, 19, 12, 10, 20),
40
+ datetime(2019, 11, 2, 12, 10, 20),
41
+ datetime(2019, 11, 3, 12, 10, 20),
42
+ ]
43
+
44
+ LOCAL_DATETIMES_DST_TS = [int(d.timestamp() * 1000) for d in LOCAL_DATETIMES_DST]
45
+
46
+ LOCAL_TIMESTAMPS = [pd.Timestamp(d) for d in LOCAL_DATETIMES]
47
+ LOCAL_TIMESTAMPS_DST = [pd.Timestamp(d) for d in LOCAL_DATETIMES_DST]
48
+
49
+ # Set up testing data
50
+ UTC = pytz.UTC
51
+
52
+ UTC_DATETIMES = [UTC.localize(d) for d in LOCAL_DATETIMES]
53
+ UTC_TIMESTAMPS = [UTC.localize(d) for d in LOCAL_TIMESTAMPS]
54
+
55
+ UTC_DATETIMES_DST = [UTC.localize(d, is_dst=True) for d in LOCAL_DATETIMES_DST]
56
+ UTC_TIMESTAMPS_DST = [UTC.localize(d, is_dst=True) for d in LOCAL_TIMESTAMPS_DST]
57
+
58
+ PST = pytz.timezone("US/Pacific")
59
+ CST = pytz.timezone("US/Central")
60
+ EST = pytz.timezone("US/Eastern")
61
+ GMT = pytz.timezone("GMT")
62
+ HKT = pytz.timezone("Asia/Hong_Kong")
63
+ JPT = pytz.timezone("Asia/Tokyo")
64
+ ACT = pytz.timezone("Australia/ACT")
65
+
66
+ TIMEZONES = [PST, CST, EST, GMT, HKT, JPT, ACT]
67
+
68
+ TZ_DATETIMES = {}
69
+ TZ_TIMESTAMPS = {}
70
+
71
+ TZ_DATETIMES_DST = {}
72
+ TZ_TIMESTAMPS_DST = {}
73
+
74
+ for TZ in TIMEZONES:
75
+ TZ_DATETIMES[TZ.zone] = [TZ.localize(d) for d in LOCAL_DATETIMES]
76
+ TZ_TIMESTAMPS[TZ.zone] = [d.tz_localize(TZ) for d in LOCAL_TIMESTAMPS]
77
+ TZ_DATETIMES_DST[TZ.zone] = [d.astimezone(TZ) for d in UTC_DATETIMES_DST]
78
+ TZ_TIMESTAMPS_DST[TZ.zone] = [d.tz_convert(TZ) for d in UTC_TIMESTAMPS_DST]
79
+
80
+ if os.name != "nt":
81
+ # no tzset on windows, run these tests on linux/mac only
82
+
83
+ class TestTableLocalDateTime(object):
84
+ """Test datetimes across configurations such as local time, timezone-aware,
85
+ timezone-naive, and UTC implementations.
86
+ """
87
+
88
+ def setup_method(self):
89
+ # To make sure that local times are not changed, set timezone to EST
90
+ os.environ["TZ"] = "US/Eastern"
91
+ time.tzset()
92
+
93
+ def teardown_method(self):
94
+ # go back to UTC at end of each test method, for consistency
95
+ os.environ["TZ"] = "UTC"
96
+ time.tzset()
97
+
98
+ def test_table_should_assume_local_time(self):
99
+ """If a datetime object has no `tzinfo`, it should be assumed to be in
100
+ local time and not be converted at all.
101
+ """
102
+ data = {"a": LOCAL_DATETIMES}
103
+ table = Table(data)
104
+ assert table.view().to_columns()["a"] == LOCAL_DATETIMES_TS
105
+
106
+ @mark.skip(reason="no numpy support in JSON Table constructor")
107
+ def test_table_should_assume_local_time_numpy_datetime64(self):
108
+ data = {"a": [np.datetime64(d) for d in LOCAL_DATETIMES]}
109
+ table = Table(data)
110
+ assert table.view().to_columns()["a"] == LOCAL_DATETIMES_TS
111
+
112
+ def test_table_should_assume_local_time_pandas_timestamp(self):
113
+ data = {"a": LOCAL_TIMESTAMPS}
114
+
115
+ # Timestamps are assumed to be in UTC by pandas
116
+ table = Table(data)
117
+
118
+ # Timestamps are read out in local time
119
+ assert table.view().to_columns()["a"] == LOCAL_DATETIMES_TS
120
+
121
+ @mark.skip(reason="no numpy support in JSON Table constructor")
122
+ def test_table_should_assume_local_time_pandas_timestamp_df(self, util: Util):
123
+ data = pd.DataFrame({"a": LOCAL_TIMESTAMPS})
124
+
125
+ # Timestamps are assumed to be in UTC by pandas
126
+ table = Table(data)
127
+
128
+ # Timestamps are read out in local time
129
+ assert table.view().to_columns()["a"] == [
130
+ util.to_timestamp(datetime(2019, 1, 10, 19, 10, 20)),
131
+ util.to_timestamp(datetime(2019, 1, 11, 6, 10, 20)),
132
+ util.to_timestamp(datetime(2019, 1, 11, 14, 10, 20)),
133
+ ]
134
+
135
+ def test_table_should_assume_local_time_dst(self):
136
+ """If a datetime object has no `tzinfo`, it should be assumed to be in
137
+ local time and not be converted at all.
138
+ """
139
+ data = {"a": LOCAL_DATETIMES_DST}
140
+ table = Table(data)
141
+ assert table.view().to_columns()["a"] == LOCAL_DATETIMES_DST_TS
142
+
143
+ @mark.skip(reason="Do not support numpy in JSON constructor")
144
+ def test_table_should_assume_local_time_numpy_datetime64_dst(self):
145
+ data = {"a": [np.datetime64(d) for d in LOCAL_DATETIMES_DST]}
146
+ table = Table(data)
147
+ assert table.view().to_columns()["a"] == LOCAL_DATETIMES_DST_TS
148
+
149
+ def test_table_should_assume_local_time_pandas_timestamp_dst(self):
150
+ data = {"a": LOCAL_TIMESTAMPS_DST}
151
+ table = Table(data)
152
+ assert table.view().to_columns()["a"] == LOCAL_DATETIMES_DST_TS
153
+
154
+ def test_table_should_assume_local_time_pandas_timestamp_dst_df(self, util):
155
+ data = pd.DataFrame({"a": LOCAL_TIMESTAMPS_DST})
156
+ table = Table(data)
157
+ assert table.view().to_columns()["a"] == [
158
+ util.to_timestamp(datetime(2019, 3, 9, 7, 10, 20)),
159
+ util.to_timestamp(datetime(2019, 3, 19, 8, 10, 20)),
160
+ util.to_timestamp(datetime(2019, 11, 2, 8, 10, 20)),
161
+ util.to_timestamp(datetime(2019, 11, 3, 7, 10, 20)),
162
+ ]
163
+
164
+ @mark.skip(reason="Python specific behavior we don't need")
165
+ def test_table_datetime_min(self, util):
166
+ data = {"a": [datetime.min]}
167
+ table = Table(data)
168
+ assert table.view().to_columns()["a"] == [
169
+ util.to_timestamp(datetime(1969, 12, 31, 19, 0))
170
+ ]
171
+
172
+ @mark.skip(reason="Python specific behavior we don't need")
173
+ def test_table_datetime_min_df(self, util):
174
+ data = pd.DataFrame({"a": [datetime.min]})
175
+ table = Table(data)
176
+ assert table.view().to_columns()["a"] == [
177
+ util.to_timestamp(datetime(1969, 12, 31, 19, 0))
178
+ ]
179
+
180
+ def test_table_datetime_1900(self, util):
181
+ table = Table({"a": "datetime"})
182
+ table.update({"a": [util.to_timestamp(datetime(1900, 1, 1))]})
183
+ assert table.view().to_columns()["a"] == [
184
+ util.to_timestamp(datetime(1900, 1, 1))
185
+ ]
186
+
187
+ def test_table_datetime_1900_df(self, util):
188
+ data = pd.DataFrame({"a": [datetime(1900, 1, 1)]})
189
+ table = Table(data)
190
+ assert table.view().to_columns()["a"] == [
191
+ util.to_timestamp(datetime(1899, 12, 31, 19))
192
+ ]
193
+
194
+ def test_table_datetime_1899(self, util: Util):
195
+ table = Table({"a": "datetime"})
196
+ table.update({"a": [datetime(1899, 1, 1)]})
197
+ assert table.view().to_columns()["a"] == [
198
+ util.to_timestamp(datetime(1898, 12, 31, 19))
199
+ ]
200
+
201
+ def test_table_datetime_1899_df(self, util):
202
+ data = pd.DataFrame({"a": [datetime(1899, 1, 1)]})
203
+ table = Table(data)
204
+ assert table.view().to_columns()["a"] == [
205
+ util.to_timestamp(datetime(1898, 12, 31, 19))
206
+ ]
207
+
208
+ def test_table_datetime_min_epoch(self, util):
209
+ data = {"a": [0]}
210
+ table = Table({"a": "datetime"})
211
+ table.update(data)
212
+ assert table.view().to_columns()["a"] == [
213
+ util.to_timestamp(datetime(1969, 12, 31, 19, 0))
214
+ ]
215
+
216
+ def test_table_datetime_cant_convert_from_int(self):
217
+ data = pd.DataFrame({"a": [0]})
218
+ table = Table({"a": "datetime"})
219
+ with raises(PerspectiveError):
220
+ table.update(data)
221
+ # assert str(ex.value) == "..."
222
+
223
+ @mark.skip(
224
+ reason="we do not support this conversion, wrote a test above to ensure it fails"
225
+ )
226
+ def test_table_datetime_min_epoch_df(self, util):
227
+ data = pd.DataFrame({"a": [0]})
228
+ table = Table({"a": "datetime"})
229
+ table.update(data)
230
+ assert table.view().to_columns()["a"] == [
231
+ util.to_timestamp(datetime(1969, 12, 31, 19, 0))
232
+ ]
233
+
234
+ @mark.skip
235
+ def test_table_datetime_max(self, util):
236
+ data = {"a": [datetime.max]}
237
+ table = Table(data)
238
+
239
+ # lol - result is converted from UTC to EST (local time)
240
+ assert table.view().to_columns()["a"] == [
241
+ util.to_timestamp(datetime(9999, 12, 31, 18, 59, 59))
242
+ ]
243
+
244
+ @mark.skip
245
+ def test_table_datetime_max_df(self, util):
246
+ data = pd.DataFrame({"a": [datetime.max]})
247
+ table = Table(data)
248
+ assert table.view().to_columns()["a"] == [
249
+ util.to_timestamp(datetime(9999, 12, 31, 18, 59, 59))
250
+ ]
251
+
252
+ class TestTableDateTimeUTCToLocal(object):
253
+ def teardown_method(self):
254
+ # Set timezone to UTC, always
255
+ os.environ["TZ"] = "UTC"
256
+ time.tzset()
257
+
258
+ @mark.skip(reason="Unsupported non-UTC timezone")
259
+ def test_table_should_convert_UTC_to_local_time_pytz_pacific(self):
260
+ """If the datetime has `tzinfo` set, use it to convert the datetime to
261
+ UTC. Make sure this works with both `pytz` and `dateutil` for
262
+ `datetime` and `pandas.Timestamp`.
263
+ """
264
+ data = {"a": UTC_DATETIMES}
265
+ table = Table(data)
266
+
267
+ os.environ["TZ"] = "US/Pacific"
268
+ time.tzset()
269
+
270
+ # Should be in PST now
271
+ assert table.view().to_columns() == {
272
+ "a": [d.astimezone(PST).replace(tzinfo=None) for d in data["a"]]
273
+ }
274
+
275
+ def test_table_should_convert_UTC_to_local_time_pytz_central(self, util: Util):
276
+ data = {"a": UTC_DATETIMES}
277
+ table = Table(data)
278
+
279
+ os.environ["TZ"] = "US/Central"
280
+ time.tzset()
281
+
282
+ # Should be in CST now
283
+ assert table.view().to_columns() == {
284
+ "a": [
285
+ util.to_timestamp(d.astimezone(CST).replace(tzinfo=None))
286
+ for d in data["a"]
287
+ ]
288
+ }
289
+
290
+ def test_table_should_convert_UTC_to_local_time_pytz_eastern(self, util: Util):
291
+ data = {"a": UTC_DATETIMES}
292
+ table = Table(data)
293
+
294
+ os.environ["TZ"] = "US/Eastern"
295
+ time.tzset()
296
+
297
+ # Should be in EST now
298
+ assert table.view().to_columns() == {
299
+ "a": [
300
+ util.to_timestamp(d.astimezone(EST).replace(tzinfo=None))
301
+ for d in data["a"]
302
+ ]
303
+ }
304
+
305
+ def test_table_should_convert_UTC_to_local_time_pytz_GMT(self, util: Util):
306
+ data = {"a": UTC_DATETIMES}
307
+ table = Table(data)
308
+
309
+ os.environ["TZ"] = "GMT"
310
+ time.tzset()
311
+
312
+ # Should be in GMT now
313
+ assert table.view().to_columns() == {
314
+ "a": [
315
+ util.to_timestamp(d.astimezone(GMT).replace(tzinfo=None))
316
+ for d in data["a"]
317
+ ]
318
+ }
319
+
320
+ def test_table_should_convert_UTC_to_local_time_pytz_HKT(self, util: Util):
321
+ data = {"a": UTC_DATETIMES}
322
+ table = Table(data)
323
+
324
+ os.environ["TZ"] = "Asia/Hong_Kong"
325
+ time.tzset()
326
+
327
+ assert table.view().to_columns() == {
328
+ "a": [
329
+ util.to_timestamp(d.astimezone(HKT).replace(tzinfo=None))
330
+ for d in data["a"]
331
+ ]
332
+ }
333
+
334
+ def test_table_should_convert_UTC_to_local_time_pytz_JPT(self, util: Util):
335
+ data = {"a": UTC_DATETIMES}
336
+ table = Table(data)
337
+
338
+ os.environ["TZ"] = "Asia/Tokyo"
339
+ time.tzset()
340
+
341
+ assert table.view().to_columns() == {
342
+ "a": [
343
+ util.to_timestamp(d.astimezone(JPT).replace(tzinfo=None))
344
+ for d in data["a"]
345
+ ]
346
+ }
347
+
348
+ def test_table_should_convert_UTC_to_local_time_pytz_ACT(self, util: Util):
349
+ data = {"a": UTC_DATETIMES}
350
+ table = Table(data)
351
+
352
+ os.environ["TZ"] = "Australia/Sydney"
353
+ time.tzset()
354
+
355
+ assert table.view().to_columns() == {
356
+ "a": [
357
+ util.to_timestamp(d.astimezone(ACT).replace(tzinfo=None))
358
+ for d in data["a"]
359
+ ]
360
+ }
361
+
362
+ def test_table_should_convert_UTC_to_local_time_dateutil_pacific(
363
+ self, util: Util
364
+ ):
365
+ data = {"a": UTC_DATETIMES}
366
+ table = Table(data)
367
+
368
+ os.environ["TZ"] = "US/Pacific"
369
+ time.tzset()
370
+
371
+ # Should be in PST now
372
+ assert table.view().to_columns() == {
373
+ "a": [
374
+ util.to_timestamp(d.astimezone(PST).replace(tzinfo=None))
375
+ for d in data["a"]
376
+ ]
377
+ }
378
+
379
+ def test_table_should_convert_UTC_to_local_time_dateutil_central(
380
+ self, util: Util
381
+ ):
382
+ data = {"a": UTC_DATETIMES}
383
+ table = Table(data)
384
+
385
+ os.environ["TZ"] = "US/Central"
386
+ time.tzset()
387
+
388
+ # Should be in CST now
389
+ assert table.view().to_columns() == {
390
+ "a": [
391
+ util.to_timestamp(d.astimezone(CST).replace(tzinfo=None))
392
+ for d in data["a"]
393
+ ]
394
+ }
395
+
396
+ def test_table_should_convert_UTC_to_local_time_dateutil_eastern(
397
+ self, util: Util
398
+ ):
399
+ data = {"a": UTC_DATETIMES}
400
+ table = Table(data)
401
+
402
+ os.environ["TZ"] = "US/Eastern"
403
+ time.tzset()
404
+
405
+ # Should be in EST now
406
+ assert table.view().to_columns() == {
407
+ "a": [
408
+ util.to_timestamp(d.astimezone(EST).replace(tzinfo=None))
409
+ for d in data["a"]
410
+ ]
411
+ }
412
+
413
+ def test_table_should_convert_UTC_to_local_time_dateutil_GMT(self, util: Util):
414
+ data = {"a": UTC_DATETIMES}
415
+ table = Table(data)
416
+
417
+ os.environ["TZ"] = "GMT"
418
+ time.tzset()
419
+
420
+ # Should be in GMT now
421
+ assert table.view().to_columns() == {
422
+ "a": [
423
+ util.to_timestamp(d.astimezone(GMT).replace(tzinfo=None))
424
+ for d in data["a"]
425
+ ]
426
+ }
427
+
428
+ def test_table_should_convert_UTC_to_local_time_dateutil_pacific_DST(
429
+ self, util: Util
430
+ ):
431
+ data = {"a": UTC_DATETIMES_DST}
432
+ table = Table(data)
433
+
434
+ os.environ["TZ"] = "US/Pacific"
435
+ time.tzset()
436
+
437
+ # Should be in PST now
438
+ assert table.view().to_columns() == {
439
+ "a": [
440
+ util.to_timestamp(d.replace(tzinfo=None))
441
+ for d in TZ_DATETIMES_DST["US/Pacific"]
442
+ ]
443
+ }
444
+
445
+ def test_table_should_convert_UTC_to_local_time_dateutil_central_DST(
446
+ self, util: Util
447
+ ):
448
+ data = {"a": UTC_DATETIMES_DST}
449
+ table = Table(data)
450
+
451
+ os.environ["TZ"] = "US/Central"
452
+ time.tzset()
453
+
454
+ # Should be in CST now
455
+ assert table.view().to_columns() == {
456
+ "a": [
457
+ util.to_timestamp(d.replace(tzinfo=None))
458
+ for d in TZ_DATETIMES_DST["US/Central"]
459
+ ]
460
+ }
461
+
462
+ def test_table_should_convert_UTC_to_local_time_dateutil_eastern_DST(
463
+ self, util: Util
464
+ ):
465
+ data = {"a": UTC_DATETIMES_DST}
466
+ table = Table(data)
467
+
468
+ os.environ["TZ"] = "US/Eastern"
469
+ time.tzset()
470
+
471
+ # Should be in EST now
472
+ assert table.view().to_columns() == {
473
+ "a": [
474
+ util.to_timestamp(d.replace(tzinfo=None))
475
+ for d in TZ_DATETIMES_DST["US/Eastern"]
476
+ ]
477
+ }
478
+
479
+ def test_table_should_convert_UTC_to_local_time_dateutil_GMT_DST(
480
+ self, util: Util
481
+ ):
482
+ data = {"a": UTC_DATETIMES_DST}
483
+ table = Table(data)
484
+
485
+ os.environ["TZ"] = "GMT"
486
+ time.tzset()
487
+
488
+ # Should be in GMT now
489
+ assert table.view().to_columns() == {
490
+ "a": [
491
+ util.to_timestamp(d.replace(tzinfo=None))
492
+ for d in TZ_DATETIMES_DST["GMT"]
493
+ ]
494
+ }
495
+
496
+ def test_table_should_convert_UTC_to_local_time_dateutil_pacific_DST_timestamp(
497
+ self, util
498
+ ):
499
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS_DST})
500
+ table = Table(data)
501
+
502
+ os.environ["TZ"] = "US/Pacific"
503
+ time.tzset()
504
+
505
+ # Should be in PST now
506
+ assert table.view().to_columns()["a"] == [
507
+ util.to_timestamp(d.replace(tzinfo=None))
508
+ for d in TZ_DATETIMES_DST["US/Pacific"]
509
+ ]
510
+
511
+ def test_table_should_convert_UTC_to_local_time_dateutil_central_DST_timestamp(
512
+ self, util
513
+ ):
514
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS_DST})
515
+ table = Table(data)
516
+
517
+ os.environ["TZ"] = "US/Central"
518
+ time.tzset()
519
+
520
+ # Should be in CST now
521
+ assert table.view().to_columns()["a"] == [
522
+ util.to_timestamp(d.replace(tzinfo=None))
523
+ for d in TZ_DATETIMES_DST["US/Central"]
524
+ ]
525
+
526
+ def test_table_should_convert_UTC_to_local_time_dateutil_eastern_DST_timestamp(
527
+ self, util
528
+ ):
529
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS_DST})
530
+ table = Table(data)
531
+
532
+ os.environ["TZ"] = "US/Eastern"
533
+ time.tzset()
534
+
535
+ # Should be in EST now
536
+ assert table.view().to_columns()["a"] == [
537
+ util.to_timestamp(d.replace(tzinfo=None))
538
+ for d in TZ_DATETIMES_DST["US/Eastern"]
539
+ ]
540
+
541
+ def test_table_should_convert_UTC_to_local_time_dateutil_GMT_DST_timestamp(
542
+ self, util
543
+ ):
544
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS_DST})
545
+ table = Table(data)
546
+
547
+ os.environ["TZ"] = "GMT"
548
+ time.tzset()
549
+
550
+ # Should be in GMT now
551
+ assert table.view().to_columns()["a"] == [
552
+ util.to_timestamp(d.replace(tzinfo=None))
553
+ for d in TZ_DATETIMES_DST["GMT"]
554
+ ]
555
+
556
+ def test_table_should_convert_UTC_to_local_time_dateutil_HKT(self, util: Util):
557
+ data = {"a": UTC_DATETIMES}
558
+ table = Table(data)
559
+
560
+ os.environ["TZ"] = "Asia/Hong_Kong"
561
+ time.tzset()
562
+
563
+ assert table.view().to_columns() == {
564
+ "a": [
565
+ util.to_timestamp(d.astimezone(HKT).replace(tzinfo=None))
566
+ for d in data["a"]
567
+ ]
568
+ }
569
+
570
+ def test_table_should_convert_UTC_to_local_time_dateutil_JPT(self, util: Util):
571
+ data = {"a": UTC_DATETIMES}
572
+ table = Table(data)
573
+
574
+ os.environ["TZ"] = "Asia/Tokyo"
575
+ time.tzset()
576
+
577
+ assert table.view().to_columns() == {
578
+ "a": [
579
+ util.to_timestamp(d.astimezone(JPT).replace(tzinfo=None))
580
+ for d in data["a"]
581
+ ]
582
+ }
583
+
584
+ def test_table_should_convert_UTC_to_local_time_dateutil_ACT(self, util: Util):
585
+ data = {"a": UTC_DATETIMES}
586
+ table = Table(data)
587
+
588
+ os.environ["TZ"] = "Australia/Sydney"
589
+ time.tzset()
590
+
591
+ ACT = tz.gettz("Australia/Sydney")
592
+
593
+ assert table.view().to_columns() == {
594
+ "a": [
595
+ util.to_timestamp(d.astimezone(ACT).replace(tzinfo=None))
596
+ for d in data["a"]
597
+ ]
598
+ }
599
+
600
+ def test_table_should_convert_UTC_to_local_time_pytz_pacific_timestamp(
601
+ self, util
602
+ ):
603
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
604
+ table = Table(data)
605
+
606
+ os.environ["TZ"] = "US/Pacific"
607
+ time.tzset()
608
+
609
+ # Should be in PST now
610
+ assert table.view().to_columns()["a"] == [
611
+ util.to_timestamp(
612
+ d.astimezone(PST).replace(tzinfo=None).to_pydatetime()
613
+ )
614
+ for d in data["a"]
615
+ ]
616
+
617
+ def test_table_should_convert_UTC_to_local_time_pytz_central_timestamp(
618
+ self, util
619
+ ):
620
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
621
+ table = Table(data)
622
+
623
+ os.environ["TZ"] = "US/Central"
624
+ time.tzset()
625
+
626
+ # Should be in CST now
627
+ assert table.view().to_columns()["a"] == [
628
+ util.to_timestamp(
629
+ d.astimezone(CST).replace(tzinfo=None).to_pydatetime()
630
+ )
631
+ for d in data["a"]
632
+ ]
633
+
634
+ def test_table_should_convert_UTC_to_local_time_pytz_eastern_timestamp(
635
+ self, util
636
+ ):
637
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
638
+ table = Table(data)
639
+
640
+ os.environ["TZ"] = "US/Eastern"
641
+ time.tzset()
642
+
643
+ # Should be in EST now
644
+ assert table.view().to_columns()["a"] == [
645
+ util.to_timestamp(
646
+ d.astimezone(EST).replace(tzinfo=None).to_pydatetime()
647
+ )
648
+ for d in data["a"]
649
+ ]
650
+
651
+ def test_table_should_convert_UTC_to_local_time_pytz_GMT_timestamp(self, util):
652
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
653
+ table = Table(data)
654
+
655
+ os.environ["TZ"] = "GMT"
656
+ time.tzset()
657
+
658
+ # Should be in GMT now
659
+ assert table.view().to_columns()["a"] == [
660
+ util.to_timestamp(
661
+ d.astimezone(GMT).replace(tzinfo=None).to_pydatetime()
662
+ )
663
+ for d in data["a"]
664
+ ]
665
+
666
+ def test_table_should_convert_UTC_to_local_time_pytz_HKT_timestamp(self, util):
667
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
668
+ table = Table(data)
669
+
670
+ os.environ["TZ"] = "Asia/Hong_Kong"
671
+ time.tzset()
672
+
673
+ assert table.view().to_columns()["a"] == [
674
+ util.to_timestamp(
675
+ d.astimezone(HKT).replace(tzinfo=None).to_pydatetime()
676
+ )
677
+ for d in data["a"]
678
+ ]
679
+
680
+ def test_table_should_convert_UTC_to_local_time_pytz_JPT_timestamp(self, util):
681
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
682
+ table = Table(data)
683
+
684
+ os.environ["TZ"] = "Asia/Tokyo"
685
+ time.tzset()
686
+
687
+ assert table.view().to_columns()["a"] == [
688
+ util.to_timestamp(
689
+ d.astimezone(JPT).replace(tzinfo=None).to_pydatetime()
690
+ )
691
+ for d in data["a"]
692
+ ]
693
+
694
+ def test_table_should_convert_UTC_to_local_time_pytz_ACT_timestamp(self, util):
695
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
696
+ table = Table(data)
697
+
698
+ os.environ["TZ"] = "Australia/Sydney"
699
+ time.tzset()
700
+
701
+ assert table.view().to_columns()["a"] == [
702
+ util.to_timestamp(
703
+ d.astimezone(ACT).replace(tzinfo=None).to_pydatetime()
704
+ )
705
+ for d in data["a"]
706
+ ]
707
+
708
+ def test_table_should_convert_UTC_to_local_time_dateutil_pacific_timestamp(
709
+ self, util
710
+ ):
711
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
712
+ table = Table(data)
713
+
714
+ os.environ["TZ"] = "US/Pacific"
715
+ time.tzset()
716
+
717
+ # Should be in PST now
718
+ assert table.view().to_columns()["a"] == [
719
+ util.to_timestamp(
720
+ d.astimezone(PST).replace(tzinfo=None).to_pydatetime()
721
+ )
722
+ for d in data["a"]
723
+ ]
724
+
725
+ def test_table_should_convert_UTC_to_local_time_dateutil_central_timestamp(
726
+ self,
727
+ util,
728
+ ):
729
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
730
+ table = Table(data)
731
+
732
+ os.environ["TZ"] = "US/Central"
733
+ time.tzset()
734
+
735
+ CST = tz.gettz("US/Central")
736
+
737
+ # Should be in CST now
738
+ assert table.view().to_columns()["a"] == [
739
+ util.to_timestamp(
740
+ d.astimezone(CST).replace(tzinfo=None).to_pydatetime()
741
+ )
742
+ for d in data["a"]
743
+ ]
744
+
745
+ def test_table_should_convert_UTC_to_local_time_dateutil_eastern_timestamp(
746
+ self, util
747
+ ):
748
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
749
+ table = Table(data)
750
+
751
+ os.environ["TZ"] = "US/Eastern"
752
+ time.tzset()
753
+
754
+ # Should be in EST now
755
+ assert table.view().to_columns()["a"] == [
756
+ util.to_timestamp(
757
+ d.astimezone(EST).replace(tzinfo=None).to_pydatetime()
758
+ )
759
+ for d in data["a"]
760
+ ]
761
+
762
+ def test_table_should_convert_UTC_to_local_time_dateutil_GMT_timestamp(
763
+ self, util
764
+ ):
765
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
766
+ table = Table(data)
767
+
768
+ os.environ["TZ"] = "GMT"
769
+ time.tzset()
770
+
771
+ GMT = tz.gettz("GMT")
772
+
773
+ # Should be in GMT now
774
+ assert table.view().to_columns()["a"] == [
775
+ util.to_timestamp(
776
+ d.astimezone(GMT).replace(tzinfo=None).to_pydatetime()
777
+ )
778
+ for d in data["a"]
779
+ ]
780
+
781
+ def test_table_should_convert_UTC_to_local_time_dateutil_HKT_timestamp(
782
+ self, util
783
+ ):
784
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
785
+ table = Table(data)
786
+
787
+ os.environ["TZ"] = "Asia/Hong_Kong"
788
+ time.tzset()
789
+
790
+ assert table.view().to_columns()["a"] == [
791
+ util.to_timestamp(
792
+ d.astimezone(HKT).replace(tzinfo=None).to_pydatetime()
793
+ )
794
+ for d in data["a"]
795
+ ]
796
+
797
+ def test_table_should_convert_UTC_to_local_time_dateutil_JPT_timestamp(
798
+ self, util
799
+ ):
800
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
801
+ table = Table(data)
802
+
803
+ os.environ["TZ"] = "Asia/Tokyo"
804
+ time.tzset()
805
+
806
+ assert table.view().to_columns()["a"] == [
807
+ util.to_timestamp(
808
+ d.astimezone(JPT).replace(tzinfo=None).to_pydatetime()
809
+ )
810
+ for d in data["a"]
811
+ ]
812
+
813
+ def test_table_should_convert_UTC_to_local_time_dateutil_ACT_timestamp(
814
+ self, util
815
+ ):
816
+ data = pd.DataFrame({"a": UTC_TIMESTAMPS})
817
+ table = Table(data)
818
+
819
+ os.environ["TZ"] = "Australia/Sydney"
820
+ time.tzset()
821
+
822
+ assert table.view().to_columns()["a"] == [
823
+ util.to_timestamp(
824
+ d.astimezone(ACT).replace(tzinfo=None).to_pydatetime()
825
+ )
826
+ for d in data["a"]
827
+ ]
828
+
829
+ class TestTableDateTimeArbitaryToLocal(object):
830
+ def teardown_method(self):
831
+ # Set timezone to UTC, always
832
+ os.environ["TZ"] = "UTC"
833
+ time.tzset()
834
+
835
+ def test_table_should_convert_PST_to_local_time_pytz_central(self, util: Util):
836
+ data = {"a": TZ_DATETIMES["US/Pacific"]}
837
+ table = Table(data)
838
+
839
+ os.environ["TZ"] = "US/Central"
840
+ time.tzset()
841
+
842
+ # Should be in CST now
843
+ assert table.view().to_columns() == {
844
+ "a": [
845
+ util.to_timestamp(d.astimezone(CST).replace(tzinfo=None))
846
+ for d in data["a"]
847
+ ]
848
+ }
849
+
850
+ def test_table_should_convert_CST_to_local_time_pytz_eastern(self, util: Util):
851
+ data = {"a": TZ_DATETIMES["US/Central"]}
852
+ table = Table(data)
853
+
854
+ os.environ["TZ"] = "US/Eastern"
855
+ time.tzset()
856
+
857
+ # Should be in EST now
858
+ assert table.view().to_columns() == {
859
+ "a": [
860
+ util.to_timestamp(d.astimezone(EST).replace(tzinfo=None))
861
+ for d in data["a"]
862
+ ]
863
+ }
864
+
865
+ def test_table_should_convert_EST_to_local_time_pytz_GMT(self, util: Util):
866
+ data = {"a": TZ_DATETIMES["US/Eastern"]}
867
+ table = Table(data)
868
+
869
+ os.environ["TZ"] = "GMT"
870
+ time.tzset()
871
+
872
+ # Should be in GMT now
873
+ assert table.view().to_columns() == {
874
+ "a": [
875
+ util.to_timestamp(d.astimezone(GMT).replace(tzinfo=None))
876
+ for d in data["a"]
877
+ ]
878
+ }
879
+
880
+ def test_table_should_convert_GMT_to_local_time_pytz_HKT(self, util: Util):
881
+ data = {"a": TZ_DATETIMES["GMT"]}
882
+ table = Table(data)
883
+
884
+ os.environ["TZ"] = "Asia/Hong_Kong"
885
+ time.tzset()
886
+
887
+ assert table.view().to_columns() == {
888
+ "a": [
889
+ util.to_timestamp(d.astimezone(HKT).replace(tzinfo=None))
890
+ for d in data["a"]
891
+ ]
892
+ }
893
+
894
+ def test_table_should_convert_HKT_to_local_time_pytz_JPT(self, util: Util):
895
+ data = {"a": TZ_DATETIMES["Asia/Hong_Kong"]}
896
+ table = Table(data)
897
+
898
+ os.environ["TZ"] = "Asia/Tokyo"
899
+ time.tzset()
900
+
901
+ assert table.view().to_columns() == {
902
+ "a": [
903
+ util.to_timestamp(d.astimezone(JPT).replace(tzinfo=None))
904
+ for d in data["a"]
905
+ ]
906
+ }
907
+
908
+ def test_table_should_convert_JPT_to_local_time_pytz_ACT(self, util: Util):
909
+ data = {"a": TZ_DATETIMES["Asia/Tokyo"]}
910
+ table = Table(data)
911
+
912
+ os.environ["TZ"] = "Australia/Sydney"
913
+ time.tzset()
914
+
915
+ assert table.view().to_columns() == {
916
+ "a": [
917
+ util.to_timestamp(d.astimezone(ACT).replace(tzinfo=None))
918
+ for d in data["a"]
919
+ ]
920
+ }
921
+
922
+ def test_table_should_convert_PST_to_local_time_dateutil_central(
923
+ self, util: Util
924
+ ):
925
+ data = {"a": TZ_DATETIMES["US/Pacific"]}
926
+ table = Table(data)
927
+
928
+ os.environ["TZ"] = "US/Central"
929
+ time.tzset()
930
+
931
+ # Should be in CST now
932
+ assert table.view().to_columns() == {
933
+ "a": [
934
+ util.to_timestamp(d.astimezone(CST).replace(tzinfo=None))
935
+ for d in data["a"]
936
+ ]
937
+ }
938
+
939
+ def test_table_should_convert_CST_to_local_time_dateutil_eastern(
940
+ self, util: Util
941
+ ):
942
+ data = {"a": TZ_DATETIMES["US/Central"]}
943
+ table = Table(data)
944
+
945
+ os.environ["TZ"] = "US/Eastern"
946
+ time.tzset()
947
+
948
+ # Should be in EST now
949
+ assert table.view().to_columns() == {
950
+ "a": [
951
+ util.to_timestamp(d.astimezone(EST).replace(tzinfo=None))
952
+ for d in data["a"]
953
+ ]
954
+ }
955
+
956
+ def test_table_should_convert_EST_to_local_time_dateutil_GMT(self, util: Util):
957
+ data = {"a": TZ_DATETIMES["US/Eastern"]}
958
+ table = Table(data)
959
+
960
+ os.environ["TZ"] = "GMT"
961
+ time.tzset()
962
+
963
+ # Should be in GMT now
964
+ assert table.view().to_columns() == {
965
+ "a": [
966
+ util.to_timestamp(d.astimezone(GMT).replace(tzinfo=None))
967
+ for d in data["a"]
968
+ ]
969
+ }
970
+
971
+ def test_table_should_convert_GMT_to_local_time_dateutil_HKT(self, util: Util):
972
+ data = {"a": TZ_DATETIMES["GMT"]}
973
+ table = Table(data)
974
+
975
+ os.environ["TZ"] = "Asia/Hong_Kong"
976
+ time.tzset()
977
+
978
+ assert table.view().to_columns() == {
979
+ "a": [
980
+ util.to_timestamp(d.astimezone(HKT).replace(tzinfo=None))
981
+ for d in data["a"]
982
+ ]
983
+ }
984
+
985
+ def test_table_should_convert_HKT_to_local_time_dateutil_JPT(self, util: Util):
986
+ data = {"a": TZ_DATETIMES["Asia/Hong_Kong"]}
987
+ table = Table(data)
988
+
989
+ os.environ["TZ"] = "Asia/Tokyo"
990
+ time.tzset()
991
+
992
+ assert table.view().to_columns() == {
993
+ "a": [
994
+ util.to_timestamp(d.astimezone(JPT).replace(tzinfo=None))
995
+ for d in data["a"]
996
+ ]
997
+ }
998
+
999
+ def test_table_should_convert_JPT_to_local_time_dateutil_ACT(self, util: Util):
1000
+ data = {"a": TZ_DATETIMES["Asia/Tokyo"]}
1001
+ table = Table(data)
1002
+
1003
+ os.environ["TZ"] = "Australia/Sydney"
1004
+ time.tzset()
1005
+
1006
+ assert table.view().to_columns() == {
1007
+ "a": [
1008
+ util.to_timestamp(d.astimezone(ACT).replace(tzinfo=None))
1009
+ for d in data["a"]
1010
+ ]
1011
+ }
1012
+
1013
+ def test_table_should_convert_PST_to_local_time_pytz_central_timestamp(
1014
+ self, util: Util
1015
+ ):
1016
+ data = {"a": TZ_TIMESTAMPS["US/Pacific"]}
1017
+ table = Table(pd.DataFrame(data))
1018
+
1019
+ os.environ["TZ"] = "US/Central"
1020
+ time.tzset()
1021
+
1022
+ # Should be in CST now
1023
+ assert table.view().to_columns()["a"] == [
1024
+ util.to_timestamp(
1025
+ d.astimezone(CST).replace(tzinfo=None).to_pydatetime()
1026
+ )
1027
+ for d in data["a"]
1028
+ ]
1029
+
1030
+ def test_table_should_convert_CST_to_local_time_pytz_eastern_timestamp(
1031
+ self, util
1032
+ ):
1033
+ data = {"a": TZ_TIMESTAMPS["US/Central"]}
1034
+ table = Table(pd.DataFrame(data))
1035
+
1036
+ os.environ["TZ"] = "US/Eastern"
1037
+ time.tzset()
1038
+
1039
+ # Should be in EST now
1040
+ assert table.view().to_columns()["a"] == [
1041
+ util.to_timestamp(
1042
+ d.astimezone(EST).replace(tzinfo=None).to_pydatetime()
1043
+ )
1044
+ for d in data["a"]
1045
+ ]
1046
+
1047
+ def test_table_should_convert_EST_to_local_time_pytz_GMT_timestamp(self, util):
1048
+ data = {"a": TZ_TIMESTAMPS["US/Eastern"]}
1049
+ table = Table(pd.DataFrame(data))
1050
+
1051
+ os.environ["TZ"] = "GMT"
1052
+ time.tzset()
1053
+
1054
+ # Should be in GMT now
1055
+ assert table.view().to_columns()["a"] == [
1056
+ util.to_timestamp(
1057
+ d.astimezone(GMT).replace(tzinfo=None).to_pydatetime()
1058
+ )
1059
+ for d in data["a"]
1060
+ ]
1061
+
1062
+ def test_table_should_convert_GMT_to_local_time_pytz_HKT_timestamp(self, util):
1063
+ data = {"a": TZ_TIMESTAMPS["GMT"]}
1064
+ table = Table(pd.DataFrame(data))
1065
+
1066
+ os.environ["TZ"] = "Asia/Hong_Kong"
1067
+ time.tzset()
1068
+
1069
+ assert table.view().to_columns()["a"] == [
1070
+ util.to_timestamp(
1071
+ d.astimezone(HKT).replace(tzinfo=None).to_pydatetime()
1072
+ )
1073
+ for d in data["a"]
1074
+ ]
1075
+
1076
+ def test_table_should_convert_HKT_to_local_time_pytz_JPT_timestamp(self, util):
1077
+ data = {"a": TZ_TIMESTAMPS["Asia/Hong_Kong"]}
1078
+ table = Table(pd.DataFrame(data))
1079
+
1080
+ os.environ["TZ"] = "Asia/Tokyo"
1081
+ time.tzset()
1082
+
1083
+ assert table.view().to_columns()["a"] == [
1084
+ util.to_timestamp(
1085
+ d.astimezone(JPT).replace(tzinfo=None).to_pydatetime()
1086
+ )
1087
+ for d in data["a"]
1088
+ ]
1089
+
1090
+ def test_table_should_convert_JPT_to_local_time_pytz_ACT_timestamp(self, util):
1091
+ data = {"a": TZ_TIMESTAMPS["Asia/Tokyo"]}
1092
+ table = Table(pd.DataFrame(data))
1093
+
1094
+ os.environ["TZ"] = "Australia/Sydney"
1095
+ time.tzset()
1096
+
1097
+ assert table.view().to_columns()["a"] == [
1098
+ util.to_timestamp(
1099
+ d.astimezone(ACT).replace(tzinfo=None).to_pydatetime()
1100
+ )
1101
+ for d in data["a"]
1102
+ ]
1103
+
1104
+ def test_table_should_convert_PST_to_local_time_dateutil_central_timestamp(
1105
+ self, util
1106
+ ):
1107
+ data = {"a": TZ_TIMESTAMPS["US/Pacific"]}
1108
+ table = Table(pd.DataFrame(data))
1109
+
1110
+ os.environ["TZ"] = "US/Central"
1111
+ time.tzset()
1112
+
1113
+ # Should be in CST now
1114
+ assert table.view().to_columns()["a"] == [
1115
+ util.to_timestamp(
1116
+ d.astimezone(CST).replace(tzinfo=None).to_pydatetime()
1117
+ )
1118
+ for d in data["a"]
1119
+ ]
1120
+
1121
+ def test_table_should_convert_CST_to_local_time_dateutil_eastern_timestamp(
1122
+ self, util
1123
+ ):
1124
+ data = {"a": TZ_TIMESTAMPS["US/Central"]}
1125
+ table = Table(pd.DataFrame(data))
1126
+
1127
+ os.environ["TZ"] = "US/Eastern"
1128
+ time.tzset()
1129
+
1130
+ # Should be in EST now
1131
+ assert table.view().to_columns()["a"] == [
1132
+ util.to_timestamp(
1133
+ d.astimezone(EST).replace(tzinfo=None).to_pydatetime()
1134
+ )
1135
+ for d in data["a"]
1136
+ ]
1137
+
1138
+ def test_table_should_convert_EST_to_local_time_dateutil_GMT_timestamp(
1139
+ self, util
1140
+ ):
1141
+ data = {"a": TZ_TIMESTAMPS["US/Eastern"]}
1142
+ table = Table(pd.DataFrame(data))
1143
+
1144
+ os.environ["TZ"] = "GMT"
1145
+ time.tzset()
1146
+
1147
+ # Should be in GMT now
1148
+ assert table.view().to_columns()["a"] == [
1149
+ util.to_timestamp(
1150
+ d.astimezone(GMT).replace(tzinfo=None).to_pydatetime()
1151
+ )
1152
+ for d in data["a"]
1153
+ ]
1154
+
1155
+ def test_table_should_convert_GMT_to_local_time_dateutil_HKT_timestamp(
1156
+ self, util
1157
+ ):
1158
+ data = {"a": TZ_TIMESTAMPS["GMT"]}
1159
+ table = Table(pd.DataFrame(data))
1160
+
1161
+ os.environ["TZ"] = "Asia/Hong_Kong"
1162
+ time.tzset()
1163
+
1164
+ assert table.view().to_columns()["a"] == [
1165
+ util.to_timestamp(
1166
+ d.astimezone(HKT).replace(tzinfo=None).to_pydatetime()
1167
+ )
1168
+ for d in data["a"]
1169
+ ]
1170
+
1171
+ def test_table_should_convert_HKT_to_local_time_dateutil_JPT_timestamp(
1172
+ self, util
1173
+ ):
1174
+ data = {"a": TZ_TIMESTAMPS["Asia/Hong_Kong"]}
1175
+ table = Table(pd.DataFrame(data))
1176
+
1177
+ os.environ["TZ"] = "Asia/Tokyo"
1178
+ time.tzset()
1179
+
1180
+ assert table.view().to_columns()["a"] == [
1181
+ util.to_timestamp(
1182
+ d.astimezone(JPT).replace(tzinfo=None).to_pydatetime()
1183
+ )
1184
+ for d in data["a"]
1185
+ ]
1186
+
1187
+ def test_table_should_convert_JPT_to_local_time_dateutil_ACT_timestamp(
1188
+ self, util
1189
+ ):
1190
+ data = {"a": TZ_TIMESTAMPS["Asia/Tokyo"]}
1191
+ table = Table(pd.DataFrame(data))
1192
+
1193
+ os.environ["TZ"] = "Australia/Sydney"
1194
+ time.tzset()
1195
+
1196
+ assert table.view().to_columns()["a"] == [
1197
+ util.to_timestamp(
1198
+ d.astimezone(ACT).replace(tzinfo=None).to_pydatetime()
1199
+ )
1200
+ for d in data["a"]
1201
+ ]
1202
+
1203
+ class TestTableDateTimeRowColumnPaths(object):
1204
+ """Assert correctness of row and column paths in different timezones."""
1205
+
1206
+ def setup_method(self):
1207
+ # To make sure that local times are not changed, set timezone to EST
1208
+ os.environ["TZ"] = "US/Eastern"
1209
+ time.tzset()
1210
+
1211
+ def teardown_method(self):
1212
+ # Set timezone to UTC, always
1213
+ os.environ["TZ"] = "UTC"
1214
+ time.tzset()
1215
+
1216
+ @mark.skip(reason="Unsupported non-UTC timezone")
1217
+ def test_table_group_by_datetime_row_path_local_time_EST(self, util: Util):
1218
+ """Make sure that string datetimes generated in Python are in
1219
+ local time and not UTC."""
1220
+ data = {"a": LOCAL_DATETIMES, "b": [i for i in range(len(LOCAL_DATETIMES))]}
1221
+
1222
+ table = Table(data)
1223
+
1224
+ view = table.view(group_by=["a"])
1225
+ assert view.to_columns() == {
1226
+ "__ROW_PATH__": [
1227
+ [],
1228
+ [util.to_timestamp(datetime(2019, 1, 11, 0, 10, 20))],
1229
+ [util.to_timestamp(datetime(2019, 1, 11, 11, 10, 20))],
1230
+ [util.to_timestamp(datetime(2019, 1, 11, 19, 10, 20))],
1231
+ ],
1232
+ "a": [3, 1, 1, 1],
1233
+ "b": [3, 0, 1, 2],
1234
+ }
1235
+
1236
+ def test_table_group_by_datetime_row_path_UTC(self, util: Util):
1237
+ """Make sure that string datetimes generated in Python are in
1238
+ UTC if the timezone is UTC.
1239
+
1240
+ Set the timezone before creating the table so that the local
1241
+ datetime is in the intended timezone, as this test asserts that
1242
+ paths in the same timezone are not edited to UTC."""
1243
+ os.environ["TZ"] = "UTC"
1244
+ time.tzset()
1245
+
1246
+ data = {"a": LOCAL_DATETIMES, "b": [i for i in range(len(LOCAL_DATETIMES))]}
1247
+
1248
+ table = Table(data)
1249
+
1250
+ view = table.view(group_by=["a"])
1251
+ assert view.to_columns() == {
1252
+ "__ROW_PATH__": [
1253
+ [],
1254
+ [util.to_timestamp(datetime(2019, 1, 11, 0, 10, 20))],
1255
+ [util.to_timestamp(datetime(2019, 1, 11, 11, 10, 20))],
1256
+ [util.to_timestamp(datetime(2019, 1, 11, 19, 10, 20))],
1257
+ ],
1258
+ "a": [3, 1, 1, 1],
1259
+ "b": [3, 0, 1, 2],
1260
+ }
1261
+
1262
+ @mark.skip(reason="Unsupported non-UTC timezone")
1263
+ def test_table_group_by_datetime_row_path_CST(self):
1264
+ """Make sure that string datetimes generated in Python are in
1265
+ CST if the timezone is CST."""
1266
+ os.environ["TZ"] = "US/Central"
1267
+ time.tzset()
1268
+
1269
+ data = {"a": LOCAL_DATETIMES, "b": [i for i in range(len(LOCAL_DATETIMES))]}
1270
+
1271
+ table = Table(data)
1272
+
1273
+ view = table.view(group_by=["a"])
1274
+ assert view.to_columns() == {
1275
+ "__ROW_PATH__": [
1276
+ [],
1277
+ [datetime(2019, 1, 11, 0, 10, 20)],
1278
+ [datetime(2019, 1, 11, 11, 10, 20)],
1279
+ [datetime(2019, 1, 11, 19, 10, 20)],
1280
+ ],
1281
+ "a": [3, 1, 1, 1],
1282
+ "b": [3, 0, 1, 2],
1283
+ }
1284
+
1285
+ @mark.skip(reason="Unsupported non-UTC timezone")
1286
+ def test_table_group_by_datetime_row_path_PST(self):
1287
+ """Make sure that string datetimes generated in Python are in
1288
+ CST if the timezone is CST."""
1289
+ os.environ["TZ"] = "US/Pacific"
1290
+ time.tzset()
1291
+
1292
+ data = {"a": LOCAL_DATETIMES, "b": [i for i in range(len(LOCAL_DATETIMES))]}
1293
+
1294
+ table = Table(data)
1295
+
1296
+ view = table.view(group_by=["a"])
1297
+ assert view.to_columns() == {
1298
+ "__ROW_PATH__": [
1299
+ [],
1300
+ [datetime(2019, 1, 11, 0, 10, 20)],
1301
+ [datetime(2019, 1, 11, 11, 10, 20)],
1302
+ [datetime(2019, 1, 11, 19, 10, 20)],
1303
+ ],
1304
+ "a": [3, 1, 1, 1],
1305
+ "b": [3, 0, 1, 2],
1306
+ }
1307
+
1308
+ class TestTableDateTimeExpressions(object):
1309
+ """Assert correctness of datetime-related expressions in
1310
+ different timezones."""
1311
+
1312
+ def setup_method(self):
1313
+ # To make sure that local times are not changed, set timezone to EST
1314
+ os.environ["TZ"] = "US/Eastern"
1315
+ time.tzset()
1316
+
1317
+ def teardown_method(self):
1318
+ # Set timezone to UTC, always
1319
+ os.environ["TZ"] = "UTC"
1320
+ time.tzset()
1321
+
1322
+ @mark.skip(reason="Unsupported non-UTC timezone")
1323
+ def test_table_now_in_EST(self, util):
1324
+ data = {"a": LOCAL_DATETIMES}
1325
+
1326
+ table = Table(data)
1327
+ now = datetime.now()
1328
+ view = table.view(expressions=["now()"])
1329
+ result = view.to_columns()
1330
+
1331
+ for item in result["now()"]:
1332
+ in_range = (
1333
+ now - timedelta(seconds=2) < item < now + timedelta(seconds=2)
1334
+ )
1335
+ assert in_range is True
1336
+
1337
+ @mark.skip(reason="Unsupported non-UTC timezone")
1338
+ def test_table_now_in_CST(self, util):
1339
+ os.environ["TZ"] = "US/Central"
1340
+ time.tzset()
1341
+
1342
+ data = {"a": LOCAL_DATETIMES}
1343
+
1344
+ table = Table(data)
1345
+ now = datetime.now()
1346
+ view = table.view(expressions=["now()"])
1347
+ result = view.to_columns()
1348
+
1349
+ for item in result["now()"]:
1350
+ in_range = (
1351
+ now - timedelta(seconds=2) < item < now + timedelta(seconds=2)
1352
+ )
1353
+ assert in_range is True
1354
+
1355
+ @mark.skip(reason="Unsupported non-UTC timezone")
1356
+ def test_table_now_in_PST(self, util):
1357
+ os.environ["TZ"] = "US/Pacific"
1358
+ time.tzset()
1359
+
1360
+ data = {"a": LOCAL_DATETIMES}
1361
+
1362
+ table = Table(data)
1363
+ now = datetime.now()
1364
+ view = table.view(expressions=["now()"])
1365
+ result = view.to_columns()
1366
+
1367
+ for item in result["now()"]:
1368
+ in_range = (
1369
+ now - timedelta(seconds=2) < item < now + timedelta(seconds=2)
1370
+ )
1371
+ assert in_range is True
1372
+
1373
+ @mark.skip(reason="Unsupported non-UTC timezone")
1374
+ def test_table_hour_of_day_in_EST(self):
1375
+ data = {"a": LOCAL_DATETIMES}
1376
+
1377
+ table = Table(data)
1378
+ view = table.view(expressions=['hour_of_day("a")'])
1379
+ result = view.to_columns()
1380
+ assert result['hour_of_day("a")'] == [0, 11, 19]
1381
+
1382
+ @mark.skip(reason="Unsupported non-UTC timezone")
1383
+ def test_table_hour_of_day_in_CST(self):
1384
+ os.environ["TZ"] = "US/Central"
1385
+ time.tzset()
1386
+
1387
+ data = {"a": LOCAL_DATETIMES}
1388
+
1389
+ table = Table(data)
1390
+ view = table.view(expressions=['hour_of_day("a")'])
1391
+ result = view.to_columns()
1392
+ assert result['hour_of_day("a")'] == [0, 11, 19]
1393
+
1394
+ @mark.skip(reason="Unsupported non-UTC timezone")
1395
+ def test_table_hour_of_day_in_PST(self):
1396
+ os.environ["TZ"] = "US/Pacific"
1397
+ time.tzset()
1398
+
1399
+ data = {"a": LOCAL_DATETIMES}
1400
+
1401
+ table = Table(data)
1402
+ view = table.view(expressions=['hour_of_day("a")'])
1403
+ result = view.to_columns()
1404
+ assert result['hour_of_day("a")'] == [0, 11, 19]
1405
+
1406
+ def test_table_day_of_week_edge_in_EST(self):
1407
+ """Make sure edge cases are fixed for day of week - if a local
1408
+ time converted to UTC is in the next day, the day of week
1409
+ computation needs to be in local time."""
1410
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1411
+
1412
+ table = Table(data)
1413
+ view = table.view(expressions=['day_of_week("a")'])
1414
+ result = view.to_columns()
1415
+ assert result['day_of_week("a")'] == ["6 Friday"]
1416
+
1417
+ def test_table_day_of_week_edge_in_CST(self):
1418
+ os.environ["TZ"] = "US/Central"
1419
+ time.tzset()
1420
+
1421
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1422
+
1423
+ table = Table(data)
1424
+ view = table.view(expressions=['day_of_week("a")'])
1425
+ result = view.to_columns()
1426
+ assert result['day_of_week("a")'] == ["6 Friday"]
1427
+
1428
+ def test_table_day_of_week_edge_in_PST(self):
1429
+ os.environ["TZ"] = "US/Pacific"
1430
+ time.tzset()
1431
+
1432
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1433
+
1434
+ table = Table(data)
1435
+ view = table.view(expressions=['day_of_week("a")'])
1436
+ result = view.to_columns()
1437
+ assert result['day_of_week("a")'] == ["6 Friday"]
1438
+
1439
+ def test_table_month_of_year_edge_in_EST(self):
1440
+ """Make sure edge cases are fixed for month of year - if a local
1441
+ time converted to UTC is in the next month, the month of year
1442
+ computation needs to be in local time."""
1443
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1444
+
1445
+ table = Table(data)
1446
+ view = table.view(expressions=['month_of_year("a")'])
1447
+ result = view.to_columns()
1448
+ assert result['month_of_year("a")'] == ["01 January"]
1449
+
1450
+ def test_table_month_of_year_edge_in_CST(self):
1451
+ os.environ["TZ"] = "US/Central"
1452
+ time.tzset()
1453
+
1454
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1455
+
1456
+ table = Table(data)
1457
+ view = table.view(expressions=['month_of_year("a")'])
1458
+ result = view.to_columns()
1459
+ assert result['month_of_year("a")'] == ["01 January"]
1460
+
1461
+ def test_table_month_of_year_edge_in_PST(self):
1462
+ os.environ["TZ"] = "US/Pacific"
1463
+ time.tzset()
1464
+
1465
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1466
+
1467
+ table = Table(data)
1468
+ view = table.view(expressions=['month_of_year("a")'])
1469
+ result = view.to_columns()
1470
+ assert result['month_of_year("a")'] == ["01 January"]
1471
+
1472
+ def test_table_day_bucket_edge_in_EST(self, util):
1473
+ """Make sure edge cases are fixed for day_bucket - if a local
1474
+ time converted to UTC is in the next day, the day_bucket
1475
+ computation needs to be in local time."""
1476
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1477
+
1478
+ table = Table(data)
1479
+ view = table.view(expressions=["bucket(\"a\", 'D')"])
1480
+ result = view.to_columns()
1481
+ assert result["bucket(\"a\", 'D')"] == [
1482
+ util.to_timestamp(datetime(2020, 1, 31))
1483
+ ]
1484
+
1485
+ def test_table_day_bucket_edge_in_CST(self, util):
1486
+ os.environ["TZ"] = "US/Central"
1487
+ time.tzset()
1488
+
1489
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1490
+
1491
+ table = Table(data)
1492
+ view = table.view(expressions=["bucket(\"a\", 'D')"])
1493
+ result = view.to_columns()
1494
+ assert result["bucket(\"a\", 'D')"] == [
1495
+ util.to_timestamp(datetime(2020, 1, 31))
1496
+ ]
1497
+
1498
+ def test_table_day_bucket_edge_in_PST(self, util):
1499
+ os.environ["TZ"] = "US/Pacific"
1500
+ time.tzset()
1501
+
1502
+ data = {"a": [datetime(2020, 1, 31, 23, 59)]}
1503
+
1504
+ table = Table(data)
1505
+ view = table.view(expressions=["bucket(\"a\", 'D')"])
1506
+ result = view.to_columns()
1507
+ assert result["bucket(\"a\", 'D')"] == [
1508
+ util.to_timestamp(datetime(2020, 1, 31))
1509
+ ]
1510
+
1511
+ def test_table_week_bucket_edge_in_EST(self, util):
1512
+ """Make sure edge cases are fixed for week_bucket - if a local
1513
+ time converted to UTC is in the next day, the week_bucket
1514
+ computation needs to be in local time."""
1515
+ data = {"a": [datetime(2020, 2, 2, 23, 59)]}
1516
+
1517
+ table = Table(data)
1518
+ view = table.view(expressions=["bucket(\"a\", 'W')"])
1519
+ result = view.to_columns()
1520
+ assert result["bucket(\"a\", 'W')"] == [
1521
+ util.to_timestamp(datetime(2020, 1, 27))
1522
+ ]
1523
+
1524
+ def test_table_week_bucket_edge_in_CST(self, util):
1525
+ os.environ["TZ"] = "US/Central"
1526
+ time.tzset()
1527
+
1528
+ data = {"a": [datetime(2020, 2, 2, 23, 59)]}
1529
+
1530
+ table = Table(data)
1531
+ view = table.view(expressions=["bucket(\"a\", 'W')"])
1532
+ result = view.to_columns()
1533
+ assert result["bucket(\"a\", 'W')"] == [
1534
+ util.to_timestamp(datetime(2020, 1, 27))
1535
+ ]
1536
+
1537
+ def test_table_week_bucket_edge_in_PST(self, util):
1538
+ os.environ["TZ"] = "US/Pacific"
1539
+ time.tzset()
1540
+
1541
+ data = {"a": [datetime(2020, 2, 2, 23, 59)]}
1542
+
1543
+ table = Table(data)
1544
+ view = table.view(expressions=["bucket(\"a\", 'W')"])
1545
+ result = view.to_columns()
1546
+ assert result["bucket(\"a\", 'W')"] == [
1547
+ util.to_timestamp(datetime(2020, 1, 27))
1548
+ ]
1549
+
1550
+ def test_table_week_bucket_edge_flip_in_EST(self, util):
1551
+ """Week bucket should flip backwards to last month."""
1552
+ data = {"a": [datetime(2020, 3, 1, 12, 59)]}
1553
+
1554
+ table = Table(data)
1555
+ view = table.view(expressions=["bucket(\"a\", 'W')"])
1556
+ result = view.to_columns()
1557
+ assert result["bucket(\"a\", 'W')"] == [
1558
+ util.to_timestamp(datetime(2020, 2, 24))
1559
+ ]
1560
+
1561
+ def test_table_week_bucket_edge_flip_in_CST(self, util):
1562
+ os.environ["TZ"] = "US/Central"
1563
+ time.tzset()
1564
+ data = {"a": [datetime(2020, 3, 1, 12, 59)]}
1565
+
1566
+ table = Table(data)
1567
+ view = table.view(expressions=["bucket(\"a\", 'W')"])
1568
+ result = view.to_columns()
1569
+ assert result["bucket(\"a\", 'W')"] == [
1570
+ util.to_timestamp(datetime(2020, 2, 24))
1571
+ ]
1572
+
1573
+ def test_table_week_bucket_edge_flip_in_PST(self, util):
1574
+ os.environ["TZ"] = "US/Pacific"
1575
+ time.tzset()
1576
+ data = {"a": [datetime(2020, 3, 1, 12, 59)]}
1577
+
1578
+ table = Table(data)
1579
+ view = table.view(expressions=["bucket(\"a\", 'W')"])
1580
+ result = view.to_columns()
1581
+ assert result["bucket(\"a\", 'W')"] == [
1582
+ util.to_timestamp(datetime(2020, 2, 24))
1583
+ ]
1584
+
1585
+ def test_table_month_bucket_edge_in_EST(self, util):
1586
+ """Make sure edge cases are fixed for month_bucket - if a local
1587
+ time converted to UTC is in the next day, the month_bucket
1588
+ computation needs to be in local time."""
1589
+ data = {"a": [datetime(2020, 6, 30, 23, 59)]}
1590
+
1591
+ table = Table(data)
1592
+ view = table.view(expressions=["bucket(\"a\", 'M')"])
1593
+ result = view.to_columns()
1594
+ assert result["bucket(\"a\", 'M')"] == [
1595
+ util.to_timestamp(datetime(2020, 6, 1))
1596
+ ]
1597
+
1598
+ def test_table_month_bucket_edge_in_CST(self, util):
1599
+ os.environ["TZ"] = "US/Central"
1600
+ time.tzset()
1601
+
1602
+ data = {"a": [datetime(2020, 6, 30, 23, 59)]}
1603
+
1604
+ table = Table(data)
1605
+ view = table.view(expressions=["bucket(\"a\", 'M')"])
1606
+ result = view.to_columns()
1607
+ assert result["bucket(\"a\", 'M')"] == [
1608
+ util.to_timestamp(datetime(2020, 6, 1))
1609
+ ]
1610
+
1611
+ def test_table_month_bucket_edge_in_PST(self, util):
1612
+ os.environ["TZ"] = "US/Pacific"
1613
+ time.tzset()
1614
+
1615
+ data = {"a": [datetime(2020, 6, 30, 23, 59)]}
1616
+
1617
+ table = Table(data)
1618
+ view = table.view(expressions=["bucket(\"a\", 'M')"])
1619
+ result = view.to_columns()
1620
+ assert result["bucket(\"a\", 'M')"] == [
1621
+ util.to_timestamp(datetime(2020, 6, 1))
1622
+ ]
1623
+
1624
+ def test_table_year_bucket_edge_in_EST(self, util):
1625
+ """Make sure edge cases are fixed for year_bucket - if a local
1626
+ time converted to UTC is in the next day, the year_bucket
1627
+ computation needs to be in local time."""
1628
+ data = {"a": [datetime(2019, 12, 31, 23, 59)]}
1629
+
1630
+ table = Table(data)
1631
+ view = table.view(expressions=["bucket(\"a\", 'Y')"])
1632
+ result = view.to_columns()
1633
+ assert result["bucket(\"a\", 'Y')"] == [
1634
+ util.to_timestamp(datetime(2019, 1, 1))
1635
+ ]
1636
+
1637
+ def test_table_year_bucket_edge_in_CST(self, util):
1638
+ os.environ["TZ"] = "US/Central"
1639
+ time.tzset()
1640
+ data = {"a": [datetime(2019, 12, 31, 23, 59)]}
1641
+
1642
+ table = Table(data)
1643
+ view = table.view(expressions=["bucket(\"a\", 'Y')"])
1644
+ result = view.to_columns()
1645
+ assert result["bucket(\"a\", 'Y')"] == [
1646
+ util.to_timestamp(datetime(2019, 1, 1))
1647
+ ]
1648
+
1649
+ def test_table_year_bucket_edge_in_PST(self, util):
1650
+ os.environ["TZ"] = "US/Pacific"
1651
+ time.tzset()
1652
+ data = {"a": [datetime(2019, 12, 31, 23, 59)]}
1653
+
1654
+ table = Table(data)
1655
+ view = table.view(expressions=["bucket(\"a\", 'Y')"])
1656
+ result = view.to_columns()
1657
+ assert result["bucket(\"a\", 'Y')"] == [
1658
+ util.to_timestamp(datetime(2019, 1, 1))
1659
+ ]
1660
+
1661
+
1662
+ class TestTableDateTimePivots(object):
1663
+ def test_table_group_by_date_correct(self, util: Util):
1664
+ data = {
1665
+ "a": [date(2020, i, 15) for i in range(1, 13)],
1666
+ "b": [i for i in range(1, 13)],
1667
+ }
1668
+ table = Table(data)
1669
+ view = table.view(group_by=["a"])
1670
+ assert view.to_columns() == {
1671
+ "__ROW_PATH__": [
1672
+ [],
1673
+ [util.to_timestamp(datetime(2020, 1, 15, 0, 0))],
1674
+ [util.to_timestamp(datetime(2020, 2, 15, 0, 0))],
1675
+ [util.to_timestamp(datetime(2020, 3, 15, 0, 0))],
1676
+ [util.to_timestamp(datetime(2020, 4, 15, 0, 0))],
1677
+ [util.to_timestamp(datetime(2020, 5, 15, 0, 0))],
1678
+ [util.to_timestamp(datetime(2020, 6, 15, 0, 0))],
1679
+ [util.to_timestamp(datetime(2020, 7, 15, 0, 0))],
1680
+ [util.to_timestamp(datetime(2020, 8, 15, 0, 0))],
1681
+ [util.to_timestamp(datetime(2020, 9, 15, 0, 0))],
1682
+ [util.to_timestamp(datetime(2020, 10, 15, 0, 0))],
1683
+ [util.to_timestamp(datetime(2020, 11, 15, 0, 0))],
1684
+ [util.to_timestamp(datetime(2020, 12, 15, 0, 0))],
1685
+ ],
1686
+ "a": [12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
1687
+ "b": [78, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
1688
+ }
1689
+
1690
+ def test_table_group_by_pandas_date_correct(self, util: Util):
1691
+ data = {
1692
+ "a": [date(2020, i, 15) for i in range(1, 13)],
1693
+ "b": [i for i in range(1, 13)],
1694
+ }
1695
+ table = Table(pd.DataFrame(data))
1696
+ view = table.view(group_by=["a"])
1697
+ assert view.to_columns() == {
1698
+ "__ROW_PATH__": [
1699
+ [],
1700
+ [util.to_timestamp(datetime(2020, 1, 15, 0, 0))],
1701
+ [util.to_timestamp(datetime(2020, 2, 15, 0, 0))],
1702
+ [util.to_timestamp(datetime(2020, 3, 15, 0, 0))],
1703
+ [util.to_timestamp(datetime(2020, 4, 15, 0, 0))],
1704
+ [util.to_timestamp(datetime(2020, 5, 15, 0, 0))],
1705
+ [util.to_timestamp(datetime(2020, 6, 15, 0, 0))],
1706
+ [util.to_timestamp(datetime(2020, 7, 15, 0, 0))],
1707
+ [util.to_timestamp(datetime(2020, 8, 15, 0, 0))],
1708
+ [util.to_timestamp(datetime(2020, 9, 15, 0, 0))],
1709
+ [util.to_timestamp(datetime(2020, 10, 15, 0, 0))],
1710
+ [util.to_timestamp(datetime(2020, 11, 15, 0, 0))],
1711
+ [util.to_timestamp(datetime(2020, 12, 15, 0, 0))],
1712
+ ],
1713
+ "index": [66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
1714
+ "a": [12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
1715
+ "b": [78, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
1716
+ }
1717
+
1718
+ def test_table_split_by_date_correct(self, util: Util):
1719
+ data = {
1720
+ "a": [date(2020, i, 15) for i in range(1, 13)],
1721
+ "b": [i for i in range(1, 13)],
1722
+ }
1723
+ table = Table(data)
1724
+ view = table.view(split_by=["a"])
1725
+ assert view.to_columns() == {
1726
+ "2020-01-15|a": [
1727
+ util.to_timestamp(datetime(2020, 1, 15, 0, 0)),
1728
+ None,
1729
+ None,
1730
+ None,
1731
+ None,
1732
+ None,
1733
+ None,
1734
+ None,
1735
+ None,
1736
+ None,
1737
+ None,
1738
+ None,
1739
+ ],
1740
+ "2020-01-15|b": [
1741
+ 1,
1742
+ None,
1743
+ None,
1744
+ None,
1745
+ None,
1746
+ None,
1747
+ None,
1748
+ None,
1749
+ None,
1750
+ None,
1751
+ None,
1752
+ None,
1753
+ ],
1754
+ "2020-02-15|a": [
1755
+ None,
1756
+ util.to_timestamp(datetime(2020, 2, 15, 0, 0)),
1757
+ None,
1758
+ None,
1759
+ None,
1760
+ None,
1761
+ None,
1762
+ None,
1763
+ None,
1764
+ None,
1765
+ None,
1766
+ None,
1767
+ ],
1768
+ "2020-02-15|b": [
1769
+ None,
1770
+ 2,
1771
+ None,
1772
+ None,
1773
+ None,
1774
+ None,
1775
+ None,
1776
+ None,
1777
+ None,
1778
+ None,
1779
+ None,
1780
+ None,
1781
+ ],
1782
+ "2020-03-15|a": [
1783
+ None,
1784
+ None,
1785
+ util.to_timestamp(datetime(2020, 3, 15, 0, 0)),
1786
+ None,
1787
+ None,
1788
+ None,
1789
+ None,
1790
+ None,
1791
+ None,
1792
+ None,
1793
+ None,
1794
+ None,
1795
+ ],
1796
+ "2020-03-15|b": [
1797
+ None,
1798
+ None,
1799
+ 3,
1800
+ None,
1801
+ None,
1802
+ None,
1803
+ None,
1804
+ None,
1805
+ None,
1806
+ None,
1807
+ None,
1808
+ None,
1809
+ ],
1810
+ "2020-04-15|a": [
1811
+ None,
1812
+ None,
1813
+ None,
1814
+ util.to_timestamp(datetime(2020, 4, 15, 0, 0)),
1815
+ None,
1816
+ None,
1817
+ None,
1818
+ None,
1819
+ None,
1820
+ None,
1821
+ None,
1822
+ None,
1823
+ ],
1824
+ "2020-04-15|b": [
1825
+ None,
1826
+ None,
1827
+ None,
1828
+ 4,
1829
+ None,
1830
+ None,
1831
+ None,
1832
+ None,
1833
+ None,
1834
+ None,
1835
+ None,
1836
+ None,
1837
+ ],
1838
+ "2020-05-15|a": [
1839
+ None,
1840
+ None,
1841
+ None,
1842
+ None,
1843
+ util.to_timestamp(datetime(2020, 5, 15, 0, 0)),
1844
+ None,
1845
+ None,
1846
+ None,
1847
+ None,
1848
+ None,
1849
+ None,
1850
+ None,
1851
+ ],
1852
+ "2020-05-15|b": [
1853
+ None,
1854
+ None,
1855
+ None,
1856
+ None,
1857
+ 5,
1858
+ None,
1859
+ None,
1860
+ None,
1861
+ None,
1862
+ None,
1863
+ None,
1864
+ None,
1865
+ ],
1866
+ "2020-06-15|a": [
1867
+ None,
1868
+ None,
1869
+ None,
1870
+ None,
1871
+ None,
1872
+ util.to_timestamp(datetime(2020, 6, 15, 0, 0)),
1873
+ None,
1874
+ None,
1875
+ None,
1876
+ None,
1877
+ None,
1878
+ None,
1879
+ ],
1880
+ "2020-06-15|b": [
1881
+ None,
1882
+ None,
1883
+ None,
1884
+ None,
1885
+ None,
1886
+ 6,
1887
+ None,
1888
+ None,
1889
+ None,
1890
+ None,
1891
+ None,
1892
+ None,
1893
+ ],
1894
+ "2020-07-15|a": [
1895
+ None,
1896
+ None,
1897
+ None,
1898
+ None,
1899
+ None,
1900
+ None,
1901
+ util.to_timestamp(datetime(2020, 7, 15, 0, 0)),
1902
+ None,
1903
+ None,
1904
+ None,
1905
+ None,
1906
+ None,
1907
+ ],
1908
+ "2020-07-15|b": [
1909
+ None,
1910
+ None,
1911
+ None,
1912
+ None,
1913
+ None,
1914
+ None,
1915
+ 7,
1916
+ None,
1917
+ None,
1918
+ None,
1919
+ None,
1920
+ None,
1921
+ ],
1922
+ "2020-08-15|a": [
1923
+ None,
1924
+ None,
1925
+ None,
1926
+ None,
1927
+ None,
1928
+ None,
1929
+ None,
1930
+ util.to_timestamp(datetime(2020, 8, 15, 0, 0)),
1931
+ None,
1932
+ None,
1933
+ None,
1934
+ None,
1935
+ ],
1936
+ "2020-08-15|b": [
1937
+ None,
1938
+ None,
1939
+ None,
1940
+ None,
1941
+ None,
1942
+ None,
1943
+ None,
1944
+ 8,
1945
+ None,
1946
+ None,
1947
+ None,
1948
+ None,
1949
+ ],
1950
+ "2020-09-15|a": [
1951
+ None,
1952
+ None,
1953
+ None,
1954
+ None,
1955
+ None,
1956
+ None,
1957
+ None,
1958
+ None,
1959
+ util.to_timestamp(datetime(2020, 9, 15, 0, 0)),
1960
+ None,
1961
+ None,
1962
+ None,
1963
+ ],
1964
+ "2020-09-15|b": [
1965
+ None,
1966
+ None,
1967
+ None,
1968
+ None,
1969
+ None,
1970
+ None,
1971
+ None,
1972
+ None,
1973
+ 9,
1974
+ None,
1975
+ None,
1976
+ None,
1977
+ ],
1978
+ "2020-10-15|a": [
1979
+ None,
1980
+ None,
1981
+ None,
1982
+ None,
1983
+ None,
1984
+ None,
1985
+ None,
1986
+ None,
1987
+ None,
1988
+ util.to_timestamp(datetime(2020, 10, 15, 0, 0)),
1989
+ None,
1990
+ None,
1991
+ ],
1992
+ "2020-10-15|b": [
1993
+ None,
1994
+ None,
1995
+ None,
1996
+ None,
1997
+ None,
1998
+ None,
1999
+ None,
2000
+ None,
2001
+ None,
2002
+ 10,
2003
+ None,
2004
+ None,
2005
+ ],
2006
+ "2020-11-15|a": [
2007
+ None,
2008
+ None,
2009
+ None,
2010
+ None,
2011
+ None,
2012
+ None,
2013
+ None,
2014
+ None,
2015
+ None,
2016
+ None,
2017
+ util.to_timestamp(datetime(2020, 11, 15, 0, 0)),
2018
+ None,
2019
+ ],
2020
+ "2020-11-15|b": [
2021
+ None,
2022
+ None,
2023
+ None,
2024
+ None,
2025
+ None,
2026
+ None,
2027
+ None,
2028
+ None,
2029
+ None,
2030
+ None,
2031
+ 11,
2032
+ None,
2033
+ ],
2034
+ "2020-12-15|a": [
2035
+ None,
2036
+ None,
2037
+ None,
2038
+ None,
2039
+ None,
2040
+ None,
2041
+ None,
2042
+ None,
2043
+ None,
2044
+ None,
2045
+ None,
2046
+ util.to_timestamp(datetime(2020, 12, 15, 0, 0)),
2047
+ ],
2048
+ "2020-12-15|b": [
2049
+ None,
2050
+ None,
2051
+ None,
2052
+ None,
2053
+ None,
2054
+ None,
2055
+ None,
2056
+ None,
2057
+ None,
2058
+ None,
2059
+ None,
2060
+ 12,
2061
+ ],
2062
+ }
2063
+
2064
+ def test_table_split_by_pandas_date_correct(self, util: Util):
2065
+ data = {
2066
+ "a": [date(2020, i, 15) for i in range(1, 13)],
2067
+ "b": [i for i in range(1, 13)],
2068
+ }
2069
+ table = Table(pd.DataFrame(data))
2070
+ view = table.view(columns=["a", "b"], split_by=["a"])
2071
+ print(f"XXXX: {view.to_columns()}")
2072
+ assert view.to_columns() == {
2073
+ "2020-01-15|a": [
2074
+ util.to_timestamp(datetime(2020, 1, 15, 0, 0)),
2075
+ None,
2076
+ None,
2077
+ None,
2078
+ None,
2079
+ None,
2080
+ None,
2081
+ None,
2082
+ None,
2083
+ None,
2084
+ None,
2085
+ None,
2086
+ ],
2087
+ "2020-01-15|b": [
2088
+ 1,
2089
+ None,
2090
+ None,
2091
+ None,
2092
+ None,
2093
+ None,
2094
+ None,
2095
+ None,
2096
+ None,
2097
+ None,
2098
+ None,
2099
+ None,
2100
+ ],
2101
+ "2020-02-15|a": [
2102
+ None,
2103
+ util.to_timestamp(datetime(2020, 2, 15, 0, 0)),
2104
+ None,
2105
+ None,
2106
+ None,
2107
+ None,
2108
+ None,
2109
+ None,
2110
+ None,
2111
+ None,
2112
+ None,
2113
+ None,
2114
+ ],
2115
+ "2020-02-15|b": [
2116
+ None,
2117
+ 2,
2118
+ None,
2119
+ None,
2120
+ None,
2121
+ None,
2122
+ None,
2123
+ None,
2124
+ None,
2125
+ None,
2126
+ None,
2127
+ None,
2128
+ ],
2129
+ "2020-03-15|a": [
2130
+ None,
2131
+ None,
2132
+ util.to_timestamp(datetime(2020, 3, 15, 0, 0)),
2133
+ None,
2134
+ None,
2135
+ None,
2136
+ None,
2137
+ None,
2138
+ None,
2139
+ None,
2140
+ None,
2141
+ None,
2142
+ ],
2143
+ "2020-03-15|b": [
2144
+ None,
2145
+ None,
2146
+ 3,
2147
+ None,
2148
+ None,
2149
+ None,
2150
+ None,
2151
+ None,
2152
+ None,
2153
+ None,
2154
+ None,
2155
+ None,
2156
+ ],
2157
+ "2020-04-15|a": [
2158
+ None,
2159
+ None,
2160
+ None,
2161
+ util.to_timestamp(datetime(2020, 4, 15, 0, 0)),
2162
+ None,
2163
+ None,
2164
+ None,
2165
+ None,
2166
+ None,
2167
+ None,
2168
+ None,
2169
+ None,
2170
+ ],
2171
+ "2020-04-15|b": [
2172
+ None,
2173
+ None,
2174
+ None,
2175
+ 4,
2176
+ None,
2177
+ None,
2178
+ None,
2179
+ None,
2180
+ None,
2181
+ None,
2182
+ None,
2183
+ None,
2184
+ ],
2185
+ "2020-05-15|a": [
2186
+ None,
2187
+ None,
2188
+ None,
2189
+ None,
2190
+ util.to_timestamp(datetime(2020, 5, 15, 0, 0)),
2191
+ None,
2192
+ None,
2193
+ None,
2194
+ None,
2195
+ None,
2196
+ None,
2197
+ None,
2198
+ ],
2199
+ "2020-05-15|b": [
2200
+ None,
2201
+ None,
2202
+ None,
2203
+ None,
2204
+ 5,
2205
+ None,
2206
+ None,
2207
+ None,
2208
+ None,
2209
+ None,
2210
+ None,
2211
+ None,
2212
+ ],
2213
+ "2020-06-15|a": [
2214
+ None,
2215
+ None,
2216
+ None,
2217
+ None,
2218
+ None,
2219
+ util.to_timestamp(datetime(2020, 6, 15, 0, 0)),
2220
+ None,
2221
+ None,
2222
+ None,
2223
+ None,
2224
+ None,
2225
+ None,
2226
+ ],
2227
+ "2020-06-15|b": [
2228
+ None,
2229
+ None,
2230
+ None,
2231
+ None,
2232
+ None,
2233
+ 6,
2234
+ None,
2235
+ None,
2236
+ None,
2237
+ None,
2238
+ None,
2239
+ None,
2240
+ ],
2241
+ "2020-07-15|a": [
2242
+ None,
2243
+ None,
2244
+ None,
2245
+ None,
2246
+ None,
2247
+ None,
2248
+ util.to_timestamp(datetime(2020, 7, 15, 0, 0)),
2249
+ None,
2250
+ None,
2251
+ None,
2252
+ None,
2253
+ None,
2254
+ ],
2255
+ "2020-07-15|b": [
2256
+ None,
2257
+ None,
2258
+ None,
2259
+ None,
2260
+ None,
2261
+ None,
2262
+ 7,
2263
+ None,
2264
+ None,
2265
+ None,
2266
+ None,
2267
+ None,
2268
+ ],
2269
+ "2020-08-15|a": [
2270
+ None,
2271
+ None,
2272
+ None,
2273
+ None,
2274
+ None,
2275
+ None,
2276
+ None,
2277
+ util.to_timestamp(datetime(2020, 8, 15, 0, 0)),
2278
+ None,
2279
+ None,
2280
+ None,
2281
+ None,
2282
+ ],
2283
+ "2020-08-15|b": [
2284
+ None,
2285
+ None,
2286
+ None,
2287
+ None,
2288
+ None,
2289
+ None,
2290
+ None,
2291
+ 8,
2292
+ None,
2293
+ None,
2294
+ None,
2295
+ None,
2296
+ ],
2297
+ "2020-09-15|a": [
2298
+ None,
2299
+ None,
2300
+ None,
2301
+ None,
2302
+ None,
2303
+ None,
2304
+ None,
2305
+ None,
2306
+ util.to_timestamp(datetime(2020, 9, 15, 0, 0)),
2307
+ None,
2308
+ None,
2309
+ None,
2310
+ ],
2311
+ "2020-09-15|b": [
2312
+ None,
2313
+ None,
2314
+ None,
2315
+ None,
2316
+ None,
2317
+ None,
2318
+ None,
2319
+ None,
2320
+ 9,
2321
+ None,
2322
+ None,
2323
+ None,
2324
+ ],
2325
+ "2020-10-15|a": [
2326
+ None,
2327
+ None,
2328
+ None,
2329
+ None,
2330
+ None,
2331
+ None,
2332
+ None,
2333
+ None,
2334
+ None,
2335
+ util.to_timestamp(datetime(2020, 10, 15, 0, 0)),
2336
+ None,
2337
+ None,
2338
+ ],
2339
+ "2020-10-15|b": [
2340
+ None,
2341
+ None,
2342
+ None,
2343
+ None,
2344
+ None,
2345
+ None,
2346
+ None,
2347
+ None,
2348
+ None,
2349
+ 10,
2350
+ None,
2351
+ None,
2352
+ ],
2353
+ "2020-11-15|a": [
2354
+ None,
2355
+ None,
2356
+ None,
2357
+ None,
2358
+ None,
2359
+ None,
2360
+ None,
2361
+ None,
2362
+ None,
2363
+ None,
2364
+ util.to_timestamp(datetime(2020, 11, 15, 0, 0)),
2365
+ None,
2366
+ ],
2367
+ "2020-11-15|b": [
2368
+ None,
2369
+ None,
2370
+ None,
2371
+ None,
2372
+ None,
2373
+ None,
2374
+ None,
2375
+ None,
2376
+ None,
2377
+ None,
2378
+ 11,
2379
+ None,
2380
+ ],
2381
+ "2020-12-15|a": [
2382
+ None,
2383
+ None,
2384
+ None,
2385
+ None,
2386
+ None,
2387
+ None,
2388
+ None,
2389
+ None,
2390
+ None,
2391
+ None,
2392
+ None,
2393
+ util.to_timestamp(datetime(2020, 12, 15, 0, 0)),
2394
+ ],
2395
+ "2020-12-15|b": [
2396
+ None,
2397
+ None,
2398
+ None,
2399
+ None,
2400
+ None,
2401
+ None,
2402
+ None,
2403
+ None,
2404
+ None,
2405
+ None,
2406
+ None,
2407
+ 12,
2408
+ ],
2409
+ }