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 <unotools/datetime.hxx>
21 #include <tools/date.hxx>
22 #include <tools/time.hxx>
23 #include <tools/datetime.hxx>
25 #include <rtl/ustrbuf.hxx>
26 #include <rtl/math.hxx>
31 sal_Int32
impl_pow(sal_Int32 x
, sal_Int32 y
)
37 return impl_pow(x
*x
, y
/2);
41 return x
* impl_pow(x
*x
, y
/2);
45 sal_Int32
pow(sal_Int32 x
, sal_Int32 y
)
48 throw std::invalid_argument("negative power is not defined in integers");
51 return impl_pow(x
, y
);
54 /** convert string to number with optional min and max values */
56 bool convertNumber( T
& rValue
,
57 const OUString
& rString
,
58 T
/*nMin*/ = -1, T
/*nMax*/ = -1)
60 sal_Bool bNeg
= sal_False
;
64 sal_Int32 nLen
= rString
.getLength();
67 while( nPos
< nLen
&& sal_Unicode(' ') == rString
[nPos
] )
70 if( nPos
< nLen
&& sal_Unicode('-') == rString
[nPos
] )
78 sal_Unicode('0') <= rString
[nPos
] &&
79 sal_Unicode('9') >= rString
[nPos
] )
81 // TODO: check overflow!
83 rValue
+= (rString
[nPos
] - sal_Unicode('0'));
93 // although the standard calls for fixed-length (zero-padded) tokens
94 // (in their integer part), we are here liberal and allow shorter tokens
95 // (when there are separators, else it is ambiguous).
97 // the token separator is OPTIONAL
98 // empty string is a valid token! (to recognise hh or hhmm or hh:mm formats)
99 // returns: success / failure
100 // in case of failure, no reference argument is changed
102 // i_str: string to extract token from
103 // index: index in i_str where to start tokenizing
104 // after return, start of *next* token (if any)
105 // if this was the last token, then the value is UNDEFINED
106 // o_strInt: output; integer part of token
107 // o_bFraction: output; was there a fractional part?
108 // o_strFrac: output; fractional part of token
109 bool impl_getISO8601TimeToken(const OUString
&i_str
, sal_Int32
&nPos
, OUString
&resInt
, bool &bFraction
, OUString
&resFrac
)
112 // all tokens are of length 2
113 const sal_Int32 nEndPos
= nPos
+ 2;
114 const sal_Unicode c0
= '0';
115 const sal_Unicode c9
= '9';
116 const sal_Unicode sep
= ':';
117 for (;nPos
< nEndPos
&& nPos
< i_str
.getLength(); ++nPos
)
119 const sal_Unicode c
= i_str
[nPos
];
122 if (c
< c0
|| c
> c9
)
124 resInt
+= OUString(c
);
126 if (nPos
== i_str
.getLength() || i_str
[nPos
] == sep
)
128 if (i_str
[nPos
] == ',' || i_str
[nPos
] == '.')
132 for (; nPos
< i_str
.getLength(); ++nPos
)
134 const sal_Unicode c
= i_str
[nPos
];
136 // fractional part allowed only in *last* token
138 if (c
< c0
|| c
> c9
)
140 resFrac
+= OUString(c
);
142 OSL_ENSURE(nPos
== i_str
.getLength(), "impl_getISO8601TimeToken internal error; expected to be at end of string");
148 inline bool getISO8601TimeToken(const OUString
&i_str
, sal_Int32
&io_index
, OUString
&o_strInt
, bool &o_bFraction
, OUString
&o_strFrac
)
152 bool bFraction
= false;
153 sal_Int32 index
= io_index
;
154 if(!impl_getISO8601TimeToken(i_str
, index
, resInt
, bFraction
, resFrac
))
161 o_bFraction
= bFraction
;
167 //.........................................................................
170 //------------------------------------------------------------------
171 void typeConvert(const Date
& _rDate
, starutil::Date
& _rOut
)
173 _rOut
.Day
= _rDate
.GetDay();
174 _rOut
.Month
= _rDate
.GetMonth();
175 _rOut
.Year
= _rDate
.GetYear();
178 //------------------------------------------------------------------
179 void typeConvert(const starutil::Date
& _rDate
, Date
& _rOut
)
181 _rOut
= Date(_rDate
.Day
, _rDate
.Month
, _rDate
.Year
);
184 //------------------------------------------------------------------
185 void typeConvert(const DateTime
& _rDateTime
, starutil::DateTime
& _rOut
)
187 _rOut
.Year
= _rDateTime
.GetYear();
188 _rOut
.Month
= _rDateTime
.GetMonth();
189 _rOut
.Day
= _rDateTime
.GetDay();
190 _rOut
.Hours
= _rDateTime
.GetHour();
191 _rOut
.Minutes
= _rDateTime
.GetMin();
192 _rOut
.Seconds
= _rDateTime
.GetSec();
193 _rOut
.NanoSeconds
= _rDateTime
.GetNanoSec();
196 //------------------------------------------------------------------
197 void typeConvert(const starutil::DateTime
& _rDateTime
, DateTime
& _rOut
)
199 Date
aDate(_rDateTime
.Day
, _rDateTime
.Month
, _rDateTime
.Year
);
200 Time
aTime(_rDateTime
.Hours
, _rDateTime
.Minutes
, _rDateTime
.Seconds
, _rDateTime
.NanoSeconds
);
201 _rOut
= DateTime(aDate
, aTime
);
204 // FIXME: these operators should be.... in toplevel namespace? announced in the .hxx file?
205 //-------------------------------------------------------------------------
206 bool operator ==(const starutil::DateTime
& _rLeft
, const starutil::DateTime
& _rRight
)
208 return ( _rLeft
.NanoSeconds
== _rRight
.NanoSeconds
) &&
209 ( _rLeft
.Seconds
== _rRight
.Seconds
) &&
210 ( _rLeft
.Minutes
== _rRight
.Minutes
) &&
211 ( _rLeft
.Hours
== _rRight
.Hours
) &&
212 ( _rLeft
.Day
== _rRight
.Day
) &&
213 ( _rLeft
.Month
== _rRight
.Month
) &&
214 ( _rLeft
.Year
== _rRight
.Year
) ;
217 //-------------------------------------------------------------------------
218 bool operator ==(const starutil::Date
& _rLeft
, const starutil::Date
& _rRight
)
220 return ( _rLeft
.Day
== _rRight
.Day
) &&
221 ( _rLeft
.Month
== _rRight
.Month
) &&
222 ( _rLeft
.Year
== _rRight
.Year
) ;
225 //-------------------------------------------------------------------------
226 bool operator ==(const starutil::Time
& _rLeft
, const starutil::Time
& _rRight
)
228 return ( _rLeft
.NanoSeconds
== _rRight
.NanoSeconds
) &&
229 ( _rLeft
.Seconds
== _rRight
.Seconds
) &&
230 ( _rLeft
.Minutes
== _rRight
.Minutes
) &&
231 ( _rLeft
.Hours
== _rRight
.Hours
) ;
234 OUString
toISO8601(const starutil::DateTime
& rDateTime
)
236 OUStringBuffer rBuffer
;
237 rBuffer
.append((sal_Int32
) rDateTime
.Year
);
239 if( rDateTime
.Month
< 10 )
241 rBuffer
.append((sal_Int32
) rDateTime
.Month
);
243 if( rDateTime
.Day
< 10 )
245 rBuffer
.append((sal_Int32
) rDateTime
.Day
);
247 if( rDateTime
.NanoSeconds
!= 0 ||
248 rDateTime
.Seconds
!= 0 ||
249 rDateTime
.Minutes
!= 0 ||
250 rDateTime
.Hours
!= 0 )
253 if( rDateTime
.Hours
< 10 )
255 rBuffer
.append((sal_Int32
) rDateTime
.Hours
);
257 if( rDateTime
.Minutes
< 10 )
259 rBuffer
.append((sal_Int32
) rDateTime
.Minutes
);
261 if( rDateTime
.Seconds
< 10 )
263 rBuffer
.append((sal_Int32
) rDateTime
.Seconds
);
264 if ( rDateTime
.NanoSeconds
> 0)
266 OSL_ENSURE(rDateTime
.NanoSeconds
< 1000000000,"NanoSeconds cannot be more than 999 999 999");
268 std::ostringstream ostr
;
271 ostr
<< rDateTime
.NanoSeconds
;
272 rBuffer
.append(OUString::createFromAscii(ostr
.str().c_str()));
275 return rBuffer
.makeStringAndClear();
278 OUString
toISO8601(const starutil::Time
& rTime
)
280 OUStringBuffer rBuffer
;
281 if( rTime
.Hours
< 10 )
283 rBuffer
.append((sal_Int32
) rTime
.Hours
);
285 if( rTime
.Minutes
< 10 )
287 rBuffer
.append((sal_Int32
) rTime
.Minutes
);
289 if( rTime
.Seconds
< 10 )
291 rBuffer
.append((sal_Int32
) rTime
.Seconds
);
292 if ( rTime
.NanoSeconds
> 0)
294 OSL_ENSURE(rTime
.NanoSeconds
< 1000000000,"NanoSeconds cannot be more than 999 999 999");
296 std::ostringstream ostr
;
299 ostr
<< rTime
.NanoSeconds
;
300 rBuffer
.append(OUString::createFromAscii(ostr
.str().c_str()));
302 return rBuffer
.makeStringAndClear();
305 /** convert ISO8601 DateTime String to util::DateTime */
306 bool ISO8601parseDateTime(const OUString
&rString
, starutil::DateTime
& rDateTime
)
308 bool bSuccess
= true;
310 rtl::OUString aDateStr
, aTimeStr
;
311 starutil::Date aDate
;
312 starutil::Time aTime
;
313 sal_Int32 nPos
= rString
.indexOf( (sal_Unicode
) 'T' );
316 aDateStr
= rString
.copy( 0, nPos
);
317 aTimeStr
= rString
.copy( nPos
+ 1 );
320 aDateStr
= rString
; // no separator: only date part
322 bSuccess
= ISO8601parseDate(aDateStr
, aDate
);
324 if ( bSuccess
&& !aTimeStr
.isEmpty() ) // time is optional
326 bSuccess
= ISO8601parseTime(aTimeStr
, aTime
);
331 rDateTime
= starutil::DateTime(aTime
.NanoSeconds
, aTime
.Seconds
, aTime
.Minutes
, aTime
.Hours
,
332 aDate
.Day
, aDate
.Month
, aDate
.Year
, false);
338 /** convert ISO8601 Date String to util::Date */
339 // TODO: supports only calendar dates YYYY-MM-DD
340 // MISSING: calendar dates YYYYMMDD YYYY-MM
341 // year, week date, ordinal date
342 bool ISO8601parseDate(const OUString
&aDateStr
, starutil::Date
& rDate
)
344 bool bSuccess
= true;
346 sal_Int32 nYear
= 1899;
347 sal_Int32 nMonth
= 12;
350 const sal_Unicode
* pStr
= aDateStr
.getStr();
351 sal_Int32 nDateTokens
= 1;
358 if ( nDateTokens
> 3 || aDateStr
.isEmpty() )
363 if ( !convertNumber
<sal_Int32
>( nYear
, aDateStr
.getToken( 0, '-', n
), 0, 9999 ) )
365 if ( nDateTokens
>= 2 )
366 if ( !convertNumber
<sal_Int32
>( nMonth
, aDateStr
.getToken( 0, '-', n
), 0, 12 ) )
368 if ( nDateTokens
>= 3 )
369 if ( !convertNumber
<sal_Int32
>( nDay
, aDateStr
.getToken( 0, '-', n
), 0, 31 ) )
375 rDate
.Year
= (sal_uInt16
)nYear
;
376 rDate
.Month
= (sal_uInt16
)nMonth
;
377 rDate
.Day
= (sal_uInt16
)nDay
;
383 /** convert ISO8601 Time String to util::Time */
384 bool ISO8601parseTime(const OUString
&aTimeStr
, starutil::Time
& rTime
)
386 bool bSuccess
= true;
391 sal_Int32 nNanoSec
= 0;
398 if (bSuccess
&& (bSuccess
= getISO8601TimeToken(aTimeStr
, n
, tokInt
, bFrac
, tokFrac
)))
400 if ( bFrac
&& n
< aTimeStr
.getLength())
401 // junk after ISO time
403 else if ( (bSuccess
= convertNumber
<sal_Int32
>( nHour
, tokInt
, 0, 23 )) )
407 sal_Int64 fracNumerator
;
408 if ( (bSuccess
= convertNumber(fracNumerator
, tokFrac
)) )
410 double frac
= static_cast<double>(fracNumerator
) / static_cast<double>(pow(10, tokFrac
.getLength()));
412 OSL_ENSURE(frac
< 1 && frac
>= 0, "ISO8601parse internal error frac hours (of hours) not between 0 and 1");
417 OSL_ENSURE(frac
< 1 && frac
>= 0, "ISO8601parse internal error frac minutes (of hours) not between 0 and 1");
422 OSL_ENSURE(frac
< 1 && frac
>= 0, "ISO8601parse internal error frac seconds (of hours) not between 0 and 1");
424 nNanoSec
= ::rtl::math::round(frac
);
428 if(n
>= aTimeStr
.getLength())
434 if (bSuccess
&& (bSuccess
= getISO8601TimeToken(aTimeStr
, n
, tokInt
, bFrac
, tokFrac
)))
436 if ( bFrac
&& n
< aTimeStr
.getLength())
437 // junk after ISO time
439 else if ( (bSuccess
= convertNumber
<sal_Int32
>( nMin
, tokInt
, 0, 59 )) )
443 sal_Int64 fracNumerator
;
444 if ( (bSuccess
= convertNumber(fracNumerator
, tokFrac
)) )
446 double frac
= static_cast<double>(fracNumerator
) / static_cast<double>(pow(10, tokFrac
.getLength()));
448 OSL_ENSURE(frac
< 1 && frac
>= 0, "ISO8601parse internal error frac minutes (of minutes) not between 0 and 1");
453 OSL_ENSURE(frac
< 1 && frac
>= 0, "ISO8601parse internal error frac seconds (of minutes) not between 0 and 1");
455 nNanoSec
= ::rtl::math::round(frac
);
459 if(n
>= aTimeStr
.getLength())
464 if (bSuccess
&& (bSuccess
= getISO8601TimeToken(aTimeStr
, n
, tokInt
, bFrac
, tokFrac
)))
466 if (n
< aTimeStr
.getLength())
467 // junk after ISO time
469 // max 60 for leap seconds
470 else if ( (bSuccess
= convertNumber
<sal_Int32
>( nSec
, tokInt
, 0, 60 )) )
474 sal_Int64 fracNumerator
;
475 if ( (bSuccess
= convertNumber(fracNumerator
, tokFrac
)) )
477 double frac
= static_cast<double>(fracNumerator
) / static_cast<double>(pow(10, tokFrac
.getLength()));
479 OSL_ENSURE(frac
< 1 && frac
>= 0, "ISO8601parse internal error frac seconds (of seconds) not between 0 and 1");
481 nNanoSec
= ::rtl::math::round(frac
);
492 const int secondsOverFlow
= (nSec
== 60) ? 61 : 60;
493 if (nNanoSec
== 1000000000)
498 if(nSec
== secondsOverFlow
)
509 rTime
.Hours
= (sal_uInt16
)nHour
;
510 rTime
.Minutes
= (sal_uInt16
)nMin
;
511 rTime
.Seconds
= (sal_uInt16
)nSec
;
512 rTime
.NanoSeconds
= nNanoSec
;
517 //.........................................................................
519 //.........................................................................
521 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */