1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 #include <tools/duration.hxx>
11 #include <tools/datetime.hxx>
12 #include <rtl/math.hxx>
13 #include <o3tl/safeint.hxx>
18 Duration::Duration(const ::DateTime
& rStart
, const ::DateTime
& rEnd
)
19 : mnDays(static_cast<const Date
&>(rEnd
) - static_cast<const Date
&>(rStart
))
21 SetTimeDiff(rStart
, rEnd
);
24 Duration::Duration(const Time
& rStart
, const Time
& rEnd
)
26 const sal_uInt16 nStartHour
= rStart
.GetHour();
27 const sal_uInt16 nEndHour
= rEnd
.GetHour();
28 if (nStartHour
>= 24 || nEndHour
>= 24)
33 mnDays
= (nEndHour
/ 24) * (aEnd
.GetTime() < 0 ? -1 : 1);
34 aEnd
.SetHour(nEndHour
% 24);
39 mnDays
-= (nStartHour
/ 24) * (aStart
.GetTime() < 0 ? -1 : 1);
40 aStart
.SetHour(nStartHour
% 24);
42 SetTimeDiff(aStart
, aEnd
);
46 SetTimeDiff(rStart
, rEnd
);
50 Duration::Duration(double fTimeInDays
, sal_uInt64 nAccuracyEpsilonNanoseconds
)
52 assert(nAccuracyEpsilonNanoseconds
<= Time::nanoSecPerSec
- 1);
54 if (fTimeInDays
< 0.0)
56 fInt
= ::rtl::math::approxCeil(fTimeInDays
);
57 fFrac
= fInt
<= fTimeInDays
? 0.0 : fTimeInDays
- fInt
;
61 fInt
= ::rtl::math::approxFloor(fTimeInDays
);
62 fFrac
= fInt
>= fTimeInDays
? 0.0 : fTimeInDays
- fInt
;
64 mnDays
= static_cast<sal_Int32
>(fInt
);
67 fFrac
*= Time::nanoSecPerDay
;
68 fFrac
= ::rtl::math::approxFloor(fFrac
);
69 sal_Int64 nNS
= static_cast<sal_Int64
>(fFrac
);
70 const sal_Int64 nN
= nNS
% Time::nanoSecPerSec
;
73 const sal_uInt64 nA
= std::abs(nN
);
74 if (nA
<= nAccuracyEpsilonNanoseconds
)
75 nNS
-= (nNS
< 0) ? -nN
: nN
;
76 else if (nA
>= Time::nanoSecPerSec
- nAccuracyEpsilonNanoseconds
)
78 const sal_Int64 nD
= Time::nanoSecPerSec
- nA
;
79 nNS
+= (nNS
< 0) ? -nD
: nD
;
80 if (std::abs(nNS
) >= Time::nanoSecPerDay
)
82 mnDays
+= nNS
/ Time::nanoSecPerDay
;
83 nNS
%= Time::nanoSecPerDay
;
87 maTime
.MakeTimeFromNS(nNS
);
88 assert(mnDays
== 0 || maTime
.GetTime() == 0 || (mnDays
< 0) == (nNS
< 0));
92 Duration::Duration(sal_Int32 nDays
, const Time
& rTime
)
95 assert(nDays
== 0 || rTime
.GetTime() == 0 || (nDays
< 0) == (rTime
.GetTime() < 0));
96 Normalize(rTime
.GetHour(), rTime
.GetMin(), rTime
.GetSec(), rTime
.GetNanoSec(),
97 ((nDays
< 0) || (rTime
.GetTime() < 0)));
100 Duration::Duration(sal_Int32 nDays
, sal_uInt32 nHours
, sal_uInt32 nMinutes
, sal_uInt32 nSeconds
,
101 sal_uInt64 nNanoseconds
)
104 Normalize(nHours
, nMinutes
, nSeconds
, nNanoseconds
, nDays
< 0);
107 Duration::Duration(sal_Int32 nDays
, sal_Int64 nTime
)
113 void Duration::Normalize(sal_uInt64 nHours
, sal_uInt64 nMinutes
, sal_uInt64 nSeconds
,
114 sal_uInt64 nNanoseconds
, bool bNegative
)
116 if (nNanoseconds
>= Time::nanoSecPerSec
)
118 nSeconds
+= nNanoseconds
/ Time::nanoSecPerSec
;
119 nNanoseconds
%= Time::nanoSecPerSec
;
121 if (nSeconds
>= Time::secondPerMinute
)
123 nMinutes
+= nSeconds
/ Time::secondPerMinute
;
124 nSeconds
%= Time::secondPerMinute
;
126 if (nMinutes
>= Time::minutePerHour
)
128 nHours
+= nMinutes
/ Time::minutePerHour
;
129 nMinutes
%= Time::minutePerHour
;
131 if (nHours
>= Time::hourPerDay
)
133 sal_Int64 nDiff
= nHours
/ Time::hourPerDay
;
134 nHours
%= Time::hourPerDay
;
135 bool bOverflow
= false;
139 bOverflow
= (nDiff
< SAL_MIN_INT32
);
140 bOverflow
|= o3tl::checked_add(mnDays
, static_cast<sal_Int32
>(nDiff
), mnDays
);
142 mnDays
= SAL_MIN_INT32
;
146 bOverflow
= (nDiff
> SAL_MAX_INT32
);
147 bOverflow
|= o3tl::checked_add(mnDays
, static_cast<sal_Int32
>(nDiff
), mnDays
);
149 mnDays
= SAL_MAX_INT32
;
154 nHours
= Time::hourPerDay
- 1;
155 nMinutes
= Time::minutePerHour
- 1;
156 nSeconds
= Time::secondPerMinute
- 1;
157 nNanoseconds
= Time::nanoSecPerSec
- 1;
160 maTime
= Time(nHours
, nMinutes
, nSeconds
, nNanoseconds
);
163 assert(mnDays
== 0 || maTime
.GetTime() == 0 || (mnDays
< 0) == (maTime
.GetTime() < 0));
166 void Duration::ApplyTime(sal_Int64 nNS
)
168 if (mnDays
> 0 && nNS
< 0)
171 nNS
= Time::nanoSecPerDay
+ nNS
;
173 else if (mnDays
< 0 && nNS
> 0)
176 nNS
= -Time::nanoSecPerDay
+ nNS
;
178 maTime
.MakeTimeFromNS(nNS
);
179 assert(mnDays
== 0 || maTime
.GetTime() == 0 || (mnDays
< 0) == (maTime
.GetTime() < 0));
182 void Duration::SetTimeDiff(const Time
& rStart
, const Time
& rEnd
)
184 const sal_Int64 nNS
= rEnd
.GetNSFromTime() - rStart
.GetNSFromTime();
188 Duration
Duration::operator-() const
190 Duration
aD(-mnDays
, -maTime
.GetTime());
194 Duration
& Duration::Add(const Duration
& rDuration
, bool& rbOverflow
)
196 rbOverflow
= o3tl::checked_add(mnDays
, rDuration
.mnDays
, mnDays
);
197 // Duration is always normalized, time values >= 24h don't occur.
198 sal_Int64 nNS
= maTime
.GetNSFromTime() + rDuration
.maTime
.GetNSFromTime();
199 if (nNS
< -Time::nanoSecPerDay
)
201 rbOverflow
|= o3tl::checked_sub(mnDays
, sal_Int32(1), mnDays
);
202 nNS
+= Time::nanoSecPerDay
;
204 else if (nNS
> Time::nanoSecPerDay
)
206 rbOverflow
|= o3tl::checked_add(mnDays
, sal_Int32(1), mnDays
);
207 nNS
-= Time::nanoSecPerDay
;
213 Duration
Duration::Mult(sal_Int32 nMult
, bool& rbOverflow
) const
215 // First try a simple calculation in nanoseconds.
219 if (o3tl::checked_multiply(static_cast<sal_Int64
>(mnDays
), static_cast<sal_Int64
>(nMult
), nDays
)
220 || o3tl::checked_multiply(nDays
, Time::nanoSecPerDay
, nDays
)
221 || o3tl::checked_multiply(maTime
.GetNSFromTime(), static_cast<sal_Int64
>(nMult
), nNS
)
222 || o3tl::checked_add(nDays
, nNS
, nNS
))
224 bBadNS
= rbOverflow
= true;
228 const sal_Int64 nD
= nNS
/ Time::nanoSecPerDay
;
229 if (nD
< SAL_MIN_INT32
|| SAL_MAX_INT32
< nD
)
234 nNS
-= nD
* Time::nanoSecPerDay
;
235 Duration
aD(static_cast<sal_Int32
>(nD
), 0);
242 // Simple calculation in overall nanoseconds overflowed, try with
243 // individual components.
244 const sal_uInt64 nMult64
= (nMult
< 0) ? -nMult
: nMult
;
248 if (o3tl::checked_multiply(static_cast<sal_uInt64
>(maTime
.GetNanoSec()), nMult64
, nN
))
251 if (o3tl::checked_multiply(static_cast<sal_uInt64
>(maTime
.GetSec()), nMult64
, nS
))
254 if (o3tl::checked_multiply(static_cast<sal_uInt64
>(maTime
.GetMin()), nMult64
, nM
))
257 if (o3tl::checked_multiply(static_cast<sal_uInt64
>(maTime
.GetHour()), nMult64
, nH
))
260 if (o3tl::checked_multiply(
261 mnDays
< 0 ? static_cast<sal_uInt64
>(-static_cast<sal_Int64
>(mnDays
))
262 : static_cast<sal_uInt64
>(mnDays
),
265 if (nN
> Time::nanoSecPerSec
)
267 const sal_uInt64 nC
= nN
/ Time::nanoSecPerSec
;
268 if (o3tl::checked_add(nS
, nC
, nS
))
270 nN
-= nC
* Time::nanoSecPerSec
;
272 if (nS
> Time::secondPerMinute
)
274 const sal_uInt64 nC
= nS
/ Time::secondPerMinute
;
275 if (o3tl::checked_add(nM
, nC
, nM
))
277 nS
-= nC
* Time::secondPerMinute
;
279 if (nM
> Time::minutePerHour
)
281 const sal_uInt64 nC
= nM
/ Time::minutePerHour
;
282 if (o3tl::checked_add(nH
, nC
, nH
))
284 nM
-= nC
* Time::minutePerHour
;
286 if (nH
> Time::hourPerDay
)
288 const sal_uInt64 nC
= nH
/ Time::hourPerDay
;
289 if (o3tl::checked_add(nD
, nC
, nD
))
291 nH
-= nC
* Time::hourPerDay
;
293 if (IsNegative() ? (static_cast<sal_uInt64
>(SAL_MAX_INT32
) + 1) < nD
294 || -static_cast<sal_Int64
>(nD
) < SAL_MIN_INT32
295 : SAL_MAX_INT32
< nD
)
299 Time
aTime(nH
, nM
, nS
, nN
);
300 if (IsNegative() == (nMult
< 0))
302 Duration
aD(nD
, aTime
.GetTime());
307 Duration
aD(-static_cast<sal_Int64
>(nD
), -aTime
.GetTime());
313 if (IsNegative() == (nMult
< 0))
315 Duration
aD(SAL_MAX_INT32
, 0);
316 aD
.ApplyTime(Time::nanoSecPerDay
- 1);
321 Duration
aD(SAL_MIN_INT32
, 0);
322 aD
.ApplyTime(-(Time::nanoSecPerDay
- 1));
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */