1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2010-2013 Kentoku SHIBA
4 Copyright(C) 2011-2015 Kouhei Sutou <kou@clear-code.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "mrn_time_converter.hpp"
22
23#ifdef min
24# undef min
25#endif
26#ifdef max
27# undef max
28#endif
29
30#include <limits>
31
32// for debug
33#define MRN_CLASS_NAME "mrn::TimeConverter"
34
35namespace mrn {
36 TimeConverter::TimeConverter() {
37 }
38
39 TimeConverter::~TimeConverter() {
40 }
41
42 time_t TimeConverter::tm_to_time_gm(struct tm *time, bool *truncated) {
43 MRN_DBUG_ENTER_METHOD();
44 *truncated = true;
45 struct tm gmdate;
46 time->tm_yday = -1;
47 time->tm_isdst = -1;
48 time_t sec_t = mktime(time);
49 if (time->tm_yday == -1) {
50 DBUG_RETURN(-1);
51 }
52 if (!gmtime_r(&sec_t, &gmdate)) {
53 DBUG_RETURN(-1);
54 }
55 int32 mrn_utc_diff_in_seconds =
56 (
57 time->tm_mday > 25 && gmdate.tm_mday == 1 ? -1 :
58 time->tm_mday == 1 && gmdate.tm_mday > 25 ? 1 :
59 time->tm_mday - gmdate.tm_mday
60 ) * 24 * 60 * 60 +
61 (time->tm_hour - gmdate.tm_hour) * 60 * 60 +
62 (time->tm_min - gmdate.tm_min) * 60 +
63 (time->tm_sec - gmdate.tm_sec);
64 DBUG_PRINT("info", ("mroonga: time->tm_year=%d", time->tm_year));
65 DBUG_PRINT("info", ("mroonga: time->tm_mon=%d", time->tm_mon));
66 DBUG_PRINT("info", ("mroonga: time->tm_mday=%d", time->tm_mday));
67 DBUG_PRINT("info", ("mroonga: time->tm_hour=%d", time->tm_hour));
68 DBUG_PRINT("info", ("mroonga: time->tm_min=%d", time->tm_min));
69 DBUG_PRINT("info", ("mroonga: time->tm_sec=%d", time->tm_sec));
70 DBUG_PRINT("info", ("mroonga: mrn_utc_diff_in_seconds=%d",
71 mrn_utc_diff_in_seconds));
72 if (mrn_utc_diff_in_seconds > 0) {
73 if (sec_t > std::numeric_limits<time_t>::max() - mrn_utc_diff_in_seconds) {
74 DBUG_RETURN(-1);
75 }
76 } else {
77 if (sec_t < std::numeric_limits<time_t>::min() - mrn_utc_diff_in_seconds) {
78 DBUG_RETURN(-1);
79 }
80 }
81 *truncated = false;
82 DBUG_RETURN(sec_t + mrn_utc_diff_in_seconds);
83 }
84
85 long long int TimeConverter::tm_to_grn_time(struct tm *time, int usec,
86 bool *truncated) {
87 MRN_DBUG_ENTER_METHOD();
88
89 long long int sec = tm_to_time_gm(time, truncated);
90
91 DBUG_PRINT("info", ("mroonga: sec=%lld", sec));
92 DBUG_PRINT("info", ("mroonga: usec=%d", usec));
93
94 long long int grn_time = *truncated ? 0 : GRN_TIME_PACK(sec, usec);
95
96 DBUG_RETURN(grn_time);
97 }
98
99 long long int TimeConverter::mysql_time_to_grn_time(MYSQL_TIME *mysql_time,
100 bool *truncated) {
101 MRN_DBUG_ENTER_METHOD();
102
103 int usec = mysql_time->second_part;
104 long long int grn_time = 0;
105
106 *truncated = false;
107 switch (mysql_time->time_type) {
108 case MYSQL_TIMESTAMP_DATE:
109 {
110 DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATE"));
111 struct tm date;
112 memset(&date, 0, sizeof(struct tm));
113 date.tm_year = mysql_time->year - TM_YEAR_BASE;
114 if (mysql_time->month > 0) {
115 date.tm_mon = mysql_time->month - 1;
116 } else {
117 date.tm_mon = 0;
118 *truncated = true;
119 }
120 if (mysql_time->day > 0) {
121 date.tm_mday = mysql_time->day;
122 } else {
123 date.tm_mday = 1;
124 *truncated = true;
125 }
126 DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year));
127 DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon));
128 DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday));
129 bool tm_truncated = false;
130 grn_time = tm_to_grn_time(&date, usec, &tm_truncated);
131 if (tm_truncated) {
132 *truncated = true;
133 }
134 }
135 break;
136 case MYSQL_TIMESTAMP_DATETIME:
137 {
138 DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATETIME"));
139 struct tm datetime;
140 memset(&datetime, 0, sizeof(struct tm));
141 datetime.tm_year = mysql_time->year - TM_YEAR_BASE;
142 if (mysql_time->month > 0) {
143 datetime.tm_mon = mysql_time->month - 1;
144 } else {
145 datetime.tm_mon = 0;
146 *truncated = true;
147 }
148 if (mysql_time->day > 0) {
149 datetime.tm_mday = mysql_time->day;
150 } else {
151 datetime.tm_mday = 1;
152 *truncated = true;
153 }
154 datetime.tm_hour = mysql_time->hour;
155 datetime.tm_min = mysql_time->minute;
156 datetime.tm_sec = mysql_time->second;
157 DBUG_PRINT("info", ("mroonga: tm_year=%d", datetime.tm_year));
158 DBUG_PRINT("info", ("mroonga: tm_mon=%d", datetime.tm_mon));
159 DBUG_PRINT("info", ("mroonga: tm_mday=%d", datetime.tm_mday));
160 DBUG_PRINT("info", ("mroonga: tm_hour=%d", datetime.tm_hour));
161 DBUG_PRINT("info", ("mroonga: tm_min=%d", datetime.tm_min));
162 DBUG_PRINT("info", ("mroonga: tm_sec=%d", datetime.tm_sec));
163 bool tm_truncated = false;
164 grn_time = tm_to_grn_time(&datetime, usec, &tm_truncated);
165 if (tm_truncated) {
166 *truncated = true;
167 }
168 }
169 break;
170 case MYSQL_TIMESTAMP_TIME:
171 {
172 DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_TIME"));
173 int sec =
174 mysql_time->hour * 60 * 60 +
175 mysql_time->minute * 60 +
176 mysql_time->second;
177 DBUG_PRINT("info", ("mroonga: sec=%d", sec));
178 grn_time = GRN_TIME_PACK(sec, usec);
179 if (mysql_time->neg) {
180 grn_time = -grn_time;
181 }
182 }
183 break;
184 default:
185 DBUG_PRINT("info", ("mroonga: default"));
186 grn_time = 0;
187 break;
188 }
189
190 DBUG_RETURN(grn_time);
191 }
192
193 void TimeConverter::grn_time_to_mysql_time(long long int grn_time,
194 MYSQL_TIME *mysql_time) {
195 MRN_DBUG_ENTER_METHOD();
196 long long int sec;
197 int usec;
198 GRN_TIME_UNPACK(grn_time, sec, usec);
199 DBUG_PRINT("info", ("mroonga: sec=%lld", sec));
200 DBUG_PRINT("info", ("mroonga: usec=%d", usec));
201 switch (mysql_time->time_type) {
202 case MYSQL_TIMESTAMP_DATE:
203 {
204 DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATE"));
205 struct tm date;
206 time_t sec_t = sec;
207 // TODO: Add error check
208 gmtime_r(&sec_t, &date);
209 DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year));
210 mysql_time->year = date.tm_year + TM_YEAR_BASE;
211 DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon));
212 mysql_time->month = date.tm_mon + 1;
213 DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday));
214 mysql_time->day = date.tm_mday;
215 }
216 break;
217 case MYSQL_TIMESTAMP_DATETIME:
218 {
219 DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATETIME"));
220 struct tm date;
221 time_t sec_t = sec;
222 // TODO: Add error check
223 gmtime_r(&sec_t, &date);
224 DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year));
225 mysql_time->year = date.tm_year + TM_YEAR_BASE;
226 DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon));
227 mysql_time->month = date.tm_mon + 1;
228 DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday));
229 mysql_time->day = date.tm_mday;
230 DBUG_PRINT("info", ("mroonga: tm_hour=%d", date.tm_hour));
231 mysql_time->hour = date.tm_hour;
232 DBUG_PRINT("info", ("mroonga: tm_min=%d", date.tm_min));
233 mysql_time->minute = date.tm_min;
234 DBUG_PRINT("info", ("mroonga: tm_sec=%d", date.tm_sec));
235 mysql_time->second = date.tm_sec;
236 mysql_time->second_part = usec;
237 }
238 break;
239 case MYSQL_TIMESTAMP_TIME:
240 DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_TIME"));
241 if (sec < 0) {
242 mysql_time->neg = true;
243 sec = -sec;
244 }
245 mysql_time->hour = static_cast<unsigned int>(sec / 60 / 60);
246 mysql_time->minute = sec / 60 % 60;
247 mysql_time->second = sec % 60;
248 mysql_time->second_part = usec;
249 break;
250 default:
251 DBUG_PRINT("info", ("mroonga: default"));
252 break;
253 }
254 DBUG_VOID_RETURN;
255 }
256
257 long long int TimeConverter::mysql_datetime_to_grn_time(long long int mysql_datetime,
258 bool *truncated) {
259 MRN_DBUG_ENTER_METHOD();
260
261 MYSQL_TIME mysql_time;
262 mysql_time.time_type = MYSQL_TIMESTAMP_DATETIME;
263 mysql_time.neg = 0;
264 mysql_time.second_part = 0;
265 mysql_time.second = (mysql_datetime % 100);
266 mysql_time.minute = (mysql_datetime / 100 % 100);
267 mysql_time.hour = (mysql_datetime / 10000 % 100);
268 mysql_time.day = (mysql_datetime / 1000000 % 100);
269 mysql_time.month = (mysql_datetime / 100000000 % 100);
270 mysql_time.year = (mysql_datetime / 10000000000LL % 10000);
271
272 long long int grn_time = mysql_time_to_grn_time(&mysql_time, truncated);
273
274 DBUG_RETURN(grn_time);
275 }
276
277 long long int TimeConverter::grn_time_to_mysql_datetime(long long int grn_time) {
278 MRN_DBUG_ENTER_METHOD();
279
280 MYSQL_TIME mysql_time;
281 mysql_time.time_type = MYSQL_TIMESTAMP_DATETIME;
282
283 grn_time_to_mysql_time(grn_time, &mysql_time);
284
285 long long int mysql_datetime =
286 (mysql_time.second * 1) +
287 (mysql_time.minute * 100) +
288 (mysql_time.hour * 10000) +
289 (mysql_time.day * 1000000) +
290 (mysql_time.month * 100000000) +
291 (mysql_time.year * 10000000000LL);
292
293 DBUG_RETURN(mysql_datetime);
294 }
295}
296