summaryrefslogtreecommitdiffstats
path: root/tools/source/datetime
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /tools/source/datetime
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/source/datetime')
-rw-r--r--tools/source/datetime/datetime.cxx291
-rw-r--r--tools/source/datetime/datetimeutils.cxx82
-rw-r--r--tools/source/datetime/duration.cxx328
-rw-r--r--tools/source/datetime/systemdatetime.cxx107
-rw-r--r--tools/source/datetime/tdate.cxx412
-rw-r--r--tools/source/datetime/ttime.cxx506
6 files changed, 1726 insertions, 0 deletions
diff --git a/tools/source/datetime/datetime.cxx b/tools/source/datetime/datetime.cxx
new file mode 100644
index 0000000000..6f9dea26c6
--- /dev/null
+++ b/tools/source/datetime/datetime.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <tools/datetime.hxx>
+#include <tools/duration.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+#include <systemdatetime.hxx>
+
+DateTime::DateTime(DateTimeInitSystem)
+ : Date( Date::EMPTY )
+ , Time( Time::EMPTY )
+{
+ sal_Int32 nD = 0;
+ sal_Int64 nT = 0;
+ if ( GetSystemDateTime( &nD, &nT ) )
+ {
+ Date::operator=( Date( nD ) );
+ SetTime( nT );
+ }
+ else
+ Date::operator=( Date( 1, 1, 1900 ) ); // Time::nTime is already 0
+}
+
+DateTime::DateTime( const css::util::DateTime& rDateTime )
+ : Date( rDateTime.Day, rDateTime.Month, rDateTime.Year ),
+ Time( rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds, rDateTime.NanoSeconds )
+{
+}
+
+DateTime& DateTime::operator =( const css::util::DateTime& rUDateTime )
+{
+ Date::operator=( Date( rUDateTime.Day, rUDateTime.Month, rUDateTime.Year));
+ Time::operator=( Time( rUDateTime));
+ return *this;
+}
+
+bool DateTime::IsBetween( const DateTime& rFrom, const DateTime& rTo ) const
+{
+ return (*this >= rFrom) && (*this <= rTo);
+}
+
+bool DateTime::operator >( const DateTime& rDateTime ) const
+{
+ return (Date::operator>( rDateTime )) ||
+ (Date::operator==( rDateTime ) && tools::Time::operator>( rDateTime ));
+}
+
+bool DateTime::operator <( const DateTime& rDateTime ) const
+{
+ return (Date::operator<( rDateTime )) ||
+ (Date::operator==( rDateTime ) && tools::Time::operator<( rDateTime ));
+}
+
+bool DateTime::operator >=( const DateTime& rDateTime ) const
+{
+ return (Date::operator>( rDateTime )) ||
+ (Date::operator==( rDateTime ) && tools::Time::operator>=( rDateTime ));
+}
+
+bool DateTime::operator <=( const DateTime& rDateTime ) const
+{
+ return (Date::operator<( rDateTime )) ||
+ (Date::operator==( rDateTime ) && tools::Time::operator<=( rDateTime ));
+}
+
+sal_Int64 DateTime::GetSecFromDateTime( const Date& rDate ) const
+{
+ if ( Date::operator<( rDate ) )
+ return 0;
+ else
+ {
+ sal_Int64 nSec = Date( *this ) - rDate;
+ nSec *= 24UL*60*60;
+ sal_Int64 nHour = GetHour();
+ sal_Int64 nMin = GetMin();
+ nSec += (nHour*3600)+(nMin*60)+GetSec();
+ return nSec;
+ }
+}
+
+void DateTime::NormalizeTimeRemainderAndApply( tools::Time& rTime )
+{
+ sal_uInt16 nHours = rTime.GetHour();
+ if ( rTime.GetTime() > 0 )
+ {
+ if (nHours >= 24)
+ {
+ AddDays( nHours / 24 );
+ nHours %= 24;
+ rTime.SetHour( nHours );
+ }
+ }
+ else if ( rTime.GetTime() != 0 )
+ {
+ if (nHours >= 24)
+ {
+ AddDays( -static_cast<sal_Int32>(nHours) / 24 );
+ nHours %= 24;
+ rTime.SetHour( nHours );
+ }
+ Date::operator--();
+ rTime = Time( 24, 0, 0 ) + rTime;
+ }
+ tools::Time::operator=( rTime );
+}
+
+DateTime& DateTime::operator +=( const tools::Time& rTime )
+{
+ tools::Time aTime = *this;
+ aTime += rTime;
+ NormalizeTimeRemainderAndApply(aTime);
+ return *this;
+}
+
+DateTime& DateTime::operator -=( const tools::Time& rTime )
+{
+ tools::Time aTime = *this;
+ aTime -= rTime;
+ NormalizeTimeRemainderAndApply(aTime);
+ return *this;
+}
+
+DateTime& DateTime::operator +=( const tools::Duration& rDuration )
+{
+ AddDays(rDuration.GetDays());
+ operator+=(rDuration.GetTime());
+ return *this;
+}
+
+DateTime operator +( const DateTime& rDateTime, sal_Int32 nDays )
+{
+ DateTime aDateTime( rDateTime );
+ aDateTime.AddDays( nDays );
+ return aDateTime;
+}
+
+DateTime operator -( const DateTime& rDateTime, sal_Int32 nDays )
+{
+ DateTime aDateTime( rDateTime );
+ aDateTime.AddDays( -nDays );
+ return aDateTime;
+}
+
+DateTime operator +( const DateTime& rDateTime, const tools::Time& rTime )
+{
+ DateTime aDateTime( rDateTime );
+ aDateTime += rTime;
+ return aDateTime;
+}
+
+DateTime operator -( const DateTime& rDateTime, const tools::Time& rTime )
+{
+ DateTime aDateTime( rDateTime );
+ aDateTime -= rTime;
+ return aDateTime;
+}
+
+DateTime operator +( const DateTime& rDateTime, const tools::Duration& rDuration )
+{
+ DateTime aDateTime(rDateTime);
+ aDateTime.AddDays( rDuration.GetDays());
+ aDateTime += rDuration.GetTime();
+ return aDateTime;
+}
+
+void DateTime::AddTime( double fTimeInDays )
+{
+ // Use Duration to diminish floating point accuracy errors.
+ tools::Duration aDuration(fTimeInDays);
+ operator+=(aDuration);
+}
+
+DateTime operator +( const DateTime& rDateTime, double fTimeInDays )
+{
+ DateTime aDateTime( rDateTime );
+ aDateTime.AddTime( fTimeInDays );
+ return aDateTime;
+}
+
+tools::Duration operator -( const DateTime& rDateTime1, const DateTime& rDateTime2 )
+{
+ return tools::Duration( rDateTime2, rDateTime1);
+}
+
+// static
+double DateTime::Sub( const DateTime& rDateTime1, const DateTime& rDateTime2 )
+{
+ if (static_cast<const tools::Time&>(rDateTime1) != static_cast<const tools::Time&>(rDateTime2))
+ {
+ // Use Duration to diminish floating point accuracy errors.
+ const tools::Duration aDuration( rDateTime2, rDateTime1);
+ return aDuration.GetInDays();
+ }
+ return static_cast<const Date&>(rDateTime1) - static_cast<const Date&>(rDateTime2);
+}
+
+void DateTime::GetWin32FileDateTime( sal_uInt32 & rLower, sal_uInt32 & rUpper ) const
+{
+ const sal_Int64 a100nPerSecond = SAL_CONST_INT64( 10000000 );
+ const sal_Int64 a100nPerDay = a100nPerSecond * sal_Int64( 60 * 60 * 24 );
+
+ // FILETIME is indirectly documented as uint64, see
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx
+ // mentioning the ULARGE_INTEGER structure.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724280.aspx
+ // mentions that if FILETIME is not less than 0x8000000000000000 then the
+ // FileTimeToSystemTime function fails, which is another indicator.
+ // Unless there's evidence that FILETIME can represent a signed offset from
+ // 1601-01-01 truncate at 0. (reading part below in
+ // CreateFromWin32FileDateTime() would had to be adapted to signed as
+ // well).
+ sal_Int16 nYear = GetYear();
+ SAL_WARN_IF( nYear < 1601, "tools.datetime", "DateTime::GetWin32FileDateTime - year < 1601: " << nYear);
+
+ sal_Int64 aTime = (nYear < 1601 ? 0 : (
+ a100nPerDay * (*this - Date(1,1,1601)) +
+ GetNSFromTime()/100));
+
+ rLower = sal_uInt32( aTime % SAL_CONST_UINT64( 0x100000000 ) );
+ rUpper = sal_uInt32( aTime / SAL_CONST_UINT64( 0x100000000 ) );
+}
+
+DateTime DateTime::CreateFromWin32FileDateTime( sal_uInt32 rLower, sal_uInt32 rUpper )
+{
+ // (rUpper|rLower) = 100-nanosecond intervals since 1601-01-01 00:00
+ const sal_uInt64 a100nPerSecond = SAL_CONST_UINT64( 10000000 );
+ const sal_uInt64 a100nPerDay = a100nPerSecond * sal_uInt64( 60 * 60 * 24 );
+
+ sal_uInt64 aTime =
+ sal_uInt64( rUpper ) * SAL_CONST_UINT64( 0x100000000 ) +
+ sal_uInt64( rLower );
+
+ SAL_WARN_IF( static_cast<sal_Int64>(aTime) < 0, "tools.datetime",
+ "DateTime::CreateFromWin32FileDateTime - absurdly high value expected?");
+
+ sal_uInt64 nDays = aTime / a100nPerDay;
+
+ Date aDate(1,1,1601);
+ // (0xffffffffffffffff / a100nPerDay = 21350398) fits into sal_Int32
+ // (0x7fffffff = 2147483647)
+ aDate.AddDays(nDays);
+
+ SAL_WARN_IF( aDate - Date(1,1,1601) != static_cast<sal_Int32>(nDays), "tools.datetime",
+ "DateTime::CreateFromWin32FileDateTime - date truncated to max");
+
+ sal_uInt64 nNanos = (aTime - (nDays * a100nPerDay)) * 100;
+ return DateTime( aDate, tools::Time(
+ static_cast<sal_uInt32>((nNanos / tools::Time::nanoSecPerHour) % sal_uInt64( 24 )),
+ static_cast<sal_uInt32>((nNanos / tools::Time::nanoSecPerMinute) % sal_uInt64( 60 )),
+ static_cast<sal_uInt32>((nNanos / tools::Time::nanoSecPerSec) % sal_uInt64( 60 )),
+ static_cast<sal_uInt64>( nNanos % tools::Time::nanoSecPerSec)));
+}
+
+DateTime DateTime::CreateFromUnixTime(const double fSecondsSinceEpoch)
+{
+ double fValue = fSecondsSinceEpoch / Time::secondPerDay;
+ const sal_Int32 nDays = static_cast <sal_Int32>(::rtl::math::approxFloor(fValue));
+
+ Date aDate (1, 1, 1970);
+ aDate.AddDays(nDays);
+ SAL_WARN_IF(aDate - Date(1, 1, 1970) != nDays, "tools.datetime",
+ "DateTime::CreateFromUnixTime - date truncated to max");
+
+ fValue -= nDays;
+
+ const sal_uInt64 nNanos = fValue * tools::Time::nanoSecPerDay;
+ return DateTime( aDate, tools::Time(
+ static_cast<sal_uInt32>((nNanos / tools::Time::nanoSecPerHour) % sal_uInt64( 24 )),
+ static_cast<sal_uInt32>((nNanos / tools::Time::nanoSecPerMinute) % sal_uInt64( 60 )),
+ static_cast<sal_uInt32>((nNanos / tools::Time::nanoSecPerSec) % sal_uInt64( 60 )),
+ static_cast<sal_uInt64>( nNanos % tools::Time::nanoSecPerSec)));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/tools/source/datetime/datetimeutils.cxx b/tools/source/datetime/datetimeutils.cxx
new file mode 100644
index 0000000000..4c3b28d49d
--- /dev/null
+++ b/tools/source/datetime/datetimeutils.cxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <tools/datetimeutils.hxx>
+#include <rtl/strbuf.hxx>
+
+
+/// Append the number as 2-digit when less than 10.
+static void lcl_AppendTwoDigits( OStringBuffer &rBuffer, sal_Int32 nNum )
+{
+ if ( nNum < 0 || nNum > 99 )
+ {
+ rBuffer.append( "00" );
+ return;
+ }
+
+ if ( nNum < 10 )
+ rBuffer.append( '0' );
+
+ rBuffer.append( nNum );
+}
+
+OString DateTimeToOString( const DateTime& rDateTime )
+{
+ const DateTime& aInUTC( rDateTime );
+// HACK: this is correct according to the spec, but MSOffice believes everybody lives
+// in UTC+0 when reading it back
+// aInUTC.ConvertToUTC();
+
+ OStringBuffer aBuffer( 25 );
+ aBuffer.append( sal_Int32( aInUTC.GetYear() ) );
+ aBuffer.append( '-' );
+
+ lcl_AppendTwoDigits( aBuffer, aInUTC.GetMonth() );
+ aBuffer.append( '-' );
+
+ lcl_AppendTwoDigits( aBuffer, aInUTC.GetDay() );
+ aBuffer.append( 'T' );
+
+ lcl_AppendTwoDigits( aBuffer, aInUTC.GetHour() );
+ aBuffer.append( ':' );
+
+ lcl_AppendTwoDigits( aBuffer, aInUTC.GetMin() );
+ aBuffer.append( ':' );
+
+ lcl_AppendTwoDigits( aBuffer, aInUTC.GetSec() );
+ aBuffer.append( 'Z' ); // we are in UTC
+
+ return aBuffer.makeStringAndClear();
+}
+
+OString DateToOString( const Date& rDate )
+{
+ tools::Time aTime( tools::Time::EMPTY );
+ return DateTimeToOString( DateTime( rDate, aTime ) );
+}
+
+OString DateToDDMMYYYYOString( const Date& rDate )
+{
+ OStringBuffer aBuffer( 25 );
+ lcl_AppendTwoDigits( aBuffer, rDate.GetDay() );
+ aBuffer.append( '/' );
+
+ lcl_AppendTwoDigits( aBuffer, rDate.GetMonth() );
+ aBuffer.append( '/' );
+
+ aBuffer.append( sal_Int32( rDate.GetYear() ) );
+
+ return aBuffer.makeStringAndClear();
+}
+
+std::ostream& operator<<(std::ostream& os, const Date& rDate)
+{
+ os << rDate.GetYear() << "-" << rDate.GetMonth() << "-" << rDate.GetDay();
+ return os;
+}
diff --git a/tools/source/datetime/duration.cxx b/tools/source/datetime/duration.cxx
new file mode 100644
index 0000000000..a655f016a1
--- /dev/null
+++ b/tools/source/datetime/duration.cxx
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <tools/duration.hxx>
+#include <tools/datetime.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/safeint.hxx>
+#include <cmath>
+
+namespace tools
+{
+Duration::Duration(const ::DateTime& rStart, const ::DateTime& rEnd)
+ : mnDays(static_cast<const Date&>(rEnd) - static_cast<const Date&>(rStart))
+{
+ SetTimeDiff(rStart, rEnd);
+}
+
+Duration::Duration(const Time& rStart, const Time& rEnd)
+{
+ const sal_uInt16 nStartHour = rStart.GetHour();
+ const sal_uInt16 nEndHour = rEnd.GetHour();
+ if (nStartHour >= 24 || nEndHour >= 24)
+ {
+ Time aEnd(rEnd);
+ if (nEndHour >= 24)
+ {
+ mnDays = (nEndHour / 24) * (aEnd.GetTime() < 0 ? -1 : 1);
+ aEnd.SetHour(nEndHour % 24);
+ }
+ Time aStart(rStart);
+ if (nStartHour >= 24)
+ {
+ mnDays -= (nStartHour / 24) * (aStart.GetTime() < 0 ? -1 : 1);
+ aStart.SetHour(nStartHour % 24);
+ }
+ SetTimeDiff(aStart, aEnd);
+ }
+ else
+ {
+ SetTimeDiff(rStart, rEnd);
+ }
+}
+
+Duration::Duration(double fTimeInDays, sal_uInt64 nAccuracyEpsilonNanoseconds)
+{
+ assert(nAccuracyEpsilonNanoseconds <= Time::nanoSecPerSec - 1);
+ double fInt, fFrac;
+ if (fTimeInDays < 0.0)
+ {
+ fInt = ::rtl::math::approxCeil(fTimeInDays);
+ fFrac = fInt <= fTimeInDays ? 0.0 : fTimeInDays - fInt;
+ }
+ else
+ {
+ fInt = ::rtl::math::approxFloor(fTimeInDays);
+ fFrac = fInt >= fTimeInDays ? 0.0 : fTimeInDays - fInt;
+ }
+ mnDays = static_cast<sal_Int32>(fInt);
+ if (fFrac)
+ {
+ fFrac *= Time::nanoSecPerDay;
+ fFrac = ::rtl::math::approxFloor(fFrac);
+ sal_Int64 nNS = static_cast<sal_Int64>(fFrac);
+ const sal_Int64 nN = nNS % Time::nanoSecPerSec;
+ if (nN)
+ {
+ const sal_uInt64 nA = std::abs(nN);
+ if (nA <= nAccuracyEpsilonNanoseconds)
+ nNS -= (nNS < 0) ? -nN : nN;
+ else if (nA >= Time::nanoSecPerSec - nAccuracyEpsilonNanoseconds)
+ {
+ const sal_Int64 nD = Time::nanoSecPerSec - nA;
+ nNS += (nNS < 0) ? -nD : nD;
+ if (std::abs(nNS) >= Time::nanoSecPerDay)
+ {
+ mnDays += nNS / Time::nanoSecPerDay;
+ nNS %= Time::nanoSecPerDay;
+ }
+ }
+ }
+ maTime.MakeTimeFromNS(nNS);
+ assert(mnDays == 0 || maTime.GetTime() == 0 || (mnDays < 0) == (nNS < 0));
+ }
+}
+
+Duration::Duration(sal_Int32 nDays, const Time& rTime)
+ : mnDays(nDays)
+{
+ assert(nDays == 0 || rTime.GetTime() == 0 || (nDays < 0) == (rTime.GetTime() < 0));
+ Normalize(rTime.GetHour(), rTime.GetMin(), rTime.GetSec(), rTime.GetNanoSec(),
+ ((nDays < 0) || (rTime.GetTime() < 0)));
+}
+
+Duration::Duration(sal_Int32 nDays, sal_uInt32 nHours, sal_uInt32 nMinutes, sal_uInt32 nSeconds,
+ sal_uInt64 nNanoseconds)
+ : mnDays(nDays)
+{
+ Normalize(nHours, nMinutes, nSeconds, nNanoseconds, nDays < 0);
+}
+
+Duration::Duration(sal_Int32 nDays, sal_Int64 nTime)
+ : maTime(nTime)
+ , mnDays(nDays)
+{
+}
+
+void Duration::Normalize(sal_uInt64 nHours, sal_uInt64 nMinutes, sal_uInt64 nSeconds,
+ sal_uInt64 nNanoseconds, bool bNegative)
+{
+ if (nNanoseconds >= Time::nanoSecPerSec)
+ {
+ nSeconds += nNanoseconds / Time::nanoSecPerSec;
+ nNanoseconds %= Time::nanoSecPerSec;
+ }
+ if (nSeconds >= Time::secondPerMinute)
+ {
+ nMinutes += nSeconds / Time::secondPerMinute;
+ nSeconds %= Time::secondPerMinute;
+ }
+ if (nMinutes >= Time::minutePerHour)
+ {
+ nHours += nMinutes / Time::minutePerHour;
+ nMinutes %= Time::minutePerHour;
+ }
+ if (nHours >= Time::hourPerDay)
+ {
+ sal_Int64 nDiff = nHours / Time::hourPerDay;
+ nHours %= Time::hourPerDay;
+ bool bOverflow = false;
+ if (bNegative)
+ {
+ nDiff = -nDiff;
+ bOverflow = (nDiff < SAL_MIN_INT32);
+ bOverflow |= o3tl::checked_add(mnDays, static_cast<sal_Int32>(nDiff), mnDays);
+ if (bOverflow)
+ mnDays = SAL_MIN_INT32;
+ }
+ else
+ {
+ bOverflow = (nDiff > SAL_MAX_INT32);
+ bOverflow |= o3tl::checked_add(mnDays, static_cast<sal_Int32>(nDiff), mnDays);
+ if (bOverflow)
+ mnDays = SAL_MAX_INT32;
+ }
+ assert(!bOverflow);
+ if (bOverflow)
+ {
+ nHours = Time::hourPerDay - 1;
+ nMinutes = Time::minutePerHour - 1;
+ nSeconds = Time::secondPerMinute - 1;
+ nNanoseconds = Time::nanoSecPerSec - 1;
+ }
+ }
+ maTime = Time(nHours, nMinutes, nSeconds, nNanoseconds);
+ if (bNegative)
+ maTime = -maTime;
+ assert(mnDays == 0 || maTime.GetTime() == 0 || (mnDays < 0) == (maTime.GetTime() < 0));
+}
+
+void Duration::ApplyTime(sal_Int64 nNS)
+{
+ if (mnDays > 0 && nNS < 0)
+ {
+ --mnDays;
+ nNS = Time::nanoSecPerDay + nNS;
+ }
+ else if (mnDays < 0 && nNS > 0)
+ {
+ ++mnDays;
+ nNS = -Time::nanoSecPerDay + nNS;
+ }
+ maTime.MakeTimeFromNS(nNS);
+ assert(mnDays == 0 || maTime.GetTime() == 0 || (mnDays < 0) == (maTime.GetTime() < 0));
+}
+
+void Duration::SetTimeDiff(const Time& rStart, const Time& rEnd)
+{
+ const sal_Int64 nNS = rEnd.GetNSFromTime() - rStart.GetNSFromTime();
+ ApplyTime(nNS);
+}
+
+Duration Duration::operator-() const
+{
+ Duration aD(-mnDays, -maTime.GetTime());
+ return aD;
+}
+
+Duration& Duration::Add(const Duration& rDuration, bool& rbOverflow)
+{
+ rbOverflow = o3tl::checked_add(mnDays, rDuration.mnDays, mnDays);
+ // Duration is always normalized, time values >= 24h don't occur.
+ sal_Int64 nNS = maTime.GetNSFromTime() + rDuration.maTime.GetNSFromTime();
+ if (nNS < -Time::nanoSecPerDay)
+ {
+ rbOverflow |= o3tl::checked_sub(mnDays, sal_Int32(1), mnDays);
+ nNS += Time::nanoSecPerDay;
+ }
+ else if (nNS > Time::nanoSecPerDay)
+ {
+ rbOverflow |= o3tl::checked_add(mnDays, sal_Int32(1), mnDays);
+ nNS -= Time::nanoSecPerDay;
+ }
+ ApplyTime(nNS);
+ return *this;
+}
+
+Duration Duration::Mult(sal_Int32 nMult, bool& rbOverflow) const
+{
+ // First try a simple calculation in nanoseconds.
+ bool bBadNS = false;
+ sal_Int64 nNS;
+ sal_Int64 nDays;
+ if (o3tl::checked_multiply(static_cast<sal_Int64>(mnDays), static_cast<sal_Int64>(nMult), nDays)
+ || o3tl::checked_multiply(nDays, Time::nanoSecPerDay, nDays)
+ || o3tl::checked_multiply(maTime.GetNSFromTime(), static_cast<sal_Int64>(nMult), nNS)
+ || o3tl::checked_add(nDays, nNS, nNS))
+ {
+ bBadNS = rbOverflow = true;
+ }
+ else
+ {
+ const sal_Int64 nD = nNS / Time::nanoSecPerDay;
+ if (nD < SAL_MIN_INT32 || SAL_MAX_INT32 < nD)
+ rbOverflow = true;
+ else
+ {
+ rbOverflow = false;
+ nNS -= nD * Time::nanoSecPerDay;
+ Duration aD(static_cast<sal_Int32>(nD), 0);
+ aD.ApplyTime(nNS);
+ return aD;
+ }
+ }
+ if (bBadNS)
+ {
+ // Simple calculation in overall nanoseconds overflowed, try with
+ // individual components.
+ const sal_uInt64 nMult64 = (nMult < 0) ? -nMult : nMult;
+ do
+ {
+ sal_uInt64 nN;
+ if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetNanoSec()), nMult64, nN))
+ break;
+ sal_uInt64 nS;
+ if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetSec()), nMult64, nS))
+ break;
+ sal_uInt64 nM;
+ if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetMin()), nMult64, nM))
+ break;
+ sal_uInt64 nH;
+ if (o3tl::checked_multiply(static_cast<sal_uInt64>(maTime.GetHour()), nMult64, nH))
+ break;
+ sal_uInt64 nD;
+ if (o3tl::checked_multiply(
+ mnDays < 0 ? static_cast<sal_uInt64>(-static_cast<sal_Int64>(mnDays))
+ : static_cast<sal_uInt64>(mnDays),
+ nMult64, nD))
+ break;
+ if (nN > Time::nanoSecPerSec)
+ {
+ const sal_uInt64 nC = nN / Time::nanoSecPerSec;
+ if (o3tl::checked_add(nS, nC, nS))
+ break;
+ nN -= nC * Time::nanoSecPerSec;
+ }
+ if (nS > Time::secondPerMinute)
+ {
+ const sal_uInt64 nC = nS / Time::secondPerMinute;
+ if (o3tl::checked_add(nM, nC, nM))
+ break;
+ nS -= nC * Time::secondPerMinute;
+ }
+ if (nM > Time::minutePerHour)
+ {
+ const sal_uInt64 nC = nM / Time::minutePerHour;
+ if (o3tl::checked_add(nH, nC, nH))
+ break;
+ nM -= nC * Time::minutePerHour;
+ }
+ if (nH > Time::hourPerDay)
+ {
+ const sal_uInt64 nC = nH / Time::hourPerDay;
+ if (o3tl::checked_add(nD, nC, nD))
+ break;
+ nH -= nC * Time::hourPerDay;
+ }
+ if (IsNegative() ? (static_cast<sal_uInt64>(SAL_MAX_INT32) + 1) < nD
+ || -static_cast<sal_Int64>(nD) < SAL_MIN_INT32
+ : SAL_MAX_INT32 < nD)
+ break;
+
+ rbOverflow = false;
+ Time aTime(nH, nM, nS, nN);
+ if (IsNegative() == (nMult < 0))
+ {
+ Duration aD(nD, aTime.GetTime());
+ return aD;
+ }
+ else
+ {
+ Duration aD(-static_cast<sal_Int64>(nD), -aTime.GetTime());
+ return aD;
+ }
+ } while (false);
+ }
+ assert(rbOverflow);
+ if (IsNegative() == (nMult < 0))
+ {
+ Duration aD(SAL_MAX_INT32, 0);
+ aD.ApplyTime(Time::nanoSecPerDay - 1);
+ return aD;
+ }
+ else
+ {
+ Duration aD(SAL_MIN_INT32, 0);
+ aD.ApplyTime(-(Time::nanoSecPerDay - 1));
+ return aD;
+ }
+}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/tools/source/datetime/systemdatetime.cxx b/tools/source/datetime/systemdatetime.cxx
new file mode 100644
index 0000000000..2115f4b620
--- /dev/null
+++ b/tools/source/datetime/systemdatetime.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#if defined(_WIN32)
+#if !defined WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#elif defined UNX
+#include <sys/time.h>
+#endif
+
+#include <time.h>
+#ifdef __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif
+
+#include <osl/diagnose.h>
+#include <systemdatetime.hxx>
+
+namespace
+{
+constexpr sal_Int32 ConvertYMDToInt(sal_Int32 nYear, sal_Int32 nMonth, sal_Int32 nDay)
+{
+ return (nYear * 10000) + (nMonth * 100) + nDay;
+}
+
+constexpr sal_Int64 ConvertHMSnToInt(sal_Int64 nHour, sal_Int64 nMin, sal_Int64 nSec,
+ sal_Int64 nNanoSec)
+{
+ return (nHour * HOUR_MASK) + (nMin * MIN_MASK) + (nSec * SEC_MASK) + nNanoSec;
+}
+}
+
+bool GetSystemDateTime(sal_Int32* pDate, sal_Int64* pTime)
+{
+#if defined(_WIN32)
+ SYSTEMTIME aDateTime;
+ GetLocalTime(&aDateTime);
+
+ if (pDate)
+ *pDate = ConvertYMDToInt(static_cast<sal_Int32>(aDateTime.wYear),
+ static_cast<sal_Int32>(aDateTime.wMonth),
+ static_cast<sal_Int32>(aDateTime.wDay));
+ if (pTime)
+ *pTime = ConvertHMSnToInt(aDateTime.wHour, aDateTime.wMinute, aDateTime.wSecond,
+ aDateTime.wMilliseconds * 1000000);
+
+ return true;
+#else
+ struct timespec tsTime;
+#if defined(__MACH__)
+ // macOS does not have clock_gettime, use clock_get_time
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ tsTime.tv_sec = mts.tv_sec;
+ tsTime.tv_nsec = mts.tv_nsec;
+#else
+ // CLOCK_REALTIME should be supported
+ // on any modern Unix, but be extra cautious
+ if (clock_gettime(CLOCK_REALTIME, &tsTime) != 0)
+ {
+ struct timeval tvTime;
+ OSL_VERIFY(gettimeofday(&tvTime, nullptr) != 0);
+ tsTime.tv_sec = tvTime.tv_sec;
+ tsTime.tv_nsec = tvTime.tv_usec * 1000;
+ }
+#endif
+
+ struct tm aTime;
+ time_t nTmpTime = tsTime.tv_sec;
+ if (localtime_r(&nTmpTime, &aTime))
+ {
+ if (pDate)
+ *pDate = ConvertYMDToInt(static_cast<sal_Int32>(aTime.tm_year + 1900),
+ static_cast<sal_Int32>(aTime.tm_mon + 1),
+ static_cast<sal_Int32>(aTime.tm_mday));
+ if (pTime)
+ *pTime = ConvertHMSnToInt(aTime.tm_hour, aTime.tm_min, aTime.tm_sec, tsTime.tv_nsec);
+ return true;
+ }
+
+ return false;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/tools/source/datetime/tdate.cxx b/tools/source/datetime/tdate.cxx
new file mode 100644
index 0000000000..e20add4303
--- /dev/null
+++ b/tools/source/datetime/tdate.cxx
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <tools/date.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+
+#include <systemdatetime.hxx>
+#include <comphelper/date.hxx>
+
+namespace
+{
+
+const sal_Int16 kYearMax = SAL_MAX_INT16;
+const sal_Int16 kYearMin = SAL_MIN_INT16;
+
+}
+
+void Date::setDateFromDMY( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ // don't warn about 0/0/0, commonly used as a default-value/no-value
+ SAL_WARN_IF( nYear == 0 && !(nYear == 0 && nMonth == 0 && nDay == 0),
+ "tools.datetime", "Date::setDateFromDMY - sure about 0 year? It's not in the calendar.");
+ assert( nMonth < 100 && "nMonth % 100 not representable" );
+ assert( nDay < 100 && "nDay % 100 not representable" );
+ if (nYear < 0)
+ mnDate =
+ (static_cast<sal_Int32>( nYear ) * 10000) -
+ (static_cast<sal_Int32>( nMonth % 100 ) * 100) -
+ static_cast<sal_Int32>( nDay % 100 );
+ else
+ mnDate =
+ (static_cast<sal_Int32>( nYear ) * 10000) +
+ (static_cast<sal_Int32>( nMonth % 100 ) * 100) +
+ static_cast<sal_Int32>( nDay % 100 );
+}
+
+void Date::SetDate( sal_Int32 nNewDate )
+{
+ assert( ((nNewDate / 10000) != 0) && "you don't want to set a 0 year, do you?" );
+ mnDate = nNewDate;
+}
+
+// static
+sal_uInt16 Date::GetDaysInMonth( sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ SAL_WARN_IF( nMonth < 1 || 12 < nMonth, "tools.datetime", "Date::GetDaysInMonth - nMonth out of bounds " << nMonth);
+ if (nMonth < 1)
+ nMonth = 1;
+ else if (12 < nMonth)
+ nMonth = 12;
+ return comphelper::date::getDaysInMonth( nMonth, nYear);
+}
+
+sal_Int32 Date::GetAsNormalizedDays() const
+{
+ // This is a very common datum we often calculate from.
+ if (mnDate == 18991230) // 1899-12-30
+ {
+#ifndef NDEBUG
+ static sal_Int32 nDays = DateToDays( GetDay(), GetMonth(), GetYear());
+ assert(nDays == 693594);
+#endif
+ return 693594;
+ }
+ // Not calling comphelper::date::convertDateToDaysNormalizing() here just
+ // avoids a second check on null-date handling like above.
+ sal_uInt16 nDay = GetDay();
+ sal_uInt16 nMonth = GetMonth();
+ sal_Int16 nYear = GetYear();
+ comphelper::date::normalize( nDay, nMonth, nYear);
+ return comphelper::date::convertDateToDays( nDay, nMonth, nYear);
+}
+
+sal_Int32 Date::DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ return comphelper::date::convertDateToDaysNormalizing( nDay, nMonth, nYear);
+}
+
+static Date lcl_DaysToDate( sal_Int32 nDays )
+{
+ sal_uInt16 nDay;
+ sal_uInt16 nMonth;
+ sal_Int16 nYear;
+ comphelper::date::convertDaysToDate( nDays, nDay, nMonth, nYear);
+ return Date( nDay, nMonth, nYear);
+}
+
+Date::Date( DateInitSystem )
+{
+ if ( !GetSystemDateTime( &mnDate, nullptr ) )
+ setDateFromDMY( 1, 1, 1900 );
+}
+
+Date::Date( const css::util::DateTime& rDateTime )
+{
+ setDateFromDMY( rDateTime.Day, rDateTime.Month, rDateTime.Year );
+}
+
+void Date::SetDay( sal_uInt16 nNewDay )
+{
+ setDateFromDMY( nNewDay, GetMonth(), GetYear() );
+}
+
+void Date::SetMonth( sal_uInt16 nNewMonth )
+{
+ setDateFromDMY( GetDay(), nNewMonth, GetYear() );
+}
+
+void Date::SetYear( sal_Int16 nNewYear )
+{
+ assert( nNewYear != 0 );
+ setDateFromDMY( GetDay(), GetMonth(), nNewYear );
+}
+
+void Date::AddYears( sal_Int16 nAddYears )
+{
+ sal_Int16 nYear = GetYear();
+ if (nYear < 0)
+ {
+ if (nAddYears < 0)
+ {
+ if (nYear < kYearMin - nAddYears)
+ nYear = kYearMin;
+ else
+ nYear += nAddYears;
+ }
+ else
+ {
+ nYear += nAddYears;
+ if (nYear == 0)
+ nYear = 1;
+ }
+ }
+ else
+ {
+ if (nAddYears > 0)
+ {
+ if (kYearMax - nAddYears < nYear)
+ nYear = kYearMax;
+ else
+ nYear += nAddYears;
+ }
+ else
+ {
+ nYear += nAddYears;
+ if (nYear == 0)
+ nYear = -1;
+ }
+ }
+
+ SetYear( nYear );
+ if (GetMonth() == 2 && GetDay() == 29 && !comphelper::date::isLeapYear( nYear))
+ SetDay(28);
+}
+
+void Date::AddMonths( sal_Int32 nAddMonths )
+{
+ sal_Int32 nMonths = GetMonth() + nAddMonths;
+ sal_Int32 nNewMonth = nMonths % 12;
+ sal_Int32 nYear = GetYear() + nMonths / 12;
+ if( nMonths <= 0 || nNewMonth == 0 )
+ --nYear;
+ if( nNewMonth <= 0 )
+ nNewMonth += 12;
+ if (nYear == 0)
+ nYear = (nAddMonths < 0 ? -1 : 1);
+ else if (nYear < kYearMin)
+ nYear = kYearMin;
+ else if (nYear > kYearMax)
+ nYear = kYearMax;
+ SetMonth( static_cast<sal_uInt16>(nNewMonth) );
+ SetYear( static_cast<sal_Int16>(nYear) );
+ Normalize();
+}
+
+DayOfWeek Date::GetDayOfWeek() const
+{
+ return static_cast<DayOfWeek>((GetAsNormalizedDays()-1) % 7);
+}
+
+sal_uInt16 Date::GetDayOfYear() const
+{
+ sal_uInt16 nDay = GetDay();
+ sal_uInt16 nMonth = GetMonth();
+ sal_Int16 nYear = GetYear();
+ Normalize( nDay, nMonth, nYear);
+
+ for( sal_uInt16 i = 1; i < nMonth; i++ )
+ nDay += comphelper::date::getDaysInMonth( i, nYear );
+ return nDay;
+}
+
+sal_uInt16 Date::GetWeekOfYear( DayOfWeek eStartDay,
+ sal_Int16 nMinimumNumberOfDaysInWeek ) const
+{
+ short nWeek;
+ short n1WDay = static_cast<short>(Date( 1, 1, GetYear() ).GetDayOfWeek());
+ short nDayOfYear = static_cast<short>(GetDayOfYear());
+
+ // weekdays start at 0, thus decrement one
+ nDayOfYear--;
+ // account for StartDay
+ n1WDay = (n1WDay+(7-static_cast<short>(eStartDay))) % 7;
+
+ if (nMinimumNumberOfDaysInWeek < 1 || 7 < nMinimumNumberOfDaysInWeek)
+ {
+ SAL_WARN( "tools.datetime", "Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek" );
+ nMinimumNumberOfDaysInWeek = 4;
+ }
+
+ if ( nMinimumNumberOfDaysInWeek == 1 )
+ {
+ nWeek = ((n1WDay+nDayOfYear)/7) + 1;
+ // Set to 53rd week only if we're not in the
+ // first week of the new year
+ if ( nWeek == 54 )
+ nWeek = 1;
+ else if ( nWeek == 53 )
+ {
+ short nDaysInYear = static_cast<short>(GetDaysInYear());
+ short nDaysNextYear = static_cast<short>(Date( 1, 1, GetNextYear() ).GetDayOfWeek());
+ nDaysNextYear = (nDaysNextYear+(7-static_cast<short>(eStartDay))) % 7;
+ if ( nDayOfYear > (nDaysInYear-nDaysNextYear-1) )
+ nWeek = 1;
+ }
+ }
+ else if ( nMinimumNumberOfDaysInWeek == 7 )
+ {
+ nWeek = ((n1WDay+nDayOfYear)/7);
+ // First week of a year is equal to the last week of the previous year
+ if ( nWeek == 0 )
+ {
+ Date aLastDatePrevYear( 31, 12, GetPrevYear() );
+ nWeek = aLastDatePrevYear.GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
+ }
+ }
+ else // ( nMinimumNumberOfDaysInWeek == something_else, commentary examples for 4 )
+ {
+ // x_monday - thursday
+ if ( n1WDay < nMinimumNumberOfDaysInWeek )
+ nWeek = 1;
+ // Friday
+ else if ( n1WDay == nMinimumNumberOfDaysInWeek )
+ nWeek = 53;
+ // Saturday
+ else if ( n1WDay == nMinimumNumberOfDaysInWeek + 1 )
+ {
+ // Year after leap year
+ if ( Date( 1, 1, GetPrevYear() ).IsLeapYear() )
+ nWeek = 53;
+ else
+ nWeek = 52;
+ }
+ // Sunday
+ else
+ nWeek = 52;
+
+ if ( (nWeek == 1) || (nDayOfYear + n1WDay > 6) )
+ {
+ if ( nWeek == 1 )
+ nWeek += (nDayOfYear + n1WDay) / 7;
+ else
+ nWeek = (nDayOfYear + n1WDay) / 7;
+ if ( nWeek == 53 )
+ {
+ // next x_Sunday == first x_Sunday in the new year
+ // == still the same week!
+ sal_Int32 nTempDays = GetAsNormalizedDays();
+
+ nTempDays += 6 - (GetDayOfWeek()+(7-static_cast<short>(eStartDay))) % 7;
+ nWeek = lcl_DaysToDate( nTempDays ).GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek );
+ }
+ }
+ }
+
+ return static_cast<sal_uInt16>(nWeek);
+}
+
+sal_uInt16 Date::GetDaysInMonth() const
+{
+ sal_uInt16 nDay = GetDay();
+ sal_uInt16 nMonth = GetMonth();
+ sal_Int16 nYear = GetYear();
+ Normalize( nDay, nMonth, nYear);
+
+ return comphelper::date::getDaysInMonth( nMonth, nYear );
+}
+
+bool Date::IsLeapYear() const
+{
+ sal_Int16 nYear = GetYear();
+ return comphelper::date::isLeapYear( nYear );
+}
+
+bool Date::IsValidAndGregorian() const
+{
+ sal_uInt16 nDay = GetDay();
+ sal_uInt16 nMonth = GetMonth();
+ sal_Int16 nYear = GetYear();
+
+ if ( !nMonth || (nMonth > 12) )
+ return false;
+ if ( !nDay || (nDay > comphelper::date::getDaysInMonth( nMonth, nYear )) )
+ return false;
+ else if ( nYear <= 1582 )
+ {
+ if ( nYear < 1582 )
+ return false;
+ else if ( nMonth < 10 )
+ return false;
+ else if ( (nMonth == 10) && (nDay < 15) )
+ return false;
+ }
+
+ return true;
+}
+
+bool Date::IsValidDate() const
+{
+ return comphelper::date::isValidDate( GetDay(), GetMonth(), GetYear());
+}
+
+//static
+bool Date::IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ return comphelper::date::isValidDate( nDay, nMonth, nYear);
+}
+
+bool Date::IsEndOfMonth() const
+{
+ return IsEndOfMonth(GetDay(), GetMonth(), GetYear());
+}
+
+//static
+bool Date::IsEndOfMonth(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
+{
+ return comphelper::date::isValidDate(nDay, nMonth, nYear)
+ && comphelper::date::getDaysInMonth(nMonth, nYear) == nDay;
+}
+
+void Date::Normalize()
+{
+ sal_uInt16 nDay = GetDay();
+ sal_uInt16 nMonth = GetMonth();
+ sal_Int16 nYear = GetYear();
+
+ if (!Normalize( nDay, nMonth, nYear))
+ return;
+
+ setDateFromDMY( nDay, nMonth, nYear );
+}
+
+//static
+bool Date::Normalize( sal_uInt16 & rDay, sal_uInt16 & rMonth, sal_Int16 & rYear )
+{
+ return comphelper::date::normalize( rDay, rMonth, rYear);
+}
+
+void Date::AddDays( sal_Int32 nDays )
+{
+ if (nDays != 0)
+ *this = lcl_DaysToDate( GetAsNormalizedDays() + nDays );
+}
+
+Date& Date::operator ++()
+{
+ *this = lcl_DaysToDate( GetAsNormalizedDays() + 1 );
+ return *this;
+}
+
+Date& Date::operator --()
+{
+ *this = lcl_DaysToDate( GetAsNormalizedDays() - 1 );
+ return *this;
+}
+
+Date operator +( const Date& rDate, sal_Int32 nDays )
+{
+ Date aDate( rDate );
+ aDate.AddDays( nDays );
+ return aDate;
+}
+
+Date operator -( const Date& rDate, sal_Int32 nDays )
+{
+ Date aDate( rDate );
+ aDate.AddDays( -nDays );
+ return aDate;
+}
+
+sal_Int32 operator -( const Date& rDate1, const Date& rDate2 )
+{
+ return rDate1.GetAsNormalizedDays() - rDate2.GetAsNormalizedDays();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/tools/source/datetime/ttime.cxx b/tools/source/datetime/ttime.cxx
new file mode 100644
index 0000000000..fcfa2e080e
--- /dev/null
+++ b/tools/source/datetime/ttime.cxx
@@ -0,0 +1,506 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#if defined(_WIN32)
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <mmsystem.h>
+#elif defined UNX
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <time.h>
+#ifdef __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif
+
+#include <rtl/math.hxx>
+#include <tools/time.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+
+#include <systemdatetime.hxx>
+
+#if defined(__sun) && defined(__GNUC__)
+extern long altzone;
+#endif
+
+namespace {
+
+ const sal_Int64 nanoSecInSec = 1000000000;
+ const sal_Int16 secInMin = 60;
+ const sal_Int16 minInHour = 60;
+
+ sal_Int64 TimeToNanoSec( const tools::Time& rTime )
+ {
+ short nSign = (rTime.GetTime() >= 0) ? +1 : -1;
+ sal_Int32 nHour = rTime.GetHour();
+ sal_Int32 nMin = rTime.GetMin();
+ sal_Int32 nSec = rTime.GetSec();
+ sal_Int32 nNanoSec = rTime.GetNanoSec();
+
+ sal_Int64 nRet = nNanoSec;
+ nRet += nSec * nanoSecInSec;
+ nRet += nMin * secInMin * nanoSecInSec;
+ nRet += nHour * minInHour * secInMin * nanoSecInSec;
+
+ return (nRet * nSign);
+ }
+
+ tools::Time NanoSecToTime( sal_Int64 nNanoSec )
+ {
+ short nSign;
+ if ( nNanoSec < 0 )
+ {
+ nNanoSec *= -1;
+ nSign = -1;
+ }
+ else
+ nSign = 1;
+
+ tools::Time aTime( 0, 0, 0, nNanoSec );
+ aTime.SetTime( aTime.GetTime() * nSign );
+ return aTime;
+ }
+
+} // anonymous namespace
+
+namespace tools {
+
+Time::Time( TimeInitSystem )
+{
+ if ( !GetSystemDateTime( nullptr, &nTime ) )
+ nTime = 0;
+}
+
+Time::Time( const tools::Time& rTime )
+{
+ nTime = rTime.nTime;
+}
+
+Time::Time( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
+{
+ init(nHour, nMin, nSec, nNanoSec);
+}
+Time::Time( const css::util::Time &_rTime )
+{
+ init(_rTime.Hours, _rTime.Minutes, _rTime.Seconds, _rTime.NanoSeconds);
+}
+Time::Time( const css::util::DateTime &_rDateTime )
+{
+ init(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds);
+}
+
+void tools::Time::init( sal_uInt32 nHour, sal_uInt32 nMin, sal_uInt32 nSec, sal_uInt64 nNanoSec )
+{
+ // normalize time
+ nSec += nNanoSec / nanoSecInSec;
+ nNanoSec %= nanoSecInSec;
+ nMin += nSec / secInMin;
+ nSec %= secInMin;
+ nHour += nMin / minInHour;
+ nMin %= minInHour;
+
+ // 922337 * HOUR_MASK = 9223370000000000000 largest possible value, 922338
+ // would be -9223364073709551616.
+ assert(HOUR_MASK * nHour >= 0 && "use tools::Duration with days instead!");
+ if (HOUR_MASK * nHour < 0)
+ nHour = 922337;
+
+ // But as is, GetHour() retrieves only sal_uInt16. Though retrieving in
+ // nanoseconds or milliseconds might be possible this is all crap.
+ assert(nHour <= SAL_MAX_UINT16 && "use tools::Duration with days instead!");
+ if (nHour > SAL_MAX_UINT16)
+ nHour = SAL_MAX_UINT16;
+
+ // construct time
+ nTime = nNanoSec +
+ nSec * SEC_MASK +
+ nMin * MIN_MASK +
+ nHour * HOUR_MASK;
+}
+
+void tools::Time::SetHour( sal_uInt16 nNewHour )
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ sal_Int32 nMin = GetMin();
+ sal_Int32 nSec = GetSec();
+ sal_Int32 nNanoSec = GetNanoSec();
+
+ nTime = nSign *
+ ( nNanoSec +
+ nSec * SEC_MASK +
+ nMin * MIN_MASK +
+ nNewHour * HOUR_MASK );
+}
+
+void tools::Time::SetMin( sal_uInt16 nNewMin )
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ sal_Int32 nHour = GetHour();
+ sal_Int32 nSec = GetSec();
+ sal_Int32 nNanoSec = GetNanoSec();
+
+ // no overflow
+ nNewMin = nNewMin % minInHour;
+
+ nTime = nSign *
+ ( nNanoSec +
+ nSec * SEC_MASK +
+ nNewMin * MIN_MASK +
+ nHour * HOUR_MASK );
+}
+
+void tools::Time::SetSec( sal_uInt16 nNewSec )
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ sal_Int32 nHour = GetHour();
+ sal_Int32 nMin = GetMin();
+ sal_Int32 nNanoSec = GetNanoSec();
+
+ // no overflow
+ nNewSec = nNewSec % secInMin;
+
+ nTime = nSign *
+ ( nNanoSec +
+ nNewSec * SEC_MASK +
+ nMin * MIN_MASK +
+ nHour * HOUR_MASK );
+}
+
+void tools::Time::SetNanoSec( sal_uInt32 nNewNanoSec )
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ sal_Int32 nHour = GetHour();
+ sal_Int32 nMin = GetMin();
+ sal_Int32 nSec = GetSec();
+
+ // no overflow
+ nNewNanoSec = nNewNanoSec % nanoSecInSec;
+
+ nTime = nSign *
+ ( nNewNanoSec +
+ nSec * SEC_MASK +
+ nMin * MIN_MASK +
+ nHour * HOUR_MASK );
+}
+
+sal_Int64 tools::Time::GetNSFromTime() const
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ sal_Int32 nHour = GetHour();
+ sal_Int32 nMin = GetMin();
+ sal_Int32 nSec = GetSec();
+ sal_Int32 nNanoSec = GetNanoSec();
+
+ return nSign *
+ ( nNanoSec +
+ nSec * nanoSecInSec +
+ nMin * (secInMin * nanoSecInSec) +
+ nHour * (minInHour * secInMin * nanoSecInSec) );
+}
+
+void tools::Time::MakeTimeFromNS( sal_Int64 nNS )
+{
+ short nSign;
+ if ( nNS < 0 )
+ {
+ nNS *= -1;
+ nSign = -1;
+ }
+ else
+ nSign = 1;
+
+ // avoid overflow when sal_uIntPtr is 32 bits
+ tools::Time aTime( 0, 0, nNS/nanoSecInSec, nNS % nanoSecInSec );
+ SetTime( aTime.GetTime() * nSign );
+}
+
+sal_Int32 tools::Time::GetMSFromTime() const
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ sal_Int32 nHour = GetHour();
+ sal_Int32 nMin = GetMin();
+ sal_Int32 nSec = GetSec();
+ sal_Int32 nNanoSec = GetNanoSec();
+
+ return nSign *
+ ( nNanoSec/1000000 +
+ nSec * 1000 +
+ nMin * 60000 +
+ nHour * 3600000 );
+}
+
+void tools::Time::MakeTimeFromMS( sal_Int32 nMS )
+{
+ short nSign;
+ if ( nMS < 0 )
+ {
+ nMS *= -1;
+ nSign = -1;
+ }
+ else
+ nSign = 1;
+
+ // avoid overflow when sal_uIntPtr is 32 bits
+ tools::Time aTime( 0, 0, nMS/1000, (nMS % 1000) * 1000000 );
+ SetTime( aTime.GetTime() * nSign );
+}
+
+double tools::Time::GetTimeInDays() const
+{
+ short nSign = (nTime >= 0) ? +1 : -1;
+ double nHour = GetHour();
+ double nMin = GetMin();
+ double nSec = GetSec();
+ double nNanoSec = GetNanoSec();
+
+ return (nHour + (nMin / 60) + (nSec / (minInHour * secInMin)) + (nNanoSec / (minInHour * secInMin * nanoSecInSec))) / 24 * nSign;
+}
+
+// static
+void tools::Time::GetClock( double fTimeInDays,
+ sal_uInt16& nHour, sal_uInt16& nMinute, sal_uInt16& nSecond,
+ double& fFractionOfSecond, int nFractionDecimals )
+{
+ const double fTime = fTimeInDays - rtl::math::approxFloor(fTimeInDays); // date part absent
+
+ // If 0 then full day (or no day), shortcut.
+ // If < 0 then approxFloor() effectively returned the ceiling (note this
+ // also holds for negative fTimeInDays values) because of a near identical
+ // value, shortcut this to a full day as well.
+ // If >= 1.0 (actually == 1.0) then fTimeInDays is a negative small value
+ // not significant for a representable time and approxFloor() returned -1,
+ // shortcut to 0:0:0, otherwise it would become 24:0:0.
+ if (fTime <= 0.0 || fTime >= 1.0)
+ {
+ nHour = nMinute = nSecond = 0;
+ fFractionOfSecond = 0.0;
+ return;
+ }
+
+ // In seconds, including milli and nano.
+ const double fRawSeconds = fTime * tools::Time::secondPerDay;
+
+ // Round to nanoseconds most, which is the highest resolution this could be
+ // influenced by, but if the original value included a date round to at
+ // most 14 significant digits (including adding 4 for *86400), otherwise we
+ // might end up with a fake precision of h:m:s.999999986 which in fact
+ // should had been h:m:s+1
+ // BUT, leave at least 2 decimals to round. Which shouldn't be a problem in
+ // practice because class Date can calculate only 8-digit days for it's
+ // sal_Int16 year range, which exactly leaves us with 14-4-8=2.
+ int nDec = 9;
+ const double fAbsTimeInDays = fabs( fTimeInDays);
+ if (fAbsTimeInDays >= 1.0)
+ {
+ const int nDig = static_cast<int>(ceil( log10( fAbsTimeInDays)));
+ nDec = std::clamp( 10 - nDig, 2, 9 );
+ }
+ double fSeconds = rtl::math::round( fRawSeconds, nDec);
+
+ // If this ended up as a full day the original value was very very close
+ // but not quite. Take that.
+ if (fSeconds >= tools::Time::secondPerDay)
+ fSeconds = fRawSeconds;
+
+ // Now do not round values (specifically not up), but truncate to the next
+ // magnitude, so 23:59:59.99 is still 23:59:59 and not 24:00:00 (or even
+ // 00:00:00 which Excel does).
+ nHour = fSeconds / tools::Time::secondPerHour;
+ fSeconds -= nHour * tools::Time::secondPerHour;
+ nMinute = fSeconds / tools::Time::secondPerMinute;
+ fSeconds -= nMinute * tools::Time::secondPerMinute;
+ nSecond = fSeconds;
+ fSeconds -= nSecond;
+
+ assert(fSeconds < 1.0); // or back to the drawing board...
+
+ if (nFractionDecimals > 0)
+ {
+ // Do not simply round the fraction, otherwise .999 would end up as .00
+ // again. Truncate instead if rounding would round up into an integer
+ // value.
+ fFractionOfSecond = rtl::math::round( fSeconds, nFractionDecimals);
+ if (fFractionOfSecond >= 1.0)
+ fFractionOfSecond = rtl::math::pow10Exp( std::trunc(
+ rtl::math::pow10Exp( fSeconds, nFractionDecimals)), -nFractionDecimals);
+ }
+ else
+ fFractionOfSecond = fSeconds;
+}
+
+Time& tools::Time::operator =( const tools::Time& rTime )
+{
+ nTime = rTime.nTime;
+ return *this;
+}
+
+Time& tools::Time::operator +=( const tools::Time& rTime )
+{
+ nTime = NanoSecToTime( TimeToNanoSec( *this ) +
+ TimeToNanoSec( rTime ) ).GetTime();
+ return *this;
+}
+
+Time& tools::Time::operator -=( const tools::Time& rTime )
+{
+ nTime = NanoSecToTime( TimeToNanoSec( *this ) -
+ TimeToNanoSec( rTime ) ).GetTime();
+ return *this;
+}
+
+Time operator +( const tools::Time& rTime1, const tools::Time& rTime2 )
+{
+ return NanoSecToTime( TimeToNanoSec( rTime1 ) +
+ TimeToNanoSec( rTime2 ) );
+}
+
+Time operator -( const tools::Time& rTime1, const tools::Time& rTime2 )
+{
+ return NanoSecToTime( TimeToNanoSec( rTime1 ) -
+ TimeToNanoSec( rTime2 ) );
+}
+
+bool tools::Time::IsEqualIgnoreNanoSec( const tools::Time& rTime ) const
+{
+ sal_Int32 n1 = (nTime < 0 ? -static_cast<sal_Int32>(GetNanoSec()) : GetNanoSec() );
+ sal_Int32 n2 = (rTime.nTime < 0 ? -static_cast<sal_Int32>(rTime.GetNanoSec()) : rTime.GetNanoSec() );
+ return (nTime - n1) == (rTime.nTime - n2);
+}
+
+Time tools::Time::GetUTCOffset()
+{
+#if defined(_WIN32)
+ TIME_ZONE_INFORMATION aTimeZone;
+ aTimeZone.Bias = 0;
+ DWORD nTimeZoneRet = GetTimeZoneInformation( &aTimeZone );
+ sal_Int32 nTempTime = aTimeZone.Bias;
+ if ( nTimeZoneRet == TIME_ZONE_ID_STANDARD )
+ nTempTime += aTimeZone.StandardBias;
+ else if ( nTimeZoneRet == TIME_ZONE_ID_DAYLIGHT )
+ nTempTime += aTimeZone.DaylightBias;
+ tools::Time aTime( 0, static_cast<sal_uInt16>(abs( nTempTime )) );
+ if ( nTempTime > 0 )
+ aTime = -aTime;
+ return aTime;
+#else
+ static sal_uInt64 nCacheTicks = 0;
+ static sal_Int32 nCacheSecOffset = -1;
+ sal_uInt64 nTicks = tools::Time::GetSystemTicks();
+ time_t nTime;
+ tm aTM;
+ short nTempTime;
+
+ // determine value again if needed
+ if ( (nCacheSecOffset == -1) ||
+ ((nTicks - nCacheTicks) > 360000) ||
+ ( nTicks < nCacheTicks ) // handle overflow
+ )
+ {
+ nTime = time( nullptr );
+ localtime_r( &nTime, &aTM );
+ auto nLocalTime = mktime( &aTM );
+#if defined(__sun)
+ // Solaris gmtime_r() seems not to handle daylight saving time
+ // flags correctly
+ auto nUTC = nLocalTime + ( aTM.tm_isdst == 0 ? timezone : altzone );
+#elif defined( LINUX )
+ // Linux mktime() seems not to handle tm_isdst correctly
+ auto nUTC = nLocalTime - aTM.tm_gmtoff;
+#else
+ gmtime_r( &nTime, &aTM );
+ auto nUTC = mktime( &aTM );
+#endif
+ nCacheTicks = nTicks;
+ nCacheSecOffset = (nLocalTime-nUTC) / 60;
+ }
+
+ nTempTime = abs( nCacheSecOffset );
+ tools::Time aTime( 0, static_cast<sal_uInt16>(nTempTime) );
+ if ( nCacheSecOffset < 0 )
+ aTime = -aTime;
+ return aTime;
+#endif
+}
+
+sal_uInt64 tools::Time::GetSystemTicks()
+{
+ return tools::Time::GetMonotonicTicks() / 1000;
+}
+
+#ifdef _WIN32
+static LARGE_INTEGER initPerformanceFrequency()
+{
+ LARGE_INTEGER nTicksPerSecond = { 0, 0 };
+ if (!QueryPerformanceFrequency(&nTicksPerSecond))
+ nTicksPerSecond.QuadPart = 0;
+ return nTicksPerSecond;
+}
+#endif
+
+sal_uInt64 tools::Time::GetMonotonicTicks()
+{
+#ifdef _WIN32
+ static const LARGE_INTEGER nTicksPerSecond = initPerformanceFrequency();
+ if (nTicksPerSecond.QuadPart > 0)
+ {
+ LARGE_INTEGER nPerformanceCount;
+ QueryPerformanceCounter(&nPerformanceCount);
+ return static_cast<sal_uInt64>(
+ ( nPerformanceCount.QuadPart * 1000 * 1000 ) / nTicksPerSecond.QuadPart );
+ }
+ else
+ {
+ return static_cast<sal_uInt64>( timeGetTime() * 1000 );
+ }
+#else
+ sal_uInt64 nMicroSeconds;
+#ifdef __MACH__
+ static mach_timebase_info_data_t info = { 0, 0 };
+ if ( 0 == info.numer )
+ mach_timebase_info( &info );
+ nMicroSeconds = mach_absolute_time() * static_cast<double>(info.numer / info.denom) / 1000;
+#else
+#if defined(_POSIX_TIMERS)
+ struct timespec currentTime;
+ clock_gettime( CLOCK_MONOTONIC, &currentTime );
+ nMicroSeconds
+ = static_cast<sal_uInt64>(currentTime.tv_sec) * 1000 * 1000 + currentTime.tv_nsec / 1000;
+#else
+ struct timeval currentTime;
+ gettimeofday( &currentTime, nullptr );
+ nMicroSeconds = static_cast<sal_uInt64>(currentTime.tv_sec) * 1000 * 1000 + currentTime.tv_usec;
+#endif
+#endif // __MACH__
+ return nMicroSeconds;
+#endif // _WIN32
+}
+
+} /* namespace tools */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */