1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2016 Brazil
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License version 2.1 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19#ifdef GRN_EMBEDDED
20# define GRN_PLUGIN_FUNCTION_TAG functions_time
21#endif
22
23#include <groonga/plugin.h>
24
25#include <math.h>
26
27typedef enum {
28 GRN_TIME_CLASSIFY_UNIT_SECOND,
29 GRN_TIME_CLASSIFY_UNIT_MINUTE,
30 GRN_TIME_CLASSIFY_UNIT_HOUR,
31 GRN_TIME_CLASSIFY_UNIT_DAY,
32 GRN_TIME_CLASSIFY_UNIT_WEEK,
33 GRN_TIME_CLASSIFY_UNIT_MONTH,
34 GRN_TIME_CLASSIFY_UNIT_YEAR
35} grn_time_classify_unit;
36
37static grn_obj *
38func_time_classify_raw(grn_ctx *ctx,
39 int n_args,
40 grn_obj **args,
41 grn_user_data *user_data,
42 const char *function_name,
43 grn_time_classify_unit unit)
44{
45 grn_obj *time;
46 uint32_t interval_raw = 1;
47 grn_obj *classed_time;
48 grn_bool accept_interval = GRN_TRUE;
49
50 switch (unit) {
51 case GRN_TIME_CLASSIFY_UNIT_SECOND :
52 case GRN_TIME_CLASSIFY_UNIT_MINUTE :
53 case GRN_TIME_CLASSIFY_UNIT_HOUR :
54 accept_interval = GRN_TRUE;
55 break;
56 case GRN_TIME_CLASSIFY_UNIT_DAY :
57 case GRN_TIME_CLASSIFY_UNIT_WEEK :
58 accept_interval = GRN_FALSE;
59 break;
60 case GRN_TIME_CLASSIFY_UNIT_MONTH :
61 case GRN_TIME_CLASSIFY_UNIT_YEAR :
62 accept_interval = GRN_TRUE;
63 break;
64 }
65
66 if (accept_interval) {
67 if (!(n_args == 1 || n_args == 2)) {
68 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
69 "%s(): "
70 "wrong number of arguments (%d for 1..2)",
71 function_name,
72 n_args);
73 return NULL;
74 }
75 } else {
76 if (n_args != 1) {
77 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
78 "%s(): "
79 "wrong number of arguments (%d for 1)",
80 function_name,
81 n_args);
82 return NULL;
83 }
84 }
85
86 time = args[0];
87 if (!(time->header.type == GRN_BULK &&
88 time->header.domain == GRN_DB_TIME)) {
89 grn_obj inspected;
90
91 GRN_TEXT_INIT(&inspected, 0);
92 grn_inspect(ctx, &inspected, time);
93 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
94 "%s(): "
95 "the first argument must be a time: "
96 "<%.*s>",
97 function_name,
98 (int)GRN_TEXT_LEN(&inspected),
99 GRN_TEXT_VALUE(&inspected));
100 GRN_OBJ_FIN(ctx, &inspected);
101 return NULL;
102 }
103
104 if (n_args == 2) {
105 grn_obj *interval;
106 grn_obj casted_interval;
107
108 interval = args[1];
109 if (!(interval->header.type == GRN_BULK &&
110 grn_type_id_is_number_family(ctx, interval->header.domain))) {
111 grn_obj inspected;
112
113 GRN_TEXT_INIT(&inspected, 0);
114 grn_inspect(ctx, &inspected, interval);
115 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
116 "%s(): "
117 "the second argument must be a number: "
118 "<%.*s>",
119 function_name,
120 (int)GRN_TEXT_LEN(&inspected),
121 GRN_TEXT_VALUE(&inspected));
122 GRN_OBJ_FIN(ctx, &inspected);
123 return NULL;
124 }
125
126 GRN_VALUE_FIX_SIZE_INIT(&casted_interval, 0, GRN_DB_UINT32);
127 grn_obj_cast(ctx, interval, &casted_interval, GRN_FALSE);
128 interval_raw = GRN_UINT32_VALUE(&casted_interval);
129 GRN_OBJ_FIN(ctx, &casted_interval);
130
131 if (interval_raw == 0) {
132 grn_obj inspected;
133
134 GRN_TEXT_INIT(&inspected, 0);
135 grn_inspect(ctx, &inspected, interval);
136 GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
137 "%s(): "
138 "the second argument must not be zero: "
139 "<%.*s>",
140 function_name,
141 (int)GRN_TEXT_LEN(&inspected),
142 GRN_TEXT_VALUE(&inspected));
143 GRN_OBJ_FIN(ctx, &inspected);
144 return NULL;
145 }
146 }
147
148 {
149 int64_t time_raw;
150 struct tm tm;
151 int64_t classed_time_raw;
152
153 time_raw = GRN_TIME_VALUE(time);
154 if (!grn_time_to_tm(ctx, time_raw, &tm)) {
155 return NULL;
156 }
157
158 switch (unit) {
159 case GRN_TIME_CLASSIFY_UNIT_SECOND :
160 tm.tm_sec = (tm.tm_sec / interval_raw) * interval_raw;
161 break;
162 case GRN_TIME_CLASSIFY_UNIT_MINUTE :
163 tm.tm_min = (tm.tm_min / interval_raw) * interval_raw;
164 tm.tm_sec = 0;
165 break;
166 case GRN_TIME_CLASSIFY_UNIT_HOUR :
167 tm.tm_hour = (tm.tm_hour / interval_raw) * interval_raw;
168 tm.tm_min = 0;
169 tm.tm_sec = 0;
170 break;
171 case GRN_TIME_CLASSIFY_UNIT_DAY :
172 tm.tm_hour = 0;
173 tm.tm_min = 0;
174 tm.tm_sec = 0;
175 break;
176 case GRN_TIME_CLASSIFY_UNIT_WEEK :
177 if ((tm.tm_mday - tm.tm_wday) >= 0) {
178 tm.tm_mday -= tm.tm_wday;
179 } else {
180 int n_underflowed_mday = -(tm.tm_mday - tm.tm_wday);
181 int mday;
182 int max_mday = 31;
183
184 if (tm.tm_mon == 0) {
185 tm.tm_year--;
186 tm.tm_mon = 11;
187 } else {
188 tm.tm_mon--;
189 }
190
191 for (mday = max_mday; mday > n_underflowed_mday; mday--) {
192 int64_t unused;
193 tm.tm_mday = mday;
194 if (grn_time_from_tm(ctx, &unused, &tm)) {
195 break;
196 }
197 }
198 tm.tm_mday -= n_underflowed_mday;
199 }
200 tm.tm_hour = 0;
201 tm.tm_min = 0;
202 tm.tm_sec = 0;
203 break;
204 case GRN_TIME_CLASSIFY_UNIT_MONTH :
205 tm.tm_mon = (tm.tm_mon / interval_raw) * interval_raw;
206 tm.tm_mday = 1;
207 tm.tm_hour = 0;
208 tm.tm_min = 0;
209 tm.tm_sec = 0;
210 break;
211 case GRN_TIME_CLASSIFY_UNIT_YEAR :
212 tm.tm_year = (((1900 + tm.tm_year) / interval_raw) * interval_raw) - 1900;
213 tm.tm_mon = 0;
214 tm.tm_mday = 1;
215 tm.tm_hour = 0;
216 tm.tm_min = 0;
217 tm.tm_sec = 0;
218 break;
219 }
220
221 if (!grn_time_from_tm(ctx, &classed_time_raw, &tm)) {
222 return NULL;
223 }
224
225 classed_time = grn_plugin_proc_alloc(ctx,
226 user_data,
227 time->header.domain,
228 0);
229 if (!classed_time) {
230 return NULL;
231 }
232 GRN_TIME_SET(ctx, classed_time, classed_time_raw);
233
234 return classed_time;
235 }
236}
237
238static grn_obj *
239func_time_classify_second(grn_ctx *ctx, int n_args, grn_obj **args,
240 grn_user_data *user_data)
241{
242 return func_time_classify_raw(ctx,
243 n_args,
244 args,
245 user_data,
246 "time_classify_second",
247 GRN_TIME_CLASSIFY_UNIT_SECOND);
248}
249
250static grn_obj *
251func_time_classify_minute(grn_ctx *ctx, int n_args, grn_obj **args,
252 grn_user_data *user_data)
253{
254 return func_time_classify_raw(ctx,
255 n_args,
256 args,
257 user_data,
258 "time_classify_minute",
259 GRN_TIME_CLASSIFY_UNIT_MINUTE);
260}
261
262static grn_obj *
263func_time_classify_hour(grn_ctx *ctx, int n_args, grn_obj **args,
264 grn_user_data *user_data)
265{
266 return func_time_classify_raw(ctx,
267 n_args,
268 args,
269 user_data,
270 "time_classify_hour",
271 GRN_TIME_CLASSIFY_UNIT_HOUR);
272}
273
274static grn_obj *
275func_time_classify_day(grn_ctx *ctx, int n_args, grn_obj **args,
276 grn_user_data *user_data)
277{
278 return func_time_classify_raw(ctx,
279 n_args,
280 args,
281 user_data,
282 "time_classify_day",
283 GRN_TIME_CLASSIFY_UNIT_DAY);
284}
285
286static grn_obj *
287func_time_classify_week(grn_ctx *ctx, int n_args, grn_obj **args,
288 grn_user_data *user_data)
289{
290 return func_time_classify_raw(ctx,
291 n_args,
292 args,
293 user_data,
294 "time_classify_week",
295 GRN_TIME_CLASSIFY_UNIT_WEEK);
296}
297
298static grn_obj *
299func_time_classify_month(grn_ctx *ctx, int n_args, grn_obj **args,
300 grn_user_data *user_data)
301{
302 return func_time_classify_raw(ctx,
303 n_args,
304 args,
305 user_data,
306 "time_classify_month",
307 GRN_TIME_CLASSIFY_UNIT_MONTH);
308}
309
310static grn_obj *
311func_time_classify_year(grn_ctx *ctx, int n_args, grn_obj **args,
312 grn_user_data *user_data)
313{
314 return func_time_classify_raw(ctx,
315 n_args,
316 args,
317 user_data,
318 "time_classify_year",
319 GRN_TIME_CLASSIFY_UNIT_YEAR);
320}
321
322grn_rc
323GRN_PLUGIN_INIT(grn_ctx *ctx)
324{
325 return ctx->rc;
326}
327
328grn_rc
329GRN_PLUGIN_REGISTER(grn_ctx *ctx)
330{
331 grn_rc rc = GRN_SUCCESS;
332
333 grn_proc_create(ctx,
334 "time_classify_second", -1,
335 GRN_PROC_FUNCTION,
336 func_time_classify_second,
337 NULL, NULL, 0, NULL);
338 grn_proc_create(ctx,
339 "time_classify_minute", -1,
340 GRN_PROC_FUNCTION,
341 func_time_classify_minute,
342 NULL, NULL, 0, NULL);
343 grn_proc_create(ctx,
344 "time_classify_hour", -1,
345 GRN_PROC_FUNCTION,
346 func_time_classify_hour,
347 NULL, NULL, 0, NULL);
348 grn_proc_create(ctx,
349 "time_classify_day", -1,
350 GRN_PROC_FUNCTION,
351 func_time_classify_day,
352 NULL, NULL, 0, NULL);
353 grn_proc_create(ctx,
354 "time_classify_week", -1,
355 GRN_PROC_FUNCTION,
356 func_time_classify_week,
357 NULL, NULL, 0, NULL);
358 grn_proc_create(ctx,
359 "time_classify_month", -1,
360 GRN_PROC_FUNCTION,
361 func_time_classify_month,
362 NULL, NULL, 0, NULL);
363 grn_proc_create(ctx,
364 "time_classify_year", -1,
365 GRN_PROC_FUNCTION,
366 func_time_classify_year,
367 NULL, NULL, 0, NULL);
368
369 return rc;
370}
371
372grn_rc
373GRN_PLUGIN_FIN(grn_ctx *ctx)
374{
375 return GRN_SUCCESS;
376}
377