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 | |
35 | namespace 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 | |