/src/dcmtk/ofstd/libsrc/oftime.cc
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (C) 2002-2026, OFFIS e.V. |
4 | | * All rights reserved. See COPYRIGHT file for details. |
5 | | * |
6 | | * This software and supporting documentation were developed by |
7 | | * |
8 | | * OFFIS e.V. |
9 | | * R&D Division Health |
10 | | * Escherweg 2 |
11 | | * D-26121 Oldenburg, Germany |
12 | | * |
13 | | * |
14 | | * Module: ofstd |
15 | | * |
16 | | * Author: Joerg Riesmeier, Harald Roesen |
17 | | * |
18 | | * Purpose: Class for time functions (Source) |
19 | | * |
20 | | */ |
21 | | |
22 | | |
23 | | #include "dcmtk/config/osconfig.h" |
24 | | |
25 | | #include "dcmtk/ofstd/oftime.h" |
26 | | #include "dcmtk/ofstd/ofstdinc.h" |
27 | | #include "dcmtk/ofstd/ofstd.h" |
28 | | #include "dcmtk/ofstd/oflimits.h" |
29 | | #include "dcmtk/ofstd/ofmath.h" |
30 | | |
31 | | #include <ctime> |
32 | | #include <cmath> |
33 | | |
34 | | BEGIN_EXTERN_C |
35 | | #ifdef HAVE_SYS_TIME_H |
36 | | #include <sys/time.h> /* for struct timeval on Linux */ |
37 | | #endif |
38 | | |
39 | | #ifndef HAVE_WINDOWS_H |
40 | | #ifndef HAVE_PROTOTYPE_GETTIMEOFDAY |
41 | | /* Ultrix has gettimeofday() but no prototype in the header files */ |
42 | | int gettimeofday(struct timeval *tp, void *); |
43 | | #endif |
44 | | #endif |
45 | | END_EXTERN_C |
46 | | |
47 | | #ifdef HAVE_WINDOWS_H |
48 | | #define WIN32_LEAN_AND_MEAN |
49 | | #include <windows.h> /* for Windows time functions */ |
50 | | #endif |
51 | | |
52 | | |
53 | | /*------------------------* |
54 | | * constant definitions * |
55 | | *------------------------*/ |
56 | | |
57 | | // use "Not a Number" (NaN) if available, an invalid value otherwise |
58 | | #ifdef HAVE_CXX11 |
59 | | const double OFTime::unspecifiedSecond = OFnumeric_limits<double>::quiet_NaN(); |
60 | | const double OFTime::unspecifiedTimeZone = OFnumeric_limits<double>::quiet_NaN(); |
61 | | #else |
62 | | const double OFTime::unspecifiedSecond = OFnumeric_limits<double>::max(); |
63 | | const double OFTime::unspecifiedTimeZone = OFnumeric_limits<double>::max(); |
64 | | #endif |
65 | | |
66 | | |
67 | | /*------------------* |
68 | | * implementation * |
69 | | *------------------*/ |
70 | | |
71 | | OFTime::OFTime() |
72 | 0 | : Hour(0), |
73 | 0 | Minute(0), |
74 | 0 | Second(unspecifiedSecond), |
75 | 0 | TimeZone(unspecifiedTimeZone) |
76 | 0 | { |
77 | 0 | } |
78 | | |
79 | | |
80 | | OFTime::OFTime(const OFTime &timeVal) |
81 | 0 | : Hour(timeVal.Hour), |
82 | 0 | Minute(timeVal.Minute), |
83 | 0 | Second(timeVal.Second), |
84 | 0 | TimeZone(timeVal.TimeZone) |
85 | 0 | { |
86 | 0 | } |
87 | | |
88 | | |
89 | | OFTime::OFTime(const unsigned int hour, |
90 | | const unsigned int minute, |
91 | | const double second, |
92 | | const double timeZone) |
93 | 0 | : Hour(hour), |
94 | 0 | Minute(minute), |
95 | 0 | Second(second), |
96 | 0 | TimeZone(timeZone) |
97 | 0 | { |
98 | 0 | } |
99 | | |
100 | | |
101 | | OFTime::~OFTime() |
102 | 0 | { |
103 | 0 | } |
104 | | |
105 | | |
106 | | OFTime &OFTime::operator=(const OFTime &timeVal) |
107 | 0 | { |
108 | 0 | Hour = timeVal.Hour; |
109 | 0 | Minute = timeVal.Minute; |
110 | 0 | Second = timeVal.Second; |
111 | 0 | TimeZone = timeVal.TimeZone; |
112 | 0 | return *this; |
113 | 0 | } |
114 | | |
115 | | |
116 | | OFBool OFTime::operator==(const OFTime &timeVal) const |
117 | 0 | { |
118 | 0 | #ifndef __i386__ |
119 | 0 | return (getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/) == timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/)); |
120 | | #else |
121 | | volatile double lhs = getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
122 | | volatile double rhs = timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
123 | | return lhs == rhs; |
124 | | #endif |
125 | 0 | } |
126 | | |
127 | | |
128 | | OFBool OFTime::operator!=(const OFTime &timeVal) const |
129 | 0 | { |
130 | 0 | #ifndef __i386__ |
131 | 0 | return (getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/) != timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/)); |
132 | | #else |
133 | | volatile double lhs = getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
134 | | volatile double rhs = timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
135 | | return lhs != rhs; |
136 | | #endif |
137 | 0 | } |
138 | | |
139 | | |
140 | | OFBool OFTime::operator<(const OFTime &timeVal) const |
141 | 0 | { |
142 | 0 | #ifndef __i386__ |
143 | 0 | return (getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/) < timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/)); |
144 | | #else |
145 | | volatile double lhs = getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
146 | | volatile double rhs = timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
147 | | return lhs < rhs; |
148 | | #endif |
149 | 0 | } |
150 | | |
151 | | |
152 | | OFBool OFTime::operator<=(const OFTime &timeVal) const |
153 | 0 | { |
154 | 0 | #ifndef __i386__ |
155 | 0 | return (getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/) <= timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/)); |
156 | | #else |
157 | | volatile double lhs = getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
158 | | volatile double rhs = timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
159 | | return lhs <= rhs; |
160 | | #endif |
161 | 0 | } |
162 | | |
163 | | |
164 | | OFBool OFTime::operator>=(const OFTime &timeVal) const |
165 | 0 | { |
166 | 0 | #ifndef __i386__ |
167 | 0 | return (getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/) >= timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/)); |
168 | | #else |
169 | | volatile double lhs = getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
170 | | volatile double rhs = timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
171 | | return lhs >= rhs; |
172 | | #endif |
173 | 0 | } |
174 | | |
175 | | |
176 | | OFBool OFTime::operator>(const OFTime &timeVal) const |
177 | 0 | { |
178 | 0 | #ifndef __i386__ |
179 | 0 | return (getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/) > timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/)); |
180 | | #else |
181 | | volatile double lhs = getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
182 | | volatile double rhs = timeVal.getTimeInSeconds(OFTrue /*useTimeZone*/, OFFalse /*normalize*/); |
183 | | return lhs > rhs; |
184 | | #endif |
185 | 0 | } |
186 | | |
187 | | |
188 | | void OFTime::clear() |
189 | 0 | { |
190 | 0 | Hour = 0; |
191 | 0 | Minute = 0; |
192 | 0 | clearSecond(); |
193 | 0 | clearTimeZone(); |
194 | 0 | } |
195 | | |
196 | | |
197 | | void OFTime::clearSecond() |
198 | 0 | { |
199 | 0 | Second = unspecifiedSecond; |
200 | 0 | } |
201 | | |
202 | | |
203 | | void OFTime::clearTimeZone() |
204 | 0 | { |
205 | 0 | TimeZone = unspecifiedTimeZone; |
206 | 0 | } |
207 | | |
208 | | |
209 | | OFBool OFTime::isValid() const |
210 | 0 | { |
211 | | /* check current time settings */ |
212 | 0 | return isTimeValid(Hour, Minute, Second, TimeZone); |
213 | 0 | } |
214 | | |
215 | | |
216 | | OFBool OFTime::hasSecond() const |
217 | 0 | { |
218 | 0 | return isSecondSpecified(Second); |
219 | 0 | } |
220 | | |
221 | | |
222 | | OFBool OFTime::hasFractionOfSecond() const |
223 | 0 | { |
224 | 0 | return hasSecond() && (floor(Second) != Second); |
225 | 0 | } |
226 | | |
227 | | |
228 | | OFBool OFTime::hasTimeZone() const |
229 | 0 | { |
230 | 0 | return isTimeZoneSpecified(TimeZone); |
231 | 0 | } |
232 | | |
233 | | |
234 | | OFBool OFTime::setTime(const unsigned int hour, |
235 | | const unsigned int minute, |
236 | | const double second, |
237 | | const double timeZone) |
238 | 0 | { |
239 | 0 | OFBool status = OFFalse; |
240 | | /* only change if the new time is valid */ |
241 | 0 | if (isTimeValid(hour, minute, second, timeZone)) |
242 | 0 | { |
243 | 0 | Hour = hour; |
244 | 0 | Minute = minute; |
245 | 0 | Second = second; |
246 | 0 | TimeZone = timeZone; |
247 | | /* report that a new time has been set */ |
248 | 0 | status = OFTrue; |
249 | 0 | } |
250 | 0 | return status; |
251 | 0 | } |
252 | | |
253 | | |
254 | | OFBool OFTime::setHour(const unsigned int hour) |
255 | 0 | { |
256 | 0 | OFBool status = OFFalse; |
257 | | /* only change the currently stored value if the new hour is valid */ |
258 | 0 | if (isTimeValid(hour, Minute, Second, TimeZone)) |
259 | 0 | { |
260 | 0 | Hour = hour; |
261 | | /* report that a new hour has been set */ |
262 | 0 | status = OFTrue; |
263 | 0 | } |
264 | 0 | return status; |
265 | 0 | } |
266 | | |
267 | | |
268 | | OFBool OFTime::setMinute(const unsigned int minute) |
269 | 0 | { |
270 | 0 | OFBool status = OFFalse; |
271 | | /* only change the currently stored value if the new minute is valid */ |
272 | 0 | if (isTimeValid(Hour, minute, Second, TimeZone)) |
273 | 0 | { |
274 | 0 | Minute = minute; |
275 | | /* report that a new minute has been set */ |
276 | 0 | status = OFTrue; |
277 | 0 | } |
278 | 0 | return status; |
279 | 0 | } |
280 | | |
281 | | |
282 | | OFBool OFTime::setSecond(const double second) |
283 | 0 | { |
284 | 0 | OFBool status = OFFalse; |
285 | | /* only change the currently stored value if the new second is valid */ |
286 | 0 | if (isTimeValid(Hour, Minute, second, TimeZone)) |
287 | 0 | { |
288 | 0 | Second = second; |
289 | | /* report that a new second has been set */ |
290 | 0 | status = OFTrue; |
291 | 0 | } |
292 | 0 | return status; |
293 | 0 | } |
294 | | |
295 | | |
296 | | OFBool OFTime::setTimeZone(const double timeZone) |
297 | 0 | { |
298 | 0 | OFBool status = OFFalse; |
299 | | /* only change the currently stored value if the new time zone is valid */ |
300 | 0 | if (isTimeZoneValid(timeZone, OFFalse /*acceptUnspecified*/)) |
301 | 0 | { |
302 | 0 | TimeZone = timeZone; |
303 | | /* report that a new time zone has been set */ |
304 | 0 | status = OFTrue; |
305 | 0 | } |
306 | 0 | return status; |
307 | 0 | } |
308 | | |
309 | | |
310 | | OFBool OFTime::setTimeZone(const signed int hour, |
311 | | const unsigned int minute) |
312 | 0 | { |
313 | | /* convert hour and minute values to one time zone value */ |
314 | 0 | const double timeZone = (hour < 0) ? OFstatic_cast(double, hour) - OFstatic_cast(double, minute) / 60 : OFstatic_cast(double, hour) + OFstatic_cast(double, minute) / 60; |
315 | | /* only change the currently stored value if the new time zone is valid */ |
316 | 0 | return setTimeZone(timeZone); |
317 | 0 | } |
318 | | |
319 | | |
320 | | OFBool OFTime::setLocalTimeZone() |
321 | 0 | { |
322 | | /* set local time zone (if available) */ |
323 | 0 | return setTimeZone(getLocalTimeZone()); |
324 | 0 | } |
325 | | |
326 | | |
327 | | OFBool OFTime::setTimeInSeconds(const double seconds, |
328 | | const double timeZone, |
329 | | const OFBool normalize) |
330 | 0 | { |
331 | 0 | OFBool status = OFFalse; |
332 | | /* only change if the new time is valid (leap second is not supported!) */ |
333 | 0 | if (normalize || ((seconds >= 0) && (seconds < 86400))) |
334 | 0 | { |
335 | | /* first normalize the value first to the valid range of [0.0,86400.0[ */ |
336 | 0 | const double normalSeconds = (normalize) ? seconds - OFstatic_cast(double, OFstatic_cast(signed long, seconds / 86400) * 86400) : seconds; |
337 | | /* compute time from given number of seconds since "00:00:00" */ |
338 | 0 | const unsigned int newHour = OFstatic_cast(unsigned int, normalSeconds / 3600); |
339 | 0 | const unsigned int newMinute = OFstatic_cast(unsigned int, (normalSeconds - OFstatic_cast(double, newHour) * 3600) / 60); |
340 | 0 | const double newSecond = normalSeconds - OFstatic_cast(double, newHour) * 3600 - OFstatic_cast(double, newMinute) * 60; |
341 | 0 | status = setTime(newHour, newMinute, newSecond, timeZone); |
342 | 0 | } |
343 | 0 | return status; |
344 | 0 | } |
345 | | |
346 | | |
347 | | OFBool OFTime::setTimeInHours(const double hours, |
348 | | const double timeZone, |
349 | | const OFBool normalize) |
350 | 0 | { |
351 | 0 | OFBool status = OFFalse; |
352 | | /* only change if the new time is valid */ |
353 | 0 | if (normalize || ((hours >= 0) && (hours < 24))) |
354 | 0 | { |
355 | | /* first normalize the value to the valid range of [0.0,24.0[ */ |
356 | 0 | const double normalHours = (normalize) ? hours - OFstatic_cast(double, OFstatic_cast(signed long, hours / 24) * 24) : hours; |
357 | | /* compute time from given number of hours since "00:00:00" */ |
358 | 0 | const unsigned int newHour = OFstatic_cast(unsigned int, normalHours); |
359 | 0 | const unsigned int newMinute = OFstatic_cast(unsigned int, (normalHours - OFstatic_cast(double, newHour)) * 60); |
360 | 0 | const double newSecond = (normalHours - OFstatic_cast(double, newHour)) * 3600 - OFstatic_cast(double, newMinute) * 60; |
361 | 0 | status = setTime(newHour, newMinute, newSecond, timeZone); |
362 | 0 | } |
363 | 0 | return status; |
364 | 0 | } |
365 | | |
366 | | |
367 | | OFBool OFTime::setCurrentTime() |
368 | 0 | { |
369 | | /* get the current system time and call the "real" function */ |
370 | 0 | return setCurrentTime(time(NULL)); |
371 | 0 | } |
372 | | |
373 | | |
374 | | OFBool OFTime::setCurrentTime(const time_t &tt) |
375 | 0 | { |
376 | 0 | OFBool status = OFFalse; |
377 | 0 | #ifdef HAVE_LOCALTIME_R |
378 | | /* use localtime_r instead of localtime */ |
379 | 0 | struct tm ltBuf; |
380 | 0 | struct tm *lt = <Buf; |
381 | 0 | localtime_r(&tt, lt); |
382 | | #else |
383 | | struct tm *lt = localtime(&tt); |
384 | | #endif |
385 | 0 | if (lt != NULL) |
386 | 0 | { |
387 | | /* store retrieved time */ |
388 | 0 | Hour = lt->tm_hour; |
389 | 0 | Minute = lt->tm_min; |
390 | 0 | Second = lt->tm_sec; |
391 | 0 | #ifdef HAVE_GMTIME_R |
392 | | /* use gmtime_r instead of gmtime */ |
393 | 0 | struct tm gtBuf; |
394 | 0 | struct tm *gt = >Buf; |
395 | 0 | gmtime_r(&tt, gt); |
396 | | #else |
397 | | /* avoid overwriting of local time structure by calling gmtime() */ |
398 | | struct tm ltBuf = *lt; |
399 | | lt = <Buf; |
400 | | struct tm *gt = gmtime(&tt); |
401 | | #endif |
402 | 0 | if (gt != NULL) |
403 | 0 | { |
404 | | /* retrieve and store the time zone */ |
405 | 0 | TimeZone = (lt->tm_hour - gt->tm_hour) + OFstatic_cast(double, lt->tm_min - gt->tm_min) / 60; |
406 | | /* correct for "day overflow" */ |
407 | 0 | if (TimeZone < -12) |
408 | 0 | TimeZone += 24; |
409 | 0 | else if (TimeZone > 12) // cannot detect time zones in the range ]+12.0,+14.0] |
410 | 0 | TimeZone -= 24; |
411 | 0 | } else { |
412 | | /* could not retrieve the time zone */ |
413 | 0 | TimeZone = unspecifiedTimeZone; |
414 | 0 | } |
415 | | #ifdef HAVE_WINDOWS_H |
416 | | /* Windows: no microseconds available, use milliseconds instead */ |
417 | | SYSTEMTIME timebuf; |
418 | | GetSystemTime(&timebuf); |
419 | | Second += OFstatic_cast(double, timebuf.wMilliseconds) / 1000; |
420 | | #else /* Unix */ |
421 | 0 | struct timeval tv; |
422 | 0 | if (gettimeofday(&tv, NULL) == 0) |
423 | 0 | Second += OFstatic_cast(double, tv.tv_usec) / 1000000; |
424 | 0 | #endif |
425 | | /* report that current system time has been set */ |
426 | 0 | status = OFTrue; |
427 | 0 | } |
428 | 0 | return status; |
429 | 0 | } |
430 | | |
431 | | |
432 | | OFBool OFTime::setISOFormattedTime(const OFString &formattedTime) |
433 | 0 | { |
434 | 0 | const size_t length = formattedTime.length(); |
435 | 0 | if (length < 3) { |
436 | | /* not enough input to scan */ |
437 | 0 | return OFFalse; |
438 | 0 | } |
439 | | |
440 | 0 | size_t pos = 0; |
441 | 0 | const char delim = formattedTime.at(pos + 2); |
442 | 0 | const OFBool delimsUsed = !isdigit(OFstatic_cast(unsigned char, delim)); |
443 | |
|
444 | 0 | unsigned int hours = 0; |
445 | 0 | unsigned int minutes = 0; |
446 | 0 | double seconds = OFTime::unspecifiedSecond; |
447 | 0 | double timeZone = OFTime::unspecifiedTimeZone; |
448 | |
|
449 | 0 | { |
450 | | /* scan for HHMM or HH:MM - hours and minutes */ |
451 | 0 | OFString hhmmFormat("%2u"); |
452 | 0 | if (delimsUsed) |
453 | 0 | { |
454 | 0 | hhmmFormat += delim; |
455 | 0 | } |
456 | 0 | hhmmFormat += "%2u%n"; |
457 | |
|
458 | 0 | const int nCharactersExpected = (delimsUsed ? 5 : 4); |
459 | 0 | int nCharactersRead = 0; |
460 | 0 | const int nReceivedArgs = sscanf(formattedTime.c_str() + pos, hhmmFormat.c_str(), &hours, &minutes, &nCharactersRead); |
461 | 0 | if ((nReceivedArgs == 2) && (nCharactersRead == nCharactersExpected)) |
462 | 0 | { |
463 | 0 | pos += nCharactersRead; |
464 | 0 | } |
465 | 0 | else |
466 | 0 | { |
467 | | /* found no valid HH[:]MM 'pattern' */ |
468 | 0 | return OFFalse; |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | | /* is there still input available that starts with a digit or a delimiter (if used)? */ |
473 | 0 | if ((pos < length) && (isdigit(OFstatic_cast(unsigned char, formattedTime.at(pos))) || (delimsUsed && (formattedTime.at(pos) == delim)))) |
474 | 0 | { |
475 | | /* scan for SS or :SS - seconds */ |
476 | 0 | OFString ssFormat; |
477 | 0 | if (delimsUsed) |
478 | 0 | { |
479 | 0 | ssFormat += delim; |
480 | 0 | } |
481 | 0 | ssFormat += "%2u%n"; |
482 | |
|
483 | 0 | unsigned int secondsInt = 0; |
484 | 0 | const int nCharactersExpected = (delimsUsed ? 3 : 2); |
485 | 0 | int nCharactersRead = 0; |
486 | 0 | const int nReceivedArgs = sscanf(formattedTime.c_str() + pos, ssFormat.c_str(), &secondsInt, &nCharactersRead); |
487 | 0 | if ((nReceivedArgs == 1) && (nCharactersRead == nCharactersExpected)) |
488 | 0 | { |
489 | 0 | pos += nCharactersRead; |
490 | 0 | seconds = secondsInt; |
491 | 0 | } |
492 | 0 | else |
493 | 0 | { |
494 | | /* found no valid [:]SS 'pattern' */ |
495 | 0 | return OFFalse; |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | | /* if seconds are used, is there still input available that starts with a dot ('.') character? */ |
500 | 0 | if (isSecondSpecified(seconds) && (pos < length) && (formattedTime.at(pos) == '.')) |
501 | 0 | { |
502 | | /* scan for .FFFFFF - fractional seconds */ |
503 | 0 | char buffer[8] = {'\0'}; |
504 | 0 | buffer[0] = '.'; |
505 | 0 | ++pos; /* overread the dot ('.') character at front */ |
506 | 0 | int nCharactersRead = 0; |
507 | 0 | const int nReceivedArgs = sscanf(formattedTime.c_str() + pos, "%6[0123456789]%n", (buffer + 1), &nCharactersRead); |
508 | 0 | OFBool fsSuccess = OFFalse; |
509 | 0 | const double fractionalSeconds = OFStandard::atof(buffer, &fsSuccess); |
510 | 0 | if ((nReceivedArgs == 1) && fsSuccess) |
511 | 0 | { |
512 | 0 | pos += nCharactersRead; |
513 | 0 | seconds += fractionalSeconds; |
514 | 0 | } |
515 | 0 | else |
516 | 0 | { |
517 | | /* found no valid .FFFFFF 'pattern' */ |
518 | 0 | return OFFalse; |
519 | 0 | } |
520 | 0 | } |
521 | | |
522 | | /* skip whitespaces in case delimiter character has been used */ |
523 | 0 | if ((pos < length) && delimsUsed) |
524 | 0 | { |
525 | 0 | const OFBool hasSpaceBeenSkipped = isspace(formattedTime.at(pos)); |
526 | 0 | while ((pos < length) && isspace(formattedTime.at(pos))) |
527 | 0 | { |
528 | 0 | ++pos; |
529 | 0 | } |
530 | 0 | if ((pos == length) && hasSpaceBeenSkipped) |
531 | 0 | { |
532 | | /* we have skipped trailing spaces but reached end of input, |
533 | | * this means that the input is/was malformed in the first place |
534 | | */ |
535 | 0 | return OFFalse; |
536 | 0 | } |
537 | 0 | } |
538 | | |
539 | | /* is there still input available that also starts with a plus ('+') or minus ('-') character? |
540 | | */ |
541 | 0 | if ((pos < length) && ((formattedTime.at(pos) == '+') || (formattedTime.at(pos) == '-'))) |
542 | 0 | { |
543 | | /* scan for &ZZZZ or &ZZ:ZZ - time zone */ |
544 | 0 | OFString tzFormat("%3d"); |
545 | 0 | if (delimsUsed) |
546 | 0 | { |
547 | 0 | tzFormat += delim; |
548 | 0 | } |
549 | 0 | tzFormat += "%2u%n"; |
550 | |
|
551 | 0 | int tzHours; |
552 | 0 | unsigned int tzMinutes; |
553 | 0 | int nCharactersRead = 0; |
554 | 0 | const int nReceivedArgs = sscanf(formattedTime.c_str() + pos, tzFormat.c_str(), &tzHours, &tzMinutes, &nCharactersRead); |
555 | 0 | if (nReceivedArgs == 2) |
556 | 0 | { |
557 | 0 | timeZone = (tzHours < 0) ? tzHours - OFstatic_cast(double, tzMinutes) / 60 |
558 | 0 | : tzHours + OFstatic_cast(double, tzMinutes) / 60; |
559 | 0 | pos += nCharactersRead; |
560 | 0 | } |
561 | 0 | else |
562 | 0 | { |
563 | | /* found no valid &ZZ[:]ZZ 'pattern' */ |
564 | 0 | return OFFalse; |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | | /* is there still input available? */ |
569 | 0 | if (pos < length) |
570 | 0 | { |
571 | | /* too much input */ |
572 | 0 | return OFFalse; |
573 | 0 | } |
574 | | |
575 | | /* all 'eaten up' */ |
576 | 0 | return setTime(hours, minutes, seconds, timeZone); |
577 | 0 | } |
578 | | |
579 | | |
580 | | OFBool OFTime::setISOFormattedTimeZone(const OFString &formattedTimeZone) |
581 | 0 | { |
582 | 0 | OFBool status = OFFalse; |
583 | 0 | const size_t length = formattedTimeZone.length(); |
584 | 0 | const OFBool hasDelimiter = (formattedTimeZone.find_first_not_of("0123456789", 1) != OFString_npos); |
585 | | /* format: +HHMM or -HHMM */ |
586 | 0 | if ((length == 5) && ((formattedTimeZone[0] == '+') || (formattedTimeZone[0] == '-'))) |
587 | 0 | { |
588 | 0 | int tzHours; |
589 | 0 | unsigned int tzMinutes; |
590 | | /* extract "HH", "MM" from time zone string */ |
591 | 0 | if (sscanf(formattedTimeZone.c_str(), "%03d%02u", &tzHours, &tzMinutes) == 2) |
592 | 0 | status = setTimeZone(tzHours, tzMinutes); |
593 | 0 | } |
594 | | /* format: +HH:MM or -HH:MM */ |
595 | 0 | else if ((length == 6) && hasDelimiter && ((formattedTimeZone[0] == '+') || (formattedTimeZone[0] == '-'))) |
596 | 0 | { |
597 | 0 | int tzHours; |
598 | 0 | unsigned int tzMinutes; |
599 | | /* ignore the delimiter "%c" */ |
600 | 0 | if (sscanf(formattedTimeZone.c_str(), "%03d%*c%02u", &tzHours, &tzMinutes) == 2) |
601 | 0 | status = setTimeZone(tzHours, tzMinutes); |
602 | 0 | } |
603 | | /* empty value */ |
604 | 0 | else if (length == 0) |
605 | 0 | { |
606 | 0 | TimeZone = unspecifiedTimeZone; |
607 | 0 | status = OFTrue; |
608 | 0 | } |
609 | 0 | return status; |
610 | 0 | } |
611 | | |
612 | | |
613 | | unsigned int OFTime::getHour() const |
614 | 0 | { |
615 | 0 | return Hour; |
616 | 0 | } |
617 | | |
618 | | |
619 | | unsigned int OFTime::getMinute() const |
620 | 0 | { |
621 | 0 | return Minute; |
622 | 0 | } |
623 | | |
624 | | |
625 | | double OFTime::getSecond() const |
626 | 0 | { |
627 | 0 | return Second; |
628 | 0 | } |
629 | | |
630 | | |
631 | | unsigned int OFTime::getIntSecond() const |
632 | 0 | { |
633 | | /* return integral value of seconds */ |
634 | 0 | return (hasSecond() ? OFstatic_cast(unsigned int, Second) : 0); |
635 | 0 | } |
636 | | |
637 | | |
638 | | unsigned int OFTime::getMilliSecond() const |
639 | 0 | { |
640 | 0 | return (hasSecond() ? OFstatic_cast(unsigned int, (Second - OFstatic_cast(unsigned int, Second)) * 1000) : 0); |
641 | 0 | } |
642 | | |
643 | | |
644 | | unsigned int OFTime::getMicroSecond() const |
645 | 0 | { |
646 | 0 | return (hasSecond() ? OFstatic_cast(unsigned int, (Second - OFstatic_cast(unsigned int, Second)) * 1000000) : 0); |
647 | 0 | } |
648 | | |
649 | | |
650 | | double OFTime::getTimeZone() const |
651 | 0 | { |
652 | | // tbc: check for valid value? |
653 | 0 | return TimeZone; |
654 | 0 | } |
655 | | |
656 | | |
657 | | double OFTime::getTimeInSeconds(const OFBool useTimeZone, |
658 | | const OFBool normalize) const |
659 | 0 | { |
660 | 0 | return getTimeInSeconds(Hour, Minute, Second, (useTimeZone) ? TimeZone : unspecifiedTimeZone, normalize); |
661 | 0 | } |
662 | | |
663 | | |
664 | | double OFTime::getTimeInHours(const OFBool useTimeZone, |
665 | | const OFBool normalize) const |
666 | 0 | { |
667 | 0 | return getTimeInHours(Hour, Minute, Second, (useTimeZone) ? TimeZone : unspecifiedTimeZone, normalize); |
668 | 0 | } |
669 | | |
670 | | |
671 | | double OFTime::getTimeInSeconds(const unsigned int hour, |
672 | | const unsigned int minute, |
673 | | const double second, |
674 | | const double timeZone, |
675 | | const OFBool normalize) |
676 | 0 | { |
677 | 0 | const double secondValue = isSecondSpecified(second) ? second : 0; |
678 | 0 | const double timeZoneOffset = isTimeZoneSpecified(timeZone) ? timeZone : 0; |
679 | | /* compute number of seconds since 00:00:00 */ |
680 | 0 | double result = ((OFstatic_cast(double, hour) - timeZoneOffset) * 60 + OFstatic_cast(double, minute)) * 60 + secondValue; |
681 | | /* normalize the result to the range [0.0,86400.0[ */ |
682 | 0 | if (normalize) |
683 | 0 | result -= OFstatic_cast(double, OFstatic_cast(unsigned long, result / 86400) * 86400); |
684 | 0 | return result; |
685 | 0 | } |
686 | | |
687 | | |
688 | | double OFTime::getTimeInHours(const unsigned int hour, |
689 | | const unsigned int minute, |
690 | | const double second, |
691 | | const double timeZone, |
692 | | const OFBool normalize) |
693 | 0 | { |
694 | 0 | const double secondValue = isSecondSpecified(second) ? second : 0; |
695 | 0 | const double timeZoneOffset = isTimeZoneSpecified(timeZone) ? timeZone : 0; |
696 | | /* compute number of hours since 00:00:00 (incl. fraction of hours) */ |
697 | 0 | double result = OFstatic_cast(double, hour) - timeZoneOffset + (OFstatic_cast(double, minute) + secondValue / 60) / 60; |
698 | | /* normalize the result to the range [0.0,24.0[ */ |
699 | 0 | if (normalize) |
700 | 0 | result -= OFstatic_cast(double, OFstatic_cast(unsigned long, result / 24) * 24); |
701 | 0 | return result; |
702 | 0 | } |
703 | | |
704 | | |
705 | | OFTime OFTime::getCoordinatedUniversalTime() const |
706 | 0 | { |
707 | | /* create a new time object */ |
708 | 0 | OFTime timeVal; |
709 | | /* convert time to UTC */ |
710 | 0 | timeVal.setTimeInHours(getTimeInHours(OFTrue /*useTimeZone*/), 0 /*timeZone*/); |
711 | | /* return by-value */ |
712 | 0 | return timeVal; |
713 | 0 | } |
714 | | |
715 | | |
716 | | OFTime OFTime::getLocalTime() const |
717 | 0 | { |
718 | | /* create a new time object */ |
719 | 0 | OFTime timeVal; |
720 | 0 | const double localTimeZone = getLocalTimeZone(); |
721 | | /* convert time to local time */ |
722 | 0 | if (TimeZone != localTimeZone) |
723 | 0 | timeVal.setTimeInHours(getTimeInHours(OFTrue /*useTimeZone*/) + localTimeZone, localTimeZone); |
724 | 0 | else |
725 | 0 | { |
726 | | /* same time zone, return currently stored time */ |
727 | 0 | timeVal = *this; |
728 | 0 | } |
729 | | /* return by-value */ |
730 | 0 | return timeVal; |
731 | 0 | } |
732 | | |
733 | | |
734 | | OFBool OFTime::getISOFormattedTime(OFString &formattedTime, |
735 | | const OFBool showSeconds, |
736 | | const OFBool showFraction, |
737 | | const OFBool showTimeZone, |
738 | | const OFBool showDelimiter, |
739 | | const OFBool createMissingPart, |
740 | | const OFString &timeZoneSeparator) const |
741 | 0 | { |
742 | 0 | OFBool status = OFFalse; |
743 | | /* check for valid time first */ |
744 | 0 | if (isValid()) |
745 | 0 | { |
746 | 0 | char buf[32]; |
747 | | /* format: HH:MM */ |
748 | 0 | if (showDelimiter) |
749 | 0 | OFStandard::snprintf(buf, sizeof(buf), "%02u:%02u", Hour, Minute); |
750 | | /* format: HHMM */ |
751 | 0 | else |
752 | 0 | OFStandard::snprintf(buf, sizeof(buf), "%02u%02u", Hour, Minute); |
753 | | /* only show seconds if requested and a value is specified (or should be created) */ |
754 | 0 | if (showSeconds && (hasSecond() || createMissingPart)) |
755 | 0 | { |
756 | 0 | const double secondValue = isSecondSpecified(Second) ? Second : 0; |
757 | | /* only show fractional part of a second if requested and a value is specified (or should be created) */ |
758 | 0 | if (showFraction && (hasFractionOfSecond() || createMissingPart)) |
759 | 0 | { |
760 | 0 | char buf2[12]; |
761 | 0 | OFStandard::ftoa(buf2, sizeof(buf2), secondValue, OFStandard::ftoa_format_f | OFStandard::ftoa_zeropad, 9, 6); |
762 | | /* format: HH:MM:SS.FFFFFF */ |
763 | 0 | if (showDelimiter) |
764 | 0 | OFStandard::strlcat(buf, ":", sizeof(buf)); |
765 | 0 | OFStandard::strlcat(buf, buf2, sizeof(buf)); |
766 | 0 | } else { |
767 | 0 | char buf2[12]; |
768 | | /* format: HH:MM:SS */ |
769 | 0 | if (showDelimiter) |
770 | 0 | OFStandard::snprintf(buf2, sizeof(buf2), ":%02u", OFstatic_cast(unsigned int, secondValue)); |
771 | | /* format: HHMMSS */ |
772 | 0 | else |
773 | 0 | OFStandard::snprintf(buf2, sizeof(buf2), "%02u", OFstatic_cast(unsigned int, secondValue)); |
774 | 0 | OFStandard::strlcat(buf, buf2, sizeof(buf)); |
775 | 0 | } |
776 | 0 | } |
777 | | /* copy converted part so far to the result variable */ |
778 | 0 | formattedTime.assign(buf); |
779 | | /* only add the time zone if requested _and_ a value is specified */ |
780 | 0 | if (showTimeZone) |
781 | 0 | { |
782 | 0 | OFString formattedTimeZone; |
783 | 0 | if (getISOFormattedTimeZone(formattedTimeZone, showDelimiter)) |
784 | 0 | { |
785 | 0 | if (!formattedTimeZone.empty()) |
786 | 0 | { |
787 | 0 | if (showDelimiter) |
788 | 0 | formattedTime += timeZoneSeparator; |
789 | | /* append time zone part to the result variable */ |
790 | 0 | formattedTime += formattedTimeZone; |
791 | 0 | } |
792 | 0 | } |
793 | 0 | } |
794 | 0 | status = OFTrue; |
795 | 0 | } |
796 | 0 | return status; |
797 | 0 | } |
798 | | |
799 | | |
800 | | OFBool OFTime::getISOFormattedTimeZone(OFString &formattedTimeZone, |
801 | | const OFBool showDelimiter) const |
802 | 0 | { |
803 | 0 | OFBool status = OFFalse; |
804 | | /* first, check for valid time zone (either specified or unspecified) */ |
805 | 0 | if (isTimeZoneValid(TimeZone, OFTrue /*acceptUnspecified*/)) |
806 | 0 | { |
807 | | /* check whether time zone is specified */ |
808 | 0 | if (isTimeZoneSpecified(TimeZone)) |
809 | 0 | { |
810 | 0 | char buf[32]; |
811 | | /* convert time zone from hours and fraction of hours to hours and minutes */ |
812 | 0 | const char tzSign = (TimeZone < 0) ? '-' : '+'; |
813 | 0 | const double tzAbs = (TimeZone < 0) ? -TimeZone : TimeZone; |
814 | 0 | const unsigned int tzHours = OFstatic_cast(unsigned int, tzAbs); |
815 | 0 | const unsigned int tzMinutes = OFstatic_cast(unsigned int, (tzAbs - tzHours) * 60); |
816 | | /* format: +HH:MM or -HH:MM */ |
817 | 0 | if (showDelimiter) |
818 | 0 | OFStandard::snprintf(buf, sizeof(buf), "%c%02u:%02u", tzSign, tzHours, tzMinutes); |
819 | | /* format: +HHMM or -HHMM */ |
820 | 0 | else |
821 | 0 | OFStandard::snprintf(buf, sizeof(buf), "%c%02u%02u", tzSign, tzHours, tzMinutes); |
822 | | /* assign value to the result variable */ |
823 | 0 | formattedTimeZone.assign(buf); |
824 | 0 | } else { |
825 | | /* clear result variable if time zone is unspecified */ |
826 | 0 | formattedTimeZone.clear(); |
827 | 0 | } |
828 | 0 | status = OFTrue; |
829 | 0 | } |
830 | 0 | return status; |
831 | 0 | } |
832 | | |
833 | | |
834 | | // -- static helper functions -- |
835 | | |
836 | | OFTime OFTime::getCurrentTime() |
837 | 0 | { |
838 | | /* create a time object with the current system time set */ |
839 | 0 | OFTime timeVal; |
840 | | /* this call might fail! */ |
841 | 0 | timeVal.setCurrentTime(); |
842 | | /* return by-value */ |
843 | 0 | return timeVal; |
844 | 0 | } |
845 | | |
846 | | |
847 | | double OFTime::getLocalTimeZone() |
848 | 0 | { |
849 | 0 | double result = unspecifiedTimeZone; |
850 | | /* determine local time zone */ |
851 | 0 | OFTime timeVal; |
852 | 0 | if (timeVal.setCurrentTime()) |
853 | 0 | result = timeVal.getTimeZone(); |
854 | 0 | return result; |
855 | 0 | } |
856 | | |
857 | | |
858 | | OFBool OFTime::isTimeValid(const unsigned int hour, |
859 | | const unsigned int minute, |
860 | | const double second, |
861 | | const double timeZone) |
862 | 0 | { |
863 | | /* check whether given time is valid (also support leap second) */ |
864 | 0 | return (hour < 24) && (minute < 60) && (!isSecondSpecified(second) || ((second >= 0) && (second <= 60))) && |
865 | 0 | isTimeZoneValid(timeZone, OFTrue /*acceptUnspecified*/); |
866 | 0 | } |
867 | | |
868 | | |
869 | | OFBool OFTime::isSecondSpecified(const double second) |
870 | 0 | { |
871 | 0 | #ifdef HAVE_CXX11 |
872 | 0 | return !(OFMath::isnan)(second); |
873 | | #else |
874 | | return (second != unspecifiedSecond); |
875 | | #endif |
876 | 0 | } |
877 | | |
878 | | |
879 | | OFBool OFTime::isTimeZoneValid(const double timeZone, |
880 | | const OFBool acceptUnspecified) |
881 | 0 | { |
882 | 0 | const OFBool timeZoneSpecified = isTimeZoneSpecified(timeZone); |
883 | | /* check whether given time zone is unspecified and/or within the valid range */ |
884 | 0 | return (acceptUnspecified && !timeZoneSpecified) || (timeZoneSpecified && (timeZone >= -12) && (timeZone <= 14)); |
885 | 0 | } |
886 | | |
887 | | |
888 | | OFBool OFTime::isTimeZoneSpecified(const double timeZone) |
889 | 0 | { |
890 | 0 | #ifdef HAVE_CXX11 |
891 | 0 | return !(OFMath::isnan)(timeZone); |
892 | | #else |
893 | | return (timeZone != unspecifiedTimeZone); |
894 | | #endif |
895 | 0 | } |
896 | | |
897 | | |
898 | | // -- output operator -- |
899 | | |
900 | | STD_NAMESPACE ostream &operator<<(STD_NAMESPACE ostream &stream, |
901 | | const OFTime &timeVal) |
902 | 0 | { |
903 | 0 | OFString tmpString; |
904 | | /* print the given time in ISO format to the stream */ |
905 | 0 | if (timeVal.getISOFormattedTime(tmpString, OFTrue /*showSeconds*/, OFTrue /*showFraction*/, OFTrue /*showTimeZone*/)) |
906 | 0 | stream << tmpString; |
907 | 0 | return stream; |
908 | 0 | } |