1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <com/sun/star/sdbc/SQLException.hpp>
22 #include <com/sun/star/util/Date.hpp>
23 #include <com/sun/star/util/Time.hpp>
24 #include <com/sun/star/util/DateTime.hpp>
25 #include <rtl/character.hxx>
26 #include <rtl/math.hxx>
27 #include <sal/log.hxx>
28 #include <unotools/datetime.hxx>
29 #include <comphelper/date.hxx>
30 #include <o3tl/string_view.hxx>
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;
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
;
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
;
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
;
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 css::util::Time
const aTime(_rDateTime
.NanoSeconds
, _rDateTime
.Seconds
,
114 _rDateTime
.Minutes
, _rDateTime
.Hours
, _rDateTime
.IsUTC
);
115 return toDateString(aDate
) + " " + toTimeString(aTime
);
118 css::util::Date
DBTypeConversion::toDate(const sal_Int32 _nVal
)
120 css::util::Date aReturn
;
121 aReturn
.Day
= static_cast<sal_uInt16
>(_nVal
% 100);
122 aReturn
.Month
= static_cast<sal_uInt16
>((_nVal
/ 100) % 100);
123 aReturn
.Year
= static_cast<sal_uInt16
>(_nVal
/ 10000);
128 css::util::Time
DBTypeConversion::toTime(const sal_Int64 _nVal
)
130 css::util::Time aReturn
;
131 sal_uInt64 unVal
= static_cast<sal_uInt64
>(_nVal
>= 0 ? _nVal
: -_nVal
);
132 aReturn
.Hours
= unVal
/ hourMask
;
133 aReturn
.Minutes
= (unVal
/ minMask
) % 100;
134 aReturn
.Seconds
= (unVal
/ secMask
) % 100;
135 aReturn
.NanoSeconds
= unVal
% secMask
;
139 sal_Int64
DBTypeConversion::getNsFromTime(const css::util::Time
& rVal
)
141 sal_Int32 nHour
= rVal
.Hours
;
142 sal_Int32 nMin
= rVal
.Minutes
;
143 sal_Int32 nSec
= rVal
.Seconds
;
144 sal_Int32 nNanoSec
= rVal
.NanoSeconds
;
147 nSec
* nanoSecInSec
+
148 nMin
* (secInMin
* nanoSecInSec
) +
149 nHour
* (minInHour
* secInMin
* nanoSecInSec
);
152 static sal_Int32
implRelativeToAbsoluteNull(const css::util::Date
& _rDate
)
154 if (_rDate
.Day
== 0 && _rDate
.Month
== 0 && _rDate
.Year
== 0)
156 // 0000-00-00 is *NOT* a valid date and passing it to the date
157 // conversion even when normalizing rightly asserts. Whatever we
158 // return here, it will be wrong. The old before commit
159 // 52ff16771ac160d27fd7beb78a4cfba22ad84f06 wrong implementation
160 // calculated -365 for that, effectively that would be a date of
161 // -0001-01-01 now but it was likely assumed that would be
162 // 0000-00-01 or even 0000-00-00 instead. Try if we get away with 0
163 // for -0001-12-31, the same that
164 // comphelper::date::convertDateToDaysNormalizing()
165 // would return if comphelper::date::normalize() wouldn't ignore
166 // such "empty" date.
170 return comphelper::date::convertDateToDaysNormalizing( _rDate
.Day
, _rDate
.Month
, _rDate
.Year
);
173 sal_Int32
DBTypeConversion::toDays(const css::util::Date
& _rVal
, const css::util::Date
& _rNullDate
)
175 return implRelativeToAbsoluteNull(_rVal
) - implRelativeToAbsoluteNull(_rNullDate
);
179 double DBTypeConversion::toDouble(const css::util::Date
& rVal
, const css::util::Date
& _rNullDate
)
181 return static_cast<double>(toDays(rVal
, _rNullDate
));
185 double DBTypeConversion::toDouble(const css::util::Time
& rVal
)
187 return static_cast<double>(getNsFromTime(rVal
)) / fNanoSecondsPerDay
;
191 double DBTypeConversion::toDouble(const css::util::DateTime
& _rVal
, const css::util::Date
& _rNullDate
)
193 sal_Int64 nTime
= toDays(css::util::Date(_rVal
.Day
, _rVal
.Month
, _rVal
.Year
), _rNullDate
);
194 css::util::Time aTimePart
;
196 aTimePart
.Hours
= _rVal
.Hours
;
197 aTimePart
.Minutes
= _rVal
.Minutes
;
198 aTimePart
.Seconds
= _rVal
.Seconds
;
199 aTimePart
.NanoSeconds
= _rVal
.NanoSeconds
;
201 return static_cast<double>(nTime
) + toDouble(aTimePart
);
204 static void addDays(const sal_Int32 nDays
, css::util::Date
& _rDate
)
206 sal_Int64 nTempDays
= implRelativeToAbsoluteNull(_rDate
);
209 if ( nTempDays
> maxDays
)
215 // TODO: can we replace that check by minDays? Would allow dates BCE
216 else if ( nTempDays
<= 0 )
223 comphelper::date::convertDaysToDate( nTempDays
, _rDate
.Day
, _rDate
.Month
, _rDate
.Year
);
226 static void subDays(const sal_Int32 nDays
, css::util::Date
& _rDate
)
228 sal_Int64 nTempDays
= implRelativeToAbsoluteNull(_rDate
);
231 if ( nTempDays
> maxDays
)
237 // TODO: can we replace that check by minDays? Would allow dates BCE
238 else if ( nTempDays
<= 0 )
245 comphelper::date::convertDaysToDate( nTempDays
, _rDate
.Day
, _rDate
.Month
, _rDate
.Year
);
248 css::util::Date
DBTypeConversion::toDate(const double dVal
, const css::util::Date
& _rNullDate
)
250 css::util::Date aRet
= _rNullDate
;
253 addDays(static_cast<sal_Int32
>(dVal
),aRet
);
255 subDays(static_cast<sal_uInt32
>(-dVal
),aRet
);
256 // x -= (sal_uInt32)(-nDays);
261 css::util::Time
DBTypeConversion::toTime(const double dVal
, short nDigits
)
263 const double nDays
= std::trunc(dVal
);
264 double fSeconds((dVal
- nDays
) * (fNanoSecondsPerDay
/ nanoSecInSec
));
265 fSeconds
= ::rtl::math::round(fSeconds
, nDigits
);
266 sal_Int64 nNS
= fSeconds
* nanoSecInSec
;
277 css::util::Time aRet
;
279 // we have to sal_Int32 here because otherwise we get an overflow
280 sal_Int64 nNanoSeconds
= nNS
;
281 sal_Int32 nSeconds
= nNanoSeconds
/ nanoSecInSec
;
282 sal_Int32 nMinutes
= nSeconds
/ secInMin
;
284 aRet
.NanoSeconds
= nNanoSeconds
% nanoSecInSec
;
285 aRet
.Seconds
= nSeconds
% secInMin
;
286 aRet
.Hours
= nMinutes
/ minInHour
;
287 aRet
.Minutes
= nMinutes
% minInHour
;
290 sal_Int64 nTime
= nSign
*
292 aRet
.Seconds
* secMask
+
293 aRet
.Minutes
* minMask
+
294 aRet
.Hours
* hourMask
);
298 aRet
.NanoSeconds
= nanoSecInSec
-1;
299 aRet
.Seconds
= secInMin
-1;
300 aRet
.Minutes
= minInHour
-1;
306 css::util::DateTime
DBTypeConversion::toDateTime(const double dVal
, const css::util::Date
& _rNullDate
)
308 css::util::DateTime aRet
;
310 if (!std::isfinite(dVal
))
312 SAL_WARN("connectivity.commontools", "DateTime has invalid value: " << dVal
);
316 css::util::Date aDate
= toDate(dVal
, _rNullDate
);
317 // there is not enough precision in a double to have both a date
318 // and a time up to nanoseconds -> limit to microseconds to have
319 // correct rounding, that is e.g. 13:00:00.000000000 instead of
320 // 12:59:59.999999790
321 css::util::Time aTime
= toTime(dVal
, 6);
323 aRet
.Day
= aDate
.Day
;
324 aRet
.Month
= aDate
.Month
;
325 aRet
.Year
= aDate
.Year
;
327 aRet
.NanoSeconds
= aTime
.NanoSeconds
;
328 aRet
.Minutes
= aTime
.Minutes
;
329 aRet
.Seconds
= aTime
.Seconds
;
330 aRet
.Hours
= aTime
.Hours
;
336 css::util::Date
DBTypeConversion::toDate(std::u16string_view _sSQLString
)
338 // get the token out of a string
339 static const sal_Unicode sDateSep
= '-';
341 sal_Int32 nIndex
= 0;
342 sal_uInt16 nYear
= 0,
345 nYear
= static_cast<sal_uInt16
>(o3tl::toInt32(o3tl::getToken(_sSQLString
, 0,sDateSep
,nIndex
)));
348 nMonth
= static_cast<sal_uInt16
>(o3tl::toInt32(o3tl::getToken(_sSQLString
, 0,sDateSep
,nIndex
)));
350 nDay
= static_cast<sal_uInt16
>(o3tl::toInt32(o3tl::getToken(_sSQLString
, 0,sDateSep
,nIndex
)));
353 return css::util::Date(nDay
,nMonth
,nYear
);
357 css::util::DateTime
DBTypeConversion::toDateTime(const OUString
& _sSQLString
)
359 //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String)
360 //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Date.html#valueOf(java.lang.String)
361 //@see http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Time.html#valueOf(java.lang.String)
364 css::util::Date aDate
= toDate(_sSQLString
);
365 css::util::Time aTime
;
366 sal_Int32 nSeparation
= _sSQLString
.indexOf( ' ' );
367 if ( -1 != nSeparation
)
369 const sal_Unicode
*p
= _sSQLString
.getStr() + nSeparation
;
370 const sal_Unicode
*const begin
= p
;
371 while (rtl::isAsciiWhiteSpace(*p
)) { ++p
; }
372 nSeparation
+= p
- begin
;
373 aTime
= toTime( _sSQLString
.subView( nSeparation
) );
376 return css::util::DateTime(aTime
.NanoSeconds
, aTime
.Seconds
, aTime
.Minutes
, aTime
.Hours
,
377 aDate
.Day
, aDate
.Month
, aDate
.Year
, false);
381 css::util::Time
DBTypeConversion::toTime(std::u16string_view _sSQLString
)
383 css::util::Time aTime
;
384 ::utl::ISO8601parseTime(_sSQLString
, aTime
);
389 } // namespace dbtools
392 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */