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