1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
25 #if !defined WIN32_LEAN_AND_MEAN
26 # define WIN32_LEAN_AND_MEAN
36 #include <mach/clock.h>
37 #include <mach/mach.h>
38 #include <mach/mach_time.h>
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__)
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
)
84 tools::Time
aTime( 0, 0, 0, nNanoSec
);
85 aTime
.SetTime( aTime
.GetTime() * nSign
);
89 } // anonymous namespace
93 Time::Time( TimeInitSystem
)
95 if ( !GetSystemDateTime( nullptr, &nTime
) )
99 Time::Time( const tools::Time
& rTime
)
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
)
120 nSec
+= nNanoSec
/ nanoSecInSec
;
121 nNanoSec
%= nanoSecInSec
;
122 nMin
+= nSec
/ secInMin
;
124 nHour
+= nMin
/ minInHour
;
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();
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();
156 nNewMin
= nNewMin
% minInHour
;
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();
173 nNewSec
= nNewSec
% secInMin
;
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();
190 nNewNanoSec
= nNewNanoSec
% nanoSecInSec
;
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();
209 nSec
* nanoSecInSec
+
210 nMin
* (secInMin
* nanoSecInSec
) +
211 nHour
* (minInHour
* secInMin
* nanoSecInSec
) );
214 void tools::Time::MakeTimeFromNS( sal_Int64 nNS
)
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();
245 void tools::Time::MakeTimeFromMS( sal_Int32 nMS
)
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
;
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;
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.
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
;
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
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
);
341 fFractionOfSecond
= fSeconds
;
344 Time
& tools::Time::operator =( const tools::Time
& rTime
)
350 Time
& tools::Time::operator +=( const tools::Time
& rTime
)
352 nTime
= NanoSecToTime( TimeToNanoSec( *this ) +
353 TimeToNanoSec( rTime
) ).GetTime();
357 Time
& tools::Time::operator -=( const tools::Time
& rTime
)
359 nTime
= NanoSecToTime( TimeToNanoSec( *this ) -
360 TimeToNanoSec( rTime
) ).GetTime();
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()
386 TIME_ZONE_INFORMATION aTimeZone
;
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
)) );
399 static sal_uInt64 nCacheTicks
= 0;
400 static sal_Int32 nCacheSecOffset
= -1;
401 sal_uInt64 nTicks
= tools::Time::GetSystemTicks();
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
);
416 // Solaris gmtime_r() seems not to handle daylight saving time
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
;
423 gmtime_r( &nTime
, &aTM
);
424 sal_Int32 nUTC
= mktime( &aTM
);
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 )
438 sal_uInt64
tools::Time::GetSystemTicks()
440 return tools::Time::GetMonotonicTicks() / 1000;
444 static LARGE_INTEGER
initPerformanceFrequency()
446 LARGE_INTEGER nTicksPerSecond
= { 0, 0 };
447 if (!QueryPerformanceFrequency(&nTicksPerSecond
))
448 nTicksPerSecond
.QuadPart
= 0;
449 return nTicksPerSecond
;
453 sal_uInt64
tools::Time::GetMonotonicTicks()
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
);
466 return static_cast<sal_uInt64
>( timeGetTime() * 1000 );
469 sal_uInt64 nMicroSeconds
;
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;
476 #if defined(USE_CLOCK_GETTIME)
477 struct timespec currentTime
;
478 clock_gettime( CLOCK_MONOTONIC
, ¤tTime
);
479 nMicroSeconds
= currentTime
.tv_sec
* 1000 * 1000 + currentTime
.tv_nsec
/ 1000;
481 struct timeval currentTime
;
482 gettimeofday( ¤tTime
, nullptr );
483 nMicroSeconds
= currentTime
.tv_sec
* 1000 * 1000 + currentTime
.tv_usec
;
486 return nMicroSeconds
;
490 } /* namespace tools */
492 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */