1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9#include "monetdb_config.h"
10#include "sql.h"
11#include "mal_instruction.h"
12
13#define do_date_trunc(val, DIVISOR) \
14 timestamp_create(timestamp_date(val), \
15 (timestamp_daytime(val) / (DIVISOR)) * (DIVISOR))
16
17#define date_trunc_time_loop(NAME, DIVISOR) \
18 do { \
19 if ( strcasecmp(*scale, NAME) == 0){ \
20 for( ; lo < hi; lo++) \
21 if (is_timestamp_nil(bt[lo])) { \
22 dt[lo] = timestamp_nil; \
23 } else { \
24 dt[lo] = do_date_trunc(bt[lo], DIVISOR); \
25 } \
26 } \
27 } while (0)
28
29static inline bool
30truncate_check(const char *scale)
31{
32 return
33 strcasecmp(scale, "millennium") == 0 ||
34 strcasecmp(scale, "century") == 0 ||
35 strcasecmp(scale, "decade") == 0 ||
36 strcasecmp(scale, "year") == 0 ||
37 strcasecmp(scale, "quarter" ) == 0 ||
38 strcasecmp(scale, "month") == 0 ||
39 strcasecmp(scale, "week") == 0 ||
40 strcasecmp(scale, "day") == 0 ||
41 strcasecmp(scale, "hour") == 0 ||
42 strcasecmp(scale, "minute") == 0 ||
43 strcasecmp(scale, "second") == 0 ||
44 strcasecmp(scale, "milliseconds") == 0 ||
45 strcasecmp(scale, "microseconds") == 0;
46}
47
48str
49bat_date_trunc(bat *res, const str *scale, const bat *bid)
50{
51 BAT *b, *bn;
52 oid lo, hi;
53 const timestamp *bt;
54 timestamp *dt;
55 char *msg = NULL;
56 date days;
57
58 if ( truncate_check(*scale) == 0)
59 throw(SQL, "batcalc.truncate_timestamp", SQLSTATE(HY005) "Improper directive ");
60
61 if ((b = BATdescriptor(*bid)) == NULL) {
62 throw(SQL, "batcalc.truncate_timestamp", SQLSTATE(HY005) "Cannot access column descriptor");
63 }
64 bn = COLnew(b->hseqbase, TYPE_timestamp, BATcount(b), TRANSIENT);
65 if (bn == NULL) {
66 BBPunfix(b->batCacheid);
67 throw(SQL, "sql.truncate", SQLSTATE(HY001) MAL_MALLOC_FAIL);
68 }
69
70 bt = (const timestamp *) Tloc(b, 0);
71 dt = (timestamp *) Tloc(bn, 0);
72
73 lo = 0;
74 hi = lo + BATcount(b);
75
76 date_trunc_time_loop("microseconds", 1);
77 date_trunc_time_loop("milliseconds", 1000);
78 date_trunc_time_loop("second", 1000000);
79 date_trunc_time_loop("minute", 1000000 * 60);
80 date_trunc_time_loop("hour", 1000000 * 60 * 24);
81
82 if ( strcasecmp(*scale, "day") == 0){
83 for( ; lo < hi; lo++)
84 if (is_timestamp_nil(bt[lo])) {
85 dt[lo] = timestamp_nil;
86 } else {
87 days = timestamp_date(bt[lo]);
88 dt[lo] = timestamp_fromdate(days);
89 }
90 }
91
92 if ( strcasecmp(*scale, "week") == 0){
93 for( ; lo < hi; lo++)
94 if (is_timestamp_nil(bt[lo])) {
95 dt[lo] = timestamp_nil;
96 } else {
97 days = timestamp_date(bt[lo]);
98 dt[lo] = timestamp_fromdate(date_add_day(days, 1 - date_dayofweek(days)));
99 }
100 }
101
102 if ( strcasecmp(*scale, "month") == 0){
103 for( ; lo < hi; lo++)
104 if (is_timestamp_nil(bt[lo])) {
105 dt[lo] = timestamp_nil;
106 } else {
107 days = timestamp_date(bt[lo]);
108 dt[lo] = timestamp_fromdate(
109 date_create(date_year(days),
110 date_month(days),
111 1));
112 }
113 }
114
115 if ( strcasecmp(*scale, "quarter") == 0){
116 for( ; lo < hi; lo++)
117 if (is_timestamp_nil(bt[lo])) {
118 dt[lo] = timestamp_nil;
119 } else {
120 days = timestamp_date(bt[lo]);
121 dt[lo] = timestamp_fromdate(
122 date_create(date_year(days),
123 (date_month(days) - 1) / 3 + 1,
124 1));
125 }
126 }
127
128 if ( strcasecmp(*scale, "year") == 0){
129 for( ; lo < hi; lo++)
130 if (is_timestamp_nil(bt[lo])) {
131 dt[lo] = timestamp_nil;
132 } else {
133 days = timestamp_date(bt[lo]);
134 dt[lo] = timestamp_fromdate(date_create(date_year(days), 1, 1));
135 }
136 }
137
138 if ( strcasecmp(*scale, "decade") == 0){
139 for( ; lo < hi; lo++)
140 if (is_timestamp_nil(bt[lo])) {
141 dt[lo] = timestamp_nil;
142 } else {
143 days = timestamp_date(bt[lo]);
144 dt[lo] = timestamp_fromdate(date_create((date_year(days) / 10) * 10, 1, 1));
145 }
146 }
147
148 if ( strcasecmp(*scale, "century") == 0){
149 for( ; lo < hi; lo++)
150 if (is_timestamp_nil(bt[lo])) {
151 dt[lo] = timestamp_nil;
152 } else {
153 days = timestamp_date(bt[lo]);
154 dt[lo] = timestamp_fromdate(date_create((date_year(days) / 100) * 100, 1, 1));
155 }
156 }
157
158 if ( strcasecmp(*scale, "millennium") == 0){
159 for( ; lo < hi; lo++)
160 if (is_timestamp_nil(bt[lo])) {
161 dt[lo] = timestamp_nil;
162 } else {
163 days = timestamp_date(bt[lo]);
164 dt[lo] = timestamp_fromdate(date_create((date_year(days) / 1000) * 1000, 1, 1));
165 }
166 }
167
168 BATsetcount(bn, (BUN) lo);
169 /* we can inherit most properties */
170 bn->tnonil = b->tnonil;
171 bn->tnil = b->tnil;
172 bn->tsorted = b->tsorted;
173 bn->trevsorted = b->trevsorted;
174 bn->tkey = false; /* can't be sure */
175 BBPkeepref(*res = bn->batCacheid);
176 BBPunfix(b->batCacheid);
177 return msg;
178}
179
180#define date_trunc_single_time(NAME, DIVISOR) \
181 do { \
182 if ( strcasecmp(*scale, NAME) == 0){ \
183 *dt = do_date_trunc(*bt, DIVISOR); \
184 } \
185 } while (0)
186
187str
188date_trunc(timestamp *dt, const str *scale, const timestamp *bt)
189{
190 str msg = MAL_SUCCEED;
191 date days;
192
193 if (truncate_check(*scale) == 0)
194 throw(SQL, "sql.truncate", SQLSTATE(HY001) "Improper directive ");
195
196 if (is_timestamp_nil(*bt)) {
197 *dt = timestamp_nil;
198 return MAL_SUCCEED;
199 }
200
201 date_trunc_single_time("microseconds", 1);
202 date_trunc_single_time("milliseconds", 1000);
203 date_trunc_single_time("second", 1000000);
204 date_trunc_single_time("minute", 1000000 * 60);
205 date_trunc_single_time("hour", 1000000 * 60 * 24);
206
207 if ( strcasecmp(*scale, "day") == 0){
208 days = timestamp_date(*bt);
209 *dt = timestamp_fromdate(days);
210 }
211
212 if ( strcasecmp(*scale, "week") == 0){
213 days = timestamp_date(*bt);
214 *dt = timestamp_fromdate(date_add_day(days, 1 - date_dayofweek(days)));
215 }
216
217 if ( strcasecmp(*scale, "month") == 0){
218 days = timestamp_date(*bt);
219 *dt = timestamp_fromdate(date_create(date_year(days), date_month(days), 1));
220 }
221
222 if ( strcasecmp(*scale, "quarter") == 0){
223 days = timestamp_date(*bt);
224 *dt = timestamp_fromdate(date_create(date_year(days), (date_month(days) - 1) / 3 + 1, 1));
225 }
226
227 if ( strcasecmp(*scale, "year") == 0){
228 days = timestamp_date(*bt);
229 *dt = timestamp_fromdate(date_create(date_year(days), 1, 1));
230 }
231
232 if ( strcasecmp(*scale, "decade") == 0){
233 days = timestamp_date(*bt);
234 *dt = timestamp_fromdate(date_create((date_year(days) / 10) * 10, 1, 1));
235 }
236
237 if ( strcasecmp(*scale, "century") == 0){
238 days = timestamp_date(*bt);
239 *dt = timestamp_fromdate(date_create((date_year(days) / 100) * 100, 1, 1));
240 }
241
242 if ( strcasecmp(*scale, "millennium") == 0){
243 days = timestamp_date(*bt);
244 *dt = timestamp_fromdate(date_create((date_year(days) / 1000) * 1000, 1, 1));
245 }
246 return msg;
247}
248