Avoid potential negative array index access to cached text.
[LibreOffice.git] / tools / source / datetime / duration.cxx
bloba655f016a1bcd8aa848f6d3d27e12431c9847796
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
8 */
10 #include <tools/duration.hxx>
11 #include <tools/datetime.hxx>
12 #include <rtl/math.hxx>
13 #include <o3tl/safeint.hxx>
14 #include <cmath>
16 namespace tools
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)
30 Time aEnd(rEnd);
31 if (nEndHour >= 24)
33 mnDays = (nEndHour / 24) * (aEnd.GetTime() < 0 ? -1 : 1);
34 aEnd.SetHour(nEndHour % 24);
36 Time aStart(rStart);
37 if (nStartHour >= 24)
39 mnDays -= (nStartHour / 24) * (aStart.GetTime() < 0 ? -1 : 1);
40 aStart.SetHour(nStartHour % 24);
42 SetTimeDiff(aStart, aEnd);
44 else
46 SetTimeDiff(rStart, rEnd);
50 Duration::Duration(double fTimeInDays, sal_uInt64 nAccuracyEpsilonNanoseconds)
52 assert(nAccuracyEpsilonNanoseconds <= Time::nanoSecPerSec - 1);
53 double fInt, fFrac;
54 if (fTimeInDays < 0.0)
56 fInt = ::rtl::math::approxCeil(fTimeInDays);
57 fFrac = fInt <= fTimeInDays ? 0.0 : fTimeInDays - fInt;
59 else
61 fInt = ::rtl::math::approxFloor(fTimeInDays);
62 fFrac = fInt >= fTimeInDays ? 0.0 : fTimeInDays - fInt;
64 mnDays = static_cast<sal_Int32>(fInt);
65 if (fFrac)
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;
71 if (nN)
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)
93 : mnDays(nDays)
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)
102 : mnDays(nDays)
104 Normalize(nHours, nMinutes, nSeconds, nNanoseconds, nDays < 0);
107 Duration::Duration(sal_Int32 nDays, sal_Int64 nTime)
108 : maTime(nTime)
109 , mnDays(nDays)
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;
136 if (bNegative)
138 nDiff = -nDiff;
139 bOverflow = (nDiff < SAL_MIN_INT32);
140 bOverflow |= o3tl::checked_add(mnDays, static_cast<sal_Int32>(nDiff), mnDays);
141 if (bOverflow)
142 mnDays = SAL_MIN_INT32;
144 else
146 bOverflow = (nDiff > SAL_MAX_INT32);
147 bOverflow |= o3tl::checked_add(mnDays, static_cast<sal_Int32>(nDiff), mnDays);
148 if (bOverflow)
149 mnDays = SAL_MAX_INT32;
151 assert(!bOverflow);
152 if (bOverflow)
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);
161 if (bNegative)
162 maTime = -maTime;
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)
170 --mnDays;
171 nNS = Time::nanoSecPerDay + nNS;
173 else if (mnDays < 0 && nNS > 0)
175 ++mnDays;
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();
185 ApplyTime(nNS);
188 Duration Duration::operator-() const
190 Duration aD(-mnDays, -maTime.GetTime());
191 return aD;
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;
209 ApplyTime(nNS);
210 return *this;
213 Duration Duration::Mult(sal_Int32 nMult, bool& rbOverflow) const
215 // First try a simple calculation in nanoseconds.
216 bool bBadNS = false;
217 sal_Int64 nNS;
218 sal_Int64 nDays;
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;
226 else
228 const sal_Int64 nD = nNS / Time::nanoSecPerDay;
229 if (nD < SAL_MIN_INT32 || SAL_MAX_INT32 < nD)
230 rbOverflow = true;
231 else
233 rbOverflow = false;
234 nNS -= nD * Time::nanoSecPerDay;
235 Duration aD(static_cast<sal_Int32>(nD), 0);
236 aD.ApplyTime(nNS);
237 return aD;
240 if (bBadNS)
242 // Simple calculation in overall nanoseconds overflowed, try with
243 // individual components.
244 const sal_uInt64 nMult64 = (nMult < 0) ? -nMult : nMult;
247 sal_uInt64 nN;
248 if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetNanoSec()), nMult64, nN))
249 break;
250 sal_uInt64 nS;
251 if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetSec()), nMult64, nS))
252 break;
253 sal_uInt64 nM;
254 if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetMin()), nMult64, nM))
255 break;
256 sal_uInt64 nH;
257 if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetHour()), nMult64, nH))
258 break;
259 sal_uInt64 nD;
260 if (o3tl::checked_multiply(
261 mnDays < 0 ? static_cast<sal_uInt64>(-static_cast<sal_Int64>(mnDays))
262 : static_cast<sal_uInt64>(mnDays),
263 nMult64, nD))
264 break;
265 if (nN > Time::nanoSecPerSec)
267 const sal_uInt64 nC = nN / Time::nanoSecPerSec;
268 if (o3tl::checked_add(nS, nC, nS))
269 break;
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))
276 break;
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))
283 break;
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))
290 break;
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)
296 break;
298 rbOverflow = false;
299 Time aTime(nH, nM, nS, nN);
300 if (IsNegative() == (nMult < 0))
302 Duration aD(nD, aTime.GetTime());
303 return aD;
305 else
307 Duration aD(-static_cast<sal_Int64>(nD), -aTime.GetTime());
308 return aD;
310 } while (false);
312 assert(rbOverflow);
313 if (IsNegative() == (nMult < 0))
315 Duration aD(SAL_MAX_INT32, 0);
316 aD.ApplyTime(Time::nanoSecPerDay - 1);
317 return aD;
319 else
321 Duration aD(SAL_MIN_INT32, 0);
322 aD.ApplyTime(-(Time::nanoSecPerDay - 1));
323 return aD;
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */