OmniSciDB  1dac507f6e
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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(tm const* tm);
35  time_t my_timegm(tm const* tm, const time_t fsc, const int32_t dimen);
36  time_t my_timegm_days(tm const* 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  time_t get_overflow_underflow_safe_epoch(tm const* tm,
51  const time_t fsc,
52  const int32_t dimen);
53 
54  /* Number of days per month (except for February in leap years). */
55  const int monoff[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
56  int is_leap_year(int year);
57  int leap_days(int y1, int y2);
58  TimeGM(){};
59  virtual ~TimeGM(){};
60 };
61 
62 template <SQLTypes SQL_TYPE>
64  public:
65  constexpr int64_t operator()(const std::string& datetime_str, const int32_t dimen) {
66  static_assert(is_datetime(SQL_TYPE));
67  str_ = datetime_str;
68  dimen_ = dimen;
69  return parseDateTimeString();
70  };
71 
72  private:
73  constexpr int64_t parseDateTimeString() {
74  char* tp = nullptr;
75  if constexpr (SQL_TYPE == kTIME) {
76  char const* x = str_.c_str();
77  parseTimePart(x, tp);
78  tm_.tm_mday = 1;
79  tm_.tm_mon = 0;
80  tm_.tm_year = 70;
81  return static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_));
82  }
84  str_.c_str(), tp, "%Y-%m-%d", "%m/%d/%Y", "%d-%b-%y", "%d/%b/%Y");
85  if (!tp) {
86  return getEpochValue();
87  }
88  char* p = nullptr;
89  parseTimePart(tp, p);
90  if constexpr (SQL_TYPE == kDATE) {
91  return static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_));
92  }
93  // handle fractional seconds
94  int64_t timeval = 0;
95  if (dimen_ > 0) {
96  const int64_t frac = parseFractionalSeconds(p);
97  timeval = static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_, frac, dimen_));
98  } else {
99  timeval = static_cast<int64_t>(TimeGM::instance().my_timegm(&tm_));
100  if (*p == '.') {
101  p++;
102  }
103  }
104  if (*p != '\0') {
105  uint32_t hour = 0;
106  sscanf(tp, "%u", &hour);
107  timeval = static_cast<int64_t>(TimeGM::instance().parse_meridians(
108  static_cast<time_t>(timeval), p, hour, dimen_));
109  }
110  return timeval;
111  }
112 
113  template <typename T>
114  constexpr void parseTimePart(T*& s, std::remove_const_t<T>*& p) {
115  if constexpr (SQL_TYPE == kDATE) {
116  if (*s == 'T' || *s == ' ') {
117  ++s;
118  }
119  detectFormatFromString(s, p, "%z");
120  return;
121  } else if constexpr (SQL_TYPE == kTIME) {
122  if (*s == 'T') {
123  ++s;
124  }
125  detectFormatFromString(s, p, "%T %z", "%T", "%H%M%S", "%R");
126  if (!p) {
127  throw std::runtime_error("Invalid TIME string " + str_);
128  }
129  return;
130  } else {
131  if (*s == 'T' || *s == ' ' || *s == ':') {
132  ++s;
133  } else {
134  throw std::runtime_error("Invalid TIMESTAMP break string " + std::string(s));
135  }
136  detectFormatFromString(s, p, "%T %z", "%T", "%H%M%S", "%R");
137  if (!p) {
138  // check for weird customer format and remove decimal seconds from string if there
139  // is a period followed by a number
140  char* startptr = nullptr;
141  char* endptr = nullptr;
142  // find last decimal in string
143  int loop = strlen(s);
144  while (loop > 0) {
145  if (s[loop] == '.') {
146  // found last period
147  startptr = &s[loop];
148  break;
149  }
150  loop--;
151  }
152  if (startptr) {
153  // look for space
154  endptr = strchr(startptr, ' ');
155  if (endptr) {
156  // ok we found a start and and end remove the decimal portion will need to
157  // capture this for later
158  memmove(startptr, endptr, strlen(endptr) + 1);
159  }
160  }
162  s, p, "%T %z", "%I . %M . %S %p"); // customers weird '.' separated date
163  }
164  if (!p) {
165  throw std::runtime_error("Invalid TIMESTAMP time string " + std::string(s));
166  }
167  }
168  }
169 
170  constexpr int64_t parseFractionalSeconds(char*& p) {
171  if (*p == '.') {
172  p++;
173  uint64_t frac_num = 0;
174  int ntotal = 0;
175  sscanf(p, "%" SCNu64 "%n", &frac_num, &ntotal);
176  return static_cast<int64_t>(
177  TimeGM::instance().parse_fractional_seconds(frac_num, ntotal, dimen_));
178  } else if (*p == '\0') {
179  return 0;
180  } else { // check for misleading/unclear syntax
181  throw std::runtime_error("Unclear syntax for leading fractional seconds: " +
182  std::string(p));
183  }
184  }
185 
186  const int64_t getEpochValue() const {
187  // Check for unix time format
188  try {
189  return static_cast<int64_t>(std::stoll(str_));
190  } catch (const std::invalid_argument& ia) {
191  throw std::runtime_error("Invalid DATE/TIMESTAMP string " + str_);
192  }
193  }
194 
195  template <typename EVAL_STRING, typename TARGET_CHAR, typename... EVAL_FORMATS>
196  inline auto detectFormatFromString(EVAL_STRING s,
197  TARGET_CHAR& p,
198  EVAL_FORMATS... eval_formats) {
199  ((p = strptime(s, eval_formats, &tm_)) || ...);
200  return;
201  }
202 
203  std::string str_;
204  int32_t dimen_;
205  std::tm tm_{0};
206 };
207 
208 #endif // TIMEGM_H
int leap_days(int y1, int y2)
Definition: timegm.cpp:46
int is_leap_year(int year)
Definition: timegm.cpp:42
constexpr void parseTimePart(T *&s, std::remove_const_t< T > *&p)
Definition: TimeGM.h:114
time_t get_overflow_underflow_safe_epoch(tm const *tm, const time_t fsc, const int32_t dimen)
Definition: timegm.cpp:124
Definition: sqltypes.h:52
Definition: TimeGM.h:32
constexpr int64_t operator()(const std::string &datetime_str, const int32_t dimen)
Definition: TimeGM.h:65
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:203
constexpr auto is_datetime(T sql_type)
Definition: sqltypes.h:194
time_t parse_fractional_seconds(uint64_t sfrac, const int32_t ntotal, const int32_t dimen)
Definition: timegm.cpp:52
time_t my_timegm(tm const *tm)
Definition: timegm.cpp:108
Constants for Builtin SQL Types supported by MapD.
constexpr int64_t parseDateTimeString()
Definition: TimeGM.h:73
const int64_t getEpochValue() const
Definition: TimeGM.h:186
const int monoff[12]
Definition: TimeGM.h:55
Definition: sqltypes.h:56
constexpr int64_t parseFractionalSeconds(char *&p)
Definition: TimeGM.h:170
time_t my_timegm_days(tm const *tm)
Definition: timegm.cpp:95
TimeGM()
Definition: TimeGM.h:58
auto detectFormatFromString(EVAL_STRING s, TARGET_CHAR &p, EVAL_FORMATS...eval_formats)
Definition: TimeGM.h:196
static TimeGM & instance()
Definition: TimeGM.h:44
virtual ~TimeGM()
Definition: TimeGM.h:59