Coverage Report

Created: 2024-02-25 06:22

/src/poco/Foundation/src/DateTime.cpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// DateTime.cpp
3
//
4
// Library: Foundation
5
// Package: DateTime
6
// Module:  DateTime
7
//
8
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9
// and Contributors.
10
//
11
// SPDX-License-Identifier: BSL-1.0
12
//
13
14
15
#include "Poco/DateTime.h"
16
#include "Poco/Timespan.h"
17
#include "Poco/Exception.h"
18
#include "Poco/Format.h"
19
#include <algorithm>
20
#include <cmath>
21
#include <ctime>
22
23
24
namespace Poco {
25
26
27
DateTime::DateTime()
28
0
{
29
0
  Timestamp now;
30
0
  _utcTime = now.utcTime();
31
0
  computeGregorian(julianDay());
32
0
  computeDaytime();
33
0
}
34
35
36
DateTime::DateTime(const tm& tmStruct):
37
  _year(tmStruct.tm_year + 1900),
38
  _month(tmStruct.tm_mon + 1),
39
  _day(tmStruct.tm_mday),
40
  _hour(tmStruct.tm_hour),
41
  _minute(tmStruct.tm_min),
42
  _second(tmStruct.tm_sec),
43
  _millisecond(0),
44
  _microsecond(0)
45
0
{
46
0
  poco_assert (_year >= 0 && _year <= 9999);
47
0
  poco_assert (_month >= 1 && _month <= 12);
48
0
  poco_assert (_day >= 1 && _day <= daysOfMonth(_year, _month));
49
0
  poco_assert (_hour >= 0 && _hour <= 23);
50
0
  poco_assert (_minute >= 0 && _minute <= 59);
51
0
  poco_assert (_second >= 0 && _second <= 60);
52
53
0
  _utcTime = toUtcTime(toJulianDay(_year, _month, _day)) + 10*(_hour*Timespan::HOURS + _minute*Timespan::MINUTES + _second*Timespan::SECONDS);
54
0
}
55
56
57
DateTime::DateTime(const Timestamp& timestamp):
58
  _utcTime(timestamp.utcTime())
59
0
{
60
0
  computeGregorian(julianDay());
61
0
  computeDaytime();
62
0
}
63
64
65
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond):
66
  _year(year),
67
  _month(month),
68
  _day(day),
69
  _hour(hour),
70
  _minute(minute),
71
  _second(second),
72
  _millisecond(millisecond),
73
  _microsecond(microsecond)
74
0
{
75
0
  if (isValid(_year, _month, _day, _hour, _minute, _second, _millisecond, _microsecond))
76
0
  {
77
0
    _utcTime = toUtcTime(toJulianDay(year, month, day)) +
78
0
      10 * (hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS +
79
0
          millisecond*Timespan::MILLISECONDS + microsecond);
80
0
  }
81
0
  else
82
0
  {
83
0
    throw Poco::InvalidArgumentException(Poco::format("Date time is %d-%d-%dT%d:%d:%d.%d.%d\n"
84
0
      "Valid values:\n"
85
0
      "0 <= year <= 9999\n"
86
0
      "1 <= month <= 12\n"
87
0
      "1 <= day <=  %d\n"
88
0
      "0 <= hour <= 23\n"
89
0
      "0 <= minute <= 59\n"
90
0
      "0 <= second <= 59\n"
91
0
      "0 <= millisecond <= 999\n"
92
0
      "0 <= microsecond <= 999",
93
0
      _year, _month, _day, _hour, _minute,
94
0
      _second, _millisecond, _microsecond,
95
0
      daysOfMonth(_year, _month)));
96
0
  }
97
0
}
98
99
100
DateTime::DateTime(double julianDay):
101
  _utcTime(toUtcTime(julianDay))
102
0
{
103
0
  computeGregorian(julianDay);
104
0
}
105
106
107
DateTime::DateTime(Timestamp::UtcTimeVal utcTime, Timestamp::TimeDiff diff):
108
  _utcTime(utcTime + diff*10)
109
0
{
110
0
  computeGregorian(julianDay());
111
0
  computeDaytime();
112
0
}
113
114
115
DateTime::DateTime(const DateTime& dateTime):
116
  _utcTime(dateTime._utcTime),
117
  _year(dateTime._year),
118
  _month(dateTime._month),
119
  _day(dateTime._day),
120
  _hour(dateTime._hour),
121
  _minute(dateTime._minute),
122
  _second(dateTime._second),
123
  _millisecond(dateTime._millisecond),
124
  _microsecond(dateTime._microsecond)
125
0
{
126
0
}
127
128
129
DateTime::~DateTime()
130
0
{
131
0
}
132
133
134
DateTime& DateTime::operator = (const DateTime& dateTime)
135
0
{
136
0
  if (&dateTime != this)
137
0
  {
138
0
    _utcTime     = dateTime._utcTime;
139
0
    _year        = dateTime._year;
140
0
    _month       = dateTime._month;
141
0
    _day         = dateTime._day;
142
0
    _hour        = dateTime._hour;
143
0
    _minute      = dateTime._minute;
144
0
    _second      = dateTime._second;
145
0
    _millisecond = dateTime._millisecond;
146
0
    _microsecond = dateTime._microsecond;
147
0
  }
148
0
  return *this;
149
0
}
150
151
152
DateTime& DateTime::operator = (const Timestamp& timestamp)
153
0
{
154
0
  _utcTime = timestamp.utcTime();
155
0
  computeGregorian(julianDay());
156
0
  computeDaytime();
157
0
  return *this;
158
0
}
159
160
161
DateTime& DateTime::operator = (double julianDay)
162
0
{
163
0
  _utcTime = toUtcTime(julianDay);
164
0
  computeGregorian(julianDay);
165
0
  return *this;
166
0
}
167
168
169
DateTime& DateTime::assign(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond)
170
0
{
171
0
  poco_assert (year >= 0 && year <= 9999);
172
0
  poco_assert (month >= 1 && month <= 12);
173
0
  poco_assert (day >= 1 && day <= daysOfMonth(year, month));
174
0
  poco_assert (hour >= 0 && hour <= 23);
175
0
  poco_assert (minute >= 0 && minute <= 59);
176
0
  poco_assert (second >= 0 && second <= 60); // allow leap seconds
177
0
  poco_assert (millisecond >= 0 && millisecond <= 999);
178
0
  poco_assert (microsecond >= 0 && microsecond <= 999);
179
180
0
  _utcTime     = toUtcTime(toJulianDay(year, month, day)) + 10*(hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS + millisecond*Timespan::MILLISECONDS + microsecond);
181
0
  _year        = year;
182
0
  _month       = month;
183
0
  _day         = day;
184
0
  _hour        = hour;
185
0
  _minute      = minute;
186
0
  _second      = second;
187
0
  _millisecond = millisecond;
188
0
  _microsecond = microsecond;
189
190
0
  return *this;
191
0
}
192
193
194
void DateTime::swap(DateTime& dateTime) noexcept
195
0
{
196
0
  std::swap(_utcTime, dateTime._utcTime);
197
0
  std::swap(_year, dateTime._year);
198
0
  std::swap(_month, dateTime._month);
199
0
  std::swap(_day, dateTime._day);
200
0
  std::swap(_hour, dateTime._hour);
201
0
  std::swap(_minute, dateTime._minute);
202
0
  std::swap(_second, dateTime._second);
203
0
  std::swap(_millisecond, dateTime._millisecond);
204
0
  std::swap(_microsecond, dateTime._microsecond);
205
0
}
206
207
208
int DateTime::dayOfWeek() const
209
0
{
210
0
  return int((std::floor(julianDay() + 1.5))) % 7;
211
0
}
212
213
214
int DateTime::dayOfYear() const
215
0
{
216
0
  int doy = 0;
217
0
  for (int month = 1; month < _month; ++month)
218
0
    doy += daysOfMonth(_year, month);
219
0
  doy += _day;
220
0
  return doy;
221
0
}
222
223
224
int DateTime::daysOfMonth(int year, int month)
225
0
{
226
0
  poco_assert (month >= 1 && month <= 12);
227
228
0
  static int daysOfMonthTable[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
229
230
0
  if (month == 2 && isLeapYear(year))
231
0
    return 29;
232
0
  else
233
0
    return daysOfMonthTable[month];
234
0
}
235
236
237
bool DateTime::isValid(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond)
238
0
{
239
0
  return
240
0
    (year >= 0 && year <= 9999) &&
241
0
    (month >= 1 && month <= 12) &&
242
0
    (day >= 1 && day <= daysOfMonth(year, month)) &&
243
0
    (hour >= 0 && hour <= 23) &&
244
0
    (minute >= 0 && minute <= 59) &&
245
0
    (second >= 0 && second <= 60) &&
246
0
    (millisecond >= 0 && millisecond <= 999) &&
247
0
    (microsecond >= 0 && microsecond <= 999);
248
0
}
249
250
251
int DateTime::week(int firstDayOfWeek) const
252
0
{
253
0
  poco_assert (firstDayOfWeek >= 0 && firstDayOfWeek <= 6);
254
255
  /// find the first firstDayOfWeek.
256
0
  int baseDay = 1;
257
0
  while (DateTime(_year, 1, baseDay).dayOfWeek() != firstDayOfWeek) ++baseDay;
258
259
0
  int doy  = dayOfYear();
260
0
  int offs = baseDay <= 4 ? 0 : 1;
261
0
  if (doy < baseDay)
262
0
    return offs;
263
0
  else
264
0
    return (doy - baseDay)/7 + 1 + offs;
265
0
}
266
267
268
double DateTime::julianDay() const
269
0
{
270
0
  return toJulianDay(_utcTime);
271
0
}
272
273
274
DateTime DateTime::operator + (const Timespan& span) const
275
0
{
276
0
  return DateTime(_utcTime, span.totalMicroseconds());
277
0
}
278
279
280
DateTime DateTime::operator - (const Timespan& span) const
281
0
{
282
0
  return DateTime(_utcTime, -span.totalMicroseconds());
283
0
}
284
285
286
Timespan DateTime::operator - (const DateTime& dateTime) const
287
0
{
288
0
  return Timespan((_utcTime - dateTime._utcTime)/10);
289
0
}
290
291
292
DateTime& DateTime::operator += (const Timespan& span)
293
0
{
294
0
  _utcTime += span.totalMicroseconds()*10;
295
0
  computeGregorian(julianDay());
296
0
  computeDaytime();
297
0
  return *this;
298
0
}
299
300
301
DateTime& DateTime::operator -= (const Timespan& span)
302
0
{
303
0
  _utcTime -= span.totalMicroseconds()*10;
304
0
  computeGregorian(julianDay());
305
0
  computeDaytime();
306
0
  return *this;
307
0
}
308
309
310
tm DateTime::makeTM() const
311
0
{
312
0
  tm tmStruct;
313
314
0
  tmStruct.tm_sec = _second;
315
0
  tmStruct.tm_min = _minute;
316
0
  tmStruct.tm_hour = _hour;
317
0
  tmStruct.tm_mday = _day;
318
0
  poco_assert (_month > 0);
319
0
  tmStruct.tm_mon = _month - 1;
320
0
  poco_assert (_year >= 1900);
321
0
  tmStruct.tm_year = _year - 1900;
322
0
  tmStruct.tm_wday = dayOfWeek();
323
0
  int doy = dayOfYear();
324
0
  poco_assert (_year >0);
325
0
  tmStruct.tm_yday = doy - 1;
326
0
  tmStruct.tm_isdst = -1;
327
328
0
  return tmStruct;
329
0
}
330
331
332
void DateTime::makeUTC(int tzd)
333
0
{
334
0
  operator -= (Timespan(((Timestamp::TimeDiff) tzd)*Timespan::SECONDS));
335
0
}
336
337
338
void DateTime::makeLocal(int tzd)
339
0
{
340
0
  operator += (Timespan(((Timestamp::TimeDiff) tzd)*Timespan::SECONDS));
341
0
}
342
343
344
double DateTime::toJulianDay(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond)
345
0
{
346
  // lookup table for (153*month - 457)/5 - note that 3 <= month <= 14.
347
0
  static int lookup[] = {-91, -60, -30, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337};
348
349
  // day to double
350
0
  double dday = double(day) + ((double((hour*60 + minute)*60 + second)*1000 + millisecond)*1000 + microsecond)/86400000000.0;
351
0
  if (month < 3)
352
0
  {
353
0
    month += 12;
354
0
    --year;
355
0
  }
356
0
  double dyear = double(year);
357
0
  return dday + lookup[month] + 365*year + std::floor(dyear/4) - std::floor(dyear/100) + std::floor(dyear/400) + 1721118.5;
358
0
}
359
360
361
void DateTime::checkLimit(short& lower, short& higher, short limit)
362
0
{
363
0
  if (lower >= limit)
364
0
  {
365
0
    higher += short(lower / limit);
366
0
    lower   = short(lower % limit);
367
0
  }
368
0
}
369
370
371
void DateTime::normalize()
372
0
{
373
0
  checkLimit(_microsecond, _millisecond, 1000);
374
0
  checkLimit(_millisecond, _second, 1000);
375
0
  checkLimit(_second, _minute, 60);
376
0
  checkLimit(_minute, _hour, 60);
377
0
  checkLimit(_hour, _day, 24);
378
379
0
  if (_day > daysOfMonth(_year, _month))
380
0
  {
381
0
    _day -= daysOfMonth(_year, _month);
382
0
    if (++_month > 12)
383
0
    {
384
0
      ++_year;
385
0
      _month -= 12;
386
0
    }
387
0
  }
388
0
}
389
390
391
void DateTime::computeGregorian(double julianDay)
392
0
{
393
0
  double z    = std::floor(julianDay - 1721118.5);
394
0
  double r    = julianDay - 1721118.5 - z;
395
0
  double g    = z - 0.25;
396
0
  double a    = std::floor(g / 36524.25);
397
0
  double b    = a - std::floor(a/4);
398
0
  _year       = short(std::floor((b + g)/365.25));
399
0
  double c    = b + z - std::floor(365.25*_year);
400
0
  _month      = short(std::floor((5*c + 456)/153));
401
0
  double dday = c - std::floor((153.0*_month - 457)/5) + r;
402
0
  _day        = short(dday);
403
0
  if (_month > 12)
404
0
  {
405
0
    ++_year;
406
0
    _month -= 12;
407
0
  }
408
0
  r      *= 24;
409
0
  _hour   = short(std::floor(r));
410
0
  r      -= std::floor(r);
411
0
  r      *= 60;
412
0
  _minute = short(std::floor(r));
413
0
  r      -= std::floor(r);
414
0
  r      *= 60;
415
0
  _second = short(std::floor(r));
416
0
  r      -= std::floor(r);
417
0
  r      *= 1000;
418
0
  _millisecond = short(std::floor(r));
419
0
  r      -= std::floor(r);
420
0
  r      *= 1000;
421
0
  _microsecond = short(r + 0.5);
422
423
0
  normalize();
424
425
0
  poco_assert_dbg (_month >= 1 && _month <= 12);
426
0
  poco_assert_dbg (_day >= 1 && _day <= daysOfMonth(_year, _month));
427
0
  poco_assert_dbg (_hour >= 0 && _hour <= 23);
428
0
  poco_assert_dbg (_minute >= 0 && _minute <= 59);
429
0
  poco_assert_dbg (_second >= 0 && _second <= 59);
430
0
  poco_assert_dbg (_millisecond >= 0 && _millisecond <= 999);
431
0
  poco_assert_dbg (_microsecond >= 0 && _microsecond <= 999);
432
0
}
433
434
435
void DateTime::computeDaytime()
436
0
{
437
0
  Timestamp::UtcTimeVal ut(_utcTime);
438
0
  if (ut < 0) {
439
    // GH3723: UtcTimeVal is negative for pre-gregorian dates
440
    // move it 1600 years to the future
441
    // keeping hour, minute, second,... for corrections
442
0
    ut += Int64(86400)*1000*1000*10*1600*365;
443
0
  }
444
0
  Timespan span(ut/10);
445
0
  int hour = span.hours();
446
  // Due to double rounding issues, the previous call to computeGregorian()
447
  // may have crossed into the next or previous day. We need to correct that.
448
0
  if (hour == 23 && _hour == 0)
449
0
  {
450
0
    _day--;
451
0
    if (_day == 0)
452
0
    {
453
0
      _month--;
454
0
      if (_month == 0)
455
0
      {
456
0
        _month = 12;
457
0
        _year--;
458
0
      }
459
0
      _day = daysOfMonth(_year, _month);
460
0
    }
461
0
  }
462
0
  else if (hour == 0 && _hour == 23)
463
0
  {
464
0
    _day++;
465
0
    if (_day > daysOfMonth(_year, _month))
466
0
    {
467
0
      _month++;
468
0
      if (_month > 12)
469
0
      {
470
0
        _month = 1;
471
0
        _year++;
472
0
      }
473
0
      _day = 1;
474
0
    }
475
0
  }
476
0
  _hour        = hour;
477
0
  _minute      = span.minutes();
478
0
  _second      = span.seconds();
479
0
  _millisecond = span.milliseconds();
480
0
  _microsecond = span.microseconds();
481
0
}
482
483
484
} // namespace Poco