LCOV - code coverage report
Current view: top level - unotools/source/misc - datetime.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 135 260 51.9 %
Date: 2014-11-03 Functions: 8 14 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       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/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      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 .
      18             :  */
      19             : 
      20             : #include <unotools/datetime.hxx>
      21             : #include <tools/date.hxx>
      22             : #include <tools/time.hxx>
      23             : #include <tools/datetime.hxx>
      24             : #include <rtl/ustrbuf.hxx>
      25             : #include <rtl/math.hxx>
      26             : 
      27             : namespace
      28             : {
      29             :     /** convert string to number with optional min and max values */
      30             :     template <typename T>
      31         108 :     bool convertNumber( T& rValue,
      32             :                         const OUString& rString,
      33             :                         T /*nMin*/ = -1, T /*nMax*/ = -1)
      34             :     {
      35         108 :         bool bNeg = false;
      36         108 :         rValue = 0;
      37             : 
      38         108 :         sal_Int32 nPos = 0L;
      39         108 :         sal_Int32 nLen = rString.getLength();
      40             : 
      41             :         // skip white space
      42         216 :         while( nPos < nLen && ' ' == rString[nPos] )
      43           0 :             nPos++;
      44             : 
      45         108 :         if( nPos < nLen && '-' == rString[nPos] )
      46             :         {
      47           0 :             bNeg = true;
      48           0 :             nPos++;
      49             :         }
      50             : 
      51             :         // get number
      52         972 :         while( nPos < nLen &&
      53         252 :                '0' <= rString[nPos] &&
      54         252 :                '9' >= rString[nPos] )
      55             :         {
      56             :             // TODO: check overflow!
      57         252 :             rValue *= 10;
      58         252 :             rValue += (rString[nPos] - sal_Unicode('0'));
      59         252 :             nPos++;
      60             :         }
      61             : 
      62         108 :         if( bNeg )
      63           0 :             rValue *= -1;
      64             : 
      65         108 :         return nPos == nLen;
      66             :     }
      67             : 
      68             :     // although the standard calls for fixed-length (zero-padded) tokens
      69             :     // (in their integer part), we are here liberal and allow shorter tokens
      70             :     // (when there are separators, else it is ambiguous).
      71             :     // Note that:
      72             :     //   the token separator is OPTIONAL
      73             :     //   empty string is a valid token! (to recognise hh or hhmm or hh:mm formats)
      74             :     // returns: success / failure
      75             :     // in case of failure, no reference argument is changed
      76             :     // arguments:
      77             :     //   i_str: string to extract token from
      78             :     //   index: index in i_str where to start tokenizing
      79             :     //          after return, start of *next* token (if any)
      80             :     //          if this was the last token, then the value is UNDEFINED
      81             :     //   o_strInt:    output; integer part of token
      82             :     //   o_bFraction: output; was there a fractional part?
      83             :     //   o_strFrac:   output; fractional part of token
      84          54 :     bool impl_getISO8601TimeToken(const OUString &i_str, sal_Int32 &nPos, OUString &resInt, bool &bFraction, OUString &resFrac)
      85             :     {
      86          54 :         bFraction = false;
      87             :         // all tokens are of length 2
      88          54 :         const sal_Int32 nEndPos = nPos + 2;
      89          54 :         const sal_Unicode c0 = '0';
      90          54 :         const sal_Unicode c9 = '9';
      91          54 :         const sal_Unicode sep = ':';
      92         162 :         for (;nPos < nEndPos && nPos < i_str.getLength(); ++nPos)
      93             :         {
      94         108 :             const sal_Unicode c = i_str[nPos];
      95         108 :             if (c == sep)
      96           0 :                 return true;
      97         108 :             if (c < c0 || c > c9)
      98           0 :                 return false;
      99         108 :             resInt += OUString(c);
     100             :         }
     101          54 :         if (nPos == i_str.getLength() || i_str[nPos] == sep)
     102          36 :             return true;
     103          18 :         if (i_str[nPos] == ',' || i_str[nPos] == '.')
     104             :         {
     105           0 :             bFraction = true;
     106           0 :             ++nPos;
     107           0 :             for (; nPos < i_str.getLength(); ++nPos)
     108             :             {
     109           0 :                 const sal_Unicode c = i_str[nPos];
     110           0 :                 if (c == 'Z' || c == '+' || c == '-')
     111             :                 {
     112           0 :                     --nPos; // we don't want to skip the tz separator
     113           0 :                     return true;
     114             :                 }
     115           0 :                 if (c == sep)
     116             :                     // fractional part allowed only in *last* token
     117           0 :                     return false;
     118           0 :                 if (c < c0 || c > c9)
     119           0 :                     return false;
     120           0 :                 resFrac += OUString(c);
     121             :             }
     122             :             OSL_ENSURE(nPos == i_str.getLength(), "impl_getISO8601TimeToken internal error; expected to be at end of string");
     123           0 :             return true;
     124             :         }
     125          18 :         if (i_str[nPos] == 'Z' || i_str[nPos] == '+' || i_str[nPos] == '-')
     126             :         {
     127          18 :             --nPos; // we don't want to skip the tz separator
     128          18 :             return true;
     129             :         }
     130             :         else
     131           0 :             return false;
     132             :     }
     133          54 :     inline bool getISO8601TimeToken(const OUString &i_str, sal_Int32 &io_index, OUString &o_strInt, bool &o_bFraction, OUString &o_strFrac)
     134             :     {
     135          54 :         OUString resInt;
     136         108 :         OUString resFrac;
     137          54 :         bool bFraction = false;
     138          54 :         sal_Int32 index = io_index;
     139          54 :         if(!impl_getISO8601TimeToken(i_str, index, resInt, bFraction, resFrac))
     140           0 :             return false;
     141             :         else
     142             :         {
     143          54 :             io_index = index+1;
     144          54 :             o_strInt = resInt;
     145          54 :             o_strFrac = resFrac;
     146          54 :             o_bFraction = bFraction;
     147          54 :             return true;
     148          54 :         }
     149             :     }
     150          18 :     inline bool getISO8601TimeZoneToken(const OUString &i_str, sal_Int32 &io_index, OUString &o_strInt)
     151             :     {
     152          18 :         const sal_Unicode c0 = '0';
     153          18 :         const sal_Unicode c9 = '9';
     154          18 :         const sal_Unicode sep = ':';
     155          18 :         if (i_str[io_index] == 'Z') // UTC timezone indicator
     156             :         {
     157          18 :             ++io_index;
     158          18 :             o_strInt = "Z";
     159          18 :             return true;
     160             :         }
     161           0 :         else if (i_str[io_index] == '+' || i_str[io_index] == '-') // other timezones indicator
     162             :         {
     163           0 :             ++io_index;
     164           0 :             o_strInt = "";
     165           0 :             for (; io_index < i_str.getLength(); ++io_index)
     166             :             {
     167           0 :                 const sal_Unicode c = i_str[io_index];
     168           0 :                 if ((c < c0 || c > c9) && c != sep)
     169           0 :                     return false;
     170           0 :                 o_strInt += OUString(c);
     171             :             }
     172           0 :             return true;
     173             :         }
     174             :         else
     175           0 :             return false;
     176             :     }
     177             : }
     178             : 
     179             : namespace utl
     180             : {
     181             : 
     182           0 : void typeConvert(const Date& _rDate, css::util::Date& _rOut)
     183             : {
     184           0 :     _rOut.Day = _rDate.GetDay();
     185           0 :     _rOut.Month = _rDate.GetMonth();
     186           0 :     _rOut.Year = _rDate.GetYear();
     187           0 : }
     188             : 
     189           0 : void typeConvert(const css::util::Date& _rDate, Date& _rOut)
     190             : {
     191           0 :     _rOut = Date(_rDate.Day, _rDate.Month, _rDate.Year);
     192           0 : }
     193             : 
     194           0 : void typeConvert(const DateTime& _rDateTime, css::util::DateTime& _rOut)
     195             : {
     196           0 :     _rOut.Year = _rDateTime.GetYear();
     197           0 :     _rOut.Month = _rDateTime.GetMonth();
     198           0 :     _rOut.Day = _rDateTime.GetDay();
     199           0 :     _rOut.Hours = _rDateTime.GetHour();
     200           0 :     _rOut.Minutes = _rDateTime.GetMin();
     201           0 :     _rOut.Seconds = _rDateTime.GetSec();
     202           0 :     _rOut.NanoSeconds = _rDateTime.GetNanoSec();
     203           0 : }
     204             : 
     205           0 : void typeConvert(const css::util::DateTime& _rDateTime, DateTime& _rOut)
     206             : {
     207           0 :     Date aDate(_rDateTime.Day, _rDateTime.Month, _rDateTime.Year);
     208           0 :     tools::Time aTime(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds);
     209           0 :     _rOut = DateTime(aDate, aTime);
     210           0 : }
     211             : 
     212          18 : void extractDate(const css::util::DateTime& _rDateTime, css::util::Date& _rOut)
     213             : {
     214          18 :     _rOut.Day = _rDateTime.Day;
     215          18 :     _rOut.Month = _rDateTime.Month;
     216          18 :     _rOut.Year = _rDateTime.Year;
     217          18 : }
     218             : 
     219           0 : OUString toISO8601(const css::util::DateTime& rDateTime)
     220             : {
     221           0 :     OUStringBuffer rBuffer;
     222           0 :     rBuffer.append((sal_Int32) rDateTime.Year);
     223           0 :     rBuffer.append('-');
     224           0 :     if( rDateTime.Month < 10 )
     225           0 :         rBuffer.append('0');
     226           0 :     rBuffer.append((sal_Int32) rDateTime.Month);
     227           0 :     rBuffer.append('-');
     228           0 :     if( rDateTime.Day < 10 )
     229           0 :         rBuffer.append('0');
     230           0 :     rBuffer.append((sal_Int32) rDateTime.Day);
     231             : 
     232           0 :     if( rDateTime.NanoSeconds != 0 ||
     233           0 :         rDateTime.Seconds     != 0 ||
     234           0 :         rDateTime.Minutes     != 0 ||
     235           0 :         rDateTime.Hours       != 0 )
     236             :     {
     237           0 :         rBuffer.append('T');
     238           0 :         if( rDateTime.Hours < 10 )
     239           0 :             rBuffer.append('0');
     240           0 :         rBuffer.append((sal_Int32) rDateTime.Hours);
     241           0 :         rBuffer.append(':');
     242           0 :         if( rDateTime.Minutes < 10 )
     243           0 :             rBuffer.append('0');
     244           0 :         rBuffer.append((sal_Int32) rDateTime.Minutes);
     245           0 :         rBuffer.append(':');
     246           0 :         if( rDateTime.Seconds < 10 )
     247           0 :             rBuffer.append('0');
     248           0 :         rBuffer.append((sal_Int32) rDateTime.Seconds);
     249           0 :         if ( rDateTime.NanoSeconds > 0)
     250             :         {
     251             :             OSL_ENSURE(rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
     252           0 :             rBuffer.append(',');
     253           0 :             std::ostringstream ostr;
     254           0 :             ostr.fill('0');
     255           0 :             ostr.width(9);
     256           0 :             ostr << rDateTime.NanoSeconds;
     257           0 :             rBuffer.append(OUString::createFromAscii(ostr.str().c_str()));
     258             :         }
     259             :     }
     260           0 :     return rBuffer.makeStringAndClear();
     261             : }
     262             : 
     263             : /** convert ISO8601 DateTime String to util::DateTime */
     264          36 : bool ISO8601parseDateTime(const OUString &rString, css::util::DateTime& rDateTime)
     265             : {
     266          36 :     bool bSuccess = true;
     267             : 
     268          72 :     rtl::OUString aDateStr, aTimeStr;
     269          36 :     css::util::Date aDate;
     270          36 :     css::util::Time aTime;
     271          36 :     sal_Int32 nPos = rString.indexOf( 'T' );
     272          36 :     if ( nPos >= 0 )
     273             :     {
     274          18 :         aDateStr = rString.copy( 0, nPos );
     275          18 :         aTimeStr = rString.copy( nPos + 1 );
     276             :     }
     277             :     else
     278          18 :         aDateStr = rString;         // no separator: only date part
     279             : 
     280          36 :     bSuccess = ISO8601parseDate(aDateStr, aDate);
     281             : 
     282          36 :     if ( bSuccess && !aTimeStr.isEmpty() )           // time is optional
     283             :     {
     284          18 :         bSuccess = ISO8601parseTime(aTimeStr, aTime);
     285             :     }
     286             : 
     287          36 :     if (bSuccess)
     288             :     {
     289             :         rDateTime = css::util::DateTime(aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
     290          18 :                aDate.Day, aDate.Month, aDate.Year, false);
     291             :     }
     292             : 
     293          72 :     return bSuccess;
     294             : }
     295             : 
     296             : /** convert ISO8601 Date String to util::Date */
     297             : // TODO: supports only calendar dates YYYY-MM-DD
     298             : // MISSING: calendar dates YYYYMMDD YYYY-MM
     299             : //          year, week date, ordinal date
     300          36 : bool ISO8601parseDate(const OUString &aDateStr, css::util::Date& rDate)
     301             : {
     302          36 :     bool bSuccess = true;
     303             : 
     304          36 :     sal_Int32 nYear    = 1899;
     305          36 :     sal_Int32 nMonth   = 12;
     306          36 :     sal_Int32 nDay     = 30;
     307             : 
     308          36 :     const sal_Unicode* pStr = aDateStr.getStr();
     309          36 :     sal_Int32 nDateTokens = 1;
     310         252 :     while ( *pStr )
     311             :     {
     312         180 :         if ( *pStr == '-' )
     313          36 :             nDateTokens++;
     314         180 :         pStr++;
     315             :     }
     316          36 :     if ( nDateTokens > 3 || aDateStr.isEmpty() )
     317          18 :         bSuccess = false;
     318             :     else
     319             :     {
     320          18 :         sal_Int32 n = 0;
     321          18 :         if ( !convertNumber<sal_Int32>( nYear, aDateStr.getToken( 0, '-', n ), 0, 9999 ) )
     322           0 :             bSuccess = false;
     323          18 :         if ( nDateTokens >= 2 )
     324          18 :             if ( !convertNumber<sal_Int32>( nMonth, aDateStr.getToken( 0, '-', n ), 0, 12 ) )
     325           0 :                 bSuccess = false;
     326          18 :         if ( nDateTokens >= 3 )
     327          18 :             if ( !convertNumber<sal_Int32>( nDay, aDateStr.getToken( 0, '-', n ), 0, 31 ) )
     328           0 :                 bSuccess = false;
     329             :     }
     330             : 
     331          36 :     if (bSuccess)
     332             :     {
     333          18 :         rDate.Year = (sal_uInt16)nYear;
     334          18 :         rDate.Month = (sal_uInt16)nMonth;
     335          18 :         rDate.Day = (sal_uInt16)nDay;
     336             :     }
     337             : 
     338          36 :     return bSuccess;
     339             : }
     340             : 
     341             : /** convert ISO8601 Time String to util::Time */
     342          18 : bool ISO8601parseTime(const OUString &aTimeStr, css::util::Time& rTime)
     343             : {
     344          18 :     bool bSuccess = true;
     345             : 
     346          18 :     sal_Int32 nHour    = 0;
     347          18 :     sal_Int32 nMin     = 0;
     348          18 :     sal_Int32 nSec     = 0;
     349          18 :     sal_Int32 nNanoSec = 0;
     350             : 
     351          18 :     sal_Int32 n = 0;
     352          18 :     OUString tokInt;
     353          36 :     OUString tokFrac;
     354          36 :     OUString tokTz;
     355          18 :     bool bFrac = false;
     356             :     // hours
     357          18 :     if (bSuccess && (bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac)))
     358             :     {
     359          18 :         if ( bFrac && n < aTimeStr.getLength())
     360             :             // is it junk or the timezone?
     361           0 :             bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
     362          18 :         if (bSuccess && (bSuccess = convertNumber<sal_Int32>( nHour, tokInt, 0, 23 )) )
     363             :         {
     364          18 :             if (bFrac)
     365             :             {
     366             :                 sal_Int64 fracNumerator;
     367           0 :                 if ( (bSuccess = convertNumber(fracNumerator, tokFrac)) )
     368             :                 {
     369           0 :                     double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
     370             :                     // minutes
     371             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac hours (of hours) not between 0 and 1");
     372           0 :                     frac *= 60;
     373           0 :                     nMin = floor(frac);
     374           0 :                     frac -=  nMin;
     375             :                     // seconds
     376             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac minutes (of hours) not between 0 and 1");
     377           0 :                     frac *= 60;
     378           0 :                     nSec = floor(frac);
     379           0 :                     frac -=  nSec;
     380             :                     // nanoseconds
     381             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of hours) not between 0 and 1");
     382           0 :                     frac *= 1000000000;
     383           0 :                     nNanoSec = ::rtl::math::round(frac);
     384             :                 }
     385           0 :                 goto end;
     386             :             }
     387          18 :             if(n >= aTimeStr.getLength())
     388           0 :                 goto end;
     389             :         }
     390             :     }
     391             : 
     392             :     // minutes
     393          18 :     if (bSuccess && (bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac)))
     394             :     {
     395          18 :         if ( bFrac && n < aTimeStr.getLength())
     396             :             // is it junk or the timezone?
     397           0 :             bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
     398          18 :         if (bSuccess && (bSuccess = convertNumber<sal_Int32>( nMin, tokInt, 0, 59 )) )
     399             :         {
     400          18 :             if (bFrac)
     401             :             {
     402             :                 sal_Int64 fracNumerator;
     403           0 :                 if ( (bSuccess = convertNumber(fracNumerator, tokFrac)) )
     404             :                 {
     405           0 :                     double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
     406             :                     // seconds
     407             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac minutes (of minutes) not between 0 and 1");
     408           0 :                     frac *= 60;
     409           0 :                     nSec = floor(frac);
     410           0 :                     frac -=  nSec;
     411             :                     // nanoseconds
     412             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of minutes) not between 0 and 1");
     413           0 :                     frac *= 1000000000;
     414           0 :                     nNanoSec = ::rtl::math::round(frac);
     415             :                 }
     416           0 :                 goto end;
     417             :             }
     418          18 :             if(n >= aTimeStr.getLength())
     419           0 :                 goto end;
     420             :         }
     421             :     }
     422             :     // seconds
     423          18 :     if (bSuccess && (bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac)))
     424             :     {
     425          18 :         if (n < aTimeStr.getLength())
     426             :             // is it junk or the timezone?
     427          18 :             bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
     428             :         // max 60 for leap seconds
     429          18 :         if (bSuccess && (bSuccess = convertNumber<sal_Int32>( nSec, tokInt, 0, 60 )) )
     430             :         {
     431          18 :             if (bFrac)
     432             :             {
     433             :                 sal_Int64 fracNumerator;
     434           0 :                 if ( (bSuccess = convertNumber(fracNumerator, tokFrac)) )
     435             :                 {
     436           0 :                     double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
     437             :                     // nanoseconds
     438             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of seconds) not between 0 and 1");
     439           0 :                     frac *= 1000000000;
     440           0 :                     nNanoSec = ::rtl::math::round(frac);
     441             :                 }
     442           0 :                 goto end;
     443             :             }
     444             :         }
     445             :     }
     446             : 
     447             :     end:
     448          18 :     if (bSuccess)
     449             :     {
     450             :         // normalise time
     451          18 :         const int secondsOverflow = (nSec == 60) ? 61 : 60;
     452          18 :         if (nNanoSec == 1000000000)
     453             :         {
     454           0 :             nNanoSec = 0;
     455           0 :             ++nSec;
     456             :         }
     457          18 :         if(nSec == secondsOverflow)
     458             :         {
     459           0 :             nSec = 0;
     460           0 :             ++nMin;
     461             :         }
     462          18 :         if(nMin == 60)
     463             :         {
     464           0 :             nMin = 0;
     465           0 :             ++nHour;
     466             :         }
     467          18 :         if(!tokTz.isEmpty())
     468          18 :             rTime.IsUTC = (tokTz == "Z");
     469             : 
     470          18 :         rTime.Hours = (sal_uInt16)nHour;
     471          18 :         rTime.Minutes = (sal_uInt16)nMin;
     472          18 :         rTime.Seconds = (sal_uInt16)nSec;
     473          18 :         rTime.NanoSeconds = nNanoSec;
     474             :     }
     475             : 
     476          36 :     return bSuccess;
     477             : }
     478             : 
     479             : }   // namespace utl
     480             : 
     481             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10