use insert function instead of for loop
[LibreOffice.git] / tools / source / datetime / ttime.cxx
blob4d0d27fea91be19e158153b2cc743599b5f52c49
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 #include <unistd.h>
33 #endif
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
42 #include <rtl/math.hxx>
43 #include <tools/time.hxx>
44 #include <com/sun/star/util/DateTime.hpp>
46 #include <systemdatetime.hxx>
48 #if defined(__sun) && defined(__GNUC__)
49 extern long altzone;
50 #endif
52 namespace tools {
54 Time::Time( TimeInitSystem )
56 if ( !GetSystemDateTime( nullptr, &nTime ) )
57 nTime = 0;
60 Time::Time( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
62 // normalize time
63 if (nNanoSec >= nanoSecPerSec)
65 nSec += nNanoSec / nanoSecPerSec;
66 nNanoSec %= nanoSecPerSec;
68 if (nSec >= secondPerMinute)
70 nMin += nSec / secondPerMinute;
71 nSec %= secondPerMinute;
73 if (nMin >= minutePerHour)
75 nHour += nMin / minutePerHour;
76 nMin %= minutePerHour;
79 // 922337 * HOUR_MASK = 9223370000000000000 largest possible value, 922338
80 // would be -9223364073709551616.
81 assert(HOUR_MASK * nHour >= 0 && "use tools::Duration with days instead!");
82 if (HOUR_MASK * nHour < 0)
83 nHour = 922337;
85 // But as is, GetHour() retrieves only sal_uInt16. Though retrieving in
86 // nanoseconds or milliseconds might be possible this is all crap.
87 assert(nHour <= SAL_MAX_UINT16 && "use tools::Duration with days instead!");
88 if (nHour > SAL_MAX_UINT16)
89 nHour = SAL_MAX_UINT16;
91 // construct time
92 nTime = assemble(nHour, nMin, nSec, nNanoSec);
94 Time::Time( const css::util::Time &_rTime )
95 : Time(_rTime.Hours, _rTime.Minutes, _rTime.Seconds, _rTime.NanoSeconds)
98 Time::Time( const css::util::DateTime &_rDateTime )
99 : Time(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds)
103 // static
104 sal_Int64 tools::Time::assemble(sal_uInt32 h, sal_uInt32 m, sal_uInt32 s, sal_uInt64 ns)
106 return ns + s * SEC_MASK + m * MIN_MASK + h * HOUR_MASK;
109 void tools::Time::SetHour( sal_uInt16 nNewHour )
111 nTime = GetSign() * assemble(nNewHour, GetMin(), GetSec(), GetNanoSec());
114 void tools::Time::SetMin( sal_uInt16 nNewMin )
116 // no overflow
117 nTime = GetSign() * assemble(GetHour(), nNewMin % minutePerHour, GetSec(), GetNanoSec());
120 void tools::Time::SetSec( sal_uInt16 nNewSec )
122 // no overflow
123 nTime = GetSign() * assemble(GetHour(), GetMin(), nNewSec % secondPerMinute, GetNanoSec());
126 void tools::Time::SetNanoSec( sal_uInt32 nNewNanoSec )
128 // no overflow
129 nTime = GetSign() * assemble(GetHour(), GetMin(), GetSec(), nNewNanoSec % nanoSecPerSec);
132 sal_Int64 tools::Time::GetNSFromTime() const
134 return GetSign() *
135 ( GetNanoSec() +
136 GetSec() * nanoSecPerSec +
137 GetMin() * nanoSecPerMinute +
138 GetHour() * nanoSecPerHour );
141 void tools::Time::MakeTimeFromNS( sal_Int64 nNS )
143 short nSign;
144 if ( nNS < 0 )
146 nNS *= -1;
147 nSign = -1;
149 else
150 nSign = 1;
152 tools::Time aTime(0, 0, 0, nNS);
153 SetTime( aTime.GetTime() * nSign );
156 sal_Int32 tools::Time::GetMSFromTime() const
158 return GetNSFromTime() / nanoPerMilli;
161 void tools::Time::MakeTimeFromMS( sal_Int32 nMS )
163 MakeTimeFromNS(nMS * nanoPerMilli);
166 double tools::Time::GetTimeInDays() const
168 return GetNSFromTime() / double(nanoSecPerDay);
171 // static
172 void tools::Time::GetClock( double fTimeInDays,
173 sal_uInt16& nHour, sal_uInt16& nMinute, sal_uInt16& nSecond,
174 double& fFractionOfSecond, int nFractionDecimals )
176 const double fTime = fTimeInDays - rtl::math::approxFloor(fTimeInDays); // date part absent
178 // If 0 then full day (or no day), shortcut.
179 // If < 0 then approxFloor() effectively returned the ceiling (note this
180 // also holds for negative fTimeInDays values) because of a near identical
181 // value, shortcut this to a full day as well.
182 // If >= 1.0 (actually == 1.0) then fTimeInDays is a negative small value
183 // not significant for a representable time and approxFloor() returned -1,
184 // shortcut to 0:0:0, otherwise it would become 24:0:0.
185 if (fTime <= 0.0 || fTime >= 1.0)
187 nHour = nMinute = nSecond = 0;
188 fFractionOfSecond = 0.0;
189 return;
192 // In seconds, including milli and nano.
193 const double fRawSeconds = fTime * tools::Time::secondPerDay;
195 // Round to nanoseconds most, which is the highest resolution this could be
196 // influenced by, but if the original value included a date round to at
197 // most 14 significant digits (including adding 4 for *86400), otherwise we
198 // might end up with a fake precision of h:m:s.999999986 which in fact
199 // should had been h:m:s+1
200 // BUT, leave at least 2 decimals to round. Which shouldn't be a problem in
201 // practice because class Date can calculate only 8-digit days for it's
202 // sal_Int16 year range, which exactly leaves us with 14-4-8=2.
203 int nDec = 9;
204 const double fAbsTimeInDays = fabs( fTimeInDays);
205 if (fAbsTimeInDays >= 1.0)
207 const int nDig = static_cast<int>(ceil( log10( fAbsTimeInDays)));
208 nDec = std::clamp( 10 - nDig, 2, 9 );
210 double fSeconds = rtl::math::round( fRawSeconds, nDec);
212 // If this ended up as a full day the original value was very very close
213 // but not quite. Take that.
214 if (fSeconds >= tools::Time::secondPerDay)
215 fSeconds = fRawSeconds;
217 // Now do not round values (specifically not up), but truncate to the next
218 // magnitude, so 23:59:59.99 is still 23:59:59 and not 24:00:00 (or even
219 // 00:00:00 which Excel does).
220 nHour = fSeconds / tools::Time::secondPerHour;
221 fSeconds -= nHour * tools::Time::secondPerHour;
222 nMinute = fSeconds / tools::Time::secondPerMinute;
223 fSeconds -= nMinute * tools::Time::secondPerMinute;
224 nSecond = fSeconds;
225 fSeconds -= nSecond;
227 assert(fSeconds < 1.0); // or back to the drawing board...
229 if (nFractionDecimals > 0)
231 // Do not simply round the fraction, otherwise .999 would end up as .00
232 // again. Truncate instead if rounding would round up into an integer
233 // value.
234 fFractionOfSecond = rtl::math::round( fSeconds, nFractionDecimals);
235 if (fFractionOfSecond >= 1.0)
236 fFractionOfSecond = rtl::math::pow10Exp( std::trunc(
237 rtl::math::pow10Exp( fSeconds, nFractionDecimals)), -nFractionDecimals);
239 else
240 fFractionOfSecond = fSeconds;
243 Time& tools::Time::operator +=( const tools::Time& rTime )
245 MakeTimeFromNS(GetNSFromTime() + rTime.GetNSFromTime());
246 return *this;
249 Time& tools::Time::operator -=( const tools::Time& rTime )
251 MakeTimeFromNS(GetNSFromTime() - rTime.GetNSFromTime());
252 return *this;
255 Time operator +( const tools::Time& rTime1, const tools::Time& rTime2 )
257 tools::Time result(rTime1);
258 return result += rTime2;
261 Time operator -( const tools::Time& rTime1, const tools::Time& rTime2 )
263 tools::Time result(rTime1);
264 return result -= rTime2;
267 bool tools::Time::IsEqualIgnoreNanoSec( const tools::Time& rTime ) const
269 return nTime / SEC_MASK == rTime.nTime / SEC_MASK;
272 Time tools::Time::GetUTCOffset()
274 #if defined(_WIN32)
275 TIME_ZONE_INFORMATION aTimeZone;
276 aTimeZone.Bias = 0;
277 DWORD nTimeZoneRet = GetTimeZoneInformation( &aTimeZone );
278 sal_Int32 nTempTime = aTimeZone.Bias;
279 if ( nTimeZoneRet == TIME_ZONE_ID_STANDARD )
280 nTempTime += aTimeZone.StandardBias;
281 else if ( nTimeZoneRet == TIME_ZONE_ID_DAYLIGHT )
282 nTempTime += aTimeZone.DaylightBias;
283 tools::Time aTime( 0, static_cast<sal_uInt16>(abs( nTempTime )) );
284 if ( nTempTime > 0 )
285 aTime = -aTime;
286 return aTime;
287 #else
288 static sal_uInt64 nCacheTicks = 0;
289 static sal_Int32 nCacheSecOffset = -1;
290 sal_uInt64 nTicks = tools::Time::GetSystemTicks();
291 time_t nTime;
292 tm aTM;
293 short nTempTime;
295 // determine value again if needed
296 if ( (nCacheSecOffset == -1) ||
297 ((nTicks - nCacheTicks) > 360000) ||
298 ( nTicks < nCacheTicks ) // handle overflow
301 nTime = time( nullptr );
302 localtime_r( &nTime, &aTM );
303 auto nLocalTime = mktime( &aTM );
304 #if defined(__sun)
305 // Solaris gmtime_r() seems not to handle daylight saving time
306 // flags correctly
307 auto nUTC = nLocalTime + ( aTM.tm_isdst == 0 ? timezone : altzone );
308 #elif defined( LINUX )
309 // Linux mktime() seems not to handle tm_isdst correctly
310 auto nUTC = nLocalTime - aTM.tm_gmtoff;
311 #else
312 gmtime_r( &nTime, &aTM );
313 auto nUTC = mktime( &aTM );
314 #endif
315 nCacheTicks = nTicks;
316 nCacheSecOffset = (nLocalTime-nUTC) / 60;
319 nTempTime = abs( nCacheSecOffset );
320 tools::Time aTime( 0, static_cast<sal_uInt16>(nTempTime) );
321 if ( nCacheSecOffset < 0 )
322 aTime = -aTime;
323 return aTime;
324 #endif
327 sal_uInt64 tools::Time::GetSystemTicks()
329 return tools::Time::GetMonotonicTicks() / 1000;
332 #ifdef _WIN32
333 static LARGE_INTEGER initPerformanceFrequency()
335 LARGE_INTEGER nTicksPerSecond = { 0, 0 };
336 if (!QueryPerformanceFrequency(&nTicksPerSecond))
337 nTicksPerSecond.QuadPart = 0;
338 return nTicksPerSecond;
340 #endif
342 sal_uInt64 tools::Time::GetMonotonicTicks()
344 #ifdef _WIN32
345 static const LARGE_INTEGER nTicksPerSecond = initPerformanceFrequency();
346 if (nTicksPerSecond.QuadPart > 0)
348 LARGE_INTEGER nPerformanceCount;
349 QueryPerformanceCounter(&nPerformanceCount);
350 return static_cast<sal_uInt64>(
351 ( nPerformanceCount.QuadPart * 1000 * 1000 ) / nTicksPerSecond.QuadPart );
353 else
355 return static_cast<sal_uInt64>( timeGetTime() * 1000 );
357 #else
358 sal_uInt64 nMicroSeconds;
359 #ifdef __MACH__
360 static mach_timebase_info_data_t info = { 0, 0 };
361 if ( 0 == info.numer )
362 mach_timebase_info( &info );
363 nMicroSeconds = mach_absolute_time() * static_cast<double>(info.numer / info.denom) / 1000;
364 #else
365 #if defined(_POSIX_TIMERS)
366 struct timespec currentTime;
367 clock_gettime( CLOCK_MONOTONIC, &currentTime );
368 nMicroSeconds
369 = static_cast<sal_uInt64>(currentTime.tv_sec) * 1000 * 1000 + currentTime.tv_nsec / 1000;
370 #else
371 struct timeval currentTime;
372 gettimeofday( &currentTime, nullptr );
373 nMicroSeconds = static_cast<sal_uInt64>(currentTime.tv_sec) * 1000 * 1000 + currentTime.tv_usec;
374 #endif
375 #endif // __MACH__
376 return nMicroSeconds;
377 #endif // _WIN32
380 } /* namespace tools */
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */