DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
DateTime.cpp
Go to the documentation of this file.
1#include "pch.h"
2#include "System/DateTime.h"
6#include "System/String.h"
7#include <chrono>
8#include <time.h>
9#include <cmath>
10#include <iomanip>
11#include <sstream>
12
13namespace DotNetDupe {
14 namespace System {
15
16 constexpr int64_t TicksPerMillisecond = 10000;
17 constexpr int64_t TicksPerSecond = TicksPerMillisecond * 1000;
18 constexpr int64_t TicksPerMinute = TicksPerSecond * 60;
19 constexpr int64_t TicksPerHour = TicksPerMinute * 60;
20 constexpr int64_t TicksPerDay = TicksPerHour * 24;
21 constexpr int64_t MinTicks = 0;
22 constexpr int64_t MaxTicks = 3155378975999999999LL;
23 constexpr int64_t UnixEpochTicks = 621355968000000000LL;
24
25 constexpr int DaysToMonth365 [] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
26 constexpr int DaysToMonth366 [] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
27
29 static int64_t DateToTicks(int year, int month, int day) {
31 if (year < 1 || year > 9999 || month < 1 || month > 12) {
32 throw ArgumentOutOfRangeException("Year, Month, and Day parameters describe an un-representable DateTime.");
33 }
34
36 const int* days = DateTime::IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
37 if (day < 1 || day > days [month] - days [month - 1]) {
38 throw ArgumentOutOfRangeException("Year, Month, and Day parameters describe an un-representable DateTime.");
39 }
40
42 int y = year - 1;
43 int n = y * 365 + y / 4 - y / 100 + y / 400 + days [month - 1] + day - 1;
44
46 return (int64_t)n * TicksPerDay;
47 }
48
50 static int64_t TimeToTicks(int hour, int minute, int second) {
52 if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) {
53 throw ArgumentOutOfRangeException("Hour, Minute, and Second parameters describe an un-representable DateTime.");
54 }
55
57 int64_t totalSeconds = (int64_t)hour * 3600 + (int64_t)minute * 60 + (int64_t)second;
58 return totalSeconds * TicksPerSecond;
59 }
60
62 static void GetDatePart(int64_t ticks, int& year, int& month, int& day) {
64 int n = (int)(ticks / TicksPerDay);
65
67 int y400 = n / 146097; n -= y400 * 146097;
68 int y100 = n / 36524; if (y100 == 4) y100 = 3; n -= y100 * 36524;
69 int y4 = n / 1461; n -= y4 * 1461;
70 int y1 = n / 365; if (y1 == 4) y1 = 3; n -= y1 * 365;
71
73 year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1;
74 const int* days = DateTime::IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
75
77 month = 1;
78 while (n >= days [month]) month++;
79 day = n - days [month - 1] + 1;
80 }
81
82 DateTime::DateTime(int year, int month, int day)
83 : m_nTicks(DateToTicks(year, month, day)), m_kind(DateTimeKind::Unspecified) { }
84
85 DateTime::DateTime(int year, int month, int day, int hour, int minute, int second)
86 : m_nTicks(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second)), m_kind(DateTimeKind::Unspecified) { }
87
88 DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
89 : m_nTicks(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second)), m_kind(kind) { }
90
91 DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
92 : m_nTicks(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond), m_kind(DateTimeKind::Unspecified) {
94 if (millisecond < 0 || millisecond >= 1000) {
95 throw ArgumentOutOfRangeException("Millisecond must be between 0 and 999.");
96 }
97 }
98
99 DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
100 : m_nTicks(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond), m_kind(kind) {
102 if (millisecond < 0 || millisecond >= 1000) {
103 throw ArgumentOutOfRangeException("Millisecond must be between 0 and 999.");
104 }
105 }
106
107 int DateTime::GetYear() const {
109 int y, m, d;
110 GetDatePart(m_nTicks, y, m, d);
111 return y;
112 }
113
114 int DateTime::GetMonth() const {
116 int y, m, d;
117 GetDatePart(m_nTicks, y, m, d);
118 return m;
119 }
120
121 int DateTime::GetDay() const {
123 int y, m, d;
124 GetDatePart(m_nTicks, y, m, d);
125 return d;
126 }
127
128 int DateTime::GetHour() const {
130 return (int)((m_nTicks / TicksPerHour) % 24);
131 }
132
135 return (int)((m_nTicks / TicksPerMinute) % 60);
136 }
137
140 return (int)((m_nTicks / TicksPerSecond) % 60);
141 }
142
145 return (int)((m_nTicks / TicksPerMillisecond) % 1000);
146 }
147
150 int y, m, d;
151 GetDatePart(m_nTicks, y, m, d);
152
153 const int* days = IsLeapYear(y) ? DaysToMonth366 : DaysToMonth365;
154 return days [m - 1] + d;
155 }
156
159 return (int)((m_nTicks / TicksPerDay + 1) % 7);
160 }
161
164 return DateTime(m_nTicks - (m_nTicks % TicksPerDay), m_kind);
165 }
166
169 return TimeSpan(m_nTicks % TicksPerDay);
170 }
171
174 return AddTicks(value.GetTicks());
175 }
176
177 DateTime DateTime::AddDays(double value) const {
179 return AddMilliseconds(value * 86400000.0);
180 }
181
182 DateTime DateTime::AddHours(double value) const {
184 return AddMilliseconds(value * 3600000.0);
185 }
186
189 int64_t ticks = (int64_t)(value * TicksPerMillisecond + (value >= 0.0 ? 0.5 : -0.5));
190 return AddTicks(ticks);
191 }
192
193 DateTime DateTime::AddMinutes(double value) const {
195 return AddMilliseconds(value * 60000.0);
196 }
197
198 DateTime DateTime::AddMonths(int months) const {
200 if (months < -120000 || months > 120000) throw ArgumentOutOfRangeException("Months value is out of range.");
201
203 int y, m, d; GetDatePart(m_nTicks, y, m, d);
204 int i = m - 1 + months;
205 if (i >= 0) { m = i % 12 + 1; y = y + i / 12; }
206 else { m = 12 + (i + 1) % 12; y = y + (i - 11) / 12; }
207
209 if (y < 1 || y > 9999) throw ArgumentOutOfRangeException("DateTime result is out of range.");
210 int days = DaysInMonth(y, m);
211 if (d > days) d = days;
212
214 return DateTime((int64_t)(DateToTicks(y, m, d) + m_nTicks % TicksPerDay), m_kind);
215 }
216
217 DateTime DateTime::AddSeconds(double value) const {
219 return AddMilliseconds(value * 1000.0);
220 }
221
222 DateTime DateTime::AddTicks(int64_t value) const {
224 int64_t ticks = m_nTicks + value;
225 if (ticks < MinTicks || ticks > MaxTicks) {
226 throw ArgumentOutOfRangeException("DateTime result is out of range.");
227 }
228
229 return DateTime(ticks, m_kind);
230 }
231
232 DateTime DateTime::AddYears(int value) const {
234 if (value < -10000 || value > 10000) {
235 throw ArgumentOutOfRangeException("Years value is out of range.");
236 }
237
239 return AddMonths(value * 12);
240 }
241
244 DateTime utc = UtcNow();
245 return utc.ToLocalTime();
246 }
247
250 auto now = std::chrono::system_clock::now();
251 auto duration = now.time_since_epoch();
252
254 int64_t micro = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
255 int64_t ticks = (micro * 10) + UnixEpochTicks;
256 return DateTime(ticks, DateTimeKind::Utc);
257 }
258
261 return Now().GetDate();
262 }
263
264 static int64_t TmToTicks(const struct tm& t, int64_t remainderTicks) {
266 int year = t.tm_year + 1900, month = t.tm_mon + 1, day = t.tm_mday;
267 int hour = t.tm_hour, minute = t.tm_min, second = t.tm_sec;
268
270 return DateToTicks(year, month, day) + TimeToTicks(hour, minute, second) + remainderTicks;
271 }
272
273 static struct tm ConvertToLocalTm(int64_t time) {
275 struct tm t;
276#if defined(_WIN32)
277 _localtime64_s(&t, &time);
278#else
279 time_t time_t_val = static_cast<time_t>(time);
280 localtime_r(&time_t_val, &t);
281#endif
282 return t;
283 }
284
285 static struct tm ConvertToUtcTm(int64_t time) {
287 struct tm t;
288#if defined(_WIN32)
289 _gmtime64_s(&t, &time);
290#else
291 time_t time_t_val = static_cast<time_t>(time);
292 gmtime_r(&time_t_val, &t);
293#endif
294 return t;
295 }
296
299 if (m_kind == DateTimeKind::Local) return *this;
300
302 int64_t time = (m_nTicks - UnixEpochTicks) / TicksPerSecond;
303 struct tm t = ConvertToLocalTm(time);
305 }
306
309 if (m_kind == DateTimeKind::Utc) return *this;
310
312 int64_t time = (m_nTicks - UnixEpochTicks) / TicksPerSecond;
313 struct tm t = ConvertToUtcTm(time);
314 return DateTime(TmToTicks(t, m_nTicks % TicksPerSecond), DateTimeKind::Utc);
315 }
316
319 return ToString("yyyy-MM-dd HH:mm:ss");
320 }
321
322 String DateTime::ToString(const String& sFormat) const {
324 std::stringstream ss;
325 int y, m, d; GetDatePart(m_nTicks, y, m, d);
326
327 ss << std::setfill('0') << std::setw(4) << y << "-"
328 << std::setw(2) << m << "-" << std::setw(2) << d << " "
329 << std::setw(2) << GetHour() << ":" << std::setw(2) << GetMinute() << ":" << std::setw(2) << GetSecond();
330 return String(ss.str().c_str());
331 }
332
333 int DateTime::DaysInMonth(int year, int month) {
335 if (month < 1 || month > 12) {
336 throw ArgumentOutOfRangeException("Month must be between 1 and 12.");
337 }
338
340 const int* days = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
341 return days [month] - days [month - 1];
342 }
343
344 bool DateTime::IsLeapYear(int year) {
346 if (year < 1 || year > 9999) {
347 throw ArgumentOutOfRangeException("Year must be between 1 and 9999.");
348 }
349
351 return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
352 }
353
354 static std::string ConvertFormat(const String& format) {
356 std::string f = format.GetRawString();
357 size_t pos = 0;
358 while ((pos = f.find("yyyy")) != std::string::npos) f.replace(pos, 4, "%Y");
359 while ((pos = f.find("MM")) != std::string::npos) f.replace(pos, 2, "%m");
360 while ((pos = f.find("dd")) != std::string::npos) f.replace(pos, 2, "%d");
361 while ((pos = f.find("HH")) != std::string::npos) f.replace(pos, 2, "%H");
362 while ((pos = f.find("mm")) != std::string::npos) f.replace(pos, 2, "%M");
363 while ((pos = f.find("ss")) != std::string::npos) f.replace(pos, 2, "%S");
364 return f;
365 }
366
367 static bool ParseDateTimeStr(const String& s, const String& format, DateTime& result) {
369 std::istringstream ss(s.GetRawString());
370 std::tm t = {};
371 std::string f = ConvertFormat(format);
372 ss >> std::get_time(&t, f.c_str());
373
374 if (ss.fail()) return false;
375
376 try {
377 result = DateTime(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
378 return true;
379 }
380 catch (const Exception&) {
382 return false;
383 }
384 catch (const std::exception&) {
386 return false;
387 }
388 }
389
390 bool DateTime::TryParseExact(const String& s, const String& format, DateTime& result) {
392 return ParseDateTimeStr(s, format, result);
393 }
394
395 bool DateTime::TryParse(const String& s, DateTime& result) {
397 return ParseDateTimeStr(s, "yyyy-MM-dd HH:mm:ss", result);
398 }
399
400 DateTime DateTime::ParseExact(const String& s, const String& format) {
402 DateTime result(1, 1, 1);
403 if (!TryParseExact(s, format, result)) {
404 throw FormatException("String was not recognized as a valid DateTime.");
405 }
406
407 return result;
408 }
409
412 DateTime result(1, 1, 1);
413 if (!TryParse(s, result)) {
414 throw FormatException("String was not recognized as a valid DateTime.");
415 }
416
417 return result;
418 }
419
420 } // namespace System
421} // namespace DotNetDupe
Defines the exception thrown when an invalid argument is provided to a method.
Defines the exception thrown when an argument value is outside the acceptable range of values.
Represents an instant in time, typically expressed as a date and time of day.
Defines the exception thrown when the format of an argument is invalid or not compliant with specific...
High-performance UTF-8 / UTF-16 string manipulation class mirroring .NET System.String.
ArgumentOutOfRangeException(const String &sMessage)
Initializes a new instance of the ArgumentOutOfRangeException class with a specified error message.
Represents an instant in time, typically expressed as a date and time of day.
Definition DateTime.h:31
int GetSecond() const
Gets the seconds component of the date represented by this instance.
Definition DateTime.cpp:138
static bool IsLeapYear(int year)
Returns an indication whether the specified year is a leap year.
Definition DateTime.cpp:344
DateTime Add(TimeSpan value) const
Returns a new DateTime that adds the value of the specified TimeSpan.
Definition DateTime.cpp:172
static DateTime ParseExact(const String &s, const String &format)
Converts the string representation of a date and time to its DateTime equivalent using specified form...
Definition DateTime.cpp:400
DateTime AddSeconds(double value) const
Returns a new DateTime that adds the specified number of seconds.
Definition DateTime.cpp:217
DateTime AddMonths(int months) const
Returns a new DateTime that adds the specified number of months.
Definition DateTime.cpp:198
DateTime AddHours(double value) const
Returns a new DateTime that adds the specified number of hours.
Definition DateTime.cpp:182
static DateTime Now()
Gets a DateTime object set to the current date and time on this computer, expressed as local time.
Definition DateTime.cpp:242
DateTime ToUniversalTime() const
Converts the value of the current DateTime object to Coordinated Universal Time (UTC).
Definition DateTime.cpp:307
int GetMonth() const
Gets the month component of the date represented by this instance.
Definition DateTime.cpp:114
static bool TryParse(const String &s, DateTime &result)
Converts the string representation of a date and time to its DateTime equivalent and returns success ...
Definition DateTime.cpp:395
int GetDayOfWeek() const
Gets the day of the week represented by this instance.
Definition DateTime.cpp:157
DateTime AddMilliseconds(double value) const
Returns a new DateTime that adds the specified number of milliseconds.
Definition DateTime.cpp:187
DateTime AddYears(int value) const
Returns a new DateTime that adds the specified number of years.
Definition DateTime.cpp:232
int GetHour() const
Gets the hour component of the date represented by this instance.
Definition DateTime.cpp:128
DateTime()
Initializes a new instance of DateTime to 0 ticks (0001-01-01 00:00:00) with Unspecified kind.
Definition DateTime.h:34
int GetYear() const
Gets the year component of the date represented by this instance.
Definition DateTime.cpp:107
static DateTime Parse(const String &s)
Converts the string representation of a date and time to its DateTime equivalent.
Definition DateTime.cpp:410
String ToString() const
Converts the value of the current DateTime object to its equivalent string representation.
Definition DateTime.cpp:317
int GetMinute() const
Gets the minute component of the date represented by this instance.
Definition DateTime.cpp:133
DateTime AddTicks(int64_t value) const
Returns a new DateTime that adds the specified number of ticks.
Definition DateTime.cpp:222
DateTime ToLocalTime() const
Converts the value of the current DateTime object to local time.
Definition DateTime.cpp:297
int GetDayOfYear() const
Gets the day of the year represented by this instance.
Definition DateTime.cpp:148
TimeSpan GetTimeOfDay() const
Gets the time of day for this instance.
Definition DateTime.cpp:167
static DateTime UtcNow()
Gets a DateTime object set to the current date and time on this computer, expressed as UTC.
Definition DateTime.cpp:248
int GetDay() const
Gets the day of the month represented by this instance.
Definition DateTime.cpp:121
int GetMillisecond() const
Gets the milliseconds component of the date represented by this instance.
Definition DateTime.cpp:143
DateTime GetDate() const
Gets the date component of this instance with the time set to 00:00:00.
Definition DateTime.cpp:162
static DateTime Today()
Gets the current date with the time component set to 00:00:00.
Definition DateTime.cpp:259
static int DaysInMonth(int year, int month)
Returns the number of days in the specified month and year.
Definition DateTime.cpp:333
DateTime AddMinutes(double value) const
Returns a new DateTime that adds the specified number of minutes.
Definition DateTime.cpp:193
static bool TryParseExact(const String &s, const String &format, DateTime &result)
Converts the string representation of a date and time to its DateTime equivalent using format and ret...
Definition DateTime.cpp:390
DateTime AddDays(double value) const
Returns a new DateTime that adds the specified number of days.
Definition DateTime.cpp:177
Represents errors that occur during application execution.
Definition Exception.h:19
FormatException(const String &sMessage)
Initializes a new instance of the FormatException class with a specified error message.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
String()
Initializes a new instance of the String class to an empty string.
Definition String.cpp:64
const char * GetRawString() const
Definition String.cpp:230
Represents a time interval (duration of time or elapsed time) measured as a positive or negative numb...
Definition TimeSpan.h:20
TimeSpan()
Initializes a new instance of TimeSpan to zero duration.
Definition TimeSpan.h:38
int64_t GetTicks() const
Gets the number of ticks that represent the value of the current TimeSpan structure.
Definition TimeSpan.h:46
constexpr int64_t TicksPerMinute
Definition DateTime.cpp:18
constexpr int64_t TicksPerDay
Definition DateTime.cpp:20
static struct tm ConvertToUtcTm(int64_t time)
Definition DateTime.cpp:285
static int64_t DateToTicks(int year, int month, int day)
Converts a Gregorian calendar date into total 100-nanosecond ticks since CE 0001-01-01.
Definition DateTime.cpp:29
constexpr int64_t TicksPerHour
Definition DateTime.cpp:19
static std::string ConvertFormat(const String &format)
Definition DateTime.cpp:354
static void GetDatePart(int64_t ticks, int &year, int &month, int &day)
Decomposes total ticks into Gregorian calendar date components (year, month, day).
Definition DateTime.cpp:62
static int64_t TmToTicks(const struct tm &t, int64_t remainderTicks)
Definition DateTime.cpp:264
constexpr int64_t MaxTicks
Definition DateTime.cpp:22
constexpr int64_t TicksPerMillisecond
Definition DateTime.cpp:16
DateTimeKind
Specifies whether a DateTime object represents local time, UTC, or is unspecified.
Definition DateTime.h:19
@ Utc
The time represented is UTC.
Definition DateTime.h:21
@ Local
The time represented is local time.
Definition DateTime.h:22
@ Unspecified
The time represented is not specified as either local time or UTC.
Definition DateTime.h:20
constexpr int64_t TicksPerSecond
Definition DateTime.cpp:17
static struct tm ConvertToLocalTm(int64_t time)
Definition DateTime.cpp:273
constexpr int64_t MinTicks
Definition DateTime.cpp:21
static bool ParseDateTimeStr(const String &s, const String &format, DateTime &result)
Definition DateTime.cpp:367
constexpr int DaysToMonth365[]
Definition DateTime.cpp:25
constexpr int64_t UnixEpochTicks
Definition DateTime.cpp:23
constexpr int DaysToMonth366[]
Definition DateTime.cpp:26
static int64_t TimeToTicks(int hour, int minute, int second)
Converts hour, minute, and second into total 100-nanosecond ticks.
Definition DateTime.cpp:50