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
37 #include <mach/clock.h>
38 #include <mach/mach.h>
39 #include <mach/mach_time.h>
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__)
54 Time::Time( TimeInitSystem
)
56 if ( !GetSystemDateTime( nullptr, &nTime
) )
60 Time::Time( sal_uInt32 nHour
, sal_uInt32 nMin
, sal_uInt32 nSec
, sal_uInt64 nNanoSec
)
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)
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
;
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
)
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
)
117 nTime
= GetSign() * assemble(GetHour(), nNewMin
% minutePerHour
, GetSec(), GetNanoSec());
120 void tools::Time::SetSec( sal_uInt16 nNewSec
)
123 nTime
= GetSign() * assemble(GetHour(), GetMin(), nNewSec
% secondPerMinute
, GetNanoSec());
126 void tools::Time::SetNanoSec( sal_uInt32 nNewNanoSec
)
129 nTime
= GetSign() * assemble(GetHour(), GetMin(), GetSec(), nNewNanoSec
% nanoSecPerSec
);
132 sal_Int64
tools::Time::GetNSFromTime() const
136 GetSec() * nanoSecPerSec
+
137 GetMin() * nanoSecPerMinute
+
138 GetHour() * nanoSecPerHour
);
141 void tools::Time::MakeTimeFromNS( sal_Int64 nNS
)
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
);
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;
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.
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
;
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
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
);
240 fFractionOfSecond
= fSeconds
;
243 Time
& tools::Time::operator +=( const tools::Time
& rTime
)
245 MakeTimeFromNS(GetNSFromTime() + rTime
.GetNSFromTime());
249 Time
& tools::Time::operator -=( const tools::Time
& rTime
)
251 MakeTimeFromNS(GetNSFromTime() - rTime
.GetNSFromTime());
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()
275 TIME_ZONE_INFORMATION aTimeZone
;
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
)) );
288 static sal_uInt64 nCacheTicks
= 0;
289 static sal_Int32 nCacheSecOffset
= -1;
290 sal_uInt64 nTicks
= tools::Time::GetSystemTicks();
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
);
305 // Solaris gmtime_r() seems not to handle daylight saving time
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
;
312 gmtime_r( &nTime
, &aTM
);
313 auto nUTC
= mktime( &aTM
);
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 )
327 sal_uInt64
tools::Time::GetSystemTicks()
329 return tools::Time::GetMonotonicTicks() / 1000;
333 static LARGE_INTEGER
initPerformanceFrequency()
335 LARGE_INTEGER nTicksPerSecond
= { 0, 0 };
336 if (!QueryPerformanceFrequency(&nTicksPerSecond
))
337 nTicksPerSecond
.QuadPart
= 0;
338 return nTicksPerSecond
;
342 sal_uInt64
tools::Time::GetMonotonicTicks()
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
);
355 return static_cast<sal_uInt64
>( timeGetTime() * 1000 );
358 sal_uInt64 nMicroSeconds
;
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;
365 #if defined(_POSIX_TIMERS)
366 struct timespec currentTime
;
367 clock_gettime( CLOCK_MONOTONIC
, ¤tTime
);
369 = static_cast<sal_uInt64
>(currentTime
.tv_sec
) * 1000 * 1000 + currentTime
.tv_nsec
/ 1000;
371 struct timeval currentTime
;
372 gettimeofday( ¤tTime
, nullptr );
373 nMicroSeconds
= static_cast<sal_uInt64
>(currentTime
.tv_sec
) * 1000 * 1000 + currentTime
.tv_usec
;
376 return nMicroSeconds
;
380 } /* namespace tools */
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */