LCOV - code coverage report
Current view: top level - unotools/source/misc - datetime.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 135 266 50.8 %
Date: 2014-04-11 Functions: 8 15 53.3 %
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          48 :     bool convertNumber( T& rValue,
      32             :                         const OUString& rString,
      33             :                         T /*nMin*/ = -1, T /*nMax*/ = -1)
      34             :     {
      35          48 :         bool bNeg = false;
      36          48 :         rValue = 0;
      37             : 
      38          48 :         sal_Int32 nPos = 0L;
      39          48 :         sal_Int32 nLen = rString.getLength();
      40             : 
      41             :         // skip white space
      42          96 :         while( nPos < nLen && ' ' == rString[nPos] )
      43           0 :             nPos++;
      44             : 
      45          48 :         if( nPos < nLen && '-' == rString[nPos] )
      46             :         {
      47           0 :             bNeg = true;
      48           0 :             nPos++;
      49             :         }
      50             : 
      51             :         // get number
      52         432 :         while( nPos < nLen &&
      53         112 :                '0' <= rString[nPos] &&
      54         112 :                '9' >= rString[nPos] )
      55             :         {
      56             :             // TODO: check overflow!
      57         112 :             rValue *= 10;
      58         112 :             rValue += (rString[nPos] - sal_Unicode('0'));
      59         112 :             nPos++;
      60             :         }
      61             : 
      62          48 :         if( bNeg )
      63           0 :             rValue *= -1;
      64             : 
      65          48 :         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          24 :     bool impl_getISO8601TimeToken(const OUString &i_str, sal_Int32 &nPos, OUString &resInt, bool &bFraction, OUString &resFrac)
      85             :     {
      86          24 :         bFraction = false;
      87             :         // all tokens are of length 2
      88          24 :         const sal_Int32 nEndPos = nPos + 2;
      89          24 :         const sal_Unicode c0 = '0';
      90          24 :         const sal_Unicode c9 = '9';
      91          24 :         const sal_Unicode sep = ':';
      92          72 :         for (;nPos < nEndPos && nPos < i_str.getLength(); ++nPos)
      93             :         {
      94          48 :             const sal_Unicode c = i_str[nPos];
      95          48 :             if (c == sep)
      96           0 :                 return true;
      97          48 :             if (c < c0 || c > c9)
      98           0 :                 return false;
      99          48 :             resInt += OUString(c);
     100             :         }
     101          24 :         if (nPos == i_str.getLength() || i_str[nPos] == sep)
     102          16 :             return true;
     103           8 :         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           8 :         if (i_str[nPos] == 'Z' || i_str[nPos] == '+' || i_str[nPos] == '-')
     126             :         {
     127           8 :             --nPos; // we don't want to skip the tz separator
     128           8 :             return true;
     129             :         }
     130             :         else
     131           0 :             return false;
     132             :     }
     133          24 :     inline bool getISO8601TimeToken(const OUString &i_str, sal_Int32 &io_index, OUString &o_strInt, bool &o_bFraction, OUString &o_strFrac)
     134             :     {
     135          24 :         OUString resInt;
     136          48 :         OUString resFrac;
     137          24 :         bool bFraction = false;
     138          24 :         sal_Int32 index = io_index;
     139          24 :         if(!impl_getISO8601TimeToken(i_str, index, resInt, bFraction, resFrac))
     140           0 :             return false;
     141             :         else
     142             :         {
     143          24 :             io_index = index+1;
     144          24 :             o_strInt = resInt;
     145          24 :             o_strFrac = resFrac;
     146          24 :             o_bFraction = bFraction;
     147          24 :             return true;
     148          24 :         }
     149             :     }
     150           8 :     inline bool getISO8601TimeZoneToken(const OUString &i_str, sal_Int32 &io_index, OUString &o_strInt)
     151             :     {
     152           8 :         const sal_Unicode c0 = '0';
     153           8 :         const sal_Unicode c9 = '9';
     154           8 :         const sal_Unicode sep = ':';
     155           8 :         if (i_str[io_index] == 'Z') // UTC timezone indicator
     156             :         {
     157           8 :             ++io_index;
     158           8 :             o_strInt = "Z";
     159           8 :             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, starutil::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 starutil::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, starutil::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 starutil::DateTime& _rDateTime, DateTime& _rOut)
     206             : {
     207           0 :     Date aDate(_rDateTime.Day, _rDateTime.Month, _rDateTime.Year);
     208           0 :     Time aTime(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds);
     209           0 :     _rOut = DateTime(aDate, aTime);
     210           0 : }
     211             : 
     212           8 : void extractDate(const starutil::DateTime& _rDateTime, starutil::Date& _rOut)
     213             : {
     214           8 :     _rOut.Day = _rDateTime.Day;
     215           8 :     _rOut.Month = _rDateTime.Month;
     216           8 :     _rOut.Year = _rDateTime.Year;
     217           8 : }
     218             : 
     219           0 : void extractTime(const starutil::DateTime& _rDateTime, starutil::Time& _rOut)
     220             : {
     221           0 :     _rOut.Hours = _rDateTime.Hours;
     222           0 :     _rOut.Minutes =  _rDateTime.Minutes;
     223           0 :     _rOut.Seconds = _rDateTime.Seconds;
     224           0 :     _rOut.NanoSeconds = _rDateTime.NanoSeconds;
     225           0 : }
     226             : 
     227           0 : OUString toISO8601(const starutil::DateTime& rDateTime)
     228             : {
     229           0 :     OUStringBuffer rBuffer;
     230           0 :     rBuffer.append((sal_Int32) rDateTime.Year);
     231           0 :     rBuffer.append('-');
     232           0 :     if( rDateTime.Month < 10 )
     233           0 :         rBuffer.append('0');
     234           0 :     rBuffer.append((sal_Int32) rDateTime.Month);
     235           0 :     rBuffer.append('-');
     236           0 :     if( rDateTime.Day < 10 )
     237           0 :         rBuffer.append('0');
     238           0 :     rBuffer.append((sal_Int32) rDateTime.Day);
     239             : 
     240           0 :     if( rDateTime.NanoSeconds != 0 ||
     241           0 :         rDateTime.Seconds     != 0 ||
     242           0 :         rDateTime.Minutes     != 0 ||
     243           0 :         rDateTime.Hours       != 0 )
     244             :     {
     245           0 :         rBuffer.append('T');
     246           0 :         if( rDateTime.Hours < 10 )
     247           0 :             rBuffer.append('0');
     248           0 :         rBuffer.append((sal_Int32) rDateTime.Hours);
     249           0 :         rBuffer.append(':');
     250           0 :         if( rDateTime.Minutes < 10 )
     251           0 :             rBuffer.append('0');
     252           0 :         rBuffer.append((sal_Int32) rDateTime.Minutes);
     253           0 :         rBuffer.append(':');
     254           0 :         if( rDateTime.Seconds < 10 )
     255           0 :             rBuffer.append('0');
     256           0 :         rBuffer.append((sal_Int32) rDateTime.Seconds);
     257           0 :         if ( rDateTime.NanoSeconds > 0)
     258             :         {
     259             :             OSL_ENSURE(rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
     260           0 :             rBuffer.append(',');
     261           0 :             std::ostringstream ostr;
     262           0 :             ostr.fill('0');
     263           0 :             ostr.width(9);
     264           0 :             ostr << rDateTime.NanoSeconds;
     265           0 :             rBuffer.append(OUString::createFromAscii(ostr.str().c_str()));
     266             :         }
     267             :     }
     268           0 :     return rBuffer.makeStringAndClear();
     269             : }
     270             : 
     271             : /** convert ISO8601 DateTime String to util::DateTime */
     272          11 : bool ISO8601parseDateTime(const OUString &rString, starutil::DateTime& rDateTime)
     273             : {
     274          11 :     bool bSuccess = true;
     275             : 
     276          22 :     rtl::OUString aDateStr, aTimeStr;
     277          11 :     starutil::Date aDate;
     278          11 :     starutil::Time aTime;
     279          11 :     sal_Int32 nPos = rString.indexOf( 'T' );
     280          11 :     if ( nPos >= 0 )
     281             :     {
     282           8 :         aDateStr = rString.copy( 0, nPos );
     283           8 :         aTimeStr = rString.copy( nPos + 1 );
     284             :     }
     285             :     else
     286           3 :         aDateStr = rString;         // no separator: only date part
     287             : 
     288          11 :     bSuccess = ISO8601parseDate(aDateStr, aDate);
     289             : 
     290          11 :     if ( bSuccess && !aTimeStr.isEmpty() )           // time is optional
     291             :     {
     292           8 :         bSuccess = ISO8601parseTime(aTimeStr, aTime);
     293             :     }
     294             : 
     295          11 :     if (bSuccess)
     296             :     {
     297             :         rDateTime = starutil::DateTime(aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
     298           8 :                aDate.Day, aDate.Month, aDate.Year, false);
     299             :     }
     300             : 
     301          22 :     return bSuccess;
     302             : }
     303             : 
     304             : /** convert ISO8601 Date String to util::Date */
     305             : // TODO: supports only calendar dates YYYY-MM-DD
     306             : // MISSING: calendar dates YYYYMMDD YYYY-MM
     307             : //          year, week date, ordinal date
     308          11 : bool ISO8601parseDate(const OUString &aDateStr, starutil::Date& rDate)
     309             : {
     310          11 :     bool bSuccess = true;
     311             : 
     312          11 :     sal_Int32 nYear    = 1899;
     313          11 :     sal_Int32 nMonth   = 12;
     314          11 :     sal_Int32 nDay     = 30;
     315             : 
     316          11 :     const sal_Unicode* pStr = aDateStr.getStr();
     317          11 :     sal_Int32 nDateTokens = 1;
     318         102 :     while ( *pStr )
     319             :     {
     320          80 :         if ( *pStr == '-' )
     321          16 :             nDateTokens++;
     322          80 :         pStr++;
     323             :     }
     324          11 :     if ( nDateTokens > 3 || aDateStr.isEmpty() )
     325           3 :         bSuccess = false;
     326             :     else
     327             :     {
     328           8 :         sal_Int32 n = 0;
     329           8 :         if ( !convertNumber<sal_Int32>( nYear, aDateStr.getToken( 0, '-', n ), 0, 9999 ) )
     330           0 :             bSuccess = false;
     331           8 :         if ( nDateTokens >= 2 )
     332           8 :             if ( !convertNumber<sal_Int32>( nMonth, aDateStr.getToken( 0, '-', n ), 0, 12 ) )
     333           0 :                 bSuccess = false;
     334           8 :         if ( nDateTokens >= 3 )
     335           8 :             if ( !convertNumber<sal_Int32>( nDay, aDateStr.getToken( 0, '-', n ), 0, 31 ) )
     336           0 :                 bSuccess = false;
     337             :     }
     338             : 
     339          11 :     if (bSuccess)
     340             :     {
     341           8 :         rDate.Year = (sal_uInt16)nYear;
     342           8 :         rDate.Month = (sal_uInt16)nMonth;
     343           8 :         rDate.Day = (sal_uInt16)nDay;
     344             :     }
     345             : 
     346          11 :     return bSuccess;
     347             : }
     348             : 
     349             : /** convert ISO8601 Time String to util::Time */
     350           8 : bool ISO8601parseTime(const OUString &aTimeStr, starutil::Time& rTime)
     351             : {
     352           8 :     bool bSuccess = true;
     353             : 
     354           8 :     sal_Int32 nHour    = 0;
     355           8 :     sal_Int32 nMin     = 0;
     356           8 :     sal_Int32 nSec     = 0;
     357           8 :     sal_Int32 nNanoSec = 0;
     358             : 
     359           8 :     sal_Int32 n = 0;
     360           8 :     OUString tokInt;
     361          16 :     OUString tokFrac;
     362          16 :     OUString tokTz;
     363           8 :     bool bFrac = false;
     364             :     // hours
     365           8 :     if (bSuccess && (bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac)))
     366             :     {
     367           8 :         if ( bFrac && n < aTimeStr.getLength())
     368             :             // is it junk or the timezone?
     369           0 :             bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
     370           8 :         if (bSuccess && (bSuccess = convertNumber<sal_Int32>( nHour, tokInt, 0, 23 )) )
     371             :         {
     372           8 :             if (bFrac)
     373             :             {
     374             :                 sal_Int64 fracNumerator;
     375           0 :                 if ( (bSuccess = convertNumber(fracNumerator, tokFrac)) )
     376             :                 {
     377           0 :                     double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
     378             :                     // minutes
     379             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac hours (of hours) not between 0 and 1");
     380           0 :                     frac *= 60;
     381           0 :                     nMin = floor(frac);
     382           0 :                     frac -=  nMin;
     383             :                     // seconds
     384             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac minutes (of hours) not between 0 and 1");
     385           0 :                     frac *= 60;
     386           0 :                     nSec = floor(frac);
     387           0 :                     frac -=  nSec;
     388             :                     // nanoseconds
     389             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of hours) not between 0 and 1");
     390           0 :                     frac *= 1000000000;
     391           0 :                     nNanoSec = ::rtl::math::round(frac);
     392             :                 }
     393           0 :                 goto end;
     394             :             }
     395           8 :             if(n >= aTimeStr.getLength())
     396           0 :                 goto end;
     397             :         }
     398             :     }
     399             : 
     400             :     // minutes
     401           8 :     if (bSuccess && (bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac)))
     402             :     {
     403           8 :         if ( bFrac && n < aTimeStr.getLength())
     404             :             // is it junk or the timezone?
     405           0 :             bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
     406           8 :         if (bSuccess && (bSuccess = convertNumber<sal_Int32>( nMin, tokInt, 0, 59 )) )
     407             :         {
     408           8 :             if (bFrac)
     409             :             {
     410             :                 sal_Int64 fracNumerator;
     411           0 :                 if ( (bSuccess = convertNumber(fracNumerator, tokFrac)) )
     412             :                 {
     413           0 :                     double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
     414             :                     // seconds
     415             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac minutes (of minutes) not between 0 and 1");
     416           0 :                     frac *= 60;
     417           0 :                     nSec = floor(frac);
     418           0 :                     frac -=  nSec;
     419             :                     // nanoseconds
     420             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of minutes) not between 0 and 1");
     421           0 :                     frac *= 1000000000;
     422           0 :                     nNanoSec = ::rtl::math::round(frac);
     423             :                 }
     424           0 :                 goto end;
     425             :             }
     426           8 :             if(n >= aTimeStr.getLength())
     427           0 :                 goto end;
     428             :         }
     429             :     }
     430             :     // seconds
     431           8 :     if (bSuccess && (bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac)))
     432             :     {
     433           8 :         if (n < aTimeStr.getLength())
     434             :             // is it junk or the timezone?
     435           8 :             bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
     436             :         // max 60 for leap seconds
     437           8 :         if (bSuccess && (bSuccess = convertNumber<sal_Int32>( nSec, tokInt, 0, 60 )) )
     438             :         {
     439           8 :             if (bFrac)
     440             :             {
     441             :                 sal_Int64 fracNumerator;
     442           0 :                 if ( (bSuccess = convertNumber(fracNumerator, tokFrac)) )
     443             :                 {
     444           0 :                     double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
     445             :                     // nanoseconds
     446             :                     OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of seconds) not between 0 and 1");
     447           0 :                     frac *= 1000000000;
     448           0 :                     nNanoSec = ::rtl::math::round(frac);
     449             :                 }
     450           0 :                 goto end;
     451             :             }
     452             :         }
     453             :     }
     454             : 
     455             :     end:
     456           8 :     if (bSuccess)
     457             :     {
     458             :         // normalise time
     459           8 :         const int secondsOverflow = (nSec == 60) ? 61 : 60;
     460           8 :         if (nNanoSec == 1000000000)
     461             :         {
     462           0 :             nNanoSec = 0;
     463           0 :             ++nSec;
     464             :         }
     465           8 :         if(nSec == secondsOverflow)
     466             :         {
     467           0 :             nSec = 0;
     468           0 :             ++nMin;
     469             :         }
     470           8 :         if(nMin == 60)
     471             :         {
     472           0 :             nMin = 0;
     473           0 :             ++nHour;
     474             :         }
     475           8 :         if(!tokTz.isEmpty())
     476           8 :             rTime.IsUTC = (tokTz == "Z");
     477             : 
     478           8 :         rTime.Hours = (sal_uInt16)nHour;
     479           8 :         rTime.Minutes = (sal_uInt16)nMin;
     480           8 :         rTime.Seconds = (sal_uInt16)nSec;
     481           8 :         rTime.NanoSeconds = nNanoSec;
     482             :     }
     483             : 
     484          16 :     return bSuccess;
     485             : }
     486             : 
     487             : }   // namespace utl
     488             : 
     489             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10