nss: upgrade to release 3.73
[LibreOffice.git] / connectivity / source / commontools / dbconversion.cxx
blob77cfdfb26ea150121f7318aa2d1a17a5b79a8ab3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <connectivity/dbconversion.hxx>
21 #include <osl/diagnose.h>
22 #include <com/sun/star/sdbc/SQLException.hpp>
23 #include <com/sun/star/util/Date.hpp>
24 #include <com/sun/star/util/Time.hpp>
25 #include <com/sun/star/util/DateTime.hpp>
26 #include <rtl/character.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <rtl/math.hxx>
29 #include <sal/log.hxx>
30 #include <unotools/datetime.hxx>
31 #include <sstream>
32 #include <iomanip>
34 namespace
36 const sal_Int64 nanoSecInSec = 1000000000;
37 const sal_Int16 secInMin = 60;
38 const sal_Int16 minInHour = 60;
40 const sal_Int64 secMask = 1000000000;
41 const sal_Int64 minMask = 100000000000LL;
42 const sal_Int64 hourMask = 10000000000000LL;
44 const double fNanoSecondsPerDay = nanoSecInSec * secInMin * minInHour * 24.0;
46 // 32767-12-31 in "(days since 0001-01-01) + 1" format
47 const sal_Int32 maxDays = 11967896;
48 // -32768-01-01 in "(days since 0001-01-01) + 1" format
49 // Yes, I know it is currently unused. Will have to be used
50 // when we implement negative years. Writing down the correct
51 // value for future reference.
52 // *** Please don't remove just because it is unused ***
53 // Lionel Élie Mamane 2017-08-02
54 // const sal_Int32 minDays = -11968270;
58 namespace dbtools
62 using namespace ::com::sun::star::uno;
63 using namespace ::com::sun::star::util;
64 using namespace ::com::sun::star::sdb;
65 using namespace ::com::sun::star::sdbc;
66 using namespace ::com::sun::star::lang;
67 using namespace ::com::sun::star::beans;
70 css::util::Date const & DBTypeConversion::getStandardDate()
72 static css::util::Date STANDARD_DB_DATE(1,1,1900);
73 return STANDARD_DB_DATE;
76 OUString DBTypeConversion::toDateString(const css::util::Date& rDate)
78 std::ostringstream ostr;
79 using std::setw;
80 ostr.fill('0');
81 ostr << setw(4) << rDate.Year << "-"
82 << setw(2) << rDate.Month << "-"
83 << setw(2) << rDate.Day;
84 return OUString::createFromAscii(ostr.str().c_str());
87 OUString DBTypeConversion::toTimeStringS(const css::util::Time& rTime)
89 std::ostringstream ostr;
90 using std::setw;
91 ostr.fill('0');
92 ostr << setw(2) << rTime.Hours << ":"
93 << setw(2) << rTime.Minutes << ":"
94 << setw(2) << rTime.Seconds;
95 return OUString::createFromAscii(ostr.str().c_str());
98 OUString DBTypeConversion::toTimeString(const css::util::Time& rTime)
100 std::ostringstream ostr;
101 using std::setw;
102 ostr.fill('0');
103 ostr << setw(2) << rTime.Hours << ":"
104 << setw(2) << rTime.Minutes << ":"
105 << setw(2) << rTime.Seconds << "."
106 << setw(9) << rTime.NanoSeconds;
107 return OUString::createFromAscii(ostr.str().c_str());
110 OUString DBTypeConversion::toDateTimeString(const css::util::DateTime& _rDateTime)
112 css::util::Date aDate(_rDateTime.Day,_rDateTime.Month,_rDateTime.Year);
113 OUStringBuffer aTemp(toDateString(aDate));
114 aTemp.append(" ");
115 css::util::Time const aTime(_rDateTime.NanoSeconds, _rDateTime.Seconds,
116 _rDateTime.Minutes, _rDateTime.Hours, _rDateTime.IsUTC);
117 aTemp.append( toTimeString(aTime) );
118 return aTemp.makeStringAndClear();
121 css::util::Date DBTypeConversion::toDate(const sal_Int32 _nVal)
123 css::util::Date aReturn;
124 aReturn.Day = static_cast<sal_uInt16>(_nVal % 100);
125 aReturn.Month = static_cast<sal_uInt16>((_nVal / 100) % 100);
126 aReturn.Year = static_cast<sal_uInt16>(_nVal / 10000);
127 return aReturn;
131 css::util::Time DBTypeConversion::toTime(const sal_Int64 _nVal)
133 css::util::Time aReturn;
134 sal_uInt64 unVal = static_cast<sal_uInt64>(_nVal >= 0 ? _nVal : -_nVal);
135 aReturn.Hours = unVal / hourMask;
136 aReturn.Minutes = (unVal / minMask) % 100;
137 aReturn.Seconds = (unVal / secMask) % 100;
138 aReturn.NanoSeconds = unVal % secMask;
139 return aReturn;
142 sal_Int64 DBTypeConversion::getNsFromTime(const css::util::Time& rVal)
144 sal_Int32 nHour = rVal.Hours;
145 sal_Int32 nMin = rVal.Minutes;
146 sal_Int32 nSec = rVal.Seconds;
147 sal_Int32 nNanoSec = rVal.NanoSeconds;
149 return nNanoSec +
150 nSec * nanoSecInSec +
151 nMin * (secInMin * nanoSecInSec) +
152 nHour * (minInHour * secInMin * nanoSecInSec);
156 const sal_Int32 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
157 31, 31, 30, 31, 30, 31 };
160 static bool implIsLeapYear(sal_Int32 _nYear)
162 return ( ((_nYear % 4) == 0)
163 && ((_nYear % 100) != 0)
166 || ((_nYear % 400) == 0)
170 static sal_Int32 implDaysInMonth(sal_Int32 _nMonth, sal_Int32 _nYear)
172 SAL_WARN_IF(_nMonth < 1 || _nMonth > 12, "connectivity.commontools", "Month has invalid value: " << _nMonth);
173 if (_nMonth < 1)
174 _nMonth = 1;
175 else if (_nMonth > 12)
176 _nMonth = 12;
177 if (_nMonth != 2)
178 return aDaysInMonth[_nMonth-1];
179 else
181 if (implIsLeapYear(_nYear))
182 return aDaysInMonth[_nMonth-1] + 1;
183 else
184 return aDaysInMonth[_nMonth-1];
188 static sal_Int32 implRelativeToAbsoluteNull(const css::util::Date& _rDate)
190 sal_Int32 nDays = 0;
192 // ripped this code from the implementation of tools::Date
193 sal_Int32 nNormalizedYear = _rDate.Year - 1;
194 nDays = nNormalizedYear * 365;
195 // leap years
196 nDays += (nNormalizedYear / 4) - (nNormalizedYear / 100) + (nNormalizedYear / 400);
198 for (sal_Int32 i = 1; i < _rDate.Month; ++i)
199 nDays += implDaysInMonth(i, _rDate.Year);
201 nDays += _rDate.Day;
202 return nDays;
205 static void implBuildFromRelative( const sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_Int16& rYear)
207 sal_Int32 nTempDays;
208 sal_Int32 i = 0;
209 bool bCalc;
213 nTempDays = nDays;
214 rYear = static_cast<sal_uInt16>((nTempDays / 365) - i);
215 nTempDays -= (rYear-1) * 365;
216 nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400);
217 bCalc = false;
218 if ( nTempDays < 1 )
220 i++;
221 bCalc = true;
223 else
225 if ( nTempDays > 365 )
227 if ( (nTempDays != 366) || !implIsLeapYear( rYear ) )
229 i--;
230 bCalc = true;
235 while ( bCalc );
237 rMonth = 1;
238 while ( nTempDays > implDaysInMonth( rMonth, rYear ) )
240 nTempDays -= implDaysInMonth( rMonth, rYear );
241 rMonth++;
243 rDay = static_cast<sal_uInt16>(nTempDays);
246 sal_Int32 DBTypeConversion::toDays(const css::util::Date& _rVal, const css::util::Date& _rNullDate)
248 return implRelativeToAbsoluteNull(_rVal) - implRelativeToAbsoluteNull(_rNullDate);
252 double DBTypeConversion::toDouble(const css::util::Date& rVal, const css::util::Date& _rNullDate)
254 return static_cast<double>(toDays(rVal, _rNullDate));
258 double DBTypeConversion::toDouble(const css::util::Time& rVal)
260 return static_cast<double>(getNsFromTime(rVal)) / fNanoSecondsPerDay;
264 double DBTypeConversion::toDouble(const css::util::DateTime& _rVal, const css::util::Date& _rNullDate)
266 sal_Int64 nTime = toDays(css::util::Date(_rVal.Day, _rVal.Month, _rVal.Year), _rNullDate);
267 css::util::Time aTimePart;
269 aTimePart.Hours = _rVal.Hours;
270 aTimePart.Minutes = _rVal.Minutes;
271 aTimePart.Seconds = _rVal.Seconds;
272 aTimePart.NanoSeconds = _rVal.NanoSeconds;
274 return static_cast<double>(nTime) + toDouble(aTimePart);
277 static void addDays(const sal_Int32 nDays, css::util::Date& _rDate)
279 sal_Int32 nTempDays = implRelativeToAbsoluteNull( _rDate );
281 nTempDays += nDays;
282 if ( nTempDays > maxDays )
284 _rDate.Day = 31;
285 _rDate.Month = 12;
286 _rDate.Year = 9999;
288 // TODO: can we replace that check by minDays? Would allow dates BCE
289 // implBuildFromRelative probably needs to be updated for the "no year 0" question
290 else if ( nTempDays <= 0 )
292 _rDate.Day = 1;
293 _rDate.Month = 1;
294 _rDate.Year = 1;
296 else
297 implBuildFromRelative( nTempDays, _rDate.Day, _rDate.Month, _rDate.Year );
300 static void subDays(const sal_Int32 nDays, css::util::Date& _rDate )
302 sal_Int32 nTempDays = implRelativeToAbsoluteNull( _rDate );
304 nTempDays -= nDays;
305 if ( nTempDays > maxDays )
307 _rDate.Day = 31;
308 _rDate.Month = 12;
309 _rDate.Year = 9999;
311 // TODO: can we replace that check by minDays? Would allow dates BCE
312 // implBuildFromRelative probably needs to be updated for the "no year 0" question
313 else if ( nTempDays <= 0 )
315 _rDate.Day = 1;
316 _rDate.Month = 1;
317 _rDate.Year = 1;
319 else
320 implBuildFromRelative( nTempDays, _rDate.Day, _rDate.Month, _rDate.Year );
323 css::util::Date DBTypeConversion::toDate(const double dVal, const css::util::Date& _rNullDate)
325 css::util::Date aRet = _rNullDate;
327 if (dVal >= 0)
328 addDays(static_cast<sal_Int32>(dVal),aRet);
329 else
330 subDays(static_cast<sal_uInt32>(-dVal),aRet);
331 // x -= (sal_uInt32)(-nDays);
333 return aRet;
336 css::util::Time DBTypeConversion::toTime(const double dVal, short nDigits)
338 const sal_Int32 nDays = static_cast<sal_Int32>(dVal);
339 sal_Int64 nNS;
341 double fSeconds((dVal - static_cast<double>(nDays)) * (fNanoSecondsPerDay / nanoSecInSec));
342 fSeconds = ::rtl::math::round( fSeconds, nDigits );
343 nNS = fSeconds * nanoSecInSec;
346 sal_Int16 nSign;
347 if ( nNS < 0 )
349 nNS *= -1;
350 nSign = -1;
352 else
353 nSign = 1;
355 css::util::Time aRet;
356 // normalize time
357 // we have to sal_Int32 here because otherwise we get an overflow
358 sal_Int64 nNanoSeconds = nNS;
359 sal_Int32 nSeconds = nNanoSeconds / nanoSecInSec;
360 sal_Int32 nMinutes = nSeconds / secInMin;
362 aRet.NanoSeconds = nNanoSeconds % nanoSecInSec;
363 aRet.Seconds = nSeconds % secInMin;
364 aRet.Hours = nMinutes / minInHour;
365 aRet.Minutes = nMinutes % minInHour;
367 // assemble time
368 sal_Int64 nTime = nSign *
369 (aRet.NanoSeconds +
370 aRet.Seconds * secMask +
371 aRet.Minutes * minMask +
372 aRet.Hours * hourMask);
374 if(nTime < 0)
376 aRet.NanoSeconds = nanoSecInSec-1;
377 aRet.Seconds = secInMin-1;
378 aRet.Minutes = minInHour-1;
379 aRet.Hours = 23;
381 return aRet;
384 css::util::DateTime DBTypeConversion::toDateTime(const double dVal, const css::util::Date& _rNullDate)
386 css::util::Date aDate = toDate(dVal, _rNullDate);
387 // there is not enough precision in a double to have both a date
388 // and a time up to nanoseconds -> limit to microseconds to have
389 // correct rounding, that is e.g. 13:00:00.000000000 instead of
390 // 12:59:59.999999790
391 css::util::Time aTime = toTime(dVal, 6);
393 css::util::DateTime aRet;
395 aRet.Day = aDate.Day;
396 aRet.Month = aDate.Month;
397 aRet.Year = aDate.Year;
399 aRet.NanoSeconds = aTime.NanoSeconds;
400 aRet.Minutes = aTime.Minutes;
401 aRet.Seconds = aTime.Seconds;
402 aRet.Hours = aTime.Hours;
405 return aRet;
408 css::util::Date DBTypeConversion::toDate(const OUString& _sSQLString)
410 // get the token out of a string
411 static const sal_Unicode sDateSep = '-';
413 sal_Int32 nIndex = 0;
414 sal_uInt16 nYear = 0,
415 nMonth = 0,
416 nDay = 0;
417 nYear = static_cast<sal_uInt16>(_sSQLString.getToken(0,sDateSep,nIndex).toInt32());
418 if(nIndex != -1)
420 nMonth = static_cast<sal_uInt16>(_sSQLString.getToken(0,sDateSep,nIndex).toInt32());
421 if(nIndex != -1)
422 nDay = static_cast<sal_uInt16>(_sSQLString.getToken(0,sDateSep,nIndex).toInt32());
425 return css::util::Date(nDay,nMonth,nYear);
429 css::util::DateTime DBTypeConversion::toDateTime(const OUString& _sSQLString)
431 //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String)
432 //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Date.html#valueOf(java.lang.String)
433 //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Time.html#valueOf(java.lang.String)
435 // the date part
436 css::util::Date aDate = toDate(_sSQLString);
437 css::util::Time aTime;
438 sal_Int32 nSeparation = _sSQLString.indexOf( ' ' );
439 if ( -1 != nSeparation )
441 const sal_Unicode *p = _sSQLString.getStr() + nSeparation;
442 const sal_Unicode *const begin = p;
443 while (rtl::isAsciiWhiteSpace(*p)) { ++p; }
444 nSeparation += p - begin;
445 aTime = toTime( _sSQLString.copy( nSeparation ) );
448 return css::util::DateTime(aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
449 aDate.Day, aDate.Month, aDate.Year, false);
453 css::util::Time DBTypeConversion::toTime(const OUString& _sSQLString)
455 css::util::Time aTime;
456 ::utl::ISO8601parseTime(_sSQLString, aTime);
457 return aTime;
461 } // namespace dbtools
464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */