Evo C++ Library v0.5.1
time.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_timestamp_h
9 #define INCL_evo_timestamp_h
10 
11 #include "string.h"
12 #include "substring.h"
13 #include "impl/systime.h"
14 
15 namespace evo {
18 
20 
22 namespace impl_time {
23  inline int parse_num(int& num, const char*& inp, const char* end) {
24  const int OVERFLOW_THRESHOLD = Int::MAX / 10; // not exact, but good enough here
25  const char* start = inp;
26  num = 0;
27  while (inp < end) {
28  if (*inp < '0' || *inp > '9')
29  break;
30  if (num >= OVERFLOW_THRESHOLD) {
31  num = 0;
32  return Int::MAX; // overflow
33  }
34  num = (num * 10) + (*inp - '0');
35  ++inp;
36  }
37  return (int)(inp - start);
38  }
39 
40  inline double parse_frac(const char*& inp, const char* end) {
41  double div = 1.0;
42  double num = 0.0;
43  while (inp < end) {
44  if (*inp < '0' || *inp > '9')
45  break;
46  num = (num * 10) + (*inp - '0');
47  div *= 10;
48  ++inp;
49  }
50  num /= div;
51  return num;
52  }
53 }
56 
86 struct Date {
87  static const int YEAR_MIN = 1000;
88  static const int YEAR_MAX = 9999;
89  static const int MONTH_MIN = 1;
90  static const int MONTH_MAX = 12;
91  static const int DAY_MIN = 1;
92  static const int DAY_MAX = 31;
93  static const ulong JDN_MIN = 2086302;
94  static const ulong JDN_MOD = 2400000;
95 
96  int year;
97  int month;
98  int day;
99 
102  { ::memset(this, 0, sizeof(Date)); }
103 
111  Date(int year, int month, int day) : year(year), month(month), day(day) {
112  }
113 
117  Date(const Date& src)
118  { ::memcpy(this, &src, sizeof(Date)); }
119 
124  Date& operator=(const Date& src)
125  { ::memcpy(this, &src, sizeof(Date)); return *this; }
126 
131  bool operator==(const Date& oth) const
132  { return (year == oth.year && month == oth.month && day == oth.day); }
133 
138  bool operator!=(const Date& oth) const
139  { return (year != oth.year || month != oth.month || day != oth.day); }
140 
145  bool operator<(const Date& oth) const
146  { return (year < oth.year || (year == oth.year && (month < oth.month || (month == oth.month && day < oth.day)))); }
147 
152  bool operator<=(const Date& oth) const
153  { return (year < oth.year || (year == oth.year && (month < oth.month || (month == oth.month && day <= oth.day)))); }
154 
159  bool operator>(const Date& oth) const
160  { return (year > oth.year || (year == oth.year && (month > oth.month || (month == oth.month && day > oth.day)))); }
161 
166  bool operator>=(const Date& oth) const
167  { return (year > oth.year || (year == oth.year && (month > oth.month || (month == oth.month && day >= oth.day)))); }
168 
173  int compare(const Date& oth) const {
174  if (year == oth.year) {
175  if (month == oth.month) {
176  if (day == oth.day)
177  return 0;
178  else if (day < oth.day)
179  return -1;
180  } else if (month < oth.month)
181  return -1;
182  } else if (year < oth.year)
183  return -1;
184  return 1;
185  }
186 
197  ulong get_jdn() const {
198  if (!validate())
199  return 0;
200  return calc_jdn(year, month, day);
201  }
202 
206  bool validate() const {
207  return (year >= YEAR_MIN && year <= YEAR_MAX &&
208  month >= MONTH_MIN && month <= MONTH_MAX &&
209  day >= DAY_MIN && day <= days_per_month(month, year));
210  }
211 
213  void set() {
214  year = month = day = 0;
215  }
216 
225  bool set(int new_year, int new_month, int new_day) {
226  if ( new_year < YEAR_MIN || new_year > YEAR_MAX ||
227  new_month < MONTH_MIN || new_month > MONTH_MAX ||
228  new_day < DAY_MIN || new_day > days_per_month(new_month, new_year) )
229  return false;
230  year = new_year;
231  month = new_month;
232  day = new_day;
233  return true;
234  }
235 
246  bool set_jdn(ulong jdn) {
247  if (jdn < JDN_MIN)
248  return false;
249  // https://en.wikipedia.org/wiki/Julian_day
250  const long f = jdn + 1401 + (((4 * jdn + 274277) / 146097) * 3) / 4 + -38;
251  const long e = 4 * f + 3;
252  const long g = (e % 1461) / 4;
253  const long h = 5 * g + 2;
254  day = (int)((h % 153) / 5 + 1);
255  month = (int)(((h / 153 + 2) % 12) + 1);
256  year = (int)((e / 1461) - 4716 + (12 + 2 - month) / 12);
257  return true;
258  }
259 
261  void set_utc() {
262  int hour, minute, second, msecond;
263  SysTimestamp::get_wall_datetime_fields_utc(year, month, day, hour, minute, second, msecond);
264  }
265 
267  void set_local() {
268  int hour, minute, second, msecond;
269  SysTimestamp::get_wall_datetime_fields_local(year, month, day, hour, minute, second, msecond);
270  }
271 
278  bool add_years(int years) {
279  if (!validate())
280  return false;
281  year += years;
282  const int maxday = days_per_month(month, year);
283  if (day > maxday)
284  day = maxday;
285  return (year >= YEAR_MIN && year <= YEAR_MAX);
286  }
287 
294  bool add_months(int months) {
295  if (!validate())
296  return false;
297  year += (months / MONTH_MAX);
298  month += (months % MONTH_MAX);
299  if (month > MONTH_MAX) {
300  ++year;
301  month -= MONTH_MAX;
302  } else if (month < MONTH_MIN) {
303  --year;
304  month += MONTH_MAX;
305  }
306  const int maxday = days_per_month(month, year);
307  if (day > maxday)
308  day = maxday;
309  return (year >= YEAR_MIN && year <= YEAR_MAX);
310  }
311 
318  bool add_days(int days) {
319  const ulong jdn = get_jdn();
320  if (jdn == 0)
321  return false;
322  set_jdn(jdn + days);
323  return (year >= YEAR_MIN && year <= YEAR_MAX);
324  }
325 
338  bool parse_std_impl(const char*& inp, const char* end) {
339  year = month = day = 0;
340  for (char delim;;) {
341  // Year
342  int digits = impl_time::parse_num(year, inp, end);
343  switch (digits) {
344  case 4:
345  break;
346  case 7: {
347  // Ordinal date (YYYYDDD)
348  const int year_day = (year % 1000);
349  year /= 1000;
350  const int days_in_year = (is_leap_year(year) ? 366 : 365);
351  if (year_day <= 0 || year_day > days_in_year)
352  return false;
353  month = 1;
354  day = 1;
355  return add_days(year_day - 1);
356  }
357  case 8:
358  // YYYYMMDD
359  day = (year % 100);
360  year /= 100;
361  if (day < DAY_MIN)
362  return false;
363 
364  month = (year % 100);
365  year /= 100;
366  if (month < MONTH_MIN || month > MONTH_MAX || day > days_per_month(month, year))
367  return false;
368 
369  inp += digits;
370  return true;
371  default:
372  return false;
373  }
374  if (inp >= end)
375  return false;
376 
377  // Delim
378  if (*inp != '-' && *inp != '/' && *inp != '.' && *inp != ' ')
379  return false;
380  delim = *inp;
381  ++inp;
382 
383  // Month
384  digits = impl_time::parse_num(month, inp, end);
385  switch (digits) {
386  case 1:
387  case 2:
388  if (month < MONTH_MIN || month > MONTH_MAX)
389  return false;
390  break;
391  case 3: {
392  // Ordinal date (YYYY-DDD)
393  const int year_day = month;
394  const int days_in_year = (is_leap_year(year) ? 366 : 365);
395  if (year_day <= 0 || year_day > days_in_year)
396  return false;
397  month = 1;
398  day = 1;
399  return add_days(year_day - 1);
400  }
401  default:
402  return false;
403  }
404 
405  // Delim
406  if (*inp != delim)
407  return false;
408  ++inp;
409 
410  // Day
411  digits = impl_time::parse_num(day, inp, end);
412  if (digits > 2 || day < DAY_MIN || day > days_per_month(month, year))
413  return false;
414  break;
415  }
416  return true;
417  }
418 
430  bool parse(const SubString& str) {
431  const char* p = str.data();
432  return parse_std_impl(p, p + str.size());
433  }
434 
446  template<class T>
447  T& format(T& out, char delim='-') const {
448  out << FmtInt(year, fDEC, fPREFIX0, 4, '0');
449  if (delim > 0)
450  out << delim << FmtInt(month, fDEC, fPREFIX0, 2, '0') << delim;
451  else
452  out << FmtInt(month, fDEC, fPREFIX0, 2, '0');
453  out << FmtInt(day, fDEC, fPREFIX0, 2, '0');
454  return out;
455  }
456 
468  template<class T>
469  T& format_yearday(T& out, char delim='-') {
470  out << FmtInt(year, fDEC, fPREFIX0, 4, '0');
471  if (delim > 0)
472  out << delim;
473  if (month > 0 && day > 0)
474  out << FmtULong(calc_jdn(year, month, day) - calc_jdn(year, 1, 1) + 1, fDEC, fPREFIX0, 3, '0');
475  else
476  out << FmtInt(1, fDEC, fPREFIX0, 3, '0');
477  return out;
478  }
479 
486  static bool is_leap_year(int year) {
487  return (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0));
488  }
489 
495  static int days_per_month(int month, bool leap_year) {
496  static const int DAYS[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
497  assert( month >= MONTH_MIN && month <= MONTH_MAX );
498  if (month == 2 && leap_year)
499  return 29;
500  return DAYS[month - 1];
501  }
502 
508  static int days_per_month(int month, int year) {
509  return days_per_month(month, is_leap_year(year));
510  }
511 
518  static SubString month_name(int month) {
519  static const char* MONTHS[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
520  if (month < MONTH_MIN || month > MONTH_MAX)
521  return SubString();
522  return MONTHS[month - 1];
523  }
524 
531  static SubString month_name3(int month) {
532  static const char* MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
533  if (month < MONTH_MIN || month > MONTH_MAX)
534  return SubString();
535  return MONTHS[month - 1];
536  }
537 
549  static ulong calc_jdn(int year, int month, int day) {
550  // https://en.wikipedia.org/wiki/Julian_day
551  return (1461 * (year + 4800 + (month - 14) / 12)) / 4 +
552  (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 -
553  (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + day - 32075;
554  }
555 };
556 
558 
587 struct TimeOfDay {
588  static const int HOUR_MIN = 0;
589  static const int HOUR_MAX = 23;
590  static const int MINUTE_MIN = 0;
591  static const int MINUTE_MAX = 59;
592  static const int SECOND_MIN = 0;
593  static const int SECOND_MAX = 60;
594  static const int MSECOND_MIN = 0;
595  static const int MSECOND_MAX = 999;
596 
597  static const int HOURS_PER_DAY = 24;
598  static const int MIN_PER_HOUR = 60;
599  static const int MIN_PER_DAY = 1440;
600  static const int SEC_PER_MIN = 60;
601  static const int SEC_PER_HOUR = 3600;
602  static const int SEC_PER_DAY = 86400;
603  static const int MSEC_PER_SEC = 1000;
604  static const int MSEC_PER_MIN = 60000;
605  static const long MSEC_PER_HOUR = 3600000;
606  static const long MSEC_PER_DAY = 86400000;
607 
608  int hour;
609  int minute;
610  int second;
611  int msecond;
612 
615  { ::memset(this, 0, sizeof(TimeOfDay)); }
616 
623  TimeOfDay(int hour, int min=0, int sec=0, int msec=0) : hour(hour), minute(min), second(sec), msecond(msec) {
624  }
625 
629  TimeOfDay(const TimeOfDay& src)
630  { ::memcpy(this, &src, sizeof(TimeOfDay)); }
631 
637  { ::memcpy(this, &src, sizeof(TimeOfDay)); return *this; }
638 
643  bool operator==(const TimeOfDay& oth) const
644  { return (hour == oth.hour && minute == oth.minute && second == oth.second && msecond == oth.msecond); }
645 
650  bool operator!=(const TimeOfDay& oth) const
651  { return (hour != oth.hour || minute != oth.minute || second != oth.second || msecond != oth.msecond); }
652 
657  bool operator<(const TimeOfDay& oth) const
658  { return (hour < oth.hour || (hour == oth.hour && (minute < oth.minute || (minute == oth.minute && (second < oth.second || (second == oth.second && msecond < oth.msecond)))))); }
659 
664  bool operator<=(const TimeOfDay& oth) const
665  { return (hour < oth.hour || (hour == oth.hour && (minute < oth.minute || (minute == oth.minute && (second < oth.second || (second == oth.second && msecond <= oth.msecond)))))); }
666 
671  bool operator>(const TimeOfDay& oth) const
672  { return (hour > oth.hour || (hour == oth.hour && (minute > oth.minute || (minute == oth.minute && (second > oth.second || (second == oth.second && msecond > oth.msecond)))))); }
673 
678  bool operator>=(const TimeOfDay& oth) const
679  { return (hour > oth.hour || (hour == oth.hour && (minute > oth.minute || (minute == oth.minute && (second > oth.second || (second == oth.second && msecond >= oth.msecond)))))); }
680 
685  int compare(const TimeOfDay& oth) const {
686  if (hour == oth.hour) {
687  if (minute == oth.minute) {
688  if (second == oth.second) {
689  if (msecond == oth.msecond)
690  return 0;
691  else if (msecond < oth.msecond)
692  return -1;
693  } else if (second < oth.second)
694  return -1;
695  } else if (minute < oth.minute)
696  return -1;
697  } else if (hour < oth.hour)
698  return -1;
699  return 1;
700  }
701 
714  double get_fraction() const {
715  return ((double)hour / HOURS_PER_DAY) +
716  ((double)minute / MIN_PER_DAY) +
717  ((double)second / SEC_PER_DAY);
718  }
719 
725  int get_days() const {
726  return (hour / HOURS_PER_DAY);
727  }
728 
739  int get_days(int& result_hour) const {
740  int days = (hour / HOURS_PER_DAY);
741  result_hour = hour - (days * HOURS_PER_DAY);
742  if (result_hour < 0) {
743  --days;
744  result_hour += HOURS_PER_DAY;
745  }
746  return days;
747  }
748 
753  bool validate(bool allow_hour_overflow=false) const {
754  return (hour >= HOUR_MIN && (allow_hour_overflow || hour <= HOUR_MAX) &&
755  minute >= MINUTE_MIN && minute <= MINUTE_MAX &&
756  second >= SECOND_MIN && second <= SECOND_MAX &&
757  msecond >= MSECOND_MIN && msecond <= MSECOND_MAX);
758  }
759 
761  void set()
762  { hour = minute = second = msecond = 0; }
763 
773  bool set(int new_hour, int new_minute=0, int new_second=0, int new_msecond=0) {
774  if ( new_hour < HOUR_MIN || new_hour > HOUR_MAX ||
775  new_minute < MINUTE_MIN || new_minute > MINUTE_MAX ||
776  new_second < SECOND_MIN || new_second > SECOND_MAX ||
777  new_msecond < MSECOND_MIN || new_msecond > MSECOND_MAX )
778  return false;
779  hour = new_hour;
780  minute = new_minute;
781  second = new_second;
782  msecond = new_msecond;
783  return true;
784  }
785 
793  void set_fraction(double tm, int msec=0) {
794  hour = (int)(tm * HOURS_PER_DAY);
795  tm -= (double)hour / HOURS_PER_DAY;
796  minute = (int)(tm * MIN_PER_DAY);
797  tm -= (double)minute / MIN_PER_DAY;
798  second = (int)(tm * SEC_PER_DAY);
799  msecond = msec;
800  }
801 
803  void set_utc() {
804  int year, month, day;
805  SysTimestamp::get_wall_datetime_fields_utc(year, month, day, hour, minute, second, msecond);
806  }
807 
809  void set_local() {
810  int year, month, day;
811  SysTimestamp::get_wall_datetime_fields_local(year, month, day, hour, minute, second, msecond);
812  }
813 
820  void add_minutes(int minutes) {
821  minute += minutes;
822  int newhours = (minute / MIN_PER_HOUR);
823  hour += newhours;
824  minute -= (newhours * MIN_PER_HOUR);
825  if (minute < 0) {
826  --hour;
827  minute += MIN_PER_HOUR;
828  }
829  }
830 
837  void add_seconds(int seconds) {
838  if (second == 60)
839  second = 59; // ignore leap second so it doesn't throw off the math
840 
841  long total_seconds = (minute * SEC_PER_MIN) + second + seconds;
842  int newhours = (total_seconds / SEC_PER_HOUR);
843  total_seconds -= (newhours * SEC_PER_HOUR);
844 
845  hour += newhours;
846  minute = (total_seconds / SEC_PER_MIN);
847  total_seconds -= (minute * SEC_PER_MIN);
848 
849  second = (int)total_seconds;
850  if (second < 0) {
851  --minute;
852  second += SEC_PER_MIN;
853  }
854  if (minute < 0) {
855  --hour;
856  minute += MIN_PER_HOUR;
857  }
858  }
859 
866  void add_milliseconds(int milliseconds) {
867  msecond += milliseconds;
868  int newseconds = (msecond / MSEC_PER_SEC);
869  msecond -= (newseconds * MSEC_PER_SEC);
870  if (msecond < 0) {
871  --newseconds;
872  msecond += MSEC_PER_SEC;
873  }
874  add_seconds(newseconds);
875  }
876 
894  bool parse_std_impl(const char*& inp, const char* end, bool allow_hour_overflow=false) {
895  const double ROUND_MSEC = 0.001;
896  hour = minute = second = msecond = 0;
897  for (;;) {
898  // Hour
899  int digits = impl_time::parse_num(hour, inp, end);
900  switch (digits) {
901  case 1:
902  case 2:
903  break;
904  case 4:
905  minute = hour % 100;
906  hour /= 100;
907  if ( hour < HOUR_MIN || (hour > HOUR_MAX && !allow_hour_overflow) ||
908  minute < MINUTE_MIN || minute > MINUTE_MAX )
909  return false;
910  if (inp < end && (*inp == '.' || *inp == ','))
911  goto frac_min;
912  return true;
913  case 6:
914  second = hour % 100;
915  hour /= 100;
916  minute = hour % 100;
917  hour /= 100;
918  if ( hour < HOUR_MIN || (hour > HOUR_MAX && !allow_hour_overflow) ||
919  minute < MINUTE_MIN || minute > MINUTE_MAX ||
920  second < SECOND_MIN || second > SECOND_MAX )
921  return false;
922  goto endfrac;
923  default:
924  return false; // invalid
925  }
926 
927  if (hour < HOUR_MIN || (hour > HOUR_MAX && !allow_hour_overflow))
928  return false;
929  if (inp >= end)
930  break;
931 
932  if (*inp == '.' || *inp == ',') {
933  // Fractional hour
934  double frac = impl_time::parse_frac(++inp, end);
935  frac *= MIN_PER_HOUR;
936  minute = (int)frac;
937  frac -= minute;
938 
939  frac *= SEC_PER_MIN;
940  second = (int)frac;
941  frac -= second;
942 
943  msecond = (int)((frac * MSEC_PER_SEC) + ROUND_MSEC);
944  return true;
945  }
946  if (*inp != ':')
947  break;
948  ++inp;
949 
950  // Minute
951  digits = impl_time::parse_num(minute, inp, end);
952  switch (digits) {
953  case 1:
954  case 2:
955  break;
956  default:
957  return false; // invalid
958  }
959  if (minute < MINUTE_MIN || minute > MINUTE_MAX)
960  return false;
961 
962  if (*inp == '.' || *inp == ',') {
963  // Fractional minute
964  frac_min:
965  double frac = impl_time::parse_frac(++inp, end);
966  frac *= SEC_PER_MIN;
967  second = (int)frac;
968  frac -= second;
969 
970  msecond = (int)((frac * MSEC_PER_SEC) + ROUND_MSEC);
971  break;
972  }
973  if (*inp != ':')
974  break;
975  ++inp;
976 
977  // Second
978  digits = impl_time::parse_num(second, inp, end);
979  switch (digits) {
980  case 1:
981  case 2:
982  break;
983  default:
984  return false; // invalid
985  }
986  if (second < SECOND_MIN || second > SECOND_MAX)
987  return false;
988 
989  endfrac:
990  if (*inp == '.' || *inp == ',') {
991  // Fractional second, i.e. millisecond
992  msecond = (int)((impl_time::parse_frac(++inp, end) * MSEC_PER_SEC) + ROUND_MSEC);
993  break;
994  }
995 
996  break;
997  }
998  return true;
999  }
1000 
1017  bool parse(const SubString& str, bool allow_hour_overflow=false) {
1018  const char* p = str.data();
1019  return parse_std_impl(p, p + str.size(), allow_hour_overflow);
1020  }
1021 
1029  template<class T>
1030  T& format(T& out, char delim=':', char msdelim=0) const {
1031  out << FmtInt(hour, fDEC, fPREFIX0, 2, '0');
1032  if (delim > 0)
1033  out << delim << FmtInt(minute, fDEC, fPREFIX0, 2, '0') << delim;
1034  else
1035  out << FmtInt(minute, fDEC, fPREFIX0, 2, '0');
1036  out << FmtInt(second, fDEC, fPREFIX0, 2, '0');
1037  if (msecond > 0 && msdelim > 0)
1038  out << msdelim << FmtInt(msecond, fDEC, fPREFIX0, 3, '0');
1039  return out;
1040  }
1041 };
1042 
1044 
1060  static const int OFFSET_MIN = -1439;
1061  static const int OFFSET_MAX = 1439;
1062  static const int OFFSET_NULL = Int::MIN;
1063 
1064  int minutes;
1065 
1068  { minutes = OFFSET_NULL; }
1069 
1073  TimeZoneOffset(int minutes) : minutes(minutes) {
1074  }
1075 
1082  TimeZoneOffset(int hours, int hour_minutes) {
1083  if (hours < 0)
1084  minutes = (hours * TimeOfDay::MIN_PER_HOUR) - hour_minutes;
1085  else
1086  minutes = (hours * TimeOfDay::MIN_PER_HOUR) + hour_minutes;
1087  }
1088 
1093  { minutes = src.minutes; }
1094 
1100  { minutes = src.minutes; return *this; }
1101 
1106  bool operator==(const TimeZoneOffset& oth) const
1107  { return (minutes == oth.minutes); }
1108 
1113  bool operator!=(const TimeZoneOffset& oth) const
1114  { return (minutes != oth.minutes); }
1115 
1120  bool operator<(const TimeZoneOffset& oth) const
1121  { return (minutes < oth.minutes); }
1122 
1127  bool operator<=(const TimeZoneOffset& oth) const
1128  { return (minutes <= oth.minutes); }
1129 
1134  bool operator>(const TimeZoneOffset& oth) const
1135  { return (minutes > oth.minutes); }
1136 
1141  bool operator>=(const TimeZoneOffset& oth) const
1142  { return (minutes >= oth.minutes); }
1143 
1148  int compare(const TimeZoneOffset& oth) const
1149  { return (minutes == oth.minutes ? 0 : (minutes < oth.minutes ? -1 : 1)); }
1150 
1154  bool null() const
1155  { return minutes == OFFSET_NULL; }
1156 
1160  bool validate() const
1161  { return (minutes == OFFSET_NULL || (minutes >= OFFSET_MIN && minutes <= OFFSET_MAX)); }
1162 
1164  void set()
1165  { minutes = OFFSET_NULL; }
1166 
1171  bool set(int new_minutes) {
1172  if (new_minutes == OFFSET_NULL || (new_minutes >= OFFSET_MIN && new_minutes <= OFFSET_MAX)) {
1173  minutes = new_minutes;
1174  return true;
1175  }
1176  return false;
1177  }
1178 
1184  bool set(int hours, int hour_minutes) {
1185  if ( hours <= -TimeOfDay::HOURS_PER_DAY || hours >= TimeOfDay::HOURS_PER_DAY ||
1186  hour_minutes < TimeOfDay::MINUTE_MIN || hour_minutes > TimeOfDay::MINUTE_MAX )
1187  return false;
1188  if (hours < 0)
1189  minutes = (hours * TimeOfDay::MIN_PER_HOUR) - hour_minutes;
1190  else
1191  minutes = (hours * TimeOfDay::MIN_PER_HOUR) + hour_minutes;
1192  return true;
1193  }
1194 
1197  minutes = 0;
1198  return *this;
1199  }
1200 
1205  minutes = SysTimestamp::tz_get_offset();
1206  return *this;
1207  }
1208 
1223  bool parse_std_impl(const char*& inp, const char* end, bool required=false) {
1224  minutes = OFFSET_NULL;
1225  if (inp >= end)
1226  return !required; // no offset
1227 
1228  int neg_mult;
1229  switch (*inp) {
1230  case 'Z':
1231  case 'z':
1232  minutes = 0;
1233  return true; // UTC
1234  case '+':
1235  neg_mult = 1;
1236  break;
1237  case '-':
1238  neg_mult = -1;
1239  break;
1240  default:
1241  if (*inp >= '0' && *inp <= '9')
1242  return false; // invalid
1243  return !required; // no offset
1244  }
1245 
1246  int hours = 0;
1247  int digits = impl_time::parse_num(hours, ++inp, end);
1248  switch (digits) {
1249  case 1:
1250  case 2:
1251  break;
1252  case 4:
1253  minutes = (hours / 100 * TimeOfDay::MIN_PER_HOUR * neg_mult) + (hours % 100 * neg_mult);
1254  return (minutes >= OFFSET_MIN && minutes <= OFFSET_MAX);
1255  default:
1256  return false;
1257  }
1258  if (hours >= TimeOfDay::HOURS_PER_DAY)
1259  return false;
1260 
1261  if (inp >= end || *inp != ':') {
1262  minutes = (hours * TimeOfDay::MIN_PER_HOUR * neg_mult);
1263  return true;
1264  }
1265  if (++inp >= end)
1266  return false;
1267 
1268  int hour_minutes = 0;
1269  digits = impl_time::parse_num(hour_minutes, inp, end);
1270  if (digits > 2 || hour_minutes > TimeOfDay::MINUTE_MAX)
1271  return false;
1272 
1273  minutes = (hours * TimeOfDay::MIN_PER_HOUR * neg_mult) + (hour_minutes * neg_mult);
1274  return true;
1275  }
1276 
1290  bool parse(const SubString& str, bool required=false) {
1291  const char* p = str.data();
1292  return parse_std_impl(p, p + str.size(), required);
1293  }
1294 
1308  template<class T>
1309  T& format(T& out, char delim=':', bool allow_z=true) const {
1310  if (minutes == OFFSET_NULL)
1311  return out;
1312  if (allow_z && minutes == 0) {
1313  out << 'Z';
1314  } else {
1315  int hours = minutes / 60;
1316  int hour_minutes = minutes % 60;
1317  if (hours < 0) {
1318  out << '-';
1319  hours *= -1;
1320  hour_minutes *= -1;
1321  } else
1322  out << '+';
1323  out << FmtInt(hours, fDEC, fPREFIX0, 2, '0');
1324  if (delim > 0)
1325  out << delim;
1326  out << FmtInt(hour_minutes, fDEC, fPREFIX0, 2, '0');
1327  }
1328  return out;
1329  }
1330 };
1331 
1333 
1364 struct DateTime {
1365  static const int OFFSET_NULL = TimeZoneOffset::OFFSET_NULL;
1366  static const ulong JDN_MIN = Date::JDN_MIN;
1367 
1371 
1374  }
1375 
1386  DateTime(int year, int month, int day, int hour=0, int minute=0, int second=0, int msecond=0, int tz_offset=OFFSET_NULL) :
1387  date(year, month, day), time(hour, minute, second, msecond), tz(tz_offset) {
1388  }
1389 
1393  DateTime(const DateTime& src) : date(src.date), time(src.time), tz(src.tz) {
1394  }
1395 
1399  DateTime(const Date& date) : date(date) {
1400  }
1401 
1406  DateTime(const Date& date, const TimeOfDay& time) : date(date), time(time) {
1407  }
1408 
1414  DateTime(const Date& date, const TimeOfDay& time, const TimeZoneOffset& tz) : date(date), time(time), tz(tz) {
1415  }
1416 
1420  DateTime(const TimeZoneOffset& tz) : tz(tz) {
1421  }
1422 
1427  DateTime& operator=(const DateTime& src) {
1428  date = src.date;
1429  time = src.time;
1430  tz = src.tz;
1431  return *this;
1432  }
1433 
1438  bool operator==(const DateTime& oth) const
1439  { return (compare_op_helper(oth) == 0); }
1440 
1445  bool operator!=(const DateTime& oth) const
1446  { return (compare_op_helper(oth) != 0); }
1447 
1455  bool operator<(const DateTime& oth) const
1456  { return (compare_op_helper(oth) < 0); }
1457 
1465  bool operator<=(const DateTime& oth) const
1466  { return (compare_op_helper(oth) <= 0); }
1467 
1475  bool operator>(const DateTime& oth) const
1476  { return (compare_op_helper(oth) > 0); }
1477 
1485  bool operator>=(const DateTime& oth) const
1486  { return (compare_op_helper(oth) >= 0); }
1487 
1495  int compare(const DateTime& oth) const
1496  { return compare_op_helper(oth); }
1497 
1501  bool validate() const
1502  { return (date.validate() && time.validate() && tz.validate()); }
1503 
1516  double get_jdn_dt() const
1517  { return date.get_jdn() + time.get_fraction(); }
1518 
1520  DateTime& set() {
1521  date.set();
1522  time.set();
1523  tz.set();
1524  return *this;
1525  }
1526 
1539  bool set(int year, int month, int day, int hour=0, int minute=0, int second=0, int msecond=0)
1540  { return (date.set(year, month, day) && time.set(hour, minute, second, msecond)); }
1541 
1550  bool set_jdn_dt(double tm, int msec=0) {
1551  if (tm < JDN_MIN || !date.set_jdn((ulong)tm))
1552  return false;
1553  time.set_fraction(tm - (long)tm, msec);
1554  return true;
1555  }
1556 
1561  SysTimestamp::get_wall_datetime_fields_utc(date.year, date.month, date.day, time.hour, time.minute, time.second, time.msecond);
1562  tz.set_utc();
1563  return *this;
1564  }
1565 
1572  SysTimestamp::get_wall_datetime_fields_local(date.year, date.month, date.day, time.hour, time.minute, time.second, time.msecond, tz.minutes);
1573  return *this;
1574  }
1575 
1582  SysTimestamp::get_wall_datetime_fields_local(date.year, date.month, date.day, time.hour, time.minute, time.second, time.msecond);
1583  tz.set();
1584  return *this;
1585  }
1586 
1593  bool add_years(int years)
1594  { return date.add_years(years); }
1595 
1602  bool add_months(int months)
1603  { return date.add_months(months); }
1604 
1611  bool add_days(int days)
1612  { return date.add_days(days); }
1613 
1620  bool add_hours(int hours)
1621  { time.hour += hours; return date.add_days(time.get_days(time.hour)); }
1622 
1629  bool add_minutes(int minutes)
1630  { time.add_minutes(minutes); return date.add_days(time.get_days(time.hour)); }
1631 
1638  bool add_seconds(int seconds)
1639  { time.add_seconds(seconds); return date.add_days(time.get_days(time.hour)); }
1640 
1647  bool add_milliseconds(int msec)
1648  { time.add_milliseconds(msec); return date.add_days(time.get_days(time.hour)); }
1649 
1661  bool parse_std_impl(const char*& inp, const char* end) {
1662  if (date.parse_std_impl(inp, end)) {
1663  if (inp < end) {
1664  switch (*inp) {
1665  case 'T':
1666  case 't':
1667  case '-':
1668  case ':':
1669  case '_':
1670  case '/':
1671  case ',':
1672  case '.':
1673  case '@':
1674  if (!time.parse_std_impl(++inp, end))
1675  return false;
1676  date.add_days(time.get_days(time.hour));
1677  break;
1678  default:
1679  time.set();
1680  break;
1681  }
1682  return tz.parse_std_impl(inp, end);
1683  } else {
1684  time.set();
1685  tz.set();
1686  }
1687  return true;
1688  }
1689  return false;
1690  }
1691 
1702  bool parse(const SubString& str) {
1703  const char* p = str.data();
1704  return parse_std_impl(p, p + str.size());
1705  }
1706 
1723  template<class T>
1724  T& format(T& out, char dt_delim='T', char d_delim='-', char t_delim=':', char msec_delim=0, char tz_delim=':') const {
1725  date.format(out, d_delim);
1726  if (dt_delim > 0)
1727  out << dt_delim;
1728  time.format(out, t_delim, msec_delim);
1729  tz.format(out, tz_delim);
1730  return out;
1731  }
1732 
1746  template<class T>
1747  T& format_std(T& out, char dt_delim='T', char msec_delim=0) const
1748  { return format(out, dt_delim, '-', ':', msec_delim, ':'); }
1749 
1763  template<class T>
1764  T& format_nodelim(T& out, char dt_delim='T', char msec_delim=0) const
1765  { return format(out, dt_delim, 0, 0, msec_delim, 0); }
1766 
1767 private:
1768  int compare_op_helper(const DateTime& oth) const {
1769  int cmp;
1770  if (tz.minutes == oth.tz.minutes || tz.null() || oth.tz.null()) {
1771  cmp = date.compare(oth.date);
1772  if (cmp == 0)
1773  cmp = time.compare(oth.time);
1774  } else {
1775  DateTime tmp(*this);
1776  tmp.add_minutes(oth.tz.minutes - tz.minutes);
1777  cmp = tmp.date.compare(oth.date);
1778  if (cmp == 0)
1779  cmp = tmp.time.compare(oth.time);
1780  }
1781  return cmp;
1782  }
1783 };
1784 
1786 
1787 }
1788 #endif
T & format_std(T &out, char dt_delim='T', char msec_delim=0) const
Format date and time to String or Stream using standard delimiters.
Definition: time.h:1747
double get_fraction() const
Get current time as a day fraction.
Definition: time.h:714
bool parse_std_impl(const char *&inp, const char *end)
Set date/time from parsing standard ISO 8601 based string.
Definition: time.h:1661
int msecond
Millisecond of second (0 - 999)
Definition: time.h:611
TimeZoneOffset & set_utc()
Set to UTC.
Definition: time.h:1196
bool add_milliseconds(int msec)
Add milliseconds to current date and time, subtract if negative.
Definition: time.h:1647
static const int MINUTE_MAX
Maximum minute value.
Definition: time.h:591
int minute
Minute of hour (0 - 59)
Definition: time.h:609
bool null() const
Get whether time zone offset is null (not set).
Definition: time.h:1154
bool operator==(const TimeZoneOffset &oth) const
Compare for equality with another time of day.
Definition: time.h:1106
bool operator==(const DateTime &oth) const
Compare for equality with another date and time.
Definition: time.h:1438
void set()
Set as null (no time zone).
Definition: time.h:1164
TimeZoneOffset(int minutes)
Constructor to initialize with a time zone offset in minutes.
Definition: time.h:1073
bool validate() const
Validate current date.
Definition: time.h:206
Evo SubString container.
T & format_nodelim(T &out, char dt_delim='T', char msec_delim=0) const
Format date and time to String or Stream using standard delimiters.
Definition: time.h:1764
DateTime & operator=(const DateTime &src)
Assignment operator.
Definition: time.h:1427
Evo String container.
T & format(T &out, char dt_delim='T', char d_delim='-', char t_delim=':', char msec_delim=0, char tz_delim=':') const
Format date and time to String or Stream using given delimiters.
Definition: time.h:1724
TimeZoneOffset(const TimeZoneOffset &src)
Copy constructor.
Definition: time.h:1092
bool operator>(const DateTime &oth) const
Compare whether greater than another date and time.
Definition: time.h:1475
bool operator>=(const Date &oth) const
Compare whether greater than or equal to another date.
Definition: time.h:166
double get_jdn_dt() const
Get Julian Day Number with time fraction.
Definition: time.h:1516
Evo system time implementation helpers.
bool add_days(int days)
Add days to current date, subtract if negative.
Definition: time.h:318
int compare(const DateTime &oth) const
Compare to another date and time.
Definition: time.h:1495
static const int MIN_PER_HOUR
Number of minutes per hour.
Definition: time.h:598
int compare(const TimeZoneOffset &oth) const
Compare to another time zone offset.
Definition: time.h:1148
DateTime()
Constructor.
Definition: time.h:1373
bool operator<(const Date &oth) const
Compare whether less than another date.
Definition: time.h:145
void set_local()
Set to current date in local time zone.
Definition: time.h:267
TimeZoneOffset & operator=(const TimeZoneOffset &src)
Assignment operator.
Definition: time.h:1099
bool operator<(const TimeOfDay &oth) const
Compare whether less than another time of day.
Definition: time.h:657
bool operator<(const TimeZoneOffset &oth) const
Compare whether less than another time of day.
Definition: time.h:1120
int hour
Hour of day (0 - 23)
Definition: time.h:608
bool set_jdn(ulong jdn)
Set date converted from Julian Day Number.
Definition: time.h:246
Date(int year, int month, int day)
Constructor to initialize with date.
Definition: time.h:111
int day
Day of month (1 - 31)
Definition: time.h:98
bool operator==(const TimeOfDay &oth) const
Compare for equality with another time of day.
Definition: time.h:643
static int days_per_month(int month, bool leap_year)
Helper to get number of days per given month.
Definition: time.h:495
DateTime & set_utc()
Set to current UTC date and time.
Definition: time.h:1560
static const T MIN
Minimum integer value.
Definition: type.h:995
static int days_per_month(int month, int year)
Helper to get number of days per given month in given year.
Definition: time.h:508
int year
4 digit year (1000 - 9999)
Definition: time.h:96
void set_utc()
Set to current UTC time of day.
Definition: time.h:803
T & format_yearday(T &out, char delim='-')
Format ordinal date to String or Stream.
Definition: time.h:469
bool parse(const SubString &str, bool allow_hour_overflow=false)
Set time of day from parsing standard time string.
Definition: time.h:1017
static const T MAX
Maximum interger value.
Definition: type.h:996
T & format(T &out, char delim=':', bool allow_z=true) const
Format timezone offset to String or Stream.
Definition: time.h:1309
bool add_years(int years)
Add years to current date, subtract if negative.
Definition: time.h:278
int compare(const Date &oth) const
Compare to another date.
Definition: time.h:173
ulong get_jdn() const
Get Julian Day Number for current date.
Definition: time.h:197
No base prefix (default)
Definition: str.h:2333
static const ulong JDN_MIN
Minimum Julian Day Number for Jan 1, 1000.
Definition: time.h:93
TimeZoneOffset tz
TimeZoneOffset fields.
Definition: time.h:1370
bool operator<=(const DateTime &oth) const
Compare whether less than or equal to another date and time.
Definition: time.h:1465
void add_seconds(int seconds)
Add seconds to current time, subtract if negative.
Definition: time.h:837
Size size() const
Get size.
void set_utc()
Set to current date in UTC.
Definition: time.h:261
bool add_minutes(int minutes)
Add minutes to current date and time, subtract if negative.
Definition: time.h:1629
Full calendar date and time of day with timezone offset.
Definition: time.h:1364
Structure holding a time zone offset from UTC.
Definition: time.h:1059
static bool is_leap_year(int year)
Helper to check whether given year is a leap year.
Definition: time.h:486
void set_local()
Set to current local time of day.
Definition: time.h:809
bool validate(T &val, T &min, T &max)
Definition: alg.h:79
Structure holding a time of day.
Definition: time.h:587
bool operator<(const DateTime &oth) const
Compare whether less than another date and time.
Definition: time.h:1455
bool add_months(int months)
Add months to current date, subtract if negative.
Definition: time.h:1602
TimeOfDay time
TimeOfDay fields.
Definition: time.h:1369
Base 10: decimal (default)
Definition: str.h:2323
FmtIntT< int > FmtInt
Explicitly format an integer.
Definition: str.h:3107
int get_days(int &result_hour) const
Get number of days and hours from current hour value.
Definition: time.h:739
bool operator<=(const Date &oth) const
Compare whether less than or equal to another date.
Definition: time.h:152
void set()
Set all fields to 0, which is a valid time (midnight).
Definition: time.h:761
TimeZoneOffset(int hours, int hour_minutes)
Constructor to initialize with a time zone offset from hours and minutes.
Definition: time.h:1082
bool parse(const SubString &str)
Set date from parsing standard date string (used internally).
Definition: time.h:430
T & format(T &out, char delim=':', char msdelim=0) const
Format time of day to String or Stream.
Definition: time.h:1030
bool validate(bool allow_hour_overflow=false) const
Validate current time of day.
Definition: time.h:753
bool validate() const
Validate current timezone offset.
Definition: time.h:1160
TimeOfDay(const TimeOfDay &src)
Copy constructor.
Definition: time.h:629
void add_minutes(int minutes)
Add minutes to current time, subtract if negative.
Definition: time.h:820
bool add_hours(int hours)
Add hours to current date and time, subtract if negative.
Definition: time.h:1620
bool validate() const
Validate current date, time, and timezone offset.
Definition: time.h:1501
bool parse_std_impl(const char *&inp, const char *end)
Set date from parsing standard date string (used internally).
Definition: time.h:338
void set_fraction(double tm, int msec=0)
Set time fields from day fraction.
Definition: time.h:793
Date & operator=(const Date &src)
Assignment operator.
Definition: time.h:124
bool operator==(const Date &oth) const
Compare for equality with another date.
Definition: time.h:131
bool operator>=(const TimeZoneOffset &oth) const
Compare whether greater than or equal to another time of day.
Definition: time.h:1141
bool operator!=(const Date &oth) const
Compare for inequality with another date.
Definition: time.h:138
int second
Second of minute (0 - 60), 60 is a special case for a leap second (rare)
Definition: time.h:610
DateTime(const Date &date, const TimeOfDay &time)
Date and TimeOfDay constructor.
Definition: time.h:1406
static const int HOURS_PER_DAY
Number of hours per day.
Definition: time.h:597
Date(const Date &src)
Copy constructor.
Definition: time.h:117
void add_milliseconds(int milliseconds)
Add milliseconds to current time, subtract if negative.
Definition: time.h:866
bool add_months(int months)
Add months to current date, subtract if negative.
Definition: time.h:294
bool set_jdn_dt(double tm, int msec=0)
Set new fields from Julian Day Number.
Definition: time.h:1550
Evo C++ Library namespace.
Definition: alg.h:11
DateTime & set_local_notz()
Set to current local date and time without time zone offset.
Definition: time.h:1581
bool operator!=(const TimeZoneOffset &oth) const
Compare for inequality with another time of day.
Definition: time.h:1113
int month
Month of year (1 - 12)
Definition: time.h:97
DateTime(const Date &date, const TimeOfDay &time, const TimeZoneOffset &tz)
Date and TimeOfDay constructor.
Definition: time.h:1414
Date()
Constructor.
Definition: time.h:101
DateTime(int year, int month, int day, int hour=0, int minute=0, int second=0, int msecond=0, int tz_offset=OFFSET_NULL)
Constructor.
Definition: time.h:1386
DateTime(const DateTime &src)
Copy constructor.
Definition: time.h:1393
static SubString month_name(int month)
Get name of given month.
Definition: time.h:518
bool operator>(const Date &oth) const
Compare whether greater than another date.
Definition: time.h:159
bool operator!=(const TimeOfDay &oth) const
Compare for inequality with another time of day.
Definition: time.h:650
bool add_years(int years)
Add years to current date, subtract if negative.
Definition: time.h:1593
bool operator>(const TimeZoneOffset &oth) const
Compare whether greater than another time of day.
Definition: time.h:1134
bool parse(const SubString &str)
Set date/time from parsing standard ISO 8601 based string.
Definition: time.h:1702
bool operator>(const TimeOfDay &oth) const
Compare whether greater than another time of day.
Definition: time.h:671
bool operator>=(const DateTime &oth) const
Compare whether greater than or equal to another date and time.
Definition: time.h:1485
static SubString month_name3(int month)
Get abbreviated name of given month.
Definition: time.h:531
bool operator!=(const DateTime &oth) const
Compare for inequality with another date and time.
Definition: time.h:1445
int get_days() const
Get number of days from current hour value.
Definition: time.h:725
bool operator<=(const TimeOfDay &oth) const
Compare whether less than or equal to another time of day.
Definition: time.h:664
static void get_wall_datetime_fields_utc(int &year, int &month, int &day, int &hour, int &minute, int &second, int &msecond)
Get fields for current real (wall clock) time for calendar date/time use (UTC).
Definition: systime.h:552
static const int OFFSET_NULL
Special value for null time zone (null is less than all other values), i.e. no assigned time zone...
Definition: time.h:1062
bool operator<=(const TimeZoneOffset &oth) const
Compare whether less than or equal to another time of day.
Definition: time.h:1127
bool parse_std_impl(const char *&inp, const char *end, bool allow_hour_overflow=false)
Set time of day from parsing standard time string (used internally).
Definition: time.h:894
static void get_wall_datetime_fields_local(int &year, int &month, int &day, int &hour, int &minute, int &second, int &msecond)
Get fields for current real (wall clock) time for calendar date/time use (Local Time).
Definition: systime.h:593
TimeOfDay & operator=(const TimeOfDay &src)
Assignment operator.
Definition: time.h:636
bool parse_std_impl(const char *&inp, const char *end, bool required=false)
Set timezone offset from parsing standard timezone offset string (used internally).
Definition: time.h:1223
static ulong calc_jdn(int year, int month, int day)
Calculate Julian Day Number from date fields.
Definition: time.h:549
DateTime & set_local()
Set to current local date and time with time zone offset.
Definition: time.h:1571
bool add_seconds(int seconds)
Add seconds to current date and time, subtract if negative.
Definition: time.h:1638
bool operator>=(const TimeOfDay &oth) const
Compare whether greater than or equal to another time of day.
Definition: time.h:678
TimeZoneOffset()
Constructor to set as null (no time zone).
Definition: time.h:1067
FmtIntT< ulong > FmtULong
Explicitly format an integer.
Definition: str.h:3117
TimeOfDay(int hour, int min=0, int sec=0, int msec=0)
Constructor to initialize with time of day.
Definition: time.h:623
DateTime(const TimeZoneOffset &tz)
Timezone offset constructor.
Definition: time.h:1420
int minutes
Time zone offset from UTC in minutes (-720 - 840), OFFSET_NULL for null, negative for the Western Hem...
Definition: time.h:1064
Reference and access existing string data.
Definition: substring.h:229
TimeZoneOffset & set_local()
Set to current time zone offset.
Definition: time.h:1204
DateTime(const Date &date)
Date constructor.
Definition: time.h:1399
TimeOfDay()
Constructor.
Definition: time.h:614
T & min(T &a, T &b)
Returns lowest of given values.
Definition: alg.h:26
void set()
Set all fields to 0, which is not a valid date.
Definition: time.h:213
Date date
Date fields.
Definition: time.h:1368
Structure holding a calendar date.
Definition: time.h:86
int compare(const TimeOfDay &oth) const
Compare to another time of day.
Definition: time.h:685
static int tz_get_offset()
Get current time zone (local time) offset from UTC in minutes.
Definition: systime.h:691
const char * data() const
Get data pointer.
T & format(T &out, char delim='-') const
Format date to String or Stream.
Definition: time.h:447
bool parse(const SubString &str, bool required=false)
Set timezone offset from parsing standard timezone offset string.
Definition: time.h:1290
bool add_days(int days)
Add days to current date, subtract if negative.
Definition: time.h:1611