Bug Summary

File:libsynthesis/src/sysync/iso8601.cpp
Warning:line 191, column 5
Value stored to 'aISOString' is never read

Annotated Source Code

1/*
2 * File: iso8601.cpp
3 *
4 * Author: Lukas Zeller (luz@plan44.ch)
5 *
6 * conversion from/to linear time scale.
7 *
8 * Copyright (c) 2002-2011 by Synthesis AG + plan44.ch
9 *
10 * 2002-05-02 : luz : extracted from sysync_utils
11 *
12 */
13
14#include "prefix_file.h"
15
16#include "iso8601.h"
17#include "stringutils.h"
18#include "timezones.h"
19
20#if defined(EXPIRES_AFTER_DATE) && !defined(FULLY_STANDALONE)
21 // only if used in sysync context
22 #include "sysync.h"
23#endif
24
25
26namespace sysync {
27
28/// @brief convert ISO8601 to timestamp and timezone
29/// @return number of successfully converted characters or 0 if no valid ISO8601 specification could be decoded
30/// @param[in] aISOString input string in ISO8601
31/// @param[out] aTimestamp representation of ISO time spec as is (no time zone conversions)
32/// @param[out] aWithTime if set, time specification was found in input string
33/// @param[out] aTimeContext:
34/// TCTX_DURATION if ISO8601 is a duration format (PnDTnHnMnS.MS)
35/// TCTX_UNKNOWN if ISO8601 does not include a time zone specification
36/// TCTX_UTC if ISO8601 ends with the "Z" specifier
37/// TCTX_DATEONLY if ISO8601 only contains a date, but no time
38/// TCTX_OFFSCONTEXT(xx) if ISO8601 explicitly specifies a UTC offset
39sInt16 ISO8601StrToTimestamp(cAppCharP aISOString, lineartime_t &aTimestamp, timecontext_t &aTimeContext)
40{
41 uInt16 y,m,d,hr,mi,s,ms; // unsigned because these can't be signed when parsing
42 bool isExtended=false;
43 sInt16 n,h,sg;
44
45 n=0;
46 // check if it might be duration
47 sg=0; // duration sign
48 // - sign
49 if (*aISOString=='-') {
50 sg=-1; // negative duration
51 aISOString++; n++;
52 }
53 else if(*aISOString=='+') {
54 sg=1; // positive duration
55 aISOString++; n++;
56 }
57 if (*aISOString=='P') {
58 if (sg==0) sg=1;
59 aISOString++; n++;
60 }
61 else if (sg!=0)
62 return 0; // we had a sign, but no 'P' -> invalid ISO8601 date
63 // if y!=0 here, this is a duration
64 if (sg!=0) {
65 // duration
66 aTimestamp = 0;
67 aTimeContext = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)) + TCTX_DURATION;
68 bool timepart = false;
69 // parse duration parts
70 while (true) {
71 if (*aISOString=='T' && !timepart) {
72 timepart = true;
73 aISOString++; n++;
74 }
75 #ifdef NO_FLOATS
76 sInt32 part;
77 h = StrToLong(aISOString,part);
78 #else
79 double part;
80 h = StrToDouble(aISOString,part);
81 #endif
82 if (h>0) {
83 aISOString+=h; n+=h;
84 // this is a part, must be followed by a designator
85 switch (*aISOString) {
86 case 'Y':
87 part*=365; goto days; // approximate year
88 case 'M': // month or minute
89 if (timepart) {
90 aTimestamp += (lineartime_t)(part*sg*secondToLinearTimeFactor*SecsPerMin); // minute
91 break;
92 }
93 else {
94 part*=30; // approximate month
95 goto days;
96 }
97 case 'W':
98 part *= 7; goto days; // one week
99 case 'D':
100 days:
101 aTimestamp += (lineartime_t)(part*sg*linearDateToTimeFactor);
102 break;
103 case 'H':
104 aTimestamp += (lineartime_t)(part*sg*secondToLinearTimeFactor*SecsPerHour);
105 break;
106 case 'S':
107 aTimestamp += (lineartime_t)(part*sg*secondToLinearTimeFactor);
108 break;
109 default:
110 return 0; // bad designator, error
111 }
112 aISOString++; n++;
113 } // if number
114 else {
115 // no more digits -> end of duration string
116 return n; // number of chars processed
117 }
118 } // while
119 } // if duration
120 // try to parse date
121 // - first should be 4 digit year
122 h = StrToUShort(aISOString,y,4);
123 if (h!=4) return 0; // no ISO8601 date
124 aISOString+=h; n+=h;
125 // - test for format
126 if (*aISOString=='-') {
127 isExtended=true;
128 aISOString++; n++;
129 }
130 // - next must be 2 digit month
131 h = StrToUShort(aISOString,m,2);
132 if (h!=2) return 0; // no ISO8601 date
133 aISOString+=h; n+=h;
134 // - check separator in case of extended format
135 if (isExtended) {
136 if (*aISOString != '-') return 0; // missing separator, no ISO8601 date
137 aISOString++; n++;
138 }
139 // - next must be 2 digit day
140 h = StrToUShort(aISOString,d,2);
141 if (h!=2) return 0; // no ISO8601 date
142 aISOString+=h; n+=h;
143 // convert date to timestamp
144 aTimestamp = date2lineartime(y,m,d);
145 // Now next must be "T" if time spec is included
146 if (*aISOString!='T') {
147 // date-only, floating
148 aTimeContext = TCTX_DATEONLY|TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
149 }
150 else {
151 // parse time as well
152 aISOString++; n++; // skip "T"
153 mi=0; s=0; ms=0; // reset optional time components
154 // - next must be 2 digit hour
155 h = StrToUShort(aISOString,hr,2);
156 if (h!=2) return 0; // no ISO8601 time, we need the hour, minimally
157 aISOString+=h; n+=h;
158 // - check separator in case of extended format
159 if (isExtended) {
160 if (*aISOString != ':') return 0; // missing separator, no ISO8601 time (Note: hour-only reduced precision does not exist for extended format)
161 aISOString++; n++;
162 }
163 // - next must be 2 digit minute (or nothing for hour-only reduced precision basic format)
164 h = StrToUShort(aISOString,mi,2);
165 if (!isExtended && h==0) goto timeok;
166 if (h!=2) return 0; // no ISO8601 time, must be 2 digits here
167 aISOString+=h; n+=h;
168 // - check separator in case of extended format
169 if (isExtended) {
170 if (*aISOString != ':') goto timeok; // no separator means hour:minute reduced precision for extended format
171 aISOString++; n++;
172 }
173 // - next must be 2 digit second (or nothing for reduced precision without seconds)
174 h = StrToUShort(aISOString,s,2);
175 if (!isExtended && h==0) goto timeok; // no seconds is ok for basic format only (in extended format, the separator must be omitted as well, which is checked above)
176 if (h!=2) return 0; // no ISO8601 time, must be 2 digits here
177 aISOString+=h; n+=h;
178 // optional fractions of seconds
179 if (*aISOString=='.') {
180 aISOString++; n++;
181 h = StrToUShort(aISOString,ms,3);
182 if (h==0) return 0; // invalid fraction specified
183 aISOString+=h; n+=h;
184 while (h<3) { ms *= 10; h++; } // make milliseconds
185 }
186 timeok:
187 // add to timestamp
188 aTimestamp += time2lineartime(hr,mi,s,ms);
189 // check for zone specification
190 h = ISO8601StrToContext(aISOString,aTimeContext);
191 aISOString+=h; n+=h;
Value stored to 'aISOString' is never read
192 }
193 // return number of characters converted
194 return n;
195} // ISO8601StrToTimestamp
196
197
198
199
200/// @brief convert ISO8601 zone offset to internal time zone
201/// @return number of successfully converted characters or 0 if no valid ISO8601 time zone spec could be decoded
202/// @param[in] aISOString input string in ISO8601 time zone offset format (or just "Z" for UTC)
203/// @param[out] aTimeContext:
204/// TCTX_UNKNOWN if no time zone specification is found
205/// TCTX_UTC if "Z" specifier found
206/// TCTX_OFFSCONTEXT(xx) if explicit UTC offset is found
207sInt16 ISO8601StrToContext(cAppCharP aISOString, timecontext_t &aTimeContext)
208{
209 sInt16 n=0,h;
210 sInt16 minoffs;
211 uInt16 offs;
212
213 aTimeContext = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
214 bool western=false;
215 // check for UTC special case
216 if (*aISOString=='Z') {
217 n++;
218 aTimeContext = TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ));
219 }
220 else {
221 // check for time zone offset
222 if (*aISOString!='+' && *aISOString!='-')
223 return 0; // error, nothing converted
224 western = *aISOString=='-'; // time is behind of UTC in the west
225 aISOString++;
226 n++;
227 h=StrToUShort(aISOString,offs,2);
228 if (h!=2)
229 return 0; // not +HH format with 2 digits, nothing converted
230 aISOString+=h; n+=h;
231 // make minutes
232 minoffs = offs*60;
233 // hour offset ok, now check minutes
234 if (*aISOString==':') { aISOString++; n++; }; // extended format
235 // get minutes, if any
236 h = StrToUShort(aISOString,offs,2);
237 if (h==2) {
238 // minute specified
239 minoffs += offs; // add minutes
240 }
241 // adjust sign
242 if (western)
243 minoffs=-minoffs; // western offset is negative
244 // return non-symbolic minute offset
245 aTimeContext = TCTX_OFFSCONTEXT(minoffs)((timecontext_t)((sInt16)(minoffs) & TCTX_OFFSETMASK ));
246 }
247 // return number of characters converted
248 return n;
249} // ISO8601StrToContext
250
251
252
253/// @brief convert timestamp to ISO8601 representation
254/// @param[out] aISOString will receive ISO8601 formatted date/time
255/// @param[in] aTimestamp representation of ISO time spec as is (no time zone conversions)
256/// @param[in] aExtFormat if set, extended format is generated
257/// @param[in] aWithFracSecs if set, fractional seconds are displayed (3 digits, milliseconds)
258/// @param[in] aTimeContext:
259/// TCTX_DURATION: timestamp is a duration and is shown in ISO8601 duration format (PnDTnHnMnS.MS)
260/// TCTX_UNKNOWN : time is shown as relative format (no "Z", no explicit offset)
261/// TCTX_TIMEONLY
262/// TCTX_UTC : time is shown with "Z" specifier
263/// TCTX_DATEONLY: only date part is shown (of date or duration)
264/// TCTX_OFFSCONTEXT(xx) : time is shown with explicit UTC offset (but not with "Z", even if offset is 0:00)
265void TimestampToISO8601Str(string &aISOString, lineartime_t aTimestamp, timecontext_t aTimeContext, bool aExtFormat, bool aWithFracSecs)
266{
267 // check for duration (should be rendered even if value is 0)
268 if (TCTX_IS_DURATION(aTimeContext)) {
269 // render as duration
270 // - sign
271 if (aTimestamp<0) {
272 aTimestamp=-aTimestamp;
273 aISOString = "-";
274 }
275 else {
276 aISOString.erase();
277 }
278 // - "P" duration designator
279 aISOString += "P";
280 // - days
281 lineardate_t days = aTimestamp / linearDateToTimeFactor;
282 if (days!=0) StringObjAppendPrintf(aISOString,"%ldD",(long)days);
283 // - time part if needed
284 if (!TCTX_IS_DATEONLY(aTimeContext) && lineartime2timeonly(aTimestamp)>(aWithFracSecs ? 0 : secondToLinearTimeFactor-1)) {
285 aISOString += "T"; // we have a time part
286 sInt16 h,m,s,ms;
287 lineartime2time(aTimestamp,&h,&m,&s,&ms);
288 if (h!=0) StringObjAppendPrintf(aISOString,"%hdH",h);
289 if (m!=0) StringObjAppendPrintf(aISOString,"%hdM",m);
290 if (aWithFracSecs && ms!=0) {
291 // with milliseconds, implies seconds as well, even if they are zero
292 StringObjAppendPrintf(aISOString,"%hd.%03hdS",s,ms);
293 }
294 else {
295 // no milliseconds, show seconds if not zero
296 if (s!=0) StringObjAppendPrintf(aISOString,"%hdS",s);
297 }
298 }
299 else if(days==0) {
300 aISOString="PT0S"; // no time part and no days, just display 0 seconds (and not negative, even if fraction might be)
301 }
302 // done
303 return;
304 }
305 // no timestamp, no string
306 if (aTimestamp==0) {
307 aISOString.erase();
308 return;
309 }
310 // Date if we want date
311 bool hasDate=false;
312 if (!TCTX_IS_TIMEONLY(aTimeContext)) {
313 // we want the date part
314 sInt16 y,m,d;
315 lineartime2date(aTimestamp,&y,&m,&d);
316 if (!TCTX_IS_DURATION(aTimeContext) || !(y==0 && m==0 && d==0)) {
317 if (aExtFormat)
318 StringObjPrintf(aISOString,"%04d-%02d-%02d",y,m,d); // Extended format
319 else
320 StringObjPrintf(aISOString,"%04d%02d%02d",y,m,d); // Basic format
321 hasDate=true;
322 }
323 }
324 else {
325 aISOString.erase();
326 }
327 // Add time if we want time
328 if (!TCTX_IS_DATEONLY(aTimeContext)) {
329 // we want the time part
330 // - add separator
331 if (hasDate) aISOString+='T';
332 // - now add the time
333 sInt16 h,m,s,ms;
334 lineartime2time(aTimestamp,&h,&m,&s,&ms);
335 if (aExtFormat)
336 StringObjAppendPrintf(aISOString,"%02hd:%02hd:%02hd",h,m,s); // Extended format
337 else
338 StringObjAppendPrintf(aISOString,"%02hd%02hd%02hd",h,m,s); // Basic format
339 // - add fractions of the second if selected and not 0
340 if (aWithFracSecs && (ms!=0)) {
341 StringObjAppendPrintf(aISOString,".%03hd",ms); // 3 decimal fraction digits for milliseconds
342 }
343 if (!TCTX_IS_DURATION(aTimeContext)) {
344 // add explicit time zone specification (or UTC "Z") if aTimecontext is a non-symbolic offset
345 ContextToISO8601StrAppend(aISOString, aTimeContext, aExtFormat);
346 }
347 }
348} // TimestampToISO8601Str
349
350
351
352/// @brief append internal time zone as ISO8601 zone offset to string
353/// @param[out] aISOString ISO8601 time zone spec will be appended to this string
354/// @param[in] aTimeContext
355/// @param[in] aExtFormat if set, extended format is generated
356bool ContextToISO8601StrAppend(string &aISOString, timecontext_t aTimeContext, bool aExtFormat)
357{
358 // check for UTC special case
359 if (TCTX_IS_UTC(aTimeContext)) {
360 aISOString += 'Z';
361 return true; // has time zone
362 }
363 // check if this is a resolved or a symbolic time zone
364 if (TCTX_IS_TZ(aTimeContext))
365 return false; // symbolic (includes unknown) - cannot append minute offset
366 // offset specified, show it
367 long moffs = TCTX_MINOFFSET(aTimeContext);
368 bool minus = moffs<0;
369 moffs = labs(moffs);
370 long hoffs = moffs / MinsPerHour;
371 moffs = moffs % MinsPerHour;
372 StringObjAppendPrintf(aISOString, "%c%02ld", minus ? '-' : '+', hoffs);
373 if (moffs!=0 || aExtFormat) {
374 // minute specification required (always so for extended format)
375 if (aExtFormat)
376 aISOString+=':'; // add separator for extended format
377 StringObjAppendPrintf(aISOString, "%02ld", moffs);
378 }
379 return true; // has time zone
380} // ContextToISO8601StrAppend
381
382
383} // namespace sysync
384
385/* eof */