LibreOffice Module tools (master) 1
ttime.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <algorithm>
23
24#if defined(_WIN32)
25#if !defined WIN32_LEAN_AND_MEAN
26# define WIN32_LEAN_AND_MEAN
27#endif
28#include <windows.h>
29#include <mmsystem.h>
30#elif defined UNX
31#include <sys/time.h>
32#include <unistd.h>
33#endif
34
35#include <time.h>
36#ifdef __MACH__
37#include <mach/clock.h>
38#include <mach/mach.h>
39#include <mach/mach_time.h>
40#endif
41
42#include <rtl/math.hxx>
43#include <tools/time.hxx>
44#include <com/sun/star/util/DateTime.hpp>
45
46#include <systemdatetime.hxx>
47
48#if defined(__sun) && defined(__GNUC__)
49extern long altzone;
50#endif
51
52namespace {
53
54 const sal_Int64 nanoSecInSec = 1000000000;
55 const sal_Int16 secInMin = 60;
56 const sal_Int16 minInHour = 60;
57
58 sal_Int64 TimeToNanoSec( const tools::Time& rTime )
59 {
60 short nSign = (rTime.GetTime() >= 0) ? +1 : -1;
61 sal_Int32 nHour = rTime.GetHour();
62 sal_Int32 nMin = rTime.GetMin();
63 sal_Int32 nSec = rTime.GetSec();
64 sal_Int32 nNanoSec = rTime.GetNanoSec();
65
66 sal_Int64 nRet = nNanoSec;
67 nRet += nSec * nanoSecInSec;
68 nRet += nMin * secInMin * nanoSecInSec;
69 nRet += nHour * minInHour * secInMin * nanoSecInSec;
70
71 return (nRet * nSign);
72 }
73
74 tools::Time NanoSecToTime( sal_Int64 nNanoSec )
75 {
76 short nSign;
77 if ( nNanoSec < 0 )
78 {
79 nNanoSec *= -1;
80 nSign = -1;
81 }
82 else
83 nSign = 1;
84
85 tools::Time aTime( 0, 0, 0, nNanoSec );
86 aTime.SetTime( aTime.GetTime() * nSign );
87 return aTime;
88 }
89
90} // anonymous namespace
91
92namespace tools {
93
95{
96 if ( !GetSystemDateTime( nullptr, &nTime ) )
97 nTime = 0;
98}
99
100Time::Time( const tools::Time& rTime )
101{
102 nTime = rTime.nTime;
103}
104
105Time::Time( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
106{
107 init(nHour, nMin, nSec, nNanoSec);
108}
109Time::Time( const css::util::Time &_rTime )
110{
111 init(_rTime.Hours, _rTime.Minutes, _rTime.Seconds, _rTime.NanoSeconds);
112}
113Time::Time( const css::util::DateTime &_rDateTime )
114{
115 init(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds);
116}
117
118void tools::Time::init( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
119{
120 // normalize time
121 nSec += nNanoSec / nanoSecInSec;
122 nNanoSec %= nanoSecInSec;
123 nMin += nSec / secInMin;
124 nSec %= secInMin;
125 nHour += nMin / minInHour;
126 nMin %= minInHour;
127
128 // 922337 * HOUR_MASK = 9223370000000000000 largest possible value, 922338
129 // would be -9223364073709551616.
130 assert(HOUR_MASK * nHour >= 0 && "use tools::Duration with days instead!");
131 if (HOUR_MASK * nHour < 0)
132 nHour = 922337;
133
134 // But as is, GetHour() retrieves only sal_uInt16. Though retrieving in
135 // nanoseconds or milliseconds might be possible this is all crap.
136 assert(nHour <= SAL_MAX_UINT16 && "use tools::Duration with days instead!");
137 if (nHour > SAL_MAX_UINT16)
138 nHour = SAL_MAX_UINT16;
139
140 // construct time
141 nTime = nNanoSec +
142 nSec * SEC_MASK +
143 nMin * MIN_MASK +
144 nHour * HOUR_MASK;
145}
146
147void tools::Time::SetHour( sal_uInt16 nNewHour )
148{
149 short nSign = (nTime >= 0) ? +1 : -1;
150 sal_Int32 nMin = GetMin();
151 sal_Int32 nSec = GetSec();
152 sal_Int32 nNanoSec = GetNanoSec();
153
154 nTime = nSign *
155 ( nNanoSec +
156 nSec * SEC_MASK +
157 nMin * MIN_MASK +
158 nNewHour * HOUR_MASK );
159}
160
161void tools::Time::SetMin( sal_uInt16 nNewMin )
162{
163 short nSign = (nTime >= 0) ? +1 : -1;
164 sal_Int32 nHour = GetHour();
165 sal_Int32 nSec = GetSec();
166 sal_Int32 nNanoSec = GetNanoSec();
167
168 // no overflow
169 nNewMin = nNewMin % minInHour;
170
171 nTime = nSign *
172 ( nNanoSec +
173 nSec * SEC_MASK +
174 nNewMin * MIN_MASK +
175 nHour * HOUR_MASK );
176}
177
178void tools::Time::SetSec( sal_uInt16 nNewSec )
179{
180 short nSign = (nTime >= 0) ? +1 : -1;
181 sal_Int32 nHour = GetHour();
182 sal_Int32 nMin = GetMin();
183 sal_Int32 nNanoSec = GetNanoSec();
184
185 // no overflow
186 nNewSec = nNewSec % secInMin;
187
188 nTime = nSign *
189 ( nNanoSec +
190 nNewSec * SEC_MASK +
191 nMin * MIN_MASK +
192 nHour * HOUR_MASK );
193}
194
195void tools::Time::SetNanoSec( sal_uInt32 nNewNanoSec )
196{
197 short nSign = (nTime >= 0) ? +1 : -1;
198 sal_Int32 nHour = GetHour();
199 sal_Int32 nMin = GetMin();
200 sal_Int32 nSec = GetSec();
201
202 // no overflow
203 nNewNanoSec = nNewNanoSec % nanoSecInSec;
204
205 nTime = nSign *
206 ( nNewNanoSec +
207 nSec * SEC_MASK +
208 nMin * MIN_MASK +
209 nHour * HOUR_MASK );
210}
211
213{
214 short nSign = (nTime >= 0) ? +1 : -1;
215 sal_Int32 nHour = GetHour();
216 sal_Int32 nMin = GetMin();
217 sal_Int32 nSec = GetSec();
218 sal_Int32 nNanoSec = GetNanoSec();
219
220 return nSign *
221 ( nNanoSec +
222 nSec * nanoSecInSec +
223 nMin * (secInMin * nanoSecInSec) +
224 nHour * (minInHour * secInMin * nanoSecInSec) );
225}
226
227void tools::Time::MakeTimeFromNS( sal_Int64 nNS )
228{
229 short nSign;
230 if ( nNS < 0 )
231 {
232 nNS *= -1;
233 nSign = -1;
234 }
235 else
236 nSign = 1;
237
238 // avoid overflow when sal_uIntPtr is 32 bits
239 tools::Time aTime( 0, 0, nNS/nanoSecInSec, nNS % nanoSecInSec );
240 SetTime( aTime.GetTime() * nSign );
241}
242
244{
245 short nSign = (nTime >= 0) ? +1 : -1;
246 sal_Int32 nHour = GetHour();
247 sal_Int32 nMin = GetMin();
248 sal_Int32 nSec = GetSec();
249 sal_Int32 nNanoSec = GetNanoSec();
250
251 return nSign *
252 ( nNanoSec/1000000 +
253 nSec * 1000 +
254 nMin * 60000 +
255 nHour * 3600000 );
256}
257
258void tools::Time::MakeTimeFromMS( sal_Int32 nMS )
259{
260 short nSign;
261 if ( nMS < 0 )
262 {
263 nMS *= -1;
264 nSign = -1;
265 }
266 else
267 nSign = 1;
268
269 // avoid overflow when sal_uIntPtr is 32 bits
270 tools::Time aTime( 0, 0, nMS/1000, (nMS % 1000) * 1000000 );
271 SetTime( aTime.GetTime() * nSign );
272}
273
275{
276 short nSign = (nTime >= 0) ? +1 : -1;
277 double nHour = GetHour();
278 double nMin = GetMin();
279 double nSec = GetSec();
280 double nNanoSec = GetNanoSec();
281
282 return (nHour + (nMin / 60) + (nSec / (minInHour * secInMin)) + (nNanoSec / (minInHour * secInMin * nanoSecInSec))) / 24 * nSign;
283}
284
285// static
286void tools::Time::GetClock( double fTimeInDays,
287 sal_uInt16& nHour, sal_uInt16& nMinute, sal_uInt16& nSecond,
288 double& fFractionOfSecond, int nFractionDecimals )
289{
290 const double fTime = fTimeInDays - rtl::math::approxFloor(fTimeInDays); // date part absent
291
292 // If 0 then full day (or no day), shortcut.
293 // If < 0 then approxFloor() effectively returned the ceiling (note this
294 // also holds for negative fTimeInDays values) because of a near identical
295 // value, shortcut this to a full day as well.
296 // If >= 1.0 (actually == 1.0) then fTimeInDays is a negative small value
297 // not significant for a representable time and approxFloor() returned -1,
298 // shortcut to 0:0:0, otherwise it would become 24:0:0.
299 if (fTime <= 0.0 || fTime >= 1.0)
300 {
301 nHour = nMinute = nSecond = 0;
302 fFractionOfSecond = 0.0;
303 return;
304 }
305
306 // In seconds, including milli and nano.
307 const double fRawSeconds = fTime * tools::Time::secondPerDay;
308
309 // Round to nanoseconds most, which is the highest resolution this could be
310 // influenced by, but if the original value included a date round to at
311 // most 14 significant digits (including adding 4 for *86400), otherwise we
312 // might end up with a fake precision of h:m:s.999999986 which in fact
313 // should had been h:m:s+1
314 // BUT, leave at least 2 decimals to round. Which shouldn't be a problem in
315 // practice because class Date can calculate only 8-digit days for it's
316 // sal_Int16 year range, which exactly leaves us with 14-4-8=2.
317 int nDec = 9;
318 const double fAbsTimeInDays = fabs( fTimeInDays);
319 if (fAbsTimeInDays >= 1.0)
320 {
321 const int nDig = static_cast<int>(ceil( log10( fAbsTimeInDays)));
322 nDec = std::clamp( 10 - nDig, 2, 9 );
323 }
324 double fSeconds = rtl::math::round( fRawSeconds, nDec);
325
326 // If this ended up as a full day the original value was very very close
327 // but not quite. Take that.
328 if (fSeconds >= tools::Time::secondPerDay)
329 fSeconds = fRawSeconds;
330
331 // Now do not round values (specifically not up), but truncate to the next
332 // magnitude, so 23:59:59.99 is still 23:59:59 and not 24:00:00 (or even
333 // 00:00:00 which Excel does).
334 nHour = fSeconds / tools::Time::secondPerHour;
335 fSeconds -= nHour * tools::Time::secondPerHour;
336 nMinute = fSeconds / tools::Time::secondPerMinute;
337 fSeconds -= nMinute * tools::Time::secondPerMinute;
338 nSecond = fSeconds;
339 fSeconds -= nSecond;
340
341 assert(fSeconds < 1.0); // or back to the drawing board...
342
343 if (nFractionDecimals > 0)
344 {
345 // Do not simply round the fraction, otherwise .999 would end up as .00
346 // again. Truncate instead if rounding would round up into an integer
347 // value.
348 fFractionOfSecond = rtl::math::round( fSeconds, nFractionDecimals);
349 if (fFractionOfSecond >= 1.0)
350 fFractionOfSecond = rtl::math::pow10Exp( std::trunc(
351 rtl::math::pow10Exp( fSeconds, nFractionDecimals)), -nFractionDecimals);
352 }
353 else
354 fFractionOfSecond = fSeconds;
355}
356
358{
359 nTime = rTime.nTime;
360 return *this;
361}
362
364{
365 nTime = NanoSecToTime( TimeToNanoSec( *this ) +
366 TimeToNanoSec( rTime ) ).GetTime();
367 return *this;
368}
369
371{
372 nTime = NanoSecToTime( TimeToNanoSec( *this ) -
373 TimeToNanoSec( rTime ) ).GetTime();
374 return *this;
375}
376
377Time operator +( const tools::Time& rTime1, const tools::Time& rTime2 )
378{
379 return NanoSecToTime( TimeToNanoSec( rTime1 ) +
380 TimeToNanoSec( rTime2 ) );
381}
382
383Time operator -( const tools::Time& rTime1, const tools::Time& rTime2 )
384{
385 return NanoSecToTime( TimeToNanoSec( rTime1 ) -
386 TimeToNanoSec( rTime2 ) );
387}
388
390{
391 sal_Int32 n1 = (nTime < 0 ? -static_cast<sal_Int32>(GetNanoSec()) : GetNanoSec() );
392 sal_Int32 n2 = (rTime.nTime < 0 ? -static_cast<sal_Int32>(rTime.GetNanoSec()) : rTime.GetNanoSec() );
393 return (nTime - n1) == (rTime.nTime - n2);
394}
395
397{
398#if defined(_WIN32)
399 TIME_ZONE_INFORMATION aTimeZone;
400 aTimeZone.Bias = 0;
401 DWORD nTimeZoneRet = GetTimeZoneInformation( &aTimeZone );
402 sal_Int32 nTempTime = aTimeZone.Bias;
403 if ( nTimeZoneRet == TIME_ZONE_ID_STANDARD )
404 nTempTime += aTimeZone.StandardBias;
405 else if ( nTimeZoneRet == TIME_ZONE_ID_DAYLIGHT )
406 nTempTime += aTimeZone.DaylightBias;
407 tools::Time aTime( 0, static_cast<sal_uInt16>(abs( nTempTime )) );
408 if ( nTempTime > 0 )
409 aTime = -aTime;
410 return aTime;
411#else
412 static sal_uInt64 nCacheTicks = 0;
413 static sal_Int32 nCacheSecOffset = -1;
414 sal_uInt64 nTicks = tools::Time::GetSystemTicks();
415 time_t nTime;
416 tm aTM;
417 short nTempTime;
418
419 // determine value again if needed
420 if ( (nCacheSecOffset == -1) ||
421 ((nTicks - nCacheTicks) > 360000) ||
422 ( nTicks < nCacheTicks ) // handle overflow
423 )
424 {
425 nTime = time( nullptr );
426 localtime_r( &nTime, &aTM );
427 auto nLocalTime = mktime( &aTM );
428#if defined(__sun)
429 // Solaris gmtime_r() seems not to handle daylight saving time
430 // flags correctly
431 auto nUTC = nLocalTime + ( aTM.tm_isdst == 0 ? timezone : altzone );
432#elif defined( LINUX )
433 // Linux mktime() seems not to handle tm_isdst correctly
434 auto nUTC = nLocalTime - aTM.tm_gmtoff;
435#else
436 gmtime_r( &nTime, &aTM );
437 auto nUTC = mktime( &aTM );
438#endif
439 nCacheTicks = nTicks;
440 nCacheSecOffset = (nLocalTime-nUTC) / 60;
441 }
442
443 nTempTime = abs( nCacheSecOffset );
444 tools::Time aTime( 0, static_cast<sal_uInt16>(nTempTime) );
445 if ( nCacheSecOffset < 0 )
446 aTime = -aTime;
447 return aTime;
448#endif
449}
450
452{
453 return tools::Time::GetMonotonicTicks() / 1000;
454}
455
456#ifdef _WIN32
457static LARGE_INTEGER initPerformanceFrequency()
458{
459 LARGE_INTEGER nTicksPerSecond = { 0, 0 };
460 if (!QueryPerformanceFrequency(&nTicksPerSecond))
461 nTicksPerSecond.QuadPart = 0;
462 return nTicksPerSecond;
463}
464#endif
465
467{
468#ifdef _WIN32
469 static const LARGE_INTEGER nTicksPerSecond = initPerformanceFrequency();
470 if (nTicksPerSecond.QuadPart > 0)
471 {
472 LARGE_INTEGER nPerformanceCount;
473 QueryPerformanceCounter(&nPerformanceCount);
474 return static_cast<sal_uInt64>(
475 ( nPerformanceCount.QuadPart * 1000 * 1000 ) / nTicksPerSecond.QuadPart );
476 }
477 else
478 {
479 return static_cast<sal_uInt64>( timeGetTime() * 1000 );
480 }
481#else
482 sal_uInt64 nMicroSeconds;
483#ifdef __MACH__
484 static mach_timebase_info_data_t info = { 0, 0 };
485 if ( 0 == info.numer )
486 mach_timebase_info( &info );
487 nMicroSeconds = mach_absolute_time() * static_cast<double>(info.numer / info.denom) / 1000;
488#else
489#if defined(_POSIX_TIMERS)
490 struct timespec currentTime;
491 clock_gettime( CLOCK_MONOTONIC, &currentTime );
492 nMicroSeconds
493 = static_cast<sal_uInt64>(currentTime.tv_sec) * 1000 * 1000 + currentTime.tv_nsec / 1000;
494#else
495 struct timeval currentTime;
496 gettimeofday( &currentTime, nullptr );
497 nMicroSeconds = static_cast<sal_uInt64>(currentTime.tv_sec) * 1000 * 1000 + currentTime.tv_usec;
498#endif
499#endif // __MACH__
500 return nMicroSeconds;
501#endif // _WIN32
502}
503
504} /* namespace tools */
505
506/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetMin(sal_uInt16 nNewMin)
Definition: ttime.cxx:161
void MakeTimeFromNS(sal_Int64 nNS)
Definition: ttime.cxx:227
sal_uInt16 GetSec() const
Definition: time.hxx:91
tools::Time & operator-=(const tools::Time &rTime)
Definition: ttime.cxx:370
static void GetClock(double fTimeInDays, sal_uInt16 &nHour, sal_uInt16 &nMinute, sal_uInt16 &nSecond, double &fFractionOfSecond, int nFractionDecimals)
Get the wall clock time particles for a (date+)time value.
Definition: ttime.cxx:286
void init(sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec)
Definition: ttime.cxx:118
sal_Int64 GetTime() const
Definition: time.hxx:78
sal_uInt16 GetMin() const
Definition: time.hxx:88
static Time GetUTCOffset()
Definition: ttime.cxx:396
void SetNanoSec(sal_uInt32 nNewNanoSec)
Definition: ttime.cxx:195
sal_Int32 GetMSFromTime() const
Definition: ttime.cxx:243
static sal_uInt64 GetSystemTicks()
Elapsed time in milliseconds (1e-3) since some unspecified starting point.
Definition: ttime.cxx:451
Time(TimeInitEmpty)
Definition: time.hxx:67
static const sal_Int64 secondPerMinute
Definition: time.hxx:55
sal_uInt16 GetHour() const
Definition: time.hxx:85
sal_uInt32 GetNanoSec() const
Definition: time.hxx:94
TimeInitSystem
Definition: time.hxx:44
void MakeTimeFromMS(sal_Int32 nMS)
Definition: ttime.cxx:258
static sal_uInt64 GetMonotonicTicks()
Elapsed time in microseconds (1e-6) since some unspecified starting point.
Definition: ttime.cxx:466
tools::Time & operator=(const tools::Time &rTime)
Definition: ttime.cxx:357
void SetSec(sal_uInt16 nNewSec)
Definition: ttime.cxx:178
double GetTimeInDays() const
12 hours == 0.5 days
Definition: ttime.cxx:274
sal_Int64 nTime
Definition: time.hxx:38
tools::Time & operator+=(const tools::Time &rTime)
Definition: ttime.cxx:363
static const sal_Int64 secondPerHour
Definition: time.hxx:60
sal_Int64 GetNSFromTime() const
Definition: ttime.cxx:212
static const sal_Int64 secondPerDay
Definition: time.hxx:61
void SetHour(sal_uInt16 nNewHour)
Definition: ttime.cxx:147
bool IsEqualIgnoreNanoSec(const tools::Time &rTime) const
Definition: ttime.cxx:389
Degree100 abs(Degree100 x)
Definition: degree.hxx:42
int n2
int n1
Note: this class is a true marvel of engineering: because the author could not decide whether it's be...
Time operator-(const tools::Time &rTime1, const tools::Time &rTime2)
Definition: ttime.cxx:383
Time operator+(const tools::Time &rTime1, const tools::Time &rTime2)
Definition: ttime.cxx:377
bool GetSystemDateTime(sal_Int32 *pDate, sal_Int64 *pTime)
Get current local timestamp.
constexpr sal_Int64 SEC_MASK
constexpr sal_Int64 HOUR_MASK
constexpr sal_Int64 MIN_MASK
TransliterationModules tm
#define SAL_MAX_UINT16