OmniSciDB  04ee39c94c
TimeGM.h
Go to the documentation of this file.
1 /*
2  * Copyright 2017 MapD Technologies, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  * @brief Functions to handle date/time types
16  **/
17 
18 #ifndef TIMEGM_H
19 #define TIMEGM_H
20 #include <cmath>
21 #include <cstring>
22 #include <ctime>
23 #include <sstream>
24 #include <type_traits>
25 #include "sqltypes.h"
26 
27 #ifndef __STDC_FORMAT_MACROS
28 #define __STDC_FORMAT_MACROS
29 #endif
30 #include <cinttypes>
31 
32 class TimeGM {
33  public:
34  time_t my_timegm(const struct tm* tm);
35  time_t my_timegm(const struct tm* tm, const time_t fsc, const int32_t dimen);
36  time_t my_timegm_days(const struct tm* tm);
37  time_t parse_fractional_seconds(uint64_t sfrac,
38  const int32_t ntotal,
39  const int32_t dimen);
40  time_t parse_meridians(const time_t& timeval,
41  const char* p,
42  const uint32_t hour,
43  const int32_t dimen);
44  static TimeGM& instance() {
45  static TimeGM timegm{};
46  return timegm;
47  }
48 
49  private:
50  /* Number of days per month (except for February in leap years). */
51  const int monoff[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
52  int is_leap_year(int year);
53  int leap_days(int y1, int y2);
54  TimeGM(){};
55  virtual ~TimeGM(){};
56 };
57 
58 template <SQLTypes SQL_TYPE>
60  public:
61  constexpr int64_t operator()(const std::string& datetime_str, const int32_t dimen) {
62  static_assert(is_datetime(SQL_TYPE));
63  str_ = datetime_str;
64  dimen_ = dimen;
65  return parseDateTimeString();
66  };
67 
68  private:
69  constexpr int64_t parseDateTimeString() {
70  char* tp = nullptr;
71  if constexpr (SQL_TYPE == kTIME) {
72  tm_.tm_hour = tm_.tm_min = tm_.tm_sec = -1;
73  parseTimePart<const char*>(str_.c_str(), tp);
74  tm_.tm_mday = 1;
75  tm_.tm_mon = 0;
76  tm_.tm_year = 70;
77  return static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_));
78  }
79  detectFormatFromString(
80  str_.c_str(), tp, "%Y-%m-%d", "%m/%d/%Y", "%d-%b-%y", "%d/%b/%Y");
81  if (!tp) {
82  return getEpochValue();
83  }
84  char* p = nullptr;
85  parseTimePart<char*&>(tp, p);
86  if constexpr (SQL_TYPE == kDATE) {
87  return static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_));
88  }
89  // handle fractional seconds
90  int64_t timeval = 0;
91  if (dimen_ > 0) {
92  const int64_t frac = parseFractionalSeconds(p);
93  timeval = static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_, frac, dimen_));
94  } else {
95  timeval = static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_));
96  if (*p == '.') {
97  p++;
98  }
99  }
100  if (*p != '\0') {
101  uint32_t hour = 0;
102  sscanf(tp, "%u", &hour);
103  timeval = static_cast<int64_t>(TimeGM::instance().parse_meridians(
104  static_cast<time_t>(timeval), p, hour, dimen_));
105  }
106  return timeval;
107  }
108 
109  template <typename T>
110  constexpr void parseTimePart(T s, char*& p) {
111  if constexpr (SQL_TYPE == kDATE) {
112  if (*s == 'T' || *s == ' ') {
113  ++s;
114  }
115  detectFormatFromString(s, p, "%z");
116  return;
117  } else if constexpr (SQL_TYPE == kTIME) {
118  detectFormatFromString(s, p, "%T %z", "%T", "%H%M%S", "%R");
119  if (tm_.tm_hour == -1 || tm_.tm_min == -1 || tm_.tm_sec == -1) {
120  throw std::runtime_error("Invalid TIME string " + str_);
121  }
122  return;
123  } else {
124  if (*s == 'T' || *s == ' ' || *s == ':') {
125  ++s;
126  } else {
127  throw std::runtime_error("Invalid TIMESTAMP break string " + std::string(s));
128  }
129  detectFormatFromString(s, p, "%T %z", "%T", "%H%M%S", "%R");
130  if (!p) {
131  // check for weird customer format and remove decimal seconds from string if there
132  // is a period followed by a number
133  char* startptr = nullptr;
134  char* endptr;
135  // find last decimal in string
136  int loop = strlen(s);
137  while (loop > 0) {
138  if (s[loop] == '.') {
139  // found last period
140  startptr = &s[loop];
141  break;
142  }
143  loop--;
144  }
145  if (startptr) {
146  // look for space
147  endptr = strchr(startptr, ' ');
148  if (endptr) {
149  // ok we found a start and and end remove the decimal portion will need to
150  // capture this for later
151  memmove(startptr, endptr, strlen(endptr) + 1);
152  }
153  }
154  detectFormatFromString(
155  s, p, "%T %z", "%I . %M . %S %p"); // customers weird '.' separated date
156  }
157  if (!p) {
158  throw std::runtime_error("Invalid TIMESTAMP time string " + std::string(s));
159  }
160  }
161  }
162 
163  constexpr int64_t parseFractionalSeconds(char*& p) {
164  if (*p == '.') {
165  p++;
166  uint64_t frac_num = 0;
167  int ntotal = 0;
168  sscanf(p, "%" SCNu64 "%n", &frac_num, &ntotal);
169  return static_cast<int64_t>(
170  TimeGM::instance().parse_fractional_seconds(frac_num, ntotal, dimen_));
171  } else if (*p == '\0') {
172  return 0;
173  } else { // check for misleading/unclear syntax
174  throw std::runtime_error("Unclear syntax for leading fractional seconds: " +
175  std::string(p));
176  }
177  }
178 
179  const int64_t getEpochValue() const {
180  // Check for unix time format
181  try {
182  return static_cast<int64_t>(std::stoll(str_));
183  } catch (const std::invalid_argument& ia) {
184  throw std::runtime_error("Invalid DATE/TIMESTAMP string " + str_);
185  }
186  }
187 
188  template <typename EVAL_STRING, typename TARGET_CHAR, typename... EVAL_FORMATS>
189  inline void detectFormatFromString(EVAL_STRING s,
190  TARGET_CHAR& p,
191  EVAL_FORMATS... eval_formats) {
192  ((p = strptime(s, eval_formats, &tm_)) || ...);
193  }
194 
195  std::string str_;
196  int32_t dimen_;
197  std::tm tm_{0};
198 };
199 
200 #endif // TIMEGM_H
int leap_days(int y1, int y2)
Definition: timegm.cpp:46
int is_leap_year(int year)
Definition: timegm.cpp:42
Definition: sqltypes.h:51
Definition: TimeGM.h:32
constexpr int64_t operator()(const std::string &datetime_str, const int32_t dimen)
Definition: TimeGM.h:61
time_t parse_meridians(const time_t &timeval, const char *p, const uint32_t hour, const int32_t dimen)
Definition: timegm.cpp:67
std::string str_
Definition: TimeGM.h:195
constexpr auto is_datetime(T sql_type)
Definition: sqltypes.h:191
time_t parse_fractional_seconds(uint64_t sfrac, const int32_t ntotal, const int32_t dimen)
Definition: timegm.cpp:52
Constants for Builtin SQL Types supported by MapD.
constexpr void parseTimePart(T s, char *&p)
Definition: TimeGM.h:110
time_t my_timegm(const struct tm *tm)
Definition: timegm.cpp:108
constexpr int64_t parseDateTimeString()
Definition: TimeGM.h:69
void detectFormatFromString(EVAL_STRING s, TARGET_CHAR &p, EVAL_FORMATS... eval_formats)
Definition: TimeGM.h:189
const int monoff[12]
Definition: TimeGM.h:51
time_t my_timegm_days(const struct tm *tm)
Definition: timegm.cpp:95
Definition: sqltypes.h:55
constexpr int64_t parseFractionalSeconds(char *&p)
Definition: TimeGM.h:163
TimeGM()
Definition: TimeGM.h:54
const int64_t getEpochValue() const
Definition: TimeGM.h:179
static TimeGM & instance()
Definition: TimeGM.h:44
virtual ~TimeGM()
Definition: TimeGM.h:55