Skip to content

Commit 2db5f97

Browse files
authored
Add Other Datetime Functions (#2477)
### What problem does this PR solve? _Add date_part, century, epoch, era, quarter, weekday, weekofyear._ Issue link: (#2033) ### Type of change - [x] New Feature (non-breaking change which adds functionality) - [x] Test cases
1 parent dc843a7 commit 2db5f97

28 files changed

+2583
-1
lines changed

python/test_pysdk/test_select.py

+143-1
Original file line numberDiff line numberDiff line change
@@ -1251,4 +1251,146 @@ def test_select_day_of_week(self, suffix):
12511251
assert res['dayofweek(c3)'][0] == 4, "The value of day_of_week(c3) should be 4"
12521252

12531253
res = db_obj.drop_table("test_select_day_of_week" + suffix)
1254-
assert res.error_code == ErrorCode.OK
1254+
assert res.error_code == ErrorCode.OK
1255+
1256+
def test_select_weekday(self, suffix):
1257+
db_obj = self.infinity_obj.get_database("default_db")
1258+
db_obj.drop_table("test_select_weekday" + suffix, ConflictType.Ignore)
1259+
db_obj.create_table("test_select_weekday" + suffix,
1260+
{"c1": {"type": "date"},
1261+
"c2": {"type": "datetime"},
1262+
"c3": {"type": "timestamp"}}, ConflictType.Error)
1263+
table_obj = db_obj.get_table("test_select_weekday" + suffix)
1264+
table_obj.insert(
1265+
[{"c1":"2025-01-16", "c2": "2025-01-31 21:44:33", "c3":"2025-01-26 20:45:11"}])
1266+
1267+
res, extra_res = table_obj.output(["weekday(c1)", "weekday(c2)", "weekday(c3)"]).to_pl()
1268+
print(res)
1269+
assert res['weekday(c1)'][0] == 4, "The value of weekday(c1) should be 4"
1270+
assert res['weekday(c2)'][0] == 5, "The value of weekday(c2) should be 5"
1271+
assert res['weekday(c3)'][0] == 0, "The value of weekday(c3) should be 0"
1272+
1273+
res = db_obj.drop_table("test_select_weekday" + suffix)
1274+
assert res.error_code == ErrorCode.OK
1275+
1276+
def test_select_era(self, suffix):
1277+
db_obj = self.infinity_obj.get_database("default_db")
1278+
db_obj.drop_table("test_select_era" + suffix, ConflictType.Ignore)
1279+
db_obj.create_table("test_select_era" + suffix,
1280+
{"c1": {"type": "date"},
1281+
"c2": {"type": "datetime"},
1282+
"c3": {"type": "timestamp"}}, ConflictType.Error)
1283+
table_obj = db_obj.get_table("test_select_era" + suffix)
1284+
table_obj.insert(
1285+
[{"c1":"2025-01-16", "c2": "44-01-16 21:44:33", "c3":"-25-01-16 20:45:11"}])
1286+
1287+
res, extra_res = table_obj.output(["era(c1)", "era(c2)", "era(c3)"]).to_pl()
1288+
print(res)
1289+
assert res['era(c1)'][0] == 1, "The value of era(c1) should be 1"
1290+
assert res['era(c2)'][0] == 1, "The value of era(c2) should be 1"
1291+
assert res['era(c3)'][0] == 0, "The value of era(c3) should be 0"
1292+
1293+
res = db_obj.drop_table("test_select_era" + suffix)
1294+
assert res.error_code == ErrorCode.OK
1295+
1296+
def test_select_epoch(self, suffix):
1297+
db_obj = self.infinity_obj.get_database("default_db")
1298+
db_obj.drop_table("test_select_epoch" + suffix, ConflictType.Ignore)
1299+
db_obj.create_table("test_select_epoch" + suffix,
1300+
{"c1": {"type": "date"},
1301+
"c2": {"type": "datetime"},
1302+
"c3": {"type": "timestamp"}}, ConflictType.Error)
1303+
table_obj = db_obj.get_table("test_select_epoch" + suffix)
1304+
table_obj.insert(
1305+
[{"c1":"1970-01-01", "c2": "1970-01-01 21:44:33", "c3":"1970-01-01 20:45:11"}])
1306+
1307+
res, extra_res = table_obj.output(["epoch(c1)", "epoch(c2)", "epoch(c3)"]).to_pl()
1308+
print(res)
1309+
assert res['epoch(c1)'][0] == 0, "The value of epoch(c1) should be 0"
1310+
assert res['epoch(c2)'][0] == 78273, "The value of epoch(c2) should be 78273"
1311+
assert res['epoch(c3)'][0] == 74711 , "The value of epoch(c3) should be 74711"
1312+
1313+
res = db_obj.drop_table("test_select_epoch" + suffix)
1314+
assert res.error_code == ErrorCode.OK
1315+
1316+
def test_select_quarter(self, suffix):
1317+
db_obj = self.infinity_obj.get_database("default_db")
1318+
db_obj.drop_table("test_select_quarter" + suffix, ConflictType.Ignore)
1319+
db_obj.create_table("test_select_quarter" + suffix,
1320+
{"c1": {"type": "date"},
1321+
"c2": {"type": "datetime"},
1322+
"c3": {"type": "timestamp"}}, ConflictType.Error)
1323+
table_obj = db_obj.get_table("test_select_quarter" + suffix)
1324+
table_obj.insert(
1325+
[{"c1":"2025-01-16", "c2": "2025-04-16 21:44:33", "c3":"2025-09-16 20:45:11"}])
1326+
1327+
res, extra_res = table_obj.output(["quarter(c1)", "quarter(c2)", "quarter(c3)"]).to_pl()
1328+
print(res)
1329+
assert res['quarter(c1)'][0] == 1, "The value of quarter(c1) should be 1"
1330+
assert res['quarter(c2)'][0] == 2, "The value of quarter(c2) should be 2"
1331+
assert res['quarter(c3)'][0] == 3, "The value of quarter(c3) should be 3"
1332+
1333+
res = db_obj.drop_table("test_select_quarter" + suffix)
1334+
assert res.error_code == ErrorCode.OK
1335+
1336+
def test_select_century(self, suffix):
1337+
db_obj = self.infinity_obj.get_database("default_db")
1338+
db_obj.drop_table("test_select_century" + suffix, ConflictType.Ignore)
1339+
db_obj.create_table("test_select_century" + suffix,
1340+
{"c1": {"type": "date"},
1341+
"c2": {"type": "datetime"},
1342+
"c3": {"type": "timestamp"}}, ConflictType.Error)
1343+
table_obj = db_obj.get_table("test_select_century" + suffix)
1344+
table_obj.insert(
1345+
[{"c1":"2025-01-16", "c2": "1925-01-16 21:44:33", "c3":"25-01-16 20:45:11"}])
1346+
1347+
res, extra_res = table_obj.output(["century(c1)", "century(c2)", "century(c3)"]).to_pl()
1348+
print(res)
1349+
assert res['century(c1)'][0] == 21, "The value of century(c1) should be 21"
1350+
assert res['century(c2)'][0] == 20, "The value of century(c2) should be 20"
1351+
assert res['century(c3)'][0] == 1, "The value of century(c3) should be 1"
1352+
1353+
res = db_obj.drop_table("test_select_century" + suffix)
1354+
assert res.error_code == ErrorCode.OK
1355+
1356+
def test_select_week_of_year(self, suffix):
1357+
db_obj = self.infinity_obj.get_database("default_db")
1358+
db_obj.drop_table("test_select_week_of_year" + suffix, ConflictType.Ignore)
1359+
db_obj.create_table("test_select_week_of_year" + suffix,
1360+
{"c1": {"type": "date"},
1361+
"c2": {"type": "datetime"},
1362+
"c3": {"type": "timestamp"}}, ConflictType.Error)
1363+
table_obj = db_obj.get_table("test_select_week_of_year" + suffix)
1364+
table_obj.insert(
1365+
[{"c1":"2025-01-16", "c2": "2025-01-01 21:44:33", "c3":"2017-12-16 20:45:11"}])
1366+
1367+
res, extra_res = table_obj.output(["weekofyear(c1)", "weekofyear(c2)", "weekofyear(c3)"]).to_pl()
1368+
print(res)
1369+
assert res['weekofyear(c1)'][0] == 3, "The value of weekofyear(c1) should be 3"
1370+
assert res['weekofyear(c2)'][0] == 1, "The value of weekofyear(c2) should be 1"
1371+
assert res['weekofyear(c3)'][0] == 50, "The value of weekofyear(c3) should be 50"
1372+
1373+
res = db_obj.drop_table("test_select_week_of_year" + suffix)
1374+
assert res.error_code == ErrorCode.OK
1375+
1376+
def test_select_date_part(self, suffix):
1377+
db_obj = self.infinity_obj.get_database("default_db")
1378+
db_obj.drop_table("test_select_date_part" + suffix, ConflictType.Ignore)
1379+
db_obj.create_table("test_select_date_part" + suffix,
1380+
{"c1": {"type": "date"},
1381+
"c2": {"type": "datetime"},
1382+
"c3": {"type": "timestamp"},
1383+
"c4": {"type": "varchar", "constraints": ["primary key", "not null"]},
1384+
"c5": {"type": "varchar", "constraints": ["not null"]}}, ConflictType.Error)
1385+
table_obj = db_obj.get_table("test_select_date_part" + suffix)
1386+
table_obj.insert(
1387+
[{"c1":"2025-01-16", "c2": "2025-01-16 21:44:33", "c3":"2025-01-16 20:45:11", "c4":"year", "c5":"month"}])
1388+
1389+
res, extra_res = table_obj.output(["datepart(c4, c1)", "datepart(c4, c2)", "datepart(c5, c3)"]).to_pl()
1390+
print(res)
1391+
assert res['(c4 datepart c1)'][0] == 2025, "The value of c4 datepart c1 should be 2025"
1392+
assert res['(c4 datepart c2)'][0] == 2025, "The value of c4 datepart c2 should be 2025"
1393+
assert res['(c5 datepart c3)'][0] == 1, "The value of c5 datepart c3 should be 1"
1394+
1395+
res = db_obj.drop_table("test_select_date_part" + suffix)
1396+
assert res.error_code == ErrorCode.OK

src/function/builtin_functions.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,22 @@ import like;
4848
import minus;
4949
import modulo;
5050
import multiply;
51+
import century;
5152
import year;
5253
import month;
5354
import day;
5455
import hour;
5556
import minute;
5657
import second;
58+
import date_part;
5759
import day_of_month;
5860
import day_of_week;
5961
import day_of_year;
62+
import era;
63+
import epoch;
64+
import weekday;
65+
import week_of_year;
66+
import quarter;
6067
import not_func;
6168
import or_func;
6269
import plus;
@@ -159,15 +166,22 @@ void BuiltinFunctions::RegisterScalarFunction() {
159166
RegisterPositionFunction(catalog_ptr_);
160167

161168
// date and time functions
169+
RegisterCenturyFunction(catalog_ptr_);
162170
RegisterYearFunction(catalog_ptr_);
163171
RegisterMonthFunction(catalog_ptr_);
164172
RegisterDayFunction(catalog_ptr_);
165173
RegisterHourFunction(catalog_ptr_);
166174
RegisterMinuteFunction(catalog_ptr_);
167175
RegisterSecondFunction(catalog_ptr_);
176+
RegisterDatePartFunction(catalog_ptr_);
168177
RegisterDayOfYearFunction(catalog_ptr_);
169178
RegisterDayOfMonthFunction(catalog_ptr_);
170179
RegisterDayOfWeekFunction(catalog_ptr_);
180+
RegisterEraFunction(catalog_ptr_);
181+
RegisterEpochFunction(catalog_ptr_);
182+
RegisterQuarterFunction(catalog_ptr_);
183+
RegisterWeekdayFunction(catalog_ptr_);
184+
RegisterWeekOfYearFunction(catalog_ptr_);
171185
}
172186

173187
void BuiltinFunctions::RegisterTableFunction() {}

src/function/scalar/century.cpp

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright(C) 2025 InfiniFlow, Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
module;
16+
#include <cmath>
17+
module century;
18+
19+
import stl;
20+
import catalog;
21+
import status;
22+
import logical_type;
23+
import infinity_exception;
24+
import scalar_function;
25+
import scalar_function_set;
26+
import third_party;
27+
import internal_types;
28+
import data_type;
29+
import column_vector;
30+
31+
namespace infinity {
32+
33+
struct CenturyFunction {
34+
template <typename TA, typename TB>
35+
static inline bool Run(TA left, TB &result) {
36+
Status status = Status::NotSupport("Not implemented");
37+
RecoverableError(status);
38+
return false;
39+
}
40+
41+
};
42+
43+
template <>
44+
inline bool CenturyFunction::Run(DateT left, BigIntT &result) {
45+
auto year = DateT::GetDatePart(left, TimeUnit::kYear);
46+
if (year > 0) {
47+
result = static_cast<i64>(ceil(year / 100.0));
48+
} else if (year < 0) {
49+
result = static_cast<i64>(floor(year / 100.0));
50+
} else {
51+
Status status = Status::InvalidParameterValue("Date", "0", "There is no year 0 in the AD/BC dating system.");
52+
RecoverableError(status);
53+
return false;
54+
}
55+
return true;
56+
}
57+
58+
template <>
59+
inline bool CenturyFunction::Run(DateTimeT left, BigIntT &result) {
60+
auto year = DateTimeT::GetDateTimePart(left, TimeUnit::kYear);
61+
if (year > 0) {
62+
result = static_cast<i64>(ceil(year / 100.0));
63+
} else if (year < 0) {
64+
result = static_cast<i64>(floor(year / 100.0));
65+
} else {
66+
Status status = Status::InvalidParameterValue("DateTime", "0", "There is no year 0 in the AD/BC dating system.");
67+
RecoverableError(status);
68+
return false;
69+
}
70+
71+
return true;
72+
}
73+
74+
template <>
75+
inline bool CenturyFunction::Run(TimestampT left, BigIntT &result) {
76+
auto year = TimestampT::GetDateTimePart(left, TimeUnit::kYear);
77+
if (year > 0) {
78+
result = static_cast<i64>(ceil(year / 100.0));
79+
} else if (year < 0) {
80+
result = static_cast<i64>(floor(year / 100.0));
81+
} else {
82+
Status status = Status::InvalidParameterValue("TimeStamp", "0", "There is no year 0 in the AD/BC dating system.");
83+
RecoverableError(status);
84+
return false;
85+
}
86+
return true;
87+
}
88+
89+
void RegisterCenturyFunction(const UniquePtr<Catalog> &catalog_ptr) {
90+
String func_name = "century";
91+
92+
SharedPtr<ScalarFunctionSet> function_set_ptr = MakeShared<ScalarFunctionSet>(func_name);
93+
94+
ScalarFunction century_date_function(func_name,
95+
{DataType(LogicalType::kDate)},
96+
{DataType(LogicalType::kBigInt)},
97+
&ScalarFunction::UnaryFunctionWithFailure<DateT, BigIntT, CenturyFunction>);
98+
function_set_ptr->AddFunction(century_date_function);
99+
100+
ScalarFunction century_datetime_function(func_name,
101+
{DataType(LogicalType::kDateTime)},
102+
{DataType(LogicalType::kBigInt)},
103+
&ScalarFunction::UnaryFunctionWithFailure<DateTimeT, BigIntT, CenturyFunction>);
104+
function_set_ptr->AddFunction(century_datetime_function);
105+
106+
ScalarFunction century_timestamp_function(func_name,
107+
{DataType(LogicalType::kTimestamp)},
108+
{DataType(LogicalType::kBigInt)},
109+
&ScalarFunction::UnaryFunctionWithFailure<TimestampT, BigIntT, CenturyFunction>);
110+
function_set_ptr->AddFunction(century_timestamp_function);
111+
112+
Catalog::AddFunctionSet(catalog_ptr.get(), function_set_ptr);
113+
}
114+
115+
} // namespace infinity

src/function/scalar/century.cppm

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// https://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
module;
14+
15+
export module century;
16+
17+
import stl;
18+
19+
namespace infinity {
20+
21+
class Catalog;
22+
23+
export void RegisterCenturyFunction(const UniquePtr<Catalog> &catalog_ptr);
24+
25+
} // namespace infinity

0 commit comments

Comments
 (0)