00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "PSTime/TimeFormat.h"
00017
00018
00019
00020
00021 #include <stdio.h>
00022 #include <string.h>
00023 #include <boost/regex.hpp>
00024 #include <algorithm>
00025 #include <sstream>
00026 #include <iomanip>
00027
00028
00029
00030
00031 #include "PSTime/Exceptions.h"
00032 #include "PSTime/TimeUtils.h"
00033
00034
00035
00036
00037
00038 using namespace std;
00039
00040 namespace {
00041
00042
00043 #define DATE_RE "(\\d{4})(?:-?(\\d{2})(?:-?(\\d{2}))?)?"
00044 #define TIME_RE "(\\d{1,2})(?::?(\\d{2})(?::?(\\d{2})(?:[.](\\d{1,9}))?)?)?"
00045 #define TZ_RE "Z|(?:([-+])(\\d{2})(?::?(\\d{2}))?)"
00046 boost::regex dtre( "^" DATE_RE "(?:(?: +|T)(?:" TIME_RE ")?(" TZ_RE ")?)?$" ) ;
00047
00048
00049 boost::regex secre ( "^S(\\d{0,10})(?:[.](\\d{1,9}))?$" ) ;
00050
00051
00052
00053
00054
00055
00056
00057 long getNsec ( const std::string& nsecStr )
00058 {
00059 char buf[10] ;
00060 unsigned ndig = nsecStr.length() ;
00061 if ( ndig > 9 ) ndig = 9 ;
00062
00063 std::copy ( nsecStr.begin(), nsecStr.begin()+ndig, buf ) ;
00064 std::fill ( buf+ndig, buf+9, '0' ) ;
00065 buf[9] = '\0' ;
00066 return strtol ( buf, 0, 10 ) ;
00067 }
00068
00069
00070 bool cmp_tm ( struct tm* lhs, struct tm* rhs )
00071 {
00072 if ( lhs->tm_year != rhs->tm_year ) return false ;
00073 if ( lhs->tm_mon != rhs->tm_mon ) return false ;
00074 if ( lhs->tm_mday != rhs->tm_mday ) return false ;
00075 if ( lhs->tm_hour != rhs->tm_hour ) return false ;
00076 if ( lhs->tm_min != rhs->tm_min ) return false ;
00077 if ( lhs->tm_sec != rhs->tm_sec ) return false ;
00078 if ( lhs->tm_isdst >= 0 and rhs->tm_isdst >= 0 ) {
00079 if ( lhs->tm_isdst != rhs->tm_isdst ) return false ;
00080 }
00081 return true ;
00082 }
00083
00084
00085
00086 void formatNsec(uint32_t nsec, int prec, std::ostream& str)
00087 {
00088
00089 if (prec < 1) prec = 1;
00090 if (prec > 9) prec = 9;
00091
00092
00093 char buf[16];
00094 snprintf(buf, sizeof buf, ".%09d", int(nsec));
00095
00096
00097 buf[prec+1] = '\0';
00098 str << buf;
00099 }
00100 }
00101
00102
00103
00104
00105
00106 namespace PSTime {
00107 namespace TimeFormat {
00108
00109
00110
00111
00112 Time
00113 parseTime( const std::string& timeStr )
00114 {
00115 time_t sec = 0 ;
00116 long nsec = 0 ;
00117
00118
00119 boost::smatch match ;
00120 if ( boost::regex_match( timeStr, match, secre ) ) {
00121
00122
00123 sec = strtoul ( match.str(1).c_str(), 0, 10 ) ;
00124 if ( match[2].matched ) nsec = getNsec( match[2] ) ;
00125
00126 } else if ( boost::regex_match( timeStr, match, dtre ) ) {
00127
00128
00129
00130 struct tm stm ;
00131 memset( &stm, 0, sizeof stm ) ;
00132
00133 stm.tm_mday = 1 ;
00134
00135
00136 stm.tm_year = strtoul ( match.str(1).c_str(), 0, 10 ) ;
00137 stm.tm_year -= 1900 ;
00138 if ( match[2].matched ) stm.tm_mon = strtoul ( match.str(2).c_str(), 0, 10 ) - 1 ;
00139 if ( stm.tm_mon < 0 or stm.tm_mon > 11 ) throw TimeParseException( ERR_LOC, "month out of range" ) ;
00140 if ( match[3].matched ) stm.tm_mday = strtoul ( match.str(3).c_str(), 0, 10 ) ;
00141 if ( stm.tm_mday < 1 or stm.tm_mday > 31 ) throw TimeParseException( ERR_LOC, "day out of range" ) ;
00142
00143
00144 if ( match[4].matched ) stm.tm_hour = strtoul ( match.str(4).c_str(), 0, 10 ) ;
00145 if ( stm.tm_hour < 0 or stm.tm_hour > 23 ) throw TimeParseException( ERR_LOC, "hours out of range" ) ;
00146 if ( match[5].matched ) stm.tm_min = strtoul ( match.str(5).c_str(), 0, 10 ) ;
00147 if ( stm.tm_min < 0 or stm.tm_min > 59 ) throw TimeParseException( ERR_LOC, "minutes out of range" ) ;
00148 if ( match[6].matched ) stm.tm_sec = strtoul ( match.str(6).c_str(), 0, 10 ) ;
00149 if ( stm.tm_sec < 0 or stm.tm_sec > 60 ) throw TimeParseException( ERR_LOC, "seconds out of range" ) ;
00150 if ( match[7].matched ) nsec = getNsec( match[7] ) ;
00151
00152 if ( match[8].matched ) {
00153
00154
00155
00156 int tzoffset_min = 0 ;
00157 if ( match[8] != "Z" ) {
00158
00159
00160 int tz_hour = strtol ( match.str(10).c_str(), 0, 10 ) ;
00161 int tz_min = 0 ;
00162 if ( match[11].matched ) tz_min = strtol ( match.str(11).c_str(), 0, 10 ) ;
00163 if ( tz_hour > 12 or tz_min > 59 ) throw TimeParseException( ERR_LOC, "timezone out of range" ) ;
00164
00165 tzoffset_min = tz_hour * 60 ;
00166 tzoffset_min += tz_min ;
00167 if ( match[9] == "-" ) tzoffset_min = -tzoffset_min ;
00168
00169 }
00170
00171 struct tm vtm = stm ;
00172 sec = TimeUtils::timegm( &stm ) ;
00173 if ( time_t(-1) == sec ) throw TimeParseException( ERR_LOC, "timegm() failed" ) ;
00174
00175
00176 if ( not ::cmp_tm( &stm, &vtm ) ) throw TimeParseException( ERR_LOC, "input time validation failed" ) ;
00177
00178
00179 sec -= tzoffset_min * 60 ;
00180
00181 } else {
00182
00183
00184
00185 stm.tm_isdst = -1 ;
00186 struct tm vtm = stm ;
00187 sec = mktime( &stm ) ;
00188 if ( time_t(-1) == sec ) throw TimeParseException( ERR_LOC, "mktime() failed" ) ;
00189
00190
00191 if ( not ::cmp_tm( &stm, &vtm ) ) throw TimeParseException( ERR_LOC, "input time validation failed" ) ;
00192
00193 }
00194
00195 } else {
00196
00197 throw TimeParseException( ERR_LOC, "failed to parse the string: "+timeStr ) ;
00198
00199 }
00200
00201 return Time( sec, nsec ) ;
00202 }
00203
00204
00205
00206
00207 std::string
00208 format ( const Time& time, const std::string& afmt, Time::Zone zone )
00209 {
00210 std::ostringstream str;
00211 format ( str, time, afmt, zone );
00212 return str.str();
00213 }
00214
00215 void
00216 format ( std::ostream& str, const Time& time, const std::string& afmt, Time::Zone zone )
00217 {
00218
00219
00220 struct tm stm = time.gettm(zone);
00221
00222 char fill = str.fill('0');
00223
00224 for (std::string::size_type p = 0; p != afmt.size(); ++p) {
00225 if (afmt[p] != '%') {
00226
00227 str << afmt[p];
00228 } else if (p+1 == afmt.size()) {
00229
00230 str << afmt[p];
00231 } else {
00232
00233
00234 ++ p;
00235
00236 switch (afmt[p]) {
00237 case '%' :
00238 str << afmt[p];
00239 break;
00240 case 'd' :
00241
00242 str << setw(2) << stm.tm_mday ;
00243 break;
00244 case 'F' :
00245
00246 str << setw(4) << stm.tm_year+1900 << '-' << setw(2) << stm.tm_mon+1
00247 << '-' << setw(2) << stm.tm_mday;
00248 break;
00249 case 'H' :
00250
00251 str << setw(2) << stm.tm_hour;
00252 break;
00253 case 'm' :
00254
00255 str << setw(2) << stm.tm_mon+1;
00256 break;
00257 case 'M' :
00258
00259 str << setw(2) << stm.tm_min;
00260 break;
00261 case 's' :
00262
00263 str << time.sec();
00264 break;
00265 case 'S' :
00266
00267 str << setw(2) << stm.tm_sec;
00268 break;
00269 case 'T' :
00270
00271 str << setw(2) << stm.tm_hour << ':' << setw(2) << stm.tm_min
00272 << ':' << setw(2) << stm.tm_sec;
00273 break;
00274 case 'Y' :
00275
00276 str << setw(4) << stm.tm_year+1900;
00277 break;
00278 case 'z' :
00279
00280 if (zone == Time::UTC) {
00281 str << 'Z';
00282 } else {
00283
00284 char sign = stm.tm_gmtoff > 0 ? '+' : '-' ;
00285 int offmin = abs(stm.tm_gmtoff / 60);
00286 int offhour = offmin / 60;
00287 offmin %= 60;
00288 str << sign << setw(2) << offhour;
00289 if (offmin) str << setw(2) << offmin;
00290 }
00291 break;
00292 case '.' :
00293
00294 if (p+2 < afmt.size() and isdigit(afmt[p+1]) and afmt[p+2] == 'f') {
00295 ::formatNsec(time.nsec(), afmt[p+1]-'0', str);
00296 p += 2;
00297 }
00298 break;
00299 case 'f' :
00300
00301 ::formatNsec(time.nsec(), 9, str);
00302 break;
00303 default :
00304
00305 str << '%' << afmt[p];
00306 break;
00307 }
00308 }
00309 }
00310
00311 str.fill(fill);
00312 }
00313
00314
00315 }
00316 }