Imported Upstream version 1.0beta1
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / timezones.cpp
1 /*
2  *  File:         timezones.cpp
3  *
4  *  Author:       Beat Forster
5  *
6  *  Timezones conversion from/to linear time scale.
7  *
8  *  Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
9  *
10  *  2004-04-18 : bfo : initial version
11  *
12  */
13
14
15 /* ToDo / open issues
16  *
17  * 1) done 04/04/18  <aISOstr> does not contain TZ info, how to bring it in ? -> new param
18  * 2)      04/04/18  <aOffsetSecs> not yet limited to one day
19  * 3) done 04/04/18  Is 20040101Z a valid 8601 ? (yes it is)
20  * 4) done 04/04/20  Fill in all valid time zones
21  * 5) done 04/04/20  Adapt straight forward conversions Enum <=> Name
22  * 6) done 04/06/14  System time zone calculation for Linux
23  * 7) done 04/06/14  InsertTZ / RemoveTZ implementation
24  * 8)      06/04/12  Offset calculation for more complicated vTZ ("$") records
25  * 9) done 07/02/26  Multi year list support
26  */
27
28 // must be first in file, everything above is ignored by MVC compilers
29 #include "prefix_file.h"
30
31 #define TIMEZONES_INTERNAL 1
32
33 // do not import the whole thing to make life easier for standalone apps
34 #ifdef FULLY_STANDALONE
35   #include "sysync_globs.h"
36   #include "sysync_debug.h"
37 #else
38   #include "sysync.h"
39 #endif
40
41 #include "lineartime.h"
42 #include "timezones.h"
43 #include "iso8601.h"
44 #include "stringutils.h"
45 #include "vtimezone.h"
46
47 namespace sysync {
48
49 static bool tzcmp( const tz_entry &t, const tz_entry &tzi );
50 static bool YearFit( const tz_entry &t, const tz_entry &tzi, GZones* g );
51
52 // ---- global structure -----------------------------------------------------------
53
54 // %%% this is now a global variable, which is BAD. Unlike a const struct array, which can be put into the
55 //     code section, this "const" is a run-time generated structure (won't work e.g. for Symbian)
56 //     We should avoid this and create that object in the app level GZones, and let further GZone objects
57 //     allow using it (without copying!) - similar mechanism as the old CopyCustomTZFrom() had
58 //     (see 8742d15b65fecf02db54ce9b7447cee09f913ad0 commit which removed it)
59  
60 const class tzdata : public std::vector<tz_entry>
61 {
62  public:
63   tzdata() {
64                 #ifndef NO_BUILTIN_TZ
65     // add the global entries
66     reserve(tctx_numtimezones);
67     for (int i=0; i<tctx_numtimezones; i++) {
68         const tbl_tz_entry &t = tbl_tz[i];
69       if (i > 0 && back().name == t.name) {
70         // the previous entry wasn't really the last of its group,
71         // fix that
72         back().groupEnd = false;
73       }
74       // add new entry, assuming that it terminates its group
75         push_back(tz_entry(
76         t.name, t.bias, t.biasDST, t.ident, t.dynYear, 
77         tChange(t.dst.wMonth, t.dst.wDayOfWeek, t.dst.wNth, t.dst.wHour, t.dst.wMinute),
78         tChange(t.std.wMonth, t.std.wDayOfWeek, t.std.wNth, t.std.wHour, t.std.wMinute),
79         true // groupEnd
80       )); 
81     }
82     //push_back(tz_entry("unknown",               0,  0, "x",    "", tChange( 0, 0,0, 0,0),  tChange( 0, 0,0, 0,0)));  //   0
83     #endif
84   }
85 } tz;
86
87
88 // ---------------------------------------------------------------------------------
89 // GZones
90
91 bool GZones::initialize()
92 {
93   bool ok = true;
94   // load system wide definitions.
95   bool nobuiltin = loadSystemZoneDefinitions(this);
96         // %%%% later, we'll load system zones not into each GZones, but only once into
97   //      a global list. Then, the return value of loadSystemZoneDefinitions() will
98   //      determine if zones from the built-in list should be added or not
99   // %%%% for now, we can't do that yet, built-in zones are always active
100   if (!nobuiltin) {
101         //%%% add entries from tz_table
102   }
103   return ok;
104 }
105
106
107 bool GZones::matchTZ(const tz_entry &aTZ, timecontext_t &aContext)
108 {
109   // keeps track of best match while iterating
110   class comparison : public visitor {
111     /** best solution so far has matching rules */
112     bool fRuleMatch;
113     /** best solution has matching location */
114     bool fLocationMatch;
115     /** best solution so far */
116     timecontext_t fContext;
117
118     /** the time zone we try to match */
119     tz_entry fTZ;
120     /** the TZID we try to match */
121     string fTZID;
122
123     /** the last entry without dynYear, i.e., the main entry of a group */
124     timecontext_t fLeadContext;
125
126     /** time zones */
127     GZones *fG;
128
129   public:
130     comparison(const tz_entry &aTZ, GZones *g) :
131       fRuleMatch(false),
132       fLocationMatch(false),
133       fContext(TCTX_UNKNOWN),
134       fTZID(aTZ.name),
135       fLeadContext(TCTX_UNKNOWN),
136       fG(g)
137     {
138       // prepare information for tzcmp() and YearFit()
139       fTZ.bias = aTZ.bias;
140       fTZ.biasDST = aTZ.biasDST;
141       fTZ.std = aTZ.std;
142       fTZ.dst = aTZ.dst;
143       // setting the year here instead of 'CUR' avoids repeated calls
144       // to MyYear() inside YearFit()
145       int year = MyYear(g);
146       StringObjPrintf(fTZ.dynYear, "%d", year);
147     }
148
149     bool visit(const tz_entry &aTZ, timecontext_t aContext)
150     {
151       if (aTZ.ident=="x") return false; // comparison with this type is not possible
152
153       bool rule_match = tzcmp(fTZ, aTZ) && YearFit(fTZ, aTZ, fG);
154
155       if (aTZ.dynYear.empty())
156         fLeadContext = aContext;
157
158       if (rule_match &&
159          !fTZ.name.empty() && // empty match is NOT better !!
160           fTZ.name == aTZ.name) {
161         // name AND rule match => best possible match, return early
162         fContext = fLeadContext;
163         return true;
164       }
165
166       if (!aTZ.location.empty() &&
167           fTZID.find(aTZ.location) != fTZID.npos) {
168         // location name is part of the TZID we try to match
169         if (!fLocationMatch ||
170             (!fRuleMatch && rule_match)) {
171           // previous match did not match location or
172           // not the rules and we do, so this match is better
173           fLocationMatch = true;
174           fRuleMatch = rule_match;
175           fContext = fLeadContext;
176           return false;
177         }
178       }
179
180       // a rule match with no better match yet?
181       if (!fLocationMatch &&
182           rule_match &&
183           !fRuleMatch) {
184         fContext = fLeadContext;
185         fRuleMatch = true;
186       }
187
188       return false;
189     } // visit
190
191     bool result(timecontext_t &aContext)
192     {
193       if (fContext != TCTX_UNKNOWN) {
194         aContext = fContext;
195         return true;
196       } else {
197         return false;
198       }
199     } // result
200   }
201
202   c(aTZ, this);
203
204   foreachTZ(c);
205
206   return c.result(aContext);
207 } // matchTZ
208
209 bool GZones::foreachTZ(visitor &v)
210 {
211   int  i; // visit hard coded elements first
212   for (i= 1; i<(int)tctx_numtimezones; i++) {
213     if (v.visit(tz[i], TCTX_ENUMCONTEXT(i))) {
214       return true;
215     }
216   }
217
218   bool result = false;
219   #ifdef MUTEX_SUPPORT
220     lockMutex(muP);
221   #endif
222
223   for (TZList::iterator pos= tzP.begin();
224        pos!=tzP.end();
225        pos++, i++) {
226     if (v.visit(*pos, TCTX_ENUMCONTEXT(i)))
227       break;
228   } // for
229
230   #ifdef MUTEX_SUPPORT
231     unlockMutex(muP);
232   #endif
233
234   return result;
235 } // foreachTZ
236
237 // ---------------------------------------------------------------------------------
238
239 // Get signed minute offset
240 sInt16 TCTX_MINOFFSET( timecontext_t tctx ) {
241   return (sInt16)(tctx & TCTX_OFFSETMASK );
242 } // TCTX_MINOFFSET
243
244
245 // Get time zone enum
246 TTimeZones TCTX_TZENUM( timecontext_t tctx ) {
247   return (TTimeZones)(tctx & TCTX_OFFSETMASK );
248 } // TCTX_TZENUM
249
250
251 // Check if <tctx> is a symbolic TZ info
252 bool TCTX_IS_TZ( timecontext_t tctx ) {
253   return (tctx & TCTX_SYMBOLIC_TZ)!=0;
254 } // TCTX_IS_TZ
255
256
257 // Check if <tctx> is a built-in symbolic TZ info
258 bool TCTX_IS_BUILTIN( timecontext_t tctx ) {
259   return
260     (tctx & TCTX_SYMBOLIC_TZ) && // is a symbolic time zone
261     (TCTX_TZENUM(tctx)<tctx_numtimezones); // and is in the internal list
262 } // TCTX_IS_BUILTIN
263
264
265 // Check if <tctx> has TCTX_DATEONLY mode set (but not TIMEONLY - both set means same as both not set)
266 bool TCTX_IS_DATEONLY( timecontext_t tctx ) {
267   return (tctx  & TCTX_DATEONLY) && !(tctx  & TCTX_TIMEONLY);
268 } // TCTX_IS_DATEONLY
269
270
271 // Check if <tctx> has TCTX_TIMEONLY mode set (but not DATEONLY - both set means same as both not set)
272 bool TCTX_IS_TIMEONLY( timecontext_t tctx ) {
273   return (tctx  & TCTX_TIMEONLY) && !(tctx  & TCTX_DATEONLY);
274 } // TCTX_IS_TIMEONLY
275
276
277 // Check if <tctx> has TCTX_DURATION mode set
278 bool TCTX_IS_DURATION( timecontext_t tctx ) {
279   return ( tctx  & TCTX_DURATION )!=0;
280 } // TCTX_IS_DURATION
281
282
283 // Check if <tctx> is a unknown time zone
284 bool TCTX_IS_UNKNOWN( timecontext_t tctx ) {
285   return TCTX_IS_TZ( tctx ) && ( TCTX_TZENUM( tctx )==tctx_tz_unknown );
286 } // TCTX_IS_UNKNOWN
287
288
289 // Check if <tctx> is the system time zone
290 bool TCTX_IS_SYSTEM ( timecontext_t tctx ) {
291   return TCTX_IS_TZ( tctx ) && ( TCTX_TZENUM( tctx )==tctx_tz_system );
292 } // TCTX_IS_SYSTEM
293
294
295 // Check if <tctx> is the UTC time zone
296 bool TCTX_IS_UTC ( timecontext_t tctx ) {
297   return TCTX_IS_TZ( tctx ) && ( TCTX_TZENUM( tctx )==tctx_tz_UTC );
298 } // TCTX_IS_SYSTEM
299
300
301 //! result is render flags from <aRFlagContext> with zone info from <aZoneContext>
302 timecontext_t TCTX_JOIN_RFLAGS_TZ ( timecontext_t aRFlagContext, timecontext_t aZoneContext )
303 {
304   return
305     (aRFlagContext &  TCTX_RFLAGMASK) |
306     (aZoneContext  & ~TCTX_RFLAGMASK);
307 } // TCTX_JOIN_RFLAGS_TZ
308
309
310
311 // ---- utility functions --------------------------------------------------------------
312 // Specific bias string. Unit: hours
313 string BiasStr( int bias )
314 {
315   char hrs[ 80 ];
316   float f= (float)bias/MinsPerHour;
317
318   if          (bias % MinsPerHour == 0) sprintf( hrs,"%3.0f    ", f ); // not with .0
319   else if (abs(bias % MinsPerHour)==30) sprintf( hrs,"%5.1f  ",   f );
320   else                                  sprintf( hrs,"%6.2f ",    f );
321
322   return hrs;
323 } // BiasStr
324
325
326 // Clear the DST info of <t>
327 void ClrDST( tz_entry &t )
328 {
329   memset( &t.dst, 0, sizeof(t.dst) );
330   memset( &t.std, 0, sizeof(t.std) );
331 } // ClrDST
332
333
334 // -------------------------------------------------------------------------------------
335 // Special cases: <year> =  0, the one w/o dynYear
336 //                       = -1, direct index
337 bool GetTZ( timecontext_t aContext, tz_entry &t, GZones* g, int year )
338 {
339   if (!TCTX_IS_TZ( aContext )) return false;
340
341   int           aTZ= aContext & TCTX_OFFSETMASK;
342   if (aTZ>=0 && aTZ<tctx_numtimezones) {
343     while (true) {
344       t= tz[ aTZ ]; if (t.dynYear.empty() || year==-1) break; // replace it by a non-dynYear
345       if    (aTZ==0)                                   break;
346              aTZ--;
347     } // while
348
349     if (year<=0) return true;
350
351     int    nx=  aTZ+1;
352     while (nx<tctx_numtimezones) { // search for the dynamic year
353       if (t.name != tz[ nx ].name) break; // no or no more
354       t=            tz[ nx ];
355       if (year<=atoi( t.dynYear.c_str() )) break; // this is the year line we are looking for
356       nx++;
357     } // while
358
359     return true; // this is it !!
360   } // if
361
362   t= tz[ tctx_tz_unknown ];  // default, if not yet ok
363   if (g==NULL) return false; // If there is no <g>, it is definitely false
364
365   // -----------------------
366   bool ok= false;
367 //if (g==NULL) g= gz();      // either <g> or global list
368
369   #ifdef MUTEX_SUPPORT
370     lockMutex( g->muP );
371   #endif
372
373   int  i = tctx_numtimezones;
374   TZList::iterator pos;
375   for (pos= g->tzP.begin();          // go thru the additional list
376        pos!=g->tzP.end(); pos++) {
377     if   (aTZ==i &&                  // no removed elements !!
378           !(pos->ident=="-")) {
379       t =  *pos;
380       ok= true;
381       if (year<=0) break; // pass unlock now
382
383              pos++;
384       while (pos!=g->tzP.end()) { // search for the dynamic year
385         if (t.name != pos->name) break; // no or no more
386         t=*pos;
387         if (year<=atoi( t.dynYear.c_str() )) break; // this is the year line we are looking for
388         pos++;
389       } // while
390
391       break; // pass unlock now
392     } // if
393
394     i++;
395   } // for
396
397   #ifdef MUTEX_SUPPORT
398     unlockMutex( g->muP );
399   #endif
400   // -----------------------
401
402   return ok;
403 } /* GetTZ */
404
405
406
407 void Get_tChange( lineartime_t tim, tChange &v, bool asDate )
408 {
409   sInt16 y, day, d, sec, ms;
410
411   lineartime2date( tim, &y,       &v.wMonth,  &day );
412   lineartime2time( tim, &v.wHour, &v.wMinute, &sec, &ms );
413
414   if (asDate) {
415     v.wDayOfWeek=  -1; // use it as date directly
416     v.wNth      = day;
417   }
418   else {
419     v.wDayOfWeek= lineartime2weekday( tim );
420     v.wNth      = ( day-1 ) / DaysPerWk + 1;
421
422     if (v.wNth==4) { // the last one within month ?
423                  d= day + DaysPerWk;
424       AdjustDay( d, v.wMonth, y );
425       if       ( d==day ) v.wNth= 5;
426     } // if
427   } // if
428 } // Get_tChange
429
430
431
432 static bool Fill_tChange( string iso8601, int bias, int biasDST, tChange &tc, bool isDST )
433 {
434   lineartime_t  l;
435   timecontext_t c;
436 //sInt16        y, day, d, sec, ms;
437
438   string::size_type rslt= ISO8601StrToTimestamp( iso8601.c_str(), l, c );
439   if    (rslt!=iso8601.length()) return false;
440
441   int                     bMins = bias;
442   if (!isDST)             bMins+= biasDST;
443   l+= seconds2lineartime( bMins*SecsPerMin );
444
445   Get_tChange( l, tc );
446
447   /*
448   lineartime2date( l, &y,        &tc.wMonth,  &day );
449   lineartime2time( l, &tc.wHour, &tc.wMinute, &sec, &ms );
450   tc.wDayOfWeek= lineartime2weekday( l );
451   tc.wNth      = ( day-1 ) / DaysPerWk + 1;
452
453   if (tc.wNth==4) { // the last one within month ?
454                d= day + DaysPerWk;
455     AdjustDay( d, tc.wMonth, y );
456     if       ( d==day ) tc.wNth= 5;
457   } // if
458   */
459
460   return true;
461 } // Fill_tChange
462
463
464 bool GetTZ( string std, string dst, int bias, int biasDST, tz_entry &t, GZones* g )
465 {
466   t.name   = "";
467   t.bias   = bias;
468   t.biasDST= biasDST;
469   t.ident  = "";
470   t.dynYear= "";
471
472   return Fill_tChange( std, bias,biasDST, t.std, false ) &&
473          Fill_tChange( dst, bias,biasDST, t.dst, true  );
474 } // GetTZ
475
476
477 static bool Same_tChange( const tChange &tCh1, const tChange &tCh2 )
478 {
479   return tCh1.wMonth    ==tCh2.wMonth     &&
480          tCh1.wDayOfWeek==tCh2.wDayOfWeek &&
481          tCh1.wNth      ==tCh2.wNth       &&
482          tCh1.wHour     ==tCh2.wHour      &&
483          tCh1.wMinute   ==tCh2.wMinute;
484 } // Same_tChange
485
486
487 /*! Compare time zone information */
488 static bool tzcmp( const tz_entry &t, const tz_entry &tzi )
489 {
490   if        (!t.name.empty() &&
491               strucmp( t.name.c_str(), tzi.name.c_str() )!=0) return false;
492
493   bool idN=   t.ident.empty();
494   if (!idN && t.ident == "?" && // search for the name only
495               strucmp( t.name.c_str(), tzi.name.c_str()  )==0) return true;
496
497   if (!idN && t.ident == "$" &&
498               t.ident == tzi.ident &&
499               strucmp( t.name.c_str(), tzi.name.c_str()  )==0) return true;
500
501   /*
502   PNCDEBUGPRINTFX( DBG_SESSION,( "tS   m=%d dw=%d n=%d h=%d M=%d\n",   t.std.wMonth,   t.std.wDayOfWeek,   t.std.wNth,
503                                                                        t.std.wHour,    t.std.wMinute ) );
504   PNCDEBUGPRINTFX( DBG_SESSION,( "tD   m=%d dw=%d n=%d h=%d M=%d\n",   t.dst.wMonth,   t.dst.wDayOfWeek,   t.dst.wNth,
505                                                                        t.dst.wHour,    t.dst.wMinute ) );
506   PNCDEBUGPRINTFX( DBG_SESSION,( "tziS m=%d dw=%d n=%d h=%d M=%d\n", tzi.std.wMonth, tzi.std.wDayOfWeek, tzi.std.wNth,
507                                                                      tzi.std.wHour,  tzi.std.wMinute ) );
508   PNCDEBUGPRINTFX( DBG_SESSION,( "tziD m=%d dw=%d n=%d h=%d M=%d\n", tzi.dst.wMonth, tzi.dst.wDayOfWeek, tzi.dst.wNth,
509                                                                      tzi.dst.wHour,  tzi.dst.wMinute ) );
510
511   PNCDEBUGPRINTFX( DBG_SESSION,( "t    bs=%d bd=%d\n",   t.bias,   t.biasDST ) );
512   PNCDEBUGPRINTFX( DBG_SESSION,( "tzi  bs=%d bd=%d\n", tzi.bias, tzi.biasDST ) );
513   */
514
515   if (t.bias!=tzi.bias) return false; // bias must be identical
516
517   if (t.ident == "o") {
518     // stop comparing, return result of last check
519     return t.biasDST == tzi.biasDST;
520   }
521
522   bool   tIsDst= DSTCond( t   );
523   bool tziIsDst= DSTCond( tzi );
524   if (tIsDst!=tziIsDst) return false; // DST cond must be on or off for both
525
526   if (tIsDst) {
527   //if (memcmp(&t.dst,    &tzi.dst, sizeof(t.dst))!=0 ||
528   //    memcmp(&t.std,    &tzi.std, sizeof(t.std))!=0 ||
529     if (!Same_tChange( t.dst,     tzi.dst ) ||
530         !Same_tChange( t.std,     tzi.std ) ||
531                        t.biasDST!=tzi.biasDST) return false;
532   } // if
533
534   return !(tzi.ident=="-") && // not removed
535                      ( idN ||
536            t.ident.empty() ||
537            t.ident == tzi.ident );
538
539   /*
540   return memcmp(&t.dst,    &tzi.dst, sizeof(t.dst))==0 &&
541          memcmp(&t.std,    &tzi.std, sizeof(t.std))==0 &&
542                  t.bias   ==tzi.bias                   &&
543                  t.biasDST==tzi.biasDST                && //%%% luz: this must be compared as well
544          strcmp( "-",       tzi.ident )!=0             && // removed
545                (   idN                     ||
546          strcmp( t.ident,   ""        )==0 ||
547          strcmp( t.ident,   tzi.ident )==0 );
548   */
549 } // tzcmp
550
551
552
553 sInt16 MyYear( GZones* g )
554 {
555   sInt16 y, m, d;
556
557   lineartime_t     t= getSystemNowAs( TCTX_UTC, g, true );
558   lineartime2date( t, &y,&m,&d );
559   return y;
560 } // MyYear
561
562
563 static bool YearFit( const tz_entry &t, const tz_entry &tzi, GZones* g )
564 {
565   int                     yearS;
566   if (t.dynYear == "CUR") yearS= MyYear( g );
567   else                    yearS= atoi( t.dynYear.c_str() );
568   if                     (yearS==0) return true;
569
570   int yearI= atoi( tzi.dynYear.c_str() );
571   if (yearI==0) return true;
572
573   if  (tzi.groupEnd) return yearS>=yearI;
574   else              return yearS<=yearI;
575 } // YearFit
576
577
578
579 /*  Returns true, if the given TZ is existing already
580  *    <t>            tz_entry to search for:
581  *                   If <t.name> == "" search for any entry with these values.
582  *                      <t.name> != "" name must fit
583  *                   If <t.ident>== "" search for any entry with these values
584  *                      <t.ident>!= "" name must fit
585  *    <aName>        is <t.name> of the found record
586  *    <aContext>     is the assigned context
587  *    <createIt>     create an entry, if not yet existing / default: false
588  *    <searchOffset> says, where to start searching       / default: at the beginning
589  *
590  *    supported <t.ident> values:
591  *      ""   any       (to search)
592  *      "?"  name only (to search)
593  *      "o"  compare offsets, but not changes (to search); name is compared if set
594  *
595  *      "x"  unknown/system
596  *      "m"  military zones
597  *      "s"  standard zones
598  *      "d"  daylight zones
599  *      "-"  removed  zones
600  *      "$"  not converted zones (pure text)
601  *      " "  all others
602  */
603 bool FoundTZ( const tz_entry &tc,
604               string        &aName,
605               timecontext_t &aContext, GZones* g, bool createIt,
606               timecontext_t searchOffset )
607 {
608   aName    = "";
609   aContext = TCTX_UNKNOWN;
610   int offs = TCTX_OFFSCONTEXT( searchOffset );
611   bool  ok = false;
612   tz_entry t = tc;
613
614   if (!t.ident.empty() && // specific items will not contain more info
615      !(t.ident==" ")) {
616     ClrDST  ( t );
617   } // if
618
619   int  i; // search hard coded elements first
620   for (i= offs+1; i<(int)tctx_numtimezones; i++) {
621     const tz_entry &tzi = tz[ i ];
622     if (tzcmp  ( t, tzi  ) &&
623         YearFit( t, tzi, g )) {
624       aName=        tzi.name;
625       ok   = true; break;
626     } // if
627   } // for
628
629   // don't go thru the mutex, if not really needed
630   if (!ok && g!=NULL) {
631     // -------------------------------------------
632   //if (g==NULL) g= gz();
633
634     #ifdef MUTEX_SUPPORT
635       lockMutex( g->muP );
636     #endif
637
638     TZList::iterator pos;
639     int j= offs-(int)tctx_numtimezones; // remaining gap to be skipped
640
641     // Search for all not removed elements first
642     for (pos= g->tzP.begin();
643          pos!=g->tzP.end(); pos++) {
644       if (j<0 &&
645           !(pos->ident=="-") && // element must not be removed
646           tzcmp( t, *pos )) {
647         aName= pos->name;
648         ok   = true; break;
649       } // if
650
651       i++;
652       j--;
653     } // for
654
655     // now check, if an already removed element can be reactivated
656     if (createIt && !ok) {
657       i=      (int)tctx_numtimezones;
658       j= offs-(int)tctx_numtimezones; // remaining gap to be skipped
659
660       for (pos= g->tzP.begin();
661            pos!=g->tzP.end(); pos++) {
662         if (j<0 &&
663             pos->ident == "-" && // removed element ?
664             tzcmp( t, *pos )) {
665           pos->ident= t.ident; // reactivate the identifier
666           aName = pos->name;  // should be the same
667           ok    = true; break;
668         } // if
669
670         i++;
671         j--;
672       } // for
673     } // if
674
675     // no such element => must be created
676     if (createIt && !ok) { // create it, if not yet ok
677       g->tzP.push_back( t );
678       ok= true;
679     } // if
680
681     #ifdef MUTEX_SUPPORT
682       unlockMutex( g->muP );
683     #endif
684     // -------------------------------------------
685   } // if
686
687   if (ok) aContext= TCTX_ENUMCONTEXT( i );
688   return  aContext!=TCTX_UNKNOWN;
689 } /* FoundTZ */
690
691
692
693 /* Remove a time zone definition, if already existing
694  * NOTE: Currently only dynamic entries can be removed
695  */
696 bool RemoveTZ( const tz_entry &t, GZones* g )
697 {
698 //printf( "RemoveTZ '%s' %d\n", t.name, t.bias );
699   bool ok= false;
700
701   // ---------------------------
702   if (g==NULL) return ok;
703 //if (g==NULL) g= gz();
704
705   #ifdef MUTEX_SUPPORT
706     lockMutex( g->muP );
707   #endif
708
709   TZList::iterator pos;
710   for (pos= g->tzP.begin();
711        pos!=g->tzP.end(); pos++) {
712     if (!(pos->ident=="-") && // element must not be removed
713         tzcmp( t, *pos )) {
714       pos->ident = "-";
715     //gz()->tzP.erase( pos ); // do not remove it, keep it persistent
716       ok= true; break;
717     } // if
718   } // for
719
720   #ifdef MUTEX_SUPPORT
721     unlockMutex( g->muP );
722   #endif
723   // ---------------------------
724
725   return ok;
726 } /* RemoveTZ */
727
728
729
730 bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g )
731 {
732   // check some special cases
733   if (strucmp(aName,"DATE")==0) {
734     aContext = TCTX_UNKNOWN|TCTX_DATEONLY;
735     return true;
736   }
737   else if (strucmp(aName,"DURATION")==0) {
738     aContext = TCTX_UNKNOWN|TCTX_DURATION;
739     return true;
740   }
741   else if (*aName==0 || strucmp(aName,"FLOATING")==0) {
742     aContext = TCTX_UNKNOWN;
743     return true;
744   }
745
746   const char* GMT= "GMT";
747   const char* UTC= "UTC";
748
749   int      v, n;
750   char*    q;
751   tz_entry t;
752
753   t.name   = aName; // prepare searching
754   t.ident  = "?";
755   t.dynYear= "";    // luz: must be initialized!
756
757   string          tName;
758   if (FoundTZ( t, tName, aContext, g )) return true;
759
760   /*
761   int  i;     aContext= TCTX_UNKNOWN;
762   for (i=0; i<(int)tctx_numtimezones; i++) {
763     if (strucmp( tz[i].name,aName )==0) { aContext= TCTX_ENUMCONTEXT(i); break; }
764   } // for
765
766   if (aContext!=TCTX_UNKNOWN) return true;
767   */
768
769   // calculate the UTC offset
770   n= 1;        q= (char *)strstr( aName,UTC );
771   if (q==NULL) q= (char *)strstr( aName,GMT );
772   if (q!=NULL  &&   strlen( q )>strlen( UTC )) {
773     q+= strlen( UTC );
774     n= MinsPerHour;
775   }
776   else {
777     q= (char*)aName;
778   } // if
779
780       v= atoi( q );
781   if (v!=0 || strcmp( q, "0" )==0
782            || strcmp( q,"+0" )==0
783            || strcmp( q,"-0" )==0) {
784     aContext= TCTX_OFFSCONTEXT(v*n); return true;
785   } // if
786
787   return false;
788 } /* TimeZoneNameToContext */
789
790
791
792 bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g,
793                                 cAppCharP aPrefIdent )
794 {
795   // check some special cases
796   if (!TCTX_IS_TZ( aContext )) {
797     short lBias=   TCTX_MINOFFSET( aContext );
798           aName= "OFFS" + HourMinStr( lBias );
799     return true;
800   } // if
801
802   if (TCTX_IS_UNKNOWN(aContext)) {
803     aName = TCTX_IS_DURATION(aContext) ? "DURATION" : (TCTX_IS_DATEONLY(aContext) ? "DATE" : "FLOATING");
804     return true;
805   }
806
807   tz_entry t;
808   aName= "UNKNOWN";
809
810         // if aPrefIndent contains "o", this means we'd like to see olson name, if possible
811   // %%% for now, we can return olson for the built-ins only
812   if (TCTX_IS_BUILTIN(aContext) && aPrefIdent && strchr(aPrefIdent, 'o')!=NULL) {
813                 #ifndef NO_BUILTIN_TZ
814         // look up in hardcoded table
815     cAppCharP oname = tbl_tz[TCTX_TZENUM(aContext)].olsonName;
816     if (oname) {
817         // we have an olson name for this entry, return it
818         aName = oname;
819       return true;
820     }
821     #endif
822   }
823
824   // %%% <aPrefIdent> has not yet any influence
825   if (GetTZ( aContext, t, g )) {
826     if (t.ident == "$") return false; // unchanged elements are not yet supported
827     aName= t.name;
828   } // if
829
830   return true;
831 } /* TimeZoneContextToName */
832
833
834
835 /*! Is it a DST time zone ? (both months must be defined for DST mode) */
836 bool DSTCond( const tz_entry &t ) {
837   return (t.dst.wMonth!=0 &&
838           t.std.wMonth!=0);
839 } // DSTCond
840
841
842
843 /* adjust to day number within month <m>, %% valid till year <y> 2099 */
844 void AdjustDay( sInt16 &d, sInt16 m, sInt16 y )
845 {
846   while (d>31)                                    d-= DaysPerWk;
847   if    (d>30 && (m==4 || m==6 || m==9 || m==11)) d-= DaysPerWk;
848   if    (d>29 &&  m==2)                           d-= DaysPerWk;
849   if    (d>28 &&  m==2 && (y % 4)!=0)             d-= DaysPerWk;
850 } // AdjustDay
851
852
853 /* Get the day where STD <=> DST switch will be done */
854 static sInt16 DaySwitch( lineartime_t aTime, const tChange* c )
855 {
856   sInt16 y, m, d, ds;
857   lineartime2date( aTime, &y, &m, &d );
858
859   if   (c->wDayOfWeek==-1) {
860     ds= c->wNth;
861   }
862   else {
863     sInt16 wkDay= lineartime2weekday( aTime );
864     ds = ( wkDay +  5*DaysPerWk -   ( d-1 )     ) % DaysPerWk;     /* wkday of 1st  */
865     ds = ( DaysPerWk - ds       + c->wDayOfWeek ) % DaysPerWk + 1; /* 1st occurance */
866     ds+= ( c->wNth-1 )*DaysPerWk;
867
868     AdjustDay( ds, c->wMonth, y );
869   } // if
870
871   return ds;
872 } // DaySwitch
873
874
875 /*! Get lineartime_t of <t> for a given <year>, either from std <toDST> or vice versa */
876 lineartime_t DST_Switch( const tz_entry &t, int bias, sInt16 aYear, bool toDST )
877 {
878   sInt16 ds;
879
880   const tChange*   c;
881   if (toDST) c= &t.dst;
882   else       c= &t.std;
883
884   lineartime_t tim= date2lineartime( aYear, c->wMonth, 1 );
885
886   ds= DaySwitch( tim, c );
887
888   sInt32      bMins = t.bias;
889   if (!toDST) bMins+= t.biasDST;
890
891   return    date2lineartime( aYear,    c->wMonth,  ds  )
892        +    time2lineartime( c->wHour, c->wMinute, 0,0 )
893        - seconds2lineartime( bMins*SecsPerMin );
894 } /* DST_Switch */
895
896
897
898 /* Check whether <aValue> of time zone <t> is DST based */
899 static bool IsDST( lineartime_t aTime, const tz_entry &t )
900 {
901   bool   ok;
902   sInt16 y, m, d, h, min, ds;
903
904   if (!DSTCond( t )) return false;
905
906   lineartime2date( aTime, &y, &m, &d );
907   lineartime2time( aTime, &h, &min, NULL, NULL );
908
909   const tChange*       c= NULL;
910   if (m==t.std.wMonth) c= &t.std;
911   if (m==t.dst.wMonth) c= &t.dst;
912
913   /* calculation is a little bit more tricky within the two switching months */
914   if (c!=NULL) {
915     /*
916     if   (c->wDayOfWeek==-1) {
917       ds= c->wNth;
918     }
919     else {
920       sInt16 wkDay= lineartime2weekday( aTime );
921       ds = ( wkDay +   5*DaysPerWk - (d-1)          ) % DaysPerWk;     // wkday of 1st
922       ds = ( DaysPerWk  - ds       + c->wDayOfWeek  ) % DaysPerWk + 1; // 1st occurance
923       ds+= ( c->wNth-1 )*DaysPerWk;
924
925       AdjustDay( ds, m, y );
926     } // if
927     */
928
929     /* <ds> is the day when dst<=>std takes place */
930         ds= DaySwitch( aTime, c );
931     if (ds        < d   ) return c==&t.dst;
932     if (ds        > d   ) return c==&t.std;
933
934     /* day  correct => compare hours */
935     if (c->wHour  < h   ) return c==&t.dst;
936     if (c->wHour  > h   ) return c==&t.std;
937
938     /* hour correct => compare minutes */
939     if (c->wMinute< min ) return c==&t.dst;
940     if (c->wMinute> min ) return c==&t.std;
941
942     /* decide for the margin, if identical */
943     return c==&t.dst;
944   } /* if */
945
946   /* northern and southern hemnisphere supported */
947   if (t.dst.wMonth<t.std.wMonth)
948          ok= m>t.dst.wMonth && m<t.std.wMonth;
949   else   ok= m<t.std.wMonth || m>t.dst.wMonth;
950   return ok;
951 } /* IsDST */
952
953
954
955 static sInt32 DST_Offs( lineartime_t aValue, const tz_entry &t, bool backwards )
956 {
957   if (backwards) aValue+= (lineartime_t)(t.bias*SecsPerMin)*secondToLinearTimeFactor;
958   if (IsDST( aValue,t )) return t.bias + t.biasDST;
959   else                   return t.bias;
960 } /* DST_Offs */
961
962
963
964 /* get offset in minutes */
965 static bool TimeZoneToOffs( lineartime_t aValue, timecontext_t aContext,
966                                  bool backwards, sInt32 &offs, GZones* g )
967 {
968   tz_entry t;
969
970   if (TCTX_IS_TZ( aContext )) {
971     offs= 0; // default
972
973     sInt16                     year;
974     lineartime2date( aValue,  &year, NULL, NULL );
975     if (GetTZ( aContext, t, g, year )) {
976       if (t.ident == "$") return false; // unchanged elements are not yet supported
977       offs= DST_Offs( aValue, t, backwards );
978     } // if
979   }
980   else {
981     offs= TCTX_MINOFFSET(aContext);
982   } // if
983
984   return true;
985 } /* TimeZoneToOffs */
986
987
988
989
990 timecontext_t SelectTZ( TDaylightSavingZone zone, int bias, int biasDST, lineartime_t tNow, bool isDbg )
991 {
992   bool dst, ok;
993   bool withDST= zone!=EDstNone;
994   timecontext_t t= tctx_tz_unknown;
995   bool special= false; // possibly needed true for NGage
996
997   int  i; // go thru the whole list of time zones
998   for (i=(int)tctx_tz_system+1; i<(int)tctx_numtimezones; i++) {
999     bool tCond= DSTCond( tz[ i ] ); // are there any DST rules ?
1000     if  (tCond==withDST) {
1001       if (withDST) dst= IsDST( tNow, tz[ i ] ); // check, if now in DST
1002       else         dst= false;
1003
1004       int                 b= bias;
1005       if (dst && special) b= bias - biasDST;
1006       if (tz[ i ].bias==b) { // the bias must fit exactly
1007         switch (zone) {
1008           case EDstEuropean :
1009           case EDstNorthern : ok= tz[ i ].dst.wMonth<tz[ i ].std.wMonth; break;
1010           case EDstSouthern : ok= tz[ i ].dst.wMonth>tz[ i ].std.wMonth; break;
1011           default           : ok= true;
1012         } // switch
1013
1014         if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION,( " %d %d dst=%d cond=%d '%s'",
1015                                         i, bias, dst, tCond, tz[ i ].name.c_str() ));
1016         if   (ok) { // check, if european time zone
1017           ok= (zone==EDstEuropean) ==
1018             (tz[ i ].name.find("Europe") != string::npos ||
1019              tz[ i ].name == "CET/CEST" ||
1020              tz[ i ].name == "Romance" ||
1021              tz[ i ].name == "GMT");
1022           if (ok) { t= i; break; }
1023         } // if
1024       } // if
1025     } // if
1026   } // for
1027
1028   if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION,( "SelectTZ: zone=%s bias=%d",
1029                                              tz[ t ].name.c_str(), bias ) );
1030   return TCTX_SYMBOLIC_TZ+t;
1031 } // SelectTZ
1032
1033
1034
1035 /* %%% luz 2009-04-02 seems of no relevance except for Symbian/Epoc -> Epoc code moved to Symbian/platform_timezones.cpp.
1036
1037    Note: added a replacement using MyContext which should return the same result (but not
1038    the debug output, as SystemTZ is still being called from sysytest.
1039
1040 timecontext_t SystemTZ( GZones *g, bool isDbg )
1041 {
1042   TDaylightSavingZone zone= EDstNone;
1043   int                 bias= 0;
1044   lineartime_t        tNow = 0;
1045
1046   #ifdef __EPOC_OS__
1047     TLocale                 tl;
1048     zone=                   tl.HomeDaylightSavingZone();
1049     TTimeIntervalSeconds o= tl.UniversalTimeOffset();
1050     bias=                o.Int() / SecsPerMin; // bias is based on minutes
1051     bool             isDst= tl.QueryHomeHasDaylightSavingOn();
1052     isDbg= true;
1053
1054     const char* zs;
1055     switch (zone) {
1056       case EDstHome     : zs= "EDstHome";     break;
1057       case EDstNone     : zs= "EDstNone";     break;
1058       case EDstEuropean : zs= "EDstEuropean"; break;
1059       case EDstNorthern : zs= "EDstNorthern"; break;
1060       case EDstSouthern : zs= "EDstSouthern"; break;
1061       default           : zs= "???";          break;
1062     } // switch
1063
1064     PNCDEBUGPRINTFX( DBG_SESSION,( "SystemTZ (EPOC): %s %d dst=%s", zs,bias, isDst ? "on":"off" ));
1065
1066     #ifdef _WIN32
1067       PNCDEBUGPRINTFX( DBG_SESSION,( "SystemTZ (EPOC): on emulator" ));
1068     #endif
1069   #endif
1070
1071   #if defined _WIN32 && !defined __EPOC_OS__
1072     TIME_ZONE_INFORMATION tzi;
1073     DWORD rslt= GetTimeZoneInformation( &tzi );
1074     zone= EDstEuropean;
1075     bias= -tzi.Bias; // as negative value
1076     PNCDEBUGPRINTFX( DBG_SESSION,( "SystemTZ (WIN): %s %d", "", bias ) );
1077   #endif
1078
1079   // is only dependendent on current time/date, if any DST zone
1080   if (zone!=EDstNone) tNow= getSystemNowAs(TCTX_SYSTEM, g);
1081   return SelectTZ( zone,bias,tNow, isDbg );
1082 } // SystemTZ
1083 */
1084
1085
1086 // Get int value <i> as string
1087 static string IntStr( sInt32 i )
1088 {
1089   const int    FLen= 15;    /* max length of (internal) item name */
1090   char      f[ FLen ];
1091   // cheating: this printf format assumes that sInt32 == int
1092   sprintf ( f, "%d", int(i) );
1093   string s= f;
1094   return s;
1095 } // IntStr
1096
1097
1098 /* get system's time zone */
1099 static bool MyContext( timecontext_t &aContext, GZones* g )
1100 {
1101   bool isDbg= false;
1102   // check for cached system time zone, return it if available
1103   if      (g) {
1104     isDbg= g->isDbg;
1105     // luz: added safety here to avoid that incorrectly initialized GZone
1106     //      (with tctx_tz_unknown instead of TCTX_UNKNOWN, as it was in timezones.h)
1107     //      does not cause that system timezone is assumed known (as UTC) and returned WRONG!)
1108     //      Note: rearranged to make non-locked writing to g->sysTZ is safe (for hacks needed pre-3.1.2.x!)
1109     timecontext_t   curSysTZ= g->sysTZ;
1110     if    (!(TCTX_IS_UNKNOWN( curSysTZ ) ||
1111              TCTX_IS_SYSTEM ( curSysTZ )
1112             ))    { aContext= curSysTZ; return true; }
1113   } // if
1114
1115         // there is no system time zone cached, we need to determine it from the operating system
1116   // - call platform specific routine
1117   bool ok = getSystemTimeZoneContext( aContext, g );
1118   
1119   if (isDbg) PNCDEBUGPRINTFX( DBG_SESSION, ( "MyContext: %08X ok=%d", aContext, ok ));
1120   
1121   // update cached system context
1122   if (g && ok) g->sysTZ= aContext; // assign the system context
1123   return   ok;
1124 } /* MyContext */
1125
1126
1127 /* %%% luz 2009-04-02 seems of no relevance except for Symbian/Epoc -> Epoc code moved to Symbian/platform_timezones.cpp
1128    added a replacement here as it is still used from sysytest, which should return the same result (but not
1129    the debug output */
1130 timecontext_t SystemTZ( GZones *g, bool isDbg )
1131 {
1132         timecontext_t tctx;
1133   if (MyContext(tctx, g))
1134         return tctx;
1135   else
1136         return TCTX_UNKNOWN;
1137 }
1138
1139
1140
1141 bool ContextForEntry( timecontext_t &aContext, tz_entry &t, bool chkNameFirst, GZones* g )
1142 {
1143         string s;
1144         string sName = t.name;
1145   bool ok = true;
1146   do {
1147     if (chkNameFirst &&       !sName.empty() &&
1148         TimeZoneNameToContext( sName.c_str(),aContext, g )) break;
1149
1150     // if there are no rules defined => switch it off
1151     if (t.dst.wMonth==0 ||
1152         t.std.wMonth==0 ||
1153         Same_tChange( t.dst, t.std )) {
1154       ClrDST( t );
1155     } // if
1156
1157                  t.name   = (char*)sName.c_str();
1158                  t.ident  = "";
1159                  t.dynYear= ""; // MUST be set as this is assigned unchecked to a string
1160                                 // (which crashes when assigned NULL or low number)
1161     if (FoundTZ( t, s, aContext, g )) break; // with this name
1162
1163                  t.name   = "";
1164                  t.dynYear= "CUR";
1165     if (FoundTZ( t, s, aContext, g )) break; // with different name
1166
1167     int i= 0;
1168     if (sName.empty()) sName= "unassigned";
1169
1170     string v;
1171     while (true) {
1172                v = sName.c_str();
1173       if (i>0) v+= "_" + IntStr( i );
1174       if (!TimeZoneNameToContext( v.c_str(),aContext, g )) break; // A timezone with this name must not exist
1175       i++;
1176     } // while
1177
1178     t.name= (char*)v.c_str();
1179
1180     string      tz_Name;
1181     FoundTZ( t, tz_Name, aContext, g, true ); // create it
1182     if (TimeZoneNameToContext( v.c_str(),aContext, g )) break;
1183     ok= false;
1184
1185     /*
1186     t.name= (char*)sName.c_str();
1187
1188     string      tzName;
1189     FoundTZ( t, tzName, aContext, g, true ); // create it
1190     if (TimeZoneNameToContext( sName.c_str(),aContext, g )) break;
1191     ok= false;
1192     */
1193   } while (false);
1194   return ok;
1195 } /* ContextFromNameAndEntry */
1196
1197
1198
1199 /* returns true, if the context rules are identical */
1200 static bool IdenticalRules( timecontext_t aSourceContext,
1201                             timecontext_t aTargetContext, GZones* g )
1202 {
1203   tz_entry ts, tt;
1204
1205   /* only performed in TZ mode */
1206   if (!GetTZ( aSourceContext, ts, g, 0 ) ||
1207       !GetTZ( aTargetContext, tt, g, 0 )) return false;
1208
1209   TTimeZones asTZ= TCTX_TZENUM( aSourceContext );
1210   TTimeZones atTZ= TCTX_TZENUM( aTargetContext );
1211   if  (asTZ==atTZ) return true; // identical
1212
1213   if (ts.ident == "$" ||
1214       tt.ident == "$") return false;
1215
1216   if              (ts.bias==tt.bias  &&
1217   // memcmp( &ts.dst, &tt.dst, sizeof(tChange))==0 &&
1218   // memcmp( &ts.std, &tt.std, sizeof(tChange))==0) return true;
1219      Same_tChange( ts.dst,  tt.dst ) &&
1220      Same_tChange( ts.std,  tt.std )) return true;
1221
1222   /*
1223   if (!TCTX_IS_TZ( aSourceContext ) ||
1224       !TCTX_IS_TZ( aTargetContext )) return false;
1225
1226   // get the time zones for calculation
1227   TTimeZones asTZ= TCTX_TZENUM(aSourceContext);
1228   TTimeZones atTZ= TCTX_TZENUM(aTargetContext);
1229
1230   if (asTZ==atTZ) return true;
1231
1232   if (         tz[ asTZ ].bias==tz[ atTZ ].bias &&
1233       memcmp( &tz[ asTZ ].dst, &tz[ atTZ ].dst, sizeof(tChange))==0 &&
1234       memcmp( &tz[ asTZ ].std, &tz[ atTZ ].std, sizeof(tChange))==0) {
1235     return true;
1236   } // if
1237   */
1238
1239   return false;
1240 } /* IdenticalRules */
1241
1242
1243
1244 /*! get system's time zone context (i.e. resolve the TCTX_SYSTEM meta-context)
1245  *  @param[in,out] aContext : context will be made non-meta, that is, if input is TCTX_SYSTEM,
1246  *                            actual time zone will be determined.
1247  */
1248 bool TzResolveMetaContext( timecontext_t &aContext, GZones* g )
1249 {
1250   if (!TCTX_IS_SYSTEM(aContext))
1251     return true; // no meta zone, just return unmodified
1252   // is meta-context TCTX_SYSTEM, determine actual symbolic context
1253   return MyContext(aContext,g);
1254 } // TzResolveMetaContext
1255
1256
1257 /* make time context non-symbolic (= calculate minute offset east of UTC for aRefTime) */
1258 bool TzResolveContext( timecontext_t &aContext, lineartime_t aRefTime, bool aRefTimeUTC, GZones* g )
1259 {
1260   sInt32 offs;
1261   // resolve possible meta context (TCTX_SYSTEM at this time)
1262   if (!TzResolveMetaContext(aContext,g)) return false;
1263   // check if already an offset (non-symbolic)
1264   if (!TCTX_IS_TZ(aContext))
1265     return true; // yes, no conversion needed
1266   // is symbolic, needs conversion to offset
1267   bool ok = TimeZoneToOffs(
1268     aRefTime, // reference time for which we want to know the offset
1269     aContext, // context
1270     aRefTimeUTC, // "backwards" means refTime is UTC and we want know offset to go back to local
1271     offs, // here we get the offset
1272     g
1273   );
1274   if (ok) {
1275     aContext = TCTX_JOIN_RFLAGS_TZ(
1276       aContext, // join original rendering flags...
1277       TCTX_OFFSCONTEXT(offs) // ...with new offset based context
1278     );
1279   }
1280   return ok;
1281 } // TzResolveContext
1282
1283
1284 /*! calculate minute offset east of UTC for aRefTime
1285  *  @param[in] aContext       : time context to resolve
1286  *  @param[out] aMinuteOffset : receives minute offset east of UTC
1287  *  @param[in] aRefTime       : reference time point for resolving the offset
1288  *  @param[in] aRefTimeUTC    : if set, reference time must be UTC,
1289  *                              otherwise, reference time must be in context of aContext
1290  */
1291 bool TzResolveToOffset( timecontext_t aContext, sInt16 &aMinuteOffset, lineartime_t aRefTime, bool aRefTimeUTC, GZones* g )
1292 {
1293   bool ok = TzResolveContext(aContext, aRefTime, aRefTimeUTC, g);
1294   if (ok) {
1295     aMinuteOffset = TCTX_MINOFFSET(aContext);
1296   }
1297   return ok;
1298 } // TzResolveToOffset
1299
1300
1301
1302 /*! Offset between two contexts (in seconds)
1303  *  Complex time zones (type "$") can't be currently resolved, they return false
1304  *  @param[in] aSourceValue   : reference time for which the offset should be calculated
1305  *  @param[in] aSourceContext : source time zone context
1306  *  @param[in] aTargetContext : source time zone context
1307  *  @param[out] sDiff         : receives offset east of UTC in seconds
1308  */
1309 bool TzOffsetSeconds( lineartime_t aSourceValue, timecontext_t aSourceContext,
1310                                                  timecontext_t aTargetContext,
1311                                                  sInt32        &sDiff, GZones* g,
1312                                                  timecontext_t aDefaultContext )
1313 {
1314   bool         sSys,  tSys,
1315                sUnk,  tUnk;
1316   sInt32       sOffs= 0, tOffs= 0; // initialize them for sure
1317   lineartime_t aTargetValue;
1318
1319   bool      ok= true;
1320   bool   isDbg= false;
1321   if (g) isDbg= g->isDbg;
1322
1323   // set default context for unknown zones
1324   if (TCTX_IS_UNKNOWN(aSourceContext)) aSourceContext=aDefaultContext;
1325   if (TCTX_IS_UNKNOWN(aTargetContext)) aTargetContext=aDefaultContext;
1326
1327   do {
1328     sDiff= 0;
1329     if (aSourceContext==aTargetContext) break; /* no conversion, if identical context */
1330
1331     /* both unknown or system is still ok */
1332     if   (TCTX_IS_UNKNOWN( aSourceContext ) &&
1333           TCTX_IS_UNKNOWN( aTargetContext )) break;
1334     sSys= TCTX_IS_SYSTEM ( aSourceContext );
1335     tSys= TCTX_IS_SYSTEM ( aTargetContext ); if (sSys && tSys) break;
1336
1337     /* calculate specifically for the system's time zone */
1338     if (sSys)   MyContext( aSourceContext, g );
1339     if (tSys)   MyContext( aTargetContext, g );
1340
1341     /* if both are unknown now, then it's good as well */
1342     sUnk= TCTX_IS_UNKNOWN( aSourceContext );
1343     tUnk= TCTX_IS_UNKNOWN( aTargetContext ); if (sUnk && tUnk)              break;
1344     /* this case can't be resolved: */       if (sUnk || tUnk) { ok= false; break; }
1345
1346     if (IdenticalRules( aSourceContext,aTargetContext, g )) break;
1347
1348     /* now do the "hard" things */
1349     if (!TimeZoneToOffs( aSourceValue, aSourceContext, false, sOffs, g )) return false;
1350                          aTargetValue= aSourceValue
1351                                      - (lineartime_t)(sOffs*SecsPerMin)*secondToLinearTimeFactor;
1352     if (!TimeZoneToOffs( aTargetValue, aTargetContext,  true, tOffs, g )) return false;
1353
1354     sDiff= ( tOffs - sOffs )*SecsPerMin;
1355   } while (false);
1356
1357   if (isDbg) {
1358     PNCDEBUGPRINTFX( DBG_SESSION,( "sSys=%d tSys=%d / sUnk=%d tUnk=%d / sOffs=%d tOffs=%d",
1359                                     sSys,tSys, sUnk,tUnk, sOffs,tOffs ));
1360     PNCDEBUGPRINTFX( DBG_SESSION,( "TzOffsetSeconds=%d", sDiff ));
1361   } // if
1362
1363   return ok;
1364 } /* TzOffsetSeconds */
1365
1366
1367 /*! Converts timestamp value from one zone to another
1368  *  Complex time zones (type "$") can't be currently resolved, they return false
1369  *  @param[in/out] aValue     : will be converted from source context to target context. If==noLinearTime==0, no conversion is done
1370  *  @param[in] aSourceContext : source time zone context
1371  *  @param[in] aTargetContext : source time zone context
1372  *  @param[in] aDefaultContext: default context to use if source or target is TCTX_UNKNOWN
1373  */
1374 bool TzConvertTimestamp( lineartime_t &aValue,   timecontext_t aSourceContext,
1375                                                  timecontext_t aTargetContext,
1376                                                                        GZones* g,
1377                                                  timecontext_t aDefaultContext )
1378 {
1379   sInt32 sdiff;
1380   if (aValue==noLinearTime) return true; // no time, don't convert
1381   bool ok = TzOffsetSeconds( aValue, aSourceContext, aTargetContext, sdiff, g, aDefaultContext );
1382   if (ok)
1383     aValue += (lineartime_t)(sdiff)*secondToLinearTimeFactor;
1384   return ok;
1385 } /* TzConvertTimestamp */
1386
1387
1388 } // namespace sysync
1389
1390
1391 /* eof */
1392