1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2009-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#include "grn_time.h"
20#include "grn_ctx.h"
21#include "grn_str.h"
22
23#include <stdio.h>
24#include <time.h>
25
26#if defined(HAVE__LOCALTIME64_S) && defined(__GNUC__)
27# ifdef _WIN64
28# define localtime_s(tm, time) _localtime64_s(tm, time)
29# else /* _WIN64 */
30# define localtime_s(tm, time) _localtime32_s(tm, time)
31# endif /* _WIN64 */
32#endif /* defined(HAVE__LOCALTIME64_S) && defined(__GNUC__) */
33
34/* fixme by 2038 */
35
36grn_rc
37grn_timeval_now(grn_ctx *ctx, grn_timeval *tv)
38{
39#ifdef HAVE_CLOCK_GETTIME
40 struct timespec t;
41 if (clock_gettime(CLOCK_REALTIME, &t)) {
42 SERR("clock_gettime");
43 } else {
44 tv->tv_sec = t.tv_sec;
45 tv->tv_nsec = t.tv_nsec;
46 }
47 return ctx->rc;
48#else /* HAVE_CLOCK_GETTIME */
49# ifdef WIN32
50 time_t t;
51 struct _timeb tb;
52 time(&t);
53 _ftime(&tb);
54 tv->tv_sec = t;
55 tv->tv_nsec = tb.millitm * (GRN_TIME_NSEC_PER_SEC / 1000);
56 return GRN_SUCCESS;
57# else /* WIN32 */
58 struct timeval t;
59 if (gettimeofday(&t, NULL)) {
60 SERR("gettimeofday");
61 } else {
62 tv->tv_sec = t.tv_sec;
63 tv->tv_nsec = GRN_TIME_USEC_TO_NSEC(t.tv_usec);
64 }
65 return ctx->rc;
66# endif /* WIN32 */
67#endif /* HAVE_CLOCK_GETTIME */
68}
69
70void
71grn_time_now(grn_ctx *ctx, grn_obj *obj)
72{
73 grn_timeval tv;
74 grn_timeval_now(ctx, &tv);
75 GRN_TIME_SET(ctx, obj, GRN_TIME_PACK(tv.tv_sec,
76 GRN_TIME_NSEC_TO_USEC(tv.tv_nsec)));
77}
78
79static grn_bool
80grn_time_t_to_tm(grn_ctx *ctx, const time_t time, struct tm *tm)
81{
82 grn_bool success;
83 const char *function_name;
84#ifdef HAVE__LOCALTIME64_S
85 function_name = "localtime_s";
86 success = (localtime_s(tm, &time) == 0);
87#else /* HAVE__LOCALTIME64_S */
88# ifdef HAVE_LOCALTIME_R
89 function_name = "localtime_r";
90 success = (localtime_r(&time, tm) != NULL);
91# else /* HAVE_LOCALTIME_R */
92 function_name = "localtime";
93 {
94 struct tm *local_tm;
95 local_tm = localtime(&time);
96 if (local_tm) {
97 success = GRN_TRUE;
98 memcpy(tm, local_tm, sizeof(struct tm));
99 } else {
100 success = GRN_FALSE;
101 }
102 }
103# endif /* HAVE_LOCALTIME_R */
104#endif /* HAVE__LOCALTIME64_S */
105 if (!success) {
106 SERR("%s: failed to convert time_t to struct tm: <%" GRN_FMT_INT64D ">",
107 function_name,
108 (int64_t)time);
109 }
110 return success;
111}
112
113struct tm *
114grn_timeval2tm(grn_ctx *ctx, grn_timeval *tv, struct tm *tm)
115{
116 if (grn_time_t_to_tm(ctx, tv->tv_sec, tm)) {
117 return tm;
118 } else {
119 return NULL;
120 }
121}
122
123grn_bool
124grn_time_to_tm(grn_ctx *ctx, int64_t time, struct tm *tm)
125{
126 int64_t sec;
127 int32_t usec;
128
129 GRN_TIME_UNPACK(time, sec, usec);
130 return grn_time_t_to_tm(ctx, sec, tm);
131}
132
133static grn_bool
134grn_time_t_from_tm(grn_ctx *ctx, time_t *time, struct tm *tm)
135{
136 grn_bool success;
137
138 tm->tm_yday = -1;
139 *time = mktime(tm);
140 success = (tm->tm_yday != -1);
141 if (!success) {
142 ERR(GRN_INVALID_ARGUMENT,
143 "mktime: failed to convert struct tm to time_t: "
144 "<%04d-%02d-%02dT%02d:%02d:%02d>(%d)",
145 1900 + tm->tm_year,
146 tm->tm_mon + 1,
147 tm->tm_mday,
148 tm->tm_hour,
149 tm->tm_min,
150 tm->tm_sec,
151 tm->tm_isdst);
152 }
153 return success;
154}
155
156grn_bool
157grn_time_from_tm(grn_ctx *ctx, int64_t *time, struct tm *tm)
158{
159 time_t sec_time_t;
160 int64_t sec;
161 int32_t usec = 0;
162
163 if (!grn_time_t_from_tm(ctx, &sec_time_t, tm)) {
164 return GRN_FALSE;
165 }
166
167 sec = sec_time_t;
168 *time = GRN_TIME_PACK(sec, usec);
169 return GRN_TRUE;
170}
171
172grn_rc
173grn_timeval2str(grn_ctx *ctx, grn_timeval *tv, char *buf, size_t buf_size)
174{
175 struct tm tm;
176 struct tm *ltm;
177 ltm = grn_timeval2tm(ctx, tv, &tm);
178 grn_snprintf(buf, buf_size, GRN_TIMEVAL_STR_SIZE,
179 GRN_TIMEVAL_STR_FORMAT,
180 ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday,
181 ltm->tm_hour, ltm->tm_min, ltm->tm_sec,
182 (int)(GRN_TIME_NSEC_TO_USEC(tv->tv_nsec)));
183 if (buf_size > GRN_TIMEVAL_STR_SIZE) {
184 buf[GRN_TIMEVAL_STR_SIZE - 1] = '\0';
185 } else {
186 buf[buf_size - 1] = '\0';
187 }
188 return ctx->rc;
189}
190
191grn_rc
192grn_str2timeval(const char *str, uint32_t str_len, grn_timeval *tv)
193{
194 struct tm tm;
195 const char *r1, *r2, *rend = str + str_len;
196 uint32_t uv;
197 memset(&tm, 0, sizeof(struct tm));
198
199 tm.tm_year = (int)grn_atoui(str, rend, &r1) - 1900;
200 if ((r1 + 1) >= rend || (*r1 != '/' && *r1 != '-')) {
201 return GRN_INVALID_ARGUMENT;
202 }
203 r1++;
204 tm.tm_mon = (int)grn_atoui(r1, rend, &r1) - 1;
205 if ((r1 + 1) >= rend || (*r1 != '/' && *r1 != '-') ||
206 tm.tm_mon < 0 || tm.tm_mon >= 12) { return GRN_INVALID_ARGUMENT; }
207 r1++;
208 tm.tm_mday = (int)grn_atoui(r1, rend, &r1);
209 if ((r1 + 1) >= rend || *r1 != ' ' ||
210 tm.tm_mday < 1 || tm.tm_mday > 31) { return GRN_INVALID_ARGUMENT; }
211
212 tm.tm_hour = (int)grn_atoui(++r1, rend, &r2);
213 if ((r2 + 1) >= rend || r1 == r2 || *r2 != ':' ||
214 tm.tm_hour < 0 || tm.tm_hour >= 24) {
215 return GRN_INVALID_ARGUMENT;
216 }
217 r1 = r2 + 1;
218 tm.tm_min = (int)grn_atoui(r1, rend, &r2);
219 if ((r2 + 1) >= rend || r1 == r2 || *r2 != ':' ||
220 tm.tm_min < 0 || tm.tm_min >= 60) {
221 return GRN_INVALID_ARGUMENT;
222 }
223 r1 = r2 + 1;
224 tm.tm_sec = (int)grn_atoui(r1, rend, &r2);
225 if (r1 == r2 ||
226 tm.tm_sec < 0 || tm.tm_sec > 61 /* leap 2sec */) {
227 return GRN_INVALID_ARGUMENT;
228 }
229 r1 = r2;
230 tm.tm_yday = -1;
231 tm.tm_isdst = -1;
232
233 /* tm_yday is set appropriately (0-365) on successful completion. */
234 tv->tv_sec = mktime(&tm);
235 if (tm.tm_yday == -1) { return GRN_INVALID_ARGUMENT; }
236 if ((r1 + 1) < rend && *r1 == '.') { r1++; }
237 uv = grn_atoi(r1, rend, &r2);
238 while (r2 < r1 + 6) {
239 uv *= 10;
240 r2++;
241 }
242 if (uv >= GRN_TIME_USEC_PER_SEC) { return GRN_INVALID_ARGUMENT; }
243 tv->tv_nsec = GRN_TIME_USEC_TO_NSEC(uv);
244 return GRN_SUCCESS;
245}
246