/
mcdatetime.cpp
403 lines (310 loc) · 9.97 KB
/
mcdatetime.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
// mcdatetime.cpp
// MUSHclient's date/time class
// Handles sub-second intervals
#include "stdafx.h"
#define MIN_DATE 0 // year 1800
#define MAX_DATE 2958465L // about year 9999
// Half a second, expressed in days
#define HALF_SECOND (1.0/172800.0)
#define SECS_IN_DAY (60.0 * 60.0 * 24.0)
// DateTime math
CmcDateTime CmcDateTime::operator+(const CmcDateTimeSpan& dateSpan) const
{
CmcDateTime dateResult; // Initializes m_status to valid
// If either operand NULL, result NULL
if (GetStatus() == null || dateSpan.GetStatus() == null)
{
dateResult.SetStatus(null);
return dateResult;
}
// If either operand invalid, result invalid
if (GetStatus() == invalid || dateSpan.GetStatus() == invalid)
{
dateResult.SetStatus(invalid);
return dateResult;
}
// Compute the actual date difference by adding underlying dates
dateResult = m_dt + dateSpan.m_span;
// Validate within range
// dateResult.CheckRange();
return dateResult;
} // end of CmcDateTime CmcDateTime::operator+
CmcDateTime CmcDateTime::operator-(const CmcDateTimeSpan& dateSpan) const
{
CmcDateTime dateResult; // Initializes m_status to valid
// If either operand NULL, result NULL
if (GetStatus() == null || dateSpan.GetStatus() == null)
{
dateResult.SetStatus(null);
return dateResult;
}
// If either operand invalid, result invalid
if (GetStatus() == invalid || dateSpan.GetStatus() == invalid)
{
dateResult.SetStatus(invalid);
return dateResult;
}
// Compute the actual date difference by subtracting underlying dates
dateResult = m_dt - dateSpan.m_span;
// Validate within range
// dateResult.CheckRange();
return dateResult;
} // end of CmcDateTime CmcDateTime::operator-
const CmcDateTime& CmcDateTime::operator+=(const CmcDateTimeSpan& dateSpan)
{
*this = *this + dateSpan;
return *this;
} // end of CmcDateTime& CmcDateTime::operator+=
const CmcDateTime& CmcDateTime::operator-=(const CmcDateTimeSpan& dateSpan)
{
*this = *this - dateSpan;
return *this;
} // end of CmcDateTime& CmcDateTime::operator-=
void CmcDateTime::CheckRange()
{
if (m_dt > MAX_DATE || m_dt < MIN_DATE) // about year 100 to about 9999
SetStatus(invalid);
}
// DateTimeSpan math
CmcDateTimeSpan CmcDateTime::operator-(const CmcDateTime& date) const
{
COleDateTimeSpan spanResult;
// If either operand NULL, result NULL
if (GetStatus() == null || date.GetStatus() == null)
{
spanResult.SetStatus(COleDateTimeSpan::null);
return (double)spanResult;
}
// If either operand invalid, result invalid
if (GetStatus() == invalid || date.GetStatus() == invalid)
{
spanResult.SetStatus(COleDateTimeSpan::invalid);
return (double)spanResult;
}
// Return result (span can't be invalid, so don't check range)
return m_dt - date.m_dt;
}
int CmcDateTime::GetYear() const
{
COleDateTime t (m_dt);
return t.GetYear ();
}
int CmcDateTime::GetMonth() const // month of year (1 = Jan)
{
COleDateTime t (m_dt);
return t.GetMonth ();
}
int CmcDateTime::GetDay() const // day of month (0-31)
{
COleDateTime t (m_dt);
return t.GetDay ();
}
int CmcDateTime::GetHour() const // hour in day (0-23)
{
COleDateTime t (m_dt);
return t.GetHour ();
}
int CmcDateTime::GetMinute() const // minute in hour (0-59)
{
COleDateTime t (m_dt);
return t.GetMinute ();
}
int CmcDateTime::GetSecond() const // second in minute (0-59)
{
COleDateTime t (m_dt);
return t.GetSecond ();
}
int CmcDateTime::GetDayOfWeek() const // 1=Sun, 2=Mon, ..., 7=Sat
{
COleDateTime t (m_dt);
return t.GetDayOfWeek ();
}
int CmcDateTime::GetDayOfYear() const // days since start of year, Jan 1 = 1
{
COleDateTime t (m_dt);
return t.GetDayOfYear ();
}
// One-based array of days in year at month start
static int MonthDays[13] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
static BOOL mcDateFromTm(WORD wYear, WORD wMonth, WORD wDay,
WORD wHour, WORD wMinute, WORD wSecond, DATE& dtDest)
{
// Validate year and month (ignore day of week and milliseconds)
if (wYear > 9999 || wMonth < 1 || wMonth > 12)
return FALSE;
// Check for leap year and set the number of days in the month
BOOL bLeapYear = ((wYear & 3) == 0) &&
((wYear % 100) != 0 || (wYear % 400) == 0);
int nDaysInMonth =
MonthDays[wMonth] - MonthDays[wMonth-1] +
((bLeapYear && wDay == 29 && wMonth == 2) ? 1 : 0);
// Finish validating the date
if (wDay < 1 || wDay > nDaysInMonth ||
wHour > 23 || wMinute > 59 ||
wSecond > 59)
{
return FALSE;
}
// Cache the date in days and time in fractional days
long nDate;
double dblTime;
//It is a valid date; make Jan 1, 1AD be 1
nDate = wYear*365L + wYear/4 - wYear/100 + wYear/400 +
MonthDays[wMonth-1] + wDay;
// If leap year and it's before March, subtract 1:
if (wMonth <= 2 && bLeapYear)
--nDate;
// Offset so that 12/30/1899 is 0
nDate -= 693959L;
dblTime = (((long)wHour * 3600L) + // hrs in seconds
((long)wMinute * 60L) + // mins in seconds
wSecond) / SECS_IN_DAY;
dtDest = (double) nDate + ((nDate >= 0) ? dblTime : -dblTime);
return TRUE;
} // end of mcDateFromTm
int CmcDateTime::SetDateTime(int nYear, int nMonth, int nDay,
int nHour, int nMin, double nSec)
{
return m_status = mcDateFromTm((WORD)nYear, (WORD)nMonth,
(WORD)nDay, (WORD)nHour, (WORD)nMin, nSec, m_dt) ?
valid : invalid;
}
/*
This considerable amount of mucking around is because the date/time routines
do not seem to return a time to more than 1-second granularity, regardless of
the fact that GetSystemTime is supposed to return time to the millisecond.
I guess this is because the real-time clocks are only updated every second.
So, what I am doing here is taking the 'real' time every RESYNC_EVERY_SECS
seconds, and then using the high-performance counter to find the number of
seconds (fractions of seconds) that elapsed since. This is then added to the
last "real" time to find the actual time, effectively with much higher
granularity.
*/
#define RESYNC_EVERY_SECS 60
CmcDateTime CmcDateTime::GetTimeNow ()
{
static bool bFirstTime = true;
static CmcDateTime last_date;
static LARGE_INTEGER last_time;
static LONGLONG iCounterFrequency = 0;
if (bFirstTime)
{
bFirstTime = false;
LARGE_INTEGER large_int_frequency;
if (QueryPerformanceFrequency (&large_int_frequency))
{
iCounterFrequency = large_int_frequency.QuadPart;
QueryPerformanceCounter (&last_time);
}
}
double secs = 0;
LARGE_INTEGER time_now;
if (iCounterFrequency)
{
QueryPerformanceCounter (&time_now);
LONGLONG offset = time_now.QuadPart - last_time.QuadPart;
secs = (double) offset / (double) iCounterFrequency;
// TRACE ("Secs = %10.4f\n", secs);
}
else
{
time_now.QuadPart = 0;
time_now.QuadPart = 0;
}
// if no high-performance counter, just query the time each time
if (iCounterFrequency == 0 ||
last_date.m_dt == 0 ||
secs > RESYNC_EVERY_SECS)
{
SYSTEMTIME systime;
GetLocalTime (&systime);
last_date = CmcDateTime(systime.wYear, systime.wMonth,
systime.wDay, systime.wHour, systime.wMinute,
(double) systime.wSecond + ( (double) systime.wMilliseconds / 1000.0) );
secs = 0;
last_time = time_now;
}
CmcDateTime this_date (last_date);
this_date.m_dt += secs / SECS_IN_DAY;
// ---- debugging
#if 0
{
SYSTEMTIME systime;
GetSystemTime (&systime);
CmcDateTime test = CmcDateTime(systime.wYear, systime.wMonth,
systime.wDay, systime.wHour, systime.wMinute,
(double) systime.wSecond + ( (double) systime.wMilliseconds / 1000.0) );
double diff = test.m_dt - this_date.m_dt;
TRACE1 ("Time difference = %10.8f\n", diff);
}
#endif
// --- end debugging
return this_date;
}
void CmcDateTimeSpan::SetDateTimeSpan(
long lDays, int nHours, int nMins, double nSecs)
{
// Set date span by breaking into fractional days (all input ranges valid)
m_span = lDays + ((double)nHours)/24 + ((double)nMins)/(24*60) +
(nSecs)/(24*60*60);
SetStatus(valid);
}
const CmcDateTimeSpan& CmcDateTimeSpan::operator=(double dblSpanSrc)
{
m_span = dblSpanSrc;
SetStatus(valid);
return *this;
}
const CmcDateTimeSpan& CmcDateTimeSpan::operator=(const CmcDateTimeSpan& dateSpanSrc)
{
m_span = dateSpanSrc.m_span;
m_status = dateSpanSrc.m_status;
return *this;
}
CmcDateTimeSpan CmcDateTimeSpan::operator+(const CmcDateTimeSpan& dateSpan) const
{
CmcDateTimeSpan dateSpanTemp;
// If either operand Null, result Null
if (GetStatus() == null || dateSpan.GetStatus() == null)
{
dateSpanTemp.SetStatus(null);
return dateSpanTemp;
}
// If either operand Invalid, result Invalid
if (GetStatus() == invalid || dateSpan.GetStatus() == invalid)
{
dateSpanTemp.SetStatus(invalid);
return dateSpanTemp;
}
// Add spans and validate within legal range
dateSpanTemp.m_span = m_span + dateSpan.m_span;
// dateSpanTemp.CheckRange();
return dateSpanTemp;
}
CmcDateTimeSpan CmcDateTimeSpan::operator-(const CmcDateTimeSpan& dateSpan) const
{
CmcDateTimeSpan dateSpanTemp;
// If either operand Null, result Null
if (GetStatus() == null || dateSpan.GetStatus() == null)
{
dateSpanTemp.SetStatus(null);
return dateSpanTemp;
}
// If either operand Invalid, result Invalid
if (GetStatus() == invalid || dateSpan.GetStatus() == invalid)
{
dateSpanTemp.SetStatus(invalid);
return dateSpanTemp;
}
// Subtract spans and validate within legal range
dateSpanTemp.m_span = m_span - dateSpan.m_span;
// dateSpanTemp.CheckRange();
return dateSpanTemp;
}
const CmcDateTimeSpan& CmcDateTimeSpan::operator+=(const CmcDateTimeSpan& dateSpan)
{ *this = *this + dateSpan; return *this; }
const CmcDateTimeSpan& CmcDateTimeSpan::operator-=(const CmcDateTimeSpan& dateSpan)
{ *this = *this - dateSpan; return *this; }
CmcDateTimeSpan CmcDateTimeSpan::operator-() const
{ return -this->m_span; }