Bump version to 21.06.18.1
[LibreOffice.git] / tools / source / datetime / ttime.cxx
blob0049c33efd9dcdb947f2ede1a135d41f375b60f1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <algorithm>
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 #endif
34 #include <time.h>
35 #ifdef __MACH__
36 #include <mach/clock.h>
37 #include <mach/mach.h>
38 #include <mach/mach_time.h>
39 #endif
41 #include <rtl/math.hxx>
42 #include <tools/time.hxx>
43 #include <com/sun/star/util/DateTime.hpp>
45 #include <systemdatetime.hxx>
47 #if defined(__sun) && defined(__GNUC__)
48 extern long altzone;
49 #endif
51 namespace {
53 const sal_Int64 nanoSecInSec = 1000000000;
54 const sal_Int16 secInMin = 60;
55 const sal_Int16 minInHour = 60;
57 sal_Int64 TimeToNanoSec( const tools::Time& rTime )
59 short nSign = (rTime.GetTime() >= 0) ? +1 : -1;
60 sal_Int32 nHour = rTime.GetHour();
61 sal_Int32 nMin = rTime.GetMin();
62 sal_Int32 nSec = rTime.GetSec();
63 sal_Int32 nNanoSec = rTime.GetNanoSec();
65 sal_Int64 nRet = nNanoSec;
66 nRet += nSec * nanoSecInSec;
67 nRet += nMin * secInMin * nanoSecInSec;
68 nRet += nHour * minInHour * secInMin * nanoSecInSec;
70 return (nRet * nSign);
73 tools::Time NanoSecToTime( sal_Int64 nNanoSec )
75 short nSign;
76 if ( nNanoSec < 0 )
78 nNanoSec *= -1;
79 nSign = -1;
81 else
82 nSign = 1;
84 tools::Time aTime( 0, 0, 0, nNanoSec );
85 aTime.SetTime( aTime.GetTime() * nSign );
86 return aTime;
89 } // anonymous namespace
91 namespace tools {
93 Time::Time( TimeInitSystem )
95 if ( !GetSystemDateTime( nullptr, &nTime ) )
96 nTime = 0;
99 Time::Time( const tools::Time& rTime )
101 nTime = rTime.nTime;
104 Time::Time( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
106 init(nHour, nMin, nSec, nNanoSec);
108 Time::Time( const css::util::Time &_rTime )
110 init(_rTime.Hours, _rTime.Minutes, _rTime.Seconds, _rTime.NanoSeconds);
112 Time::Time( const css::util::DateTime &_rDateTime )
114 init(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds);
117 void tools::Time::init( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
119 // normalize time
120 nSec += nNanoSec / nanoSecInSec;
121 nNanoSec %= nanoSecInSec;
122 nMin += nSec / secInMin;
123 nSec %= secInMin;
124 nHour += nMin / minInHour;
125 nMin %= minInHour;
127 // construct time
128 nTime = nNanoSec +
129 nSec * SEC_MASK +
130 nMin * MIN_MASK +
131 nHour * HOUR_MASK;
134 void tools::Time::SetHour( sal_uInt16 nNewHour )
136 short nSign = (nTime >= 0) ? +1 : -1;
137 sal_Int32 nMin = GetMin();
138 sal_Int32 nSec = GetSec();
139 sal_Int32 nNanoSec = GetNanoSec();
141 nTime = nSign *
142 ( nNanoSec +
143 nSec * SEC_MASK +
144 nMin * MIN_MASK +
145 nNewHour * HOUR_MASK );
148 void tools::Time::SetMin( sal_uInt16 nNewMin )
150 short nSign = (nTime >= 0) ? +1 : -1;
151 sal_Int32 nHour = GetHour();
152 sal_Int32 nSec = GetSec();
153 sal_Int32 nNanoSec = GetNanoSec();
155 // no overflow
156 nNewMin = nNewMin % minInHour;
158 nTime = nSign *
159 ( nNanoSec +
160 nSec * SEC_MASK +
161 nNewMin * MIN_MASK +
162 nHour * HOUR_MASK );
165 void tools::Time::SetSec( sal_uInt16 nNewSec )
167 short nSign = (nTime >= 0) ? +1 : -1;
168 sal_Int32 nHour = GetHour();
169 sal_Int32 nMin = GetMin();
170 sal_Int32 nNanoSec = GetNanoSec();
172 // no overflow
173 nNewSec = nNewSec % secInMin;
175 nTime = nSign *
176 ( nNanoSec +
177 nNewSec * SEC_MASK +
178 nMin * MIN_MASK +
179 nHour * HOUR_MASK );
182 void tools::Time::SetNanoSec( sal_uInt32 nNewNanoSec )
184 short nSign = (nTime >= 0) ? +1 : -1;
185 sal_Int32 nHour = GetHour();
186 sal_Int32 nMin = GetMin();
187 sal_Int32 nSec = GetSec();
189 // no overflow
190 nNewNanoSec = nNewNanoSec % nanoSecInSec;
192 nTime = nSign *
193 ( nNewNanoSec +
194 nSec * SEC_MASK +
195 nMin * MIN_MASK +
196 nHour * HOUR_MASK );
199 sal_Int64 tools::Time::GetNSFromTime() const
201 short nSign = (nTime >= 0) ? +1 : -1;
202 sal_Int32 nHour = GetHour();
203 sal_Int32 nMin = GetMin();
204 sal_Int32 nSec = GetSec();
205 sal_Int32 nNanoSec = GetNanoSec();
207 return nSign *
208 ( nNanoSec +
209 nSec * nanoSecInSec +
210 nMin * (secInMin * nanoSecInSec) +
211 nHour * (minInHour * secInMin * nanoSecInSec) );
214 void tools::Time::MakeTimeFromNS( sal_Int64 nNS )
216 short nSign;
217 if ( nNS < 0 )
219 nNS *= -1;
220 nSign = -1;
222 else
223 nSign = 1;
225 // avoid overflow when sal_uIntPtr is 32 bits
226 tools::Time aTime( 0, 0, nNS/nanoSecInSec, nNS % nanoSecInSec );
227 SetTime( aTime.GetTime() * nSign );
230 sal_Int32 tools::Time::GetMSFromTime() const
232 short nSign = (nTime >= 0) ? +1 : -1;
233 sal_Int32 nHour = GetHour();
234 sal_Int32 nMin = GetMin();
235 sal_Int32 nSec = GetSec();
236 sal_Int32 nNanoSec = GetNanoSec();
238 return nSign *
239 ( nNanoSec/1000000 +
240 nSec * 1000 +
241 nMin * 60000 +
242 nHour * 3600000 );
245 void tools::Time::MakeTimeFromMS( sal_Int32 nMS )
247 short nSign;
248 if ( nMS < 0 )
250 nMS *= -1;
251 nSign = -1;
253 else
254 nSign = 1;
256 // avoid overflow when sal_uIntPtr is 32 bits
257 tools::Time aTime( 0, 0, nMS/1000, (nMS % 1000) * 1000000 );
258 SetTime( aTime.GetTime() * nSign );
261 double tools::Time::GetTimeInDays() const
263 short nSign = (nTime >= 0) ? +1 : -1;
264 double nHour = GetHour();
265 double nMin = GetMin();
266 double nSec = GetSec();
267 double nNanoSec = GetNanoSec();
269 return (nHour + (nMin / 60) + (nSec / (minInHour * secInMin)) + (nNanoSec / (minInHour * secInMin * nanoSecInSec))) / 24 * nSign;
272 // static
273 void tools::Time::GetClock( double fTimeInDays,
274 sal_uInt16& nHour, sal_uInt16& nMinute, sal_uInt16& nSecond,
275 double& fFractionOfSecond, int nFractionDecimals )
277 const double fTime = fTimeInDays - rtl::math::approxFloor(fTimeInDays); // date part absent
279 // If 0 then full day (or no day), shortcut.
280 // If < 0 then approxFloor() effectively returned the ceiling (note this
281 // also holds for negative fTimeInDays values) because of a near identical
282 // value, shortcut this to a full day as well.
283 // If >= 1.0 (actually == 1.0) then fTimeInDays is a negative small value
284 // not significant for a representable time and approxFloor() returned -1,
285 // shortcut to 0:0:0, otherwise it would become 24:0:0.
286 if (fTime <= 0.0 || fTime >= 1.0)
288 nHour = nMinute = nSecond = 0;
289 fFractionOfSecond = 0.0;
290 return;
293 // In seconds, including milli and nano.
294 const double fRawSeconds = fTime * tools::Time::secondPerDay;
296 // Round to nanoseconds most, which is the highest resolution this could be
297 // influenced by, but if the original value included a date round to at
298 // most 14 significant digits (including adding 4 for *86400), otherwise we
299 // might end up with a fake precision of h:m:s.999999986 which in fact
300 // should had been h:m:s+1
301 // BUT, leave at least 2 decimals to round. Which shouldn't be a problem in
302 // practice because class Date can calculate only 8-digit days for it's
303 // sal_Int16 year range, which exactly leaves us with 14-4-8=2.
304 int nDec = 9;
305 const double fAbsTimeInDays = fabs( fTimeInDays);
306 if (fAbsTimeInDays >= 1.0)
308 const int nDig = static_cast<int>(ceil( log10( fAbsTimeInDays)));
309 nDec = std::clamp( 10 - nDig, 2, 9 );
311 double fSeconds = rtl::math::round( fRawSeconds, nDec);
313 // If this ended up as a full day the original value was very very close
314 // but not quite. Take that.
315 if (fSeconds >= tools::Time::secondPerDay)
316 fSeconds = fRawSeconds;
318 // Now do not round values (specifically not up), but truncate to the next
319 // magnitude, so 23:59:59.99 is still 23:59:59 and not 24:00:00 (or even
320 // 00:00:00 which Excel does).
321 nHour = fSeconds / tools::Time::secondPerHour;
322 fSeconds -= nHour * tools::Time::secondPerHour;
323 nMinute = fSeconds / tools::Time::secondPerMinute;
324 fSeconds -= nMinute * tools::Time::secondPerMinute;
325 nSecond = fSeconds;
326 fSeconds -= nSecond;
328 assert(fSeconds < 1.0); // or back to the drawing board...
330 if (nFractionDecimals > 0)
332 // Do not simply round the fraction, otherwise .999 would end up as .00
333 // again. Truncate instead if rounding would round up into an integer
334 // value.
335 fFractionOfSecond = rtl::math::round( fSeconds, nFractionDecimals);
336 if (fFractionOfSecond >= 1.0)
337 fFractionOfSecond = rtl::math::pow10Exp( std::trunc(
338 rtl::math::pow10Exp( fSeconds, nFractionDecimals)), -nFractionDecimals);
340 else
341 fFractionOfSecond = fSeconds;
344 Time& tools::Time::operator =( const tools::Time& rTime )
346 nTime = rTime.nTime;
347 return *this;
350 Time& tools::Time::operator +=( const tools::Time& rTime )
352 nTime = NanoSecToTime( TimeToNanoSec( *this ) +
353 TimeToNanoSec( rTime ) ).GetTime();
354 return *this;
357 Time& tools::Time::operator -=( const tools::Time& rTime )
359 nTime = NanoSecToTime( TimeToNanoSec( *this ) -
360 TimeToNanoSec( rTime ) ).GetTime();
361 return *this;
364 Time operator +( const tools::Time& rTime1, const tools::Time& rTime2 )
366 return NanoSecToTime( TimeToNanoSec( rTime1 ) +
367 TimeToNanoSec( rTime2 ) );
370 Time operator -( const tools::Time& rTime1, const tools::Time& rTime2 )
372 return NanoSecToTime( TimeToNanoSec( rTime1 ) -
373 TimeToNanoSec( rTime2 ) );
376 bool tools::Time::IsEqualIgnoreNanoSec( const tools::Time& rTime ) const
378 sal_Int32 n1 = (nTime < 0 ? -static_cast<sal_Int32>(GetNanoSec()) : GetNanoSec() );
379 sal_Int32 n2 = (rTime.nTime < 0 ? -static_cast<sal_Int32>(rTime.GetNanoSec()) : rTime.GetNanoSec() );
380 return (nTime - n1) == (rTime.nTime - n2);
383 Time tools::Time::GetUTCOffset()
385 #if defined(_WIN32)
386 TIME_ZONE_INFORMATION aTimeZone;
387 aTimeZone.Bias = 0;
388 DWORD nTimeZoneRet = GetTimeZoneInformation( &aTimeZone );
389 sal_Int32 nTempTime = aTimeZone.Bias;
390 if ( nTimeZoneRet == TIME_ZONE_ID_STANDARD )
391 nTempTime += aTimeZone.StandardBias;
392 else if ( nTimeZoneRet == TIME_ZONE_ID_DAYLIGHT )
393 nTempTime += aTimeZone.DaylightBias;
394 tools::Time aTime( 0, static_cast<sal_uInt16>(abs( nTempTime )) );
395 if ( nTempTime > 0 )
396 aTime = -aTime;
397 return aTime;
398 #else
399 static sal_uInt64 nCacheTicks = 0;
400 static sal_Int32 nCacheSecOffset = -1;
401 sal_uInt64 nTicks = tools::Time::GetSystemTicks();
402 time_t nTime;
403 tm aTM;
404 short nTempTime;
406 // determine value again if needed
407 if ( (nCacheSecOffset == -1) ||
408 ((nTicks - nCacheTicks) > 360000) ||
409 ( nTicks < nCacheTicks ) // handle overflow
412 nTime = time( nullptr );
413 localtime_r( &nTime, &aTM );
414 sal_Int32 nLocalTime = mktime( &aTM );
415 #if defined(__sun)
416 // Solaris gmtime_r() seems not to handle daylight saving time
417 // flags correctly
418 nUTC = nLocalTime + ( aTM.tm_isdst == 0 ? timezone : altzone );
419 #elif defined( LINUX )
420 // Linux mktime() seems not to handle tm_isdst correctly
421 sal_Int32 nUTC = nLocalTime - aTM.tm_gmtoff;
422 #else
423 gmtime_r( &nTime, &aTM );
424 sal_Int32 nUTC = mktime( &aTM );
425 #endif
426 nCacheTicks = nTicks;
427 nCacheSecOffset = (nLocalTime-nUTC) / 60;
430 nTempTime = abs( nCacheSecOffset );
431 tools::Time aTime( 0, static_cast<sal_uInt16>(nTempTime) );
432 if ( nCacheSecOffset < 0 )
433 aTime = -aTime;
434 return aTime;
435 #endif
438 sal_uInt64 tools::Time::GetSystemTicks()
440 return tools::Time::GetMonotonicTicks() / 1000;
443 #ifdef _WIN32
444 static LARGE_INTEGER initPerformanceFrequency()
446 LARGE_INTEGER nTicksPerSecond = { 0, 0 };
447 if (!QueryPerformanceFrequency(&nTicksPerSecond))
448 nTicksPerSecond.QuadPart = 0;
449 return nTicksPerSecond;
451 #endif
453 sal_uInt64 tools::Time::GetMonotonicTicks()
455 #ifdef _WIN32
456 static const LARGE_INTEGER nTicksPerSecond = initPerformanceFrequency();
457 if (nTicksPerSecond.QuadPart > 0)
459 LARGE_INTEGER nPerformanceCount;
460 QueryPerformanceCounter(&nPerformanceCount);
461 return static_cast<sal_uInt64>(
462 ( nPerformanceCount.QuadPart * 1000 * 1000 ) / nTicksPerSecond.QuadPart );
464 else
466 return static_cast<sal_uInt64>( timeGetTime() * 1000 );
468 #else
469 sal_uInt64 nMicroSeconds;
470 #ifdef __MACH__
471 static mach_timebase_info_data_t info = { 0, 0 };
472 if ( 0 == info.numer )
473 mach_timebase_info( &info );
474 nMicroSeconds = mach_absolute_time() * static_cast<double>(info.numer / info.denom) / 1000;
475 #else
476 #if defined(USE_CLOCK_GETTIME)
477 struct timespec currentTime;
478 clock_gettime( CLOCK_MONOTONIC, &currentTime );
479 nMicroSeconds = currentTime.tv_sec * 1000 * 1000 + currentTime.tv_nsec / 1000;
480 #else
481 struct timeval currentTime;
482 gettimeofday( &currentTime, nullptr );
483 nMicroSeconds = currentTime.tv_sec * 1000 * 1000 + currentTime.tv_usec;
484 #endif
485 #endif // __MACH__
486 return nMicroSeconds;
487 #endif // _WIN32
490 } /* namespace tools */
492 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */