diff options
Diffstat (limited to 'connectivity/source/commontools/dbconversion.cxx')
-rw-r--r-- | connectivity/source/commontools/dbconversion.cxx | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/connectivity/source/commontools/dbconversion.cxx b/connectivity/source/commontools/dbconversion.cxx new file mode 100644 index 000000000..af80efb60 --- /dev/null +++ b/connectivity/source/commontools/dbconversion.cxx @@ -0,0 +1,463 @@ +/* -*- 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 <connectivity/dbconversion.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <rtl/character.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <unotools/datetime.hxx> +#include <o3tl/string_view.hxx> +#include <sstream> +#include <iomanip> + +namespace +{ + const sal_Int64 nanoSecInSec = 1000000000; + const sal_Int16 secInMin = 60; + const sal_Int16 minInHour = 60; + + const sal_Int64 secMask = 1000000000; + const sal_Int64 minMask = 100000000000LL; + const sal_Int64 hourMask = 10000000000000LL; + + const double fNanoSecondsPerDay = nanoSecInSec * secInMin * minInHour * 24.0; + + // 32767-12-31 in "(days since 0001-01-01) + 1" format + const sal_Int32 maxDays = 11967896; + // -32768-01-01 in "(days since 0001-01-01) + 1" format + // Yes, I know it is currently unused. Will have to be used + // when we implement negative years. Writing down the correct + // value for future reference. + // *** Please don't remove just because it is unused *** + // Lionel Élie Mamane 2017-08-02 + // const sal_Int32 minDays = -11968270; +} + + +namespace dbtools +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + + + css::util::Date const & DBTypeConversion::getStandardDate() + { + static css::util::Date STANDARD_DB_DATE(1,1,1900); + return STANDARD_DB_DATE; + } + + OUString DBTypeConversion::toDateString(const css::util::Date& rDate) + { + std::ostringstream ostr; + using std::setw; + ostr.fill('0'); + ostr << setw(4) << rDate.Year << "-" + << setw(2) << rDate.Month << "-" + << setw(2) << rDate.Day; + return OUString::createFromAscii(ostr.str().c_str()); + } + + OUString DBTypeConversion::toTimeStringS(const css::util::Time& rTime) + { + std::ostringstream ostr; + using std::setw; + ostr.fill('0'); + ostr << setw(2) << rTime.Hours << ":" + << setw(2) << rTime.Minutes << ":" + << setw(2) << rTime.Seconds; + return OUString::createFromAscii(ostr.str().c_str()); + } + + OUString DBTypeConversion::toTimeString(const css::util::Time& rTime) + { + std::ostringstream ostr; + using std::setw; + ostr.fill('0'); + ostr << setw(2) << rTime.Hours << ":" + << setw(2) << rTime.Minutes << ":" + << setw(2) << rTime.Seconds << "." + << setw(9) << rTime.NanoSeconds; + return OUString::createFromAscii(ostr.str().c_str()); + } + + OUString DBTypeConversion::toDateTimeString(const css::util::DateTime& _rDateTime) + { + css::util::Date aDate(_rDateTime.Day,_rDateTime.Month,_rDateTime.Year); + css::util::Time const aTime(_rDateTime.NanoSeconds, _rDateTime.Seconds, + _rDateTime.Minutes, _rDateTime.Hours, _rDateTime.IsUTC); + return toDateString(aDate) + " " + toTimeString(aTime); + } + + css::util::Date DBTypeConversion::toDate(const sal_Int32 _nVal) + { + css::util::Date aReturn; + aReturn.Day = static_cast<sal_uInt16>(_nVal % 100); + aReturn.Month = static_cast<sal_uInt16>((_nVal / 100) % 100); + aReturn.Year = static_cast<sal_uInt16>(_nVal / 10000); + return aReturn; + } + + + css::util::Time DBTypeConversion::toTime(const sal_Int64 _nVal) + { + css::util::Time aReturn; + sal_uInt64 unVal = static_cast<sal_uInt64>(_nVal >= 0 ? _nVal : -_nVal); + aReturn.Hours = unVal / hourMask; + aReturn.Minutes = (unVal / minMask) % 100; + aReturn.Seconds = (unVal / secMask) % 100; + aReturn.NanoSeconds = unVal % secMask; + return aReturn; + } + + sal_Int64 DBTypeConversion::getNsFromTime(const css::util::Time& rVal) + { + sal_Int32 nHour = rVal.Hours; + sal_Int32 nMin = rVal.Minutes; + sal_Int32 nSec = rVal.Seconds; + sal_Int32 nNanoSec = rVal.NanoSeconds; + + return nNanoSec + + nSec * nanoSecInSec + + nMin * (secInMin * nanoSecInSec) + + nHour * (minInHour * secInMin * nanoSecInSec); + } + + + const sal_Int32 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 }; + + + static bool implIsLeapYear(sal_Int32 _nYear) + { + return ( ((_nYear % 4) == 0) + && ((_nYear % 100) != 0) + ) + + || ((_nYear % 400) == 0) + ; + } + + static sal_Int32 implDaysInMonth(sal_Int32 _nMonth, sal_Int32 _nYear) + { + SAL_WARN_IF(_nMonth < 1 || _nMonth > 12, "connectivity.commontools", "Month has invalid value: " << _nMonth); + if (_nMonth < 1) + _nMonth = 1; + else if (_nMonth > 12) + _nMonth = 12; + if (_nMonth != 2) + return aDaysInMonth[_nMonth-1]; + else + { + if (implIsLeapYear(_nYear)) + return aDaysInMonth[_nMonth-1] + 1; + else + return aDaysInMonth[_nMonth-1]; + } + } + + static sal_Int32 implRelativeToAbsoluteNull(const css::util::Date& _rDate) + { + sal_Int32 nDays = 0; + + // ripped this code from the implementation of tools::Date + sal_Int32 nNormalizedYear = _rDate.Year - 1; + nDays = nNormalizedYear * 365; + // leap years + nDays += (nNormalizedYear / 4) - (nNormalizedYear / 100) + (nNormalizedYear / 400); + + for (sal_Int32 i = 1; i < _rDate.Month; ++i) + nDays += implDaysInMonth(i, _rDate.Year); + + nDays += _rDate.Day; + return nDays; + } + + static void implBuildFromRelative( const sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_Int16& rYear) + { + sal_Int32 nTempDays; + sal_Int32 i = 0; + bool bCalc; + + do + { + nTempDays = nDays; + rYear = static_cast<sal_uInt16>((nTempDays / 365) - i); + nTempDays -= (rYear-1) * 365; + nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400); + bCalc = false; + if ( nTempDays < 1 ) + { + i++; + bCalc = true; + } + else + { + if ( nTempDays > 365 ) + { + if ( (nTempDays != 366) || !implIsLeapYear( rYear ) ) + { + i--; + bCalc = true; + } + } + } + } + while ( bCalc ); + + rMonth = 1; + while ( nTempDays > implDaysInMonth( rMonth, rYear ) ) + { + nTempDays -= implDaysInMonth( rMonth, rYear ); + rMonth++; + } + rDay = static_cast<sal_uInt16>(nTempDays); + } + + sal_Int32 DBTypeConversion::toDays(const css::util::Date& _rVal, const css::util::Date& _rNullDate) + { + return implRelativeToAbsoluteNull(_rVal) - implRelativeToAbsoluteNull(_rNullDate); + } + + + double DBTypeConversion::toDouble(const css::util::Date& rVal, const css::util::Date& _rNullDate) + { + return static_cast<double>(toDays(rVal, _rNullDate)); + } + + + double DBTypeConversion::toDouble(const css::util::Time& rVal) + { + return static_cast<double>(getNsFromTime(rVal)) / fNanoSecondsPerDay; + } + + + double DBTypeConversion::toDouble(const css::util::DateTime& _rVal, const css::util::Date& _rNullDate) + { + sal_Int64 nTime = toDays(css::util::Date(_rVal.Day, _rVal.Month, _rVal.Year), _rNullDate); + css::util::Time aTimePart; + + aTimePart.Hours = _rVal.Hours; + aTimePart.Minutes = _rVal.Minutes; + aTimePart.Seconds = _rVal.Seconds; + aTimePart.NanoSeconds = _rVal.NanoSeconds; + + return static_cast<double>(nTime) + toDouble(aTimePart); + } + + static void addDays(const sal_Int32 nDays, css::util::Date& _rDate) + { + sal_Int64 nTempDays = implRelativeToAbsoluteNull(_rDate); + + nTempDays += nDays; + if ( nTempDays > maxDays ) + { + _rDate.Day = 31; + _rDate.Month = 12; + _rDate.Year = 9999; + } + // TODO: can we replace that check by minDays? Would allow dates BCE + // implBuildFromRelative probably needs to be updated for the "no year 0" question + else if ( nTempDays <= 0 ) + { + _rDate.Day = 1; + _rDate.Month = 1; + _rDate.Year = 1; + } + else + implBuildFromRelative( nTempDays, _rDate.Day, _rDate.Month, _rDate.Year ); + } + + static void subDays(const sal_Int32 nDays, css::util::Date& _rDate ) + { + sal_Int64 nTempDays = implRelativeToAbsoluteNull(_rDate); + + nTempDays -= nDays; + if ( nTempDays > maxDays ) + { + _rDate.Day = 31; + _rDate.Month = 12; + _rDate.Year = 9999; + } + // TODO: can we replace that check by minDays? Would allow dates BCE + // implBuildFromRelative probably needs to be updated for the "no year 0" question + else if ( nTempDays <= 0 ) + { + _rDate.Day = 1; + _rDate.Month = 1; + _rDate.Year = 1; + } + else + implBuildFromRelative( nTempDays, _rDate.Day, _rDate.Month, _rDate.Year ); + } + + css::util::Date DBTypeConversion::toDate(const double dVal, const css::util::Date& _rNullDate) + { + css::util::Date aRet = _rNullDate; + + if (dVal >= 0) + addDays(static_cast<sal_Int32>(dVal),aRet); + else + subDays(static_cast<sal_uInt32>(-dVal),aRet); + // x -= (sal_uInt32)(-nDays); + + return aRet; + } + + css::util::Time DBTypeConversion::toTime(const double dVal, short nDigits) + { + const double nDays = std::trunc(dVal); + double fSeconds((dVal - nDays) * (fNanoSecondsPerDay / nanoSecInSec)); + fSeconds = ::rtl::math::round(fSeconds, nDigits); + sal_Int64 nNS = fSeconds * nanoSecInSec; + + sal_Int16 nSign; + if ( nNS < 0 ) + { + nNS *= -1; + nSign = -1; + } + else + nSign = 1; + + css::util::Time aRet; + // normalize time + // we have to sal_Int32 here because otherwise we get an overflow + sal_Int64 nNanoSeconds = nNS; + sal_Int32 nSeconds = nNanoSeconds / nanoSecInSec; + sal_Int32 nMinutes = nSeconds / secInMin; + + aRet.NanoSeconds = nNanoSeconds % nanoSecInSec; + aRet.Seconds = nSeconds % secInMin; + aRet.Hours = nMinutes / minInHour; + aRet.Minutes = nMinutes % minInHour; + + // assemble time + sal_Int64 nTime = nSign * + (aRet.NanoSeconds + + aRet.Seconds * secMask + + aRet.Minutes * minMask + + aRet.Hours * hourMask); + + if(nTime < 0) + { + aRet.NanoSeconds = nanoSecInSec-1; + aRet.Seconds = secInMin-1; + aRet.Minutes = minInHour-1; + aRet.Hours = 23; + } + return aRet; + } + + css::util::DateTime DBTypeConversion::toDateTime(const double dVal, const css::util::Date& _rNullDate) + { + css::util::DateTime aRet; + + if (!std::isfinite(dVal)) + { + SAL_WARN("connectivity.commontools", "DateTime has invalid value: " << dVal); + return aRet; + } + + css::util::Date aDate = toDate(dVal, _rNullDate); + // there is not enough precision in a double to have both a date + // and a time up to nanoseconds -> limit to microseconds to have + // correct rounding, that is e.g. 13:00:00.000000000 instead of + // 12:59:59.999999790 + css::util::Time aTime = toTime(dVal, 6); + + aRet.Day = aDate.Day; + aRet.Month = aDate.Month; + aRet.Year = aDate.Year; + + aRet.NanoSeconds = aTime.NanoSeconds; + aRet.Minutes = aTime.Minutes; + aRet.Seconds = aTime.Seconds; + aRet.Hours = aTime.Hours; + + + return aRet; + } + + css::util::Date DBTypeConversion::toDate(std::u16string_view _sSQLString) + { + // get the token out of a string + static const sal_Unicode sDateSep = '-'; + + sal_Int32 nIndex = 0; + sal_uInt16 nYear = 0, + nMonth = 0, + nDay = 0; + nYear = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(_sSQLString, 0,sDateSep,nIndex))); + if(nIndex != -1) + { + nMonth = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(_sSQLString, 0,sDateSep,nIndex))); + if(nIndex != -1) + nDay = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(_sSQLString, 0,sDateSep,nIndex))); + } + + return css::util::Date(nDay,nMonth,nYear); + } + + + css::util::DateTime DBTypeConversion::toDateTime(const OUString& _sSQLString) + { + //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String) + //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Date.html#valueOf(java.lang.String) + //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Time.html#valueOf(java.lang.String) + + // the date part + css::util::Date aDate = toDate(_sSQLString); + css::util::Time aTime; + sal_Int32 nSeparation = _sSQLString.indexOf( ' ' ); + if ( -1 != nSeparation ) + { + const sal_Unicode *p = _sSQLString.getStr() + nSeparation; + const sal_Unicode *const begin = p; + while (rtl::isAsciiWhiteSpace(*p)) { ++p; } + nSeparation += p - begin; + aTime = toTime( _sSQLString.subView( nSeparation ) ); + } + + return css::util::DateTime(aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours, + aDate.Day, aDate.Month, aDate.Year, false); + } + + + css::util::Time DBTypeConversion::toTime(std::u16string_view _sSQLString) + { + css::util::Time aTime; + ::utl::ISO8601parseTime(_sSQLString, aTime); + return aTime; + } + + +} // namespace dbtools + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |