6 * Timezones conversion from/to linear time scale.
8 * Copyright (c) 2004-2009 by Synthesis AG (www.synthesis.ch)
10 * 2004-04-18 : bfo : initial version
20 #include "lineartime.h"
21 #include "debuglogger.h"
24 #include "platform_mutex.h"
28 using namespace std; // for string and list
32 // include the built-in time zone table
37 /// time zone table entry definition
38 typedef struct tChangeStruct {
41 short wNth; // nth occurance
53 tChangeStruct(short aMonth,
59 wDayOfWeek(aDayOfWeek),
69 std::string name; /**< name, same as TZID in
70 VTIMEZONE, e.g. CET/CEST or
71 /softwarestudio.org/Tzfile/Europe/Berlin;
72 see also dst/stdName */
73 std::string stdName; /**< optional standard time name, e.g. CEST; must be
74 set if "name" is not a concatenation of
75 standard and daylight name (CET/CEST);
76 the vCalendar 1.0 code relies on that */
77 std::string dstName; /**< used instead of splitting
78 "name" if (and only if)
79 stdName is set; may be empty
80 in zones without daylight
82 std::string location; /**< location string as used in Olson TZID, e.g. Europe/Berlin */
83 short bias; /**< minutes difference to UTC (west negative, east positive */
84 short biasDST; /**< minutes difference to bias (not UTC!) */
85 std::string ident; /**< see FoundTZ() */
86 std::string dynYear; /**< if this time zone rule is assigned to a specific year range */
87 tChange dst; /**< describes when daylight saving time will become active */
88 tChange std; /**< describes when standard time will become active */
89 bool groupEnd; /**< true if the entry which follows this one belongs to a different zone or last entry */
97 tz_entry( const std::string &aName,
100 const std::string &aIdent,
101 const std::string &aDynYear,
120 //const int DST_Bias= 60; // General DST offset (in minutes)
122 // Symbian timezone categories
123 // visible for all other systems as well
125 enum TDaylightSavingZone {
126 EDstHome =0x40000000,
136 const uInt32 TCTX_OFFSETMASK = 0x0000FFFF;
138 // time context flags
139 // - symbolic zone flag
140 const uInt32 TCTX_SYMBOLIC_TZ= 0x00010000;
142 const uInt32 TCTX_DATEONLY = 0x00020000;
143 const uInt32 TCTX_TIMEONLY = 0x00040000;
144 const uInt32 TCTX_DURATION = 0x00080000;
146 const uInt32 TCTX_RFLAGMASK = TCTX_DATEONLY+TCTX_TIMEONLY+TCTX_DURATION;
148 //! Get signed minute offset
149 sInt16 TCTX_MINOFFSET( timecontext_t tctx );
151 //! Get time zone enum
152 TTimeZones TCTX_TZENUM( timecontext_t tctx );
155 // macro to get time zone context
156 #define TCTX_ENUMCONTEXT( tzenum ) ((timecontext_t) ((tzenum) | TCTX_SYMBOLIC_TZ))
157 #define TCTX_UNKNOWN TCTX_ENUMCONTEXT( tctx_tz_unknown )
158 #define TCTX_SYSTEM TCTX_ENUMCONTEXT( tctx_tz_system )
159 #define TCTX_UTC TCTX_ENUMCONTEXT( tctx_tz_UTC )
160 #define TCTX_OFFSCONTEXT( offs ) ((timecontext_t)((sInt16)(offs) & TCTX_OFFSETMASK ))
164 // macro to get time zone and other flags
165 #define TCTX_IS_TZ( tctx ) ((bool)(tctx & TCTX_SYMBOLIC_TZ))
166 #define TCTX_IS_DATEONLY( tctx ) ((bool)(tctx & TCTX_DATEONLY ))
169 //! Check if <tctx> is a symbolic TZ info
170 bool TCTX_IS_TZ ( timecontext_t tctx );
172 // Check if <tctx> is a built-in symbolic TZ info
173 bool TCTX_IS_BUILTIN ( timecontext_t tctx );
175 //! Check if <tctx> has TCTX_DATEONLY mode set
176 bool TCTX_IS_DATEONLY( timecontext_t tctx );
178 //! Check if <tctx> has TCTX_TIMEONLY mode set
179 bool TCTX_IS_TIMEONLY( timecontext_t tctx );
181 //! Check if <tctx> has TCTX_DURATION mode set
182 bool TCTX_IS_DURATION( timecontext_t tctx );
184 //! Check if <tctx> is a unknown time zone
185 bool TCTX_IS_UNKNOWN ( timecontext_t tctx );
187 //! Check if <tctx> is the system time zone
188 bool TCTX_IS_SYSTEM ( timecontext_t tctx );
190 //! Check if <tctx> is the UTC time zone
191 bool TCTX_IS_UTC ( timecontext_t tctx );
193 //! result is render flags from <aRFlagContext> with zone info from <aZoneContext>
194 timecontext_t TCTX_JOIN_RFLAGS_TZ ( timecontext_t aRFlagContext, timecontext_t aZoneContext );
198 // ---- utility functions -------------------------------------------------------------
199 /*! Specific bias string. Unit: hours */
200 string BiasStr( int bias );
202 /*! Clear the DST info of <t> */
203 void ClrDST( tz_entry &t );
206 typedef std::list<tz_entry> TZList;
215 predefinedSysTZ= TCTX_UNKNOWN; // no predefined system time zone
216 sysTZ= predefinedSysTZ; // default to predefined zone, if none, this will be obtained from OS APIs
217 isDbg= false; // !!! IMPORTANT: do NOT enable this except for test targets, as it leads to recursions (debugPrintf calls time routines!)
218 fSystemZoneDefinitionsFinalized = false;
226 /*! @brief populate GZones with system information
228 * Sets predefinedSysTZ, sysTZ and adds time zones
229 * to tzP, if that information can be found on the
232 * Returns false in case of a fatal error.
236 /*! @brief log and/or add more GZones
238 * Called after config was read and normal debug logging
241 void loggingStarted();
243 /*! @brief find a matching time zone
245 * This returns the best match, with "better" defined as (best
247 * - exact name AND exact rule set match
248 * - existing entry has a location (currently only the case for
249 * time zones imported from libical) AND that location is part of the
250 * name being searched for AND the rule matches
251 * - as before, but with the rule match
252 * - exact rule set match
254 * If there are multiple entries which are equally good, the first one
257 * When doing rule matching, the dynYear value of an existing
258 * entry has to match the current year as implemented by the
259 * FitYear() function in timezone.cpp. In most (all?) cases this
260 * has the effect that historic rules are skipped. Likewise,
261 * VTIMEZONE information sent out is based on the current rules
262 * for the zone, regardless of when the event itself takes place.
264 * This is approach is intentional: it helps avoid mismatches
265 * when historic rules of one zone match the current rules of
266 * another. Furthermore, it helps peers which do rule-based
267 * matching and only know the current rules of each zone.
269 * @param aTZ entry with rules and name set; ident is ignored
270 * @retval aContext the matching time zone context ID; it always
271 * refers to the tz_entry without a dynYear
272 * @return true if match found
274 bool matchTZ(const tz_entry &aTZ, TDebugLogger *aLogP, timecontext_t &aContext);
278 /** @return true to stop iterating */
279 virtual bool visit(const tz_entry &aTZ, timecontext_t aContext) = 0;
280 virtual ~visitor() {} // destructor
283 /*! @brief invoke visitor once for each time zone
284 * @return true if the visitor returned true
286 bool foreachTZ(visitor &v);
288 void ResetCache(void) {
289 sysTZ= predefinedSysTZ; // reset cached system time zone to make sure it is re-evaluated
295 TZList tzP; // the list of additional time zones
296 timecontext_t predefinedSysTZ; // can be set to a specific zone to override zone returned by OS API
297 timecontext_t sysTZ; // the system's time zone, will be calculated,
298 // if set to tctx_tz_unknown
299 bool isDbg; // write debug information
300 bool fSystemZoneDefinitionsFinalized; // finalizeSystemZoneDefinitions() already called
303 uInt32 getDbgMask; // allow debugging in a specific context
304 TDebugLogger* getDbgLogger;
310 // visible for debugging only
311 timecontext_t SystemTZ( GZones *g, bool isDbg= false );
312 timecontext_t SelectTZ( TDaylightSavingZone zone, int bias, int biasDST, lineartime_t tNow,
315 // visible for platform_timezones.cpp/.mm
316 bool ContextForEntry( timecontext_t &aContext, tz_entry &t, bool chkNameFirst, GZones* g );
317 void Get_tChange( lineartime_t tim, tChange &v, bool asDate= false );
321 /*! Get <tz_entry> from <aContext>
324 bool GetTZ( timecontext_t aContext, tz_entry &t, GZones* g, int year= 0 );
325 bool GetTZ( string std, string dst, int bias, int biasDST, tz_entry &t, GZones* g );
327 /*! Get the current year
329 sInt16 MyYear( GZones* g );
332 /*! Returns true, if the given TZ is existing already
333 * <t> tz_entry to search for:
334 * If <t.name> == "" search for any entry with these values.
335 * <t.name> != "" name must fit
336 * If <t.ident>== "" search for any entry with these values
337 * <t.ident>!= "" ident must fit
338 * <t.ident>== "?" search for the name <t.name> only.
340 * <aName> is <t.name> of the found record
341 * <aContext> is the assigned context
342 * <g> global list of additional time zones
343 * <createIt> create an entry, if not yet existing / default: false
344 * <searchOffset> says, where to start searching / default: at the beginning
346 bool FoundTZ( const tz_entry &t,
348 timecontext_t &aContext,
350 bool createIt = false,
351 timecontext_t searchOffset= tctx_tz_unknown );
354 /*! Remove an existing entry
355 * Currently, elements of the hard coded list can't be removed
357 bool RemoveTZ( const tz_entry &t, GZones* g );
360 /*! Is it a DST time zone ? (both months must be defined for DST mode) */
361 bool DSTCond( const tz_entry &t );
363 /*! Adjust to day number within month <m>, %% valid till year <y> 2099 */
364 void AdjustDay( sInt16 &d, sInt16 m, sInt16 y );
366 /*! Get lineartime_t of <t> for a given <year>, either from std <toDST> or vice versa */
367 lineartime_t DST_Switch( const tz_entry &t, int bias, sInt16 aYear, bool toDST );
369 /*! Convert time zone name into context
370 * @param[in] aName : context name to resolve
371 * @param[out] aContext : context for this aName
372 * @param[in] g : global list of additional time zones
375 bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g );
377 /*! Convert context into time zone name, a preferred name can be given
378 * @param[in] aContext : time context to resolve
379 * @param[out] aName : context name for this aContext
380 * @param[in] g : global list of additional time zones
381 * @param[in] aPrefIdent : preferred name, if more than one is fitting
384 bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g, cAppCharP aPrefIdent= "" );
388 /*! get system's time zone context (i.e. resolve the TCTX_SYSTEM meta-context)
389 * @param[in,out] aContext : context will be made non-meta, that is, if input is TCTX_SYSTEM,
390 * actual time zone will be determined.
391 * @param[in] g : global list of additional time zones
393 bool TzResolveMetaContext( timecontext_t &aContext, GZones* g );
395 /*! make time context non-symbolic (= calculate minute offset east of UTC for aRefTime)
396 * but retain other time context flags in aContext
397 * @param[in,out] aContext : context will be made non-symbolic, that is resolved to minute offset east of UTC
398 * @param[in] aRefTime : reference time point for resolving the offset
399 * @param[in] aRefTimeUTC : if set, reference time must be UTC,
400 * otherwise, reference time must be in context of aContext
401 * @param[in] g : global list of additional time zones
403 bool TzResolveContext( timecontext_t &aContext, lineartime_t aRefTime, bool aRefTimeUTC, GZones* g );
405 /*! calculate minute offset east of UTC for aRefTime
406 * @param[in] aContext : time context to resolve
407 * @param[out] aMinuteOffset : receives minute offset east of UTC
408 * @param[in] aRefTime : reference time point for resolving the offset
409 * @param[in] aRefTimeUTC : if set, reference time must be UTC,
410 * otherwise, reference time must be in context of aContext
411 * @param[in] g : global list of additional time zones
413 bool TzResolveToOffset( timecontext_t aContext, sInt16 &aMinuteOffset,
414 lineartime_t aRefTime,
419 /*! Offset between two contexts (in seconds)
420 * Complex time zones (type "$") can't be currently resolved, they return false
421 * @param[in] aSourceValue : reference time for which the offset should be calculated
422 * @param[in] aSourceContext : source time zone context
423 * @param[in] aTargetContext : source time zone context
424 * @param[out] sDiff : receives offset east of UTC in seconds
425 * @param[in] g : global list of additional time zones
426 * @param[in] aDefaultContext: default context to use if source or target is TCTX_UNKNOWN
428 bool TzOffsetSeconds( lineartime_t aSourceValue, timecontext_t aSourceContext,
429 timecontext_t aTargetContext,
432 timecontext_t aDefaultContext= TCTX_UNKNOWN);
436 /*! Converts timestamp value from one zone to another
437 * Complex time zones (type "$") can't be currently resolved, they return false
438 * @param[in/out] aValue : will be converted from source context to target context
439 * @param[in] aSourceContext : source time zone context
440 * @param[in] aTargetContext : source time zone context
441 * @param[in] g : global list of additional time zones
442 * @param[in] aDefaultContext: default context to use if source or target is TCTX_UNKNOWN
444 bool TzConvertTimestamp( lineartime_t &aValue, timecontext_t aSourceContext,
445 timecontext_t aTargetContext,
447 timecontext_t aDefaultContext = TCTX_UNKNOWN);
450 /*! Prototypes for platform-specific implementation of time-zone-related routines
451 * which are implemented in platform_time.cpp
454 /*! @brief get system real time
455 * @return system's real time in lineartime_t scale, in specified time zone context
456 * @param[in] aTimeContext : desired output time zone
457 * @param[in] g : global list of additional time zones
458 * @param[in] aNoOffset : no offset calculation, if true (to avoid recursive calls)
460 lineartime_t getSystemNowAs( timecontext_t aTimeContext, GZones* g, bool aNoOffset= false );
463 /*! Prototypes for platform-specific implementation of time-zone-related routines
464 * which are implemented in platform_timezones.cpp/.mm
467 /*! @brief platform specific loading of time zone definitions
468 * @return true if this list is considered complete (i.e. no built-in zones should be used additionally)
469 * @param[in/out] aGZones : the GZones object where system zones should be loaded into
470 * @note this is called at construction of the SyncAppBase before any logging facilities are
471 * available. This routine should load enough time zone information such that config
472 * can be read and conversion between UTC and system local time is possible.
473 * Use finalizeSystemZoneDefinitions() to add time zones with full logging available.
475 bool loadSystemZoneDefinitions( GZones* aGZones );
477 /*! @brief second opportunity to load platform specific time zone definitions with logging available (and config already parsed)
478 * Called only once per GZones instance.
479 * @param[in/out] aGZones : the GZones object where additional system zones should be loaded into
481 void finalizeSystemZoneDefinitions( GZones* aGZones );
483 /*! @brief get current system time zone
484 * @return true if successful
485 * @param[out] aContext : the time zone context representing the current system time zone.
486 * @param[in] aGZones : the GZones object.
488 bool getSystemTimeZoneContext( timecontext_t &aContext, GZones* aGZones );
492 } // namespace sysync