Skip to content

Commit 7ef18a0

Browse files
committed
Add additional clickhouse database datetime functions
Specifically, toStartOfMinute, toStartOfFiveMinutes, toStartOfTenminutes, toStartOfFifteenMinutes and toStartOfHour
1 parent 8f0bee7 commit 7ef18a0

File tree

2 files changed

+164
-4
lines changed

2 files changed

+164
-4
lines changed

clickhouse_backend/models/functions/datetime.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
from .base import Func
77

88
__all__ = [
9+
"toStartOfMinute",
10+
"toStartOfFiveMinutes",
11+
"toStartOfTenMinutes",
12+
"toStartOfFifteenMinutes",
13+
"toStartOfHour",
914
"toYYYYMM",
1015
"toYYYYMMDD",
1116
"toYYYYMMDDhhmmss",
@@ -39,3 +44,36 @@ class toYYYYMMDD(toYYYYMM):
3944

4045
class toYYYYMMDDhhmmss(toYYYYMM):
4146
output_field = fields.UInt64Field()
47+
48+
49+
class toStartOfMinute(Func):
50+
output_field = models.fields.DateTimeField()
51+
52+
def __init__(self, *expressions):
53+
arity = len(expressions)
54+
if arity < 1 or arity > 1:
55+
raise TypeError(
56+
"'%s' takes 1 argument (%s given)"
57+
% (
58+
self.__class__.__name__,
59+
len(expressions),
60+
)
61+
)
62+
63+
super().__init__(*expressions)
64+
65+
66+
class toStartOfFiveMinutes(toStartOfMinute):
67+
pass
68+
69+
70+
class toStartOfTenMinutes(toStartOfMinute):
71+
pass
72+
73+
74+
class toStartOfFifteenMinutes(toStartOfMinute):
75+
pass
76+
77+
78+
class toStartOfHour(toStartOfMinute):
79+
pass

tests/clickhouse_functions/test_datetime.py

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ def setUpTestData(cls):
1717
alias="smithj",
1818
# https://stackoverflow.com/a/18862958
1919
birthday=pytz.timezone(get_timezone()).localize(
20-
datetime(2023, 11, 30, 16), is_dst=False
20+
datetime(2023, 11, 30, hour=16, minute=12, second=15), is_dst=False
2121
),
2222
)
2323
cls.elena = Author.objects.create(
2424
name="Élena Jordan",
2525
alias="elena",
26-
birthday=pytz.utc.localize(datetime(2023, 11, 30, 16), is_dst=False),
26+
birthday=pytz.utc.localize(
27+
datetime(2023, 11, 30, hour=16, minute=59, second=59), is_dst=False
28+
),
2729
)
2830

2931
def test_yyyymm(self):
@@ -50,8 +52,128 @@ def test_yyyymmddhhmmss(self):
5052
john = Author.objects.annotate(v=models.toYYYYMMDDhhmmss("birthday")).get(
5153
id=self.john.id
5254
)
53-
self.assertEqual(john.v, 20231130160000)
55+
self.assertEqual(john.v, 20231130161215)
5456
elena = Author.objects.annotate(
5557
v=models.toYYYYMMDDhhmmss("birthday", "Asia/Shanghai")
5658
).get(id=self.elena.id)
57-
self.assertEqual(elena.v, 20231201000000)
59+
self.assertEqual(elena.v, 20231201005959)
60+
61+
def test_tostartofminute(self):
62+
john = Author.objects.annotate(v=models.toStartOfMinute("birthday")).get(
63+
id=self.john.id
64+
)
65+
self.assertEqual(
66+
john.v,
67+
datetime(
68+
2023,
69+
11,
70+
30,
71+
hour=16,
72+
minute=12,
73+
second=00,
74+
),
75+
)
76+
77+
elena = Author.objects.annotate(v=models.toStartOfMinute("birthday")).get(
78+
id=self.elena.id
79+
)
80+
self.assertEqual(
81+
elena.v,
82+
datetime(2023, 11, 30, hour=10, minute=59, second=00),
83+
)
84+
85+
def test_tostartoffiveminutes(self):
86+
john = Author.objects.annotate(v=models.toStartOfFiveMinutes("birthday")).get(
87+
id=self.john.id
88+
)
89+
self.assertEqual(
90+
john.v,
91+
datetime(
92+
2023,
93+
11,
94+
30,
95+
hour=16,
96+
minute=10,
97+
second=00,
98+
),
99+
)
100+
101+
elena = Author.objects.annotate(v=models.toStartOfFiveMinutes("birthday")).get(
102+
id=self.elena.id
103+
)
104+
self.assertEqual(
105+
elena.v,
106+
datetime(2023, 11, 30, hour=10, minute=55, second=00),
107+
)
108+
109+
def test_tostartoftenminutes(self):
110+
john = Author.objects.annotate(v=models.toStartOfTenMinutes("birthday")).get(
111+
id=self.john.id
112+
)
113+
self.assertEqual(
114+
john.v,
115+
datetime(
116+
2023,
117+
11,
118+
30,
119+
hour=16,
120+
minute=10,
121+
second=00,
122+
),
123+
)
124+
125+
elena = Author.objects.annotate(v=models.toStartOfTenMinutes("birthday")).get(
126+
id=self.elena.id
127+
)
128+
self.assertEqual(
129+
elena.v,
130+
datetime(2023, 11, 30, hour=10, minute=50, second=00),
131+
)
132+
133+
def test_tostartoffifteenminutes(self):
134+
john = Author.objects.annotate(
135+
v=models.toStartOfFifteenMinutes("birthday")
136+
).get(id=self.john.id)
137+
self.assertEqual(
138+
john.v,
139+
datetime(
140+
2023,
141+
11,
142+
30,
143+
hour=16,
144+
minute=00,
145+
second=00,
146+
),
147+
)
148+
149+
elena = Author.objects.annotate(
150+
v=models.toStartOfFifteenMinutes("birthday")
151+
).get(id=self.elena.id)
152+
self.assertEqual(
153+
elena.v,
154+
datetime(2023, 11, 30, hour=10, minute=45, second=00),
155+
)
156+
157+
def test_tostartofhour(self):
158+
john = Author.objects.annotate(v=models.toStartOfHour("birthday")).get(
159+
id=self.john.id
160+
)
161+
self.assertEqual(
162+
john.v,
163+
datetime(
164+
2023,
165+
11,
166+
30,
167+
hour=16,
168+
minute=00,
169+
second=00,
170+
),
171+
)
172+
173+
elena = Author.objects.annotate(v=models.toStartOfHour("birthday")).get(
174+
id=self.elena.id
175+
)
176+
self.assertEqual(
177+
elena.v,
178+
datetime(2023, 11, 30, hour=10, minute=00, second=00),
179+
)

0 commit comments

Comments
 (0)