summaryrefslogtreecommitdiffstats
path: root/tools/source/datetime
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /tools/source/datetime
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/source/datetime')
-rw-r--r--tools/source/datetime/datetime.cxx306
-rw-r--r--tools/source/datetime/datetimeutils.cxx82
-rw-r--r--tools/source/datetime/systemdatetime.cxx107
-rw-r--r--tools/source/datetime/tdate.cxx597
-rw-r--r--tools/source/datetime/ttime.cxx494
5 files changed, 1586 insertions, 0 deletions
diff --git a/tools/source/datetime/datetime.cxx b/tools/source/datetime/datetime.cxx
new file mode 100644
index 000000000..00790ff78
--- /dev/null
+++ b/tools/source/datetime/datetime.cxx
@@ -0,0 +1,306 @@
+/* -*- 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 <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;
+ }
+}
+
+DateTime& DateTime::operator +=( const tools::Time& rTime )
+{
+ tools::Time aTime = *this;
+ aTime += rTime;
+ sal_uInt16 nHours = aTime.GetHour();
+ if ( aTime.GetTime() > 0 )
+ {
+ while ( nHours >= 24 )
+ {
+ Date::operator++();
+ nHours -= 24;
+ }
+ aTime.SetHour( nHours );
+ }
+ else if ( aTime.GetTime() != 0 )
+ {
+ while ( nHours >= 24 )
+ {
+ Date::operator--();
+ nHours -= 24;
+ }
+ Date::operator--();
+ aTime = Time( 24, 0, 0 )+aTime;
+ }
+ tools::Time::operator=( aTime );
+
+ return *this;
+}
+
+DateTime& DateTime::operator -=( const tools::Time& rTime )
+{
+ tools::Time aTime = *this;
+ aTime -= rTime;
+ sal_uInt16 nHours = aTime.GetHour();
+ if ( aTime.GetTime() > 0 )
+ {
+ while ( nHours >= 24 )
+ {
+ Date::operator++();
+ nHours -= 24;
+ }
+ aTime.SetHour( nHours );
+ }
+ else if ( aTime.GetTime() != 0 )
+ {
+ while ( nHours >= 24 )
+ {
+ Date::operator--();
+ nHours -= 24;
+ }
+ Date::operator--();
+ aTime = Time( 24, 0, 0 )+aTime;
+ }
+ tools::Time::operator=( aTime );
+
+ 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;
+}
+
+void DateTime::AddTime( double fTimeInDays )
+{
+ 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;
+ }
+ AddDays( sal_Int32(fInt) ); // full days
+ if ( fFrac )
+ {
+ tools::Time aTime(0); // default ctor calls system time, we don't need that
+ fFrac *= ::tools::Time::nanoSecPerDay; // time expressed in nanoseconds
+ aTime.MakeTimeFromNS( static_cast<sal_Int64>(fFrac) ); // method handles negative ns
+ operator+=( aTime );
+ }
+}
+
+DateTime operator +( const DateTime& rDateTime, double fTimeInDays )
+{
+ DateTime aDateTime( rDateTime );
+ aDateTime.AddTime( fTimeInDays );
+ return aDateTime;
+}
+
+double operator -( const DateTime& rDateTime1, const DateTime& rDateTime2 )
+{
+ sal_Int32 nDays = static_cast<const Date&>(rDateTime1)
+ - static_cast<const Date&>(rDateTime2);
+ sal_Int64 nTime = rDateTime1.GetNSFromTime() - rDateTime2.GetNSFromTime();
+ if ( nTime )
+ {
+ double fTime = double(nTime);
+ fTime /= ::tools::Time::nanoSecPerDay; // convert from nanoseconds to fraction
+ if ( nDays < 0 && fTime > 0.0 )
+ fTime = 1.0 - fTime;
+ return double(nDays) + fTime;
+ }
+ return double(nDays);
+}
+
+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 000000000..4c3b28d49
--- /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/systemdatetime.cxx b/tools/source/datetime/systemdatetime.cxx
new file mode 100644
index 000000000..2115f4b62
--- /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 000000000..979611333
--- /dev/null
+++ b/tools/source/datetime/tdate.cxx
@@ -0,0 +1,597 @@
+/* -*- 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>
+
+const sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31 };
+
+// Once upon a time the number of days we internally handled was limited to
+// MAX_DAYS 3636532. That changed with a full 16-bit year.
+// Assuming the first valid positive date in a proleptic Gregorian calendar is
+// 0001-01-01, this resulted in an end date of 9957-06-26.
+// Hence we documented that years up to and including 9956 are handled.
+/* XXX: it is unclear history why this value was chosen, the representable
+ * 9999-12-31 would be 3652060 days from 0001-01-01. Even 9998-12-31 to
+ * distinguish from a maximum possible date would be 3651695.
+ * There is connectivity/source/commontools/dbconversion.cxx that still has the
+ * same value to calculate with css::util::Date */
+/* XXX can that dbconversion cope with years > 9999 or negative years at all?
+ * Database fields may be limited to positive 4 digits. */
+
+const sal_Int32 MIN_DAYS = -11968265; // -32768-01-01
+const sal_Int32 MAX_DAYS = 11967900; // 32767-12-31
+
+namespace
+{
+
+const sal_Int16 kYearMax = SAL_MAX_INT16;
+const sal_Int16 kYearMin = SAL_MIN_INT16;
+
+// Days until start of year from zero, so month and day of month can be added.
+// year 1 => 0 days, year 2 => 365 days, ...
+// year -1 => -366 days, year -2 => -731 days, ...
+sal_Int32 ImpYearToDays( sal_Int16 nYear )
+{
+ assert( nYear != 0 );
+ sal_Int32 nOffset;
+ sal_Int32 nYr;
+ if (nYear < 0)
+ {
+ nOffset = -366;
+ nYr = nYear + 1;
+ }
+ else
+ {
+ nOffset = 0;
+ nYr = nYear - 1;
+ }
+ return nOffset + nYr*365 + nYr/4 - nYr/100 + nYr/400;
+}
+
+bool ImpIsLeapYear( sal_Int16 nYear )
+{
+ // Leap years BCE are -1, -5, -9, ...
+ // See
+ // https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar#Usage
+ // https://en.wikipedia.org/wiki/0_(year)#History_of_astronomical_usage
+ assert( nYear != 0 );
+ if (nYear < 0)
+ nYear = -nYear - 1;
+ return ( ( ((nYear % 4) == 0) && ((nYear % 100) != 0) ) ||
+ ( (nYear % 400) == 0 ) );
+}
+
+// All callers must have sanitized or normalized month and year values!
+sal_uInt16 ImplDaysInMonth( sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ if ( nMonth != 2 )
+ return aDaysInMonth[nMonth-1];
+ else
+ {
+ if (ImpIsLeapYear(nYear))
+ return aDaysInMonth[nMonth-1] + 1;
+ else
+ return aDaysInMonth[nMonth-1];
+ }
+}
+
+}
+
+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 ImplDaysInMonth( nMonth, nYear);
+}
+
+sal_Int32 Date::GetAsNormalizedDays() const
+{
+ // This is a very common datum we often calculate from.
+ if (mnDate == 18991230) // 1899-12-30
+ {
+ assert(DateToDays( GetDay(), GetMonth(), GetYear() ) == 693594);
+ return 693594;
+ }
+ return DateToDays( GetDay(), GetMonth(), GetYear() );
+}
+
+sal_Int32 Date::DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ Normalize( nDay, nMonth, nYear);
+
+ sal_Int32 nDays = ImpYearToDays(nYear);
+ for( sal_uInt16 i = 1; i < nMonth; i++ )
+ nDays += ImplDaysInMonth(i,nYear);
+ nDays += nDay;
+ return nDays;
+}
+
+static Date lcl_DaysToDate( sal_Int32 nDays )
+{
+ if ( nDays <= MIN_DAYS )
+ return Date( 1, 1, kYearMin );
+ if ( nDays >= MAX_DAYS )
+ return Date( 31, 12, kYearMax );
+
+ // Day 0 is -0001-12-31, day 1 is 0001-01-01
+ const sal_Int16 nSign = (nDays <= 0 ? -1 : 1);
+ sal_Int32 nTempDays;
+ sal_Int32 i = 0;
+ bool bCalc;
+
+ sal_Int16 nYear;
+ do
+ {
+ nYear = static_cast<sal_Int16>((nDays / 365) - (i * nSign));
+ if (nYear == 0)
+ nYear = nSign;
+ nTempDays = nDays - ImpYearToDays(nYear);
+ bCalc = false;
+ if ( nTempDays < 1 )
+ {
+ i += nSign;
+ bCalc = true;
+ }
+ else
+ {
+ if ( nTempDays > 365 )
+ {
+ if ( (nTempDays != 366) || !ImpIsLeapYear( nYear ) )
+ {
+ i -= nSign;
+ bCalc = true;
+ }
+ }
+ }
+ }
+ while ( bCalc );
+
+ sal_uInt16 nMonth = 1;
+ while ( nTempDays > ImplDaysInMonth( nMonth, nYear ) )
+ {
+ nTempDays -= ImplDaysInMonth( nMonth, nYear );
+ ++nMonth;
+ }
+
+ return Date( static_cast<sal_uInt16>(nTempDays), 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 && !ImpIsLeapYear( 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 += ::ImplDaysInMonth( 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 ImplDaysInMonth( nMonth, nYear );
+}
+
+bool Date::IsLeapYear() const
+{
+ sal_Int16 nYear = GetYear();
+ return ImpIsLeapYear( 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 > ImplDaysInMonth( 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 IsValidDate( GetDay(), GetMonth(), GetYear());
+}
+
+//static
+bool Date::IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
+{
+ if (nYear == 0)
+ return false;
+ if ( !nMonth || (nMonth > 12) )
+ return false;
+ if ( !nDay || (nDay > ImplDaysInMonth( nMonth, nYear )) )
+ return false;
+ return true;
+}
+
+bool Date::IsEndOfMonth() const
+{
+ return IsEndOfMonth(GetDay(), GetMonth(), GetYear());
+}
+
+//static
+bool Date::IsEndOfMonth(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
+{
+ return IsValidDate(nDay, nMonth, nYear) && ImplDaysInMonth(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 )
+{
+ if (IsValidDate( rDay, rMonth, rYear))
+ return false;
+
+ if (rDay == 0 && rMonth == 0 && rYear == 0)
+ return false; // empty date
+
+ if (rDay == 0)
+ {
+ if (rMonth == 0)
+ ; // nothing, handled below
+ else
+ --rMonth;
+ // Last day of month is determined at the end.
+ }
+
+ if (rMonth > 12)
+ {
+ rYear += rMonth / 12;
+ rMonth = rMonth % 12;
+ if (rYear == 0)
+ rYear = 1;
+ }
+ if (rMonth == 0)
+ {
+ --rYear;
+ if (rYear == 0)
+ rYear = -1;
+ rMonth = 12;
+ }
+
+ if (rYear < 0)
+ {
+ sal_uInt16 nDays;
+ while (rDay > (nDays = ImplDaysInMonth( rMonth, rYear)))
+ {
+ rDay -= nDays;
+ if (rMonth > 1)
+ --rMonth;
+ else
+ {
+ if (rYear == kYearMin)
+ {
+ rDay = 1;
+ rMonth = 1;
+ return true;
+ }
+ --rYear;
+ rMonth = 12;
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nDays;
+ while (rDay > (nDays = ImplDaysInMonth( rMonth, rYear)))
+ {
+ rDay -= nDays;
+ if (rMonth < 12)
+ ++rMonth;
+ else
+ {
+ if (rYear == kYearMax)
+ {
+ rDay = 31;
+ rMonth = 12;
+ return true;
+ }
+ ++rYear;
+ rMonth = 1;
+ }
+ }
+ }
+
+ if (rDay == 0)
+ rDay = ImplDaysInMonth( rMonth, rYear);
+
+ return true;
+}
+
+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 000000000..ee9e42720
--- /dev/null
+++ b/tools/source/datetime/ttime.cxx
@@ -0,0 +1,494 @@
+/* -*- 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;
+
+ // 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 );
+ sal_Int32 nLocalTime = mktime( &aTM );
+#if defined(__sun)
+ // Solaris gmtime_r() seems not to handle daylight saving time
+ // flags correctly
+ nUTC = nLocalTime + ( aTM.tm_isdst == 0 ? timezone : altzone );
+#elif defined( LINUX )
+ // Linux mktime() seems not to handle tm_isdst correctly
+ sal_Int32 nUTC = nLocalTime - aTM.tm_gmtoff;
+#else
+ gmtime_r( &nTime, &aTM );
+ sal_Int32 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: */