• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

KCal Library

  • sources
  • kde-4.12
  • kdepimlibs
  • kcal
icaltimezones.cpp
1 /*
2  This file is part of the kcal library.
3 
4  Copyright (c) 2005-2007 David Jarvie <djarvie@kde.org>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 #include "icaltimezones.h"
23 #include "icalformat.h"
24 #include "icalformat_p.h"
25 
26 extern "C" {
27  #include <libical/ical.h>
28  #include <libical/icaltimezone.h>
29 }
30 #include <ksystemtimezone.h>
31 #include <kdatetime.h>
32 #include <kdebug.h>
33 
34 #include <QtCore/QDateTime>
35 #include <QtCore/QString>
36 #include <QtCore/QList>
37 #include <QtCore/QVector>
38 #include <QtCore/QSet>
39 #include <QtCore/QFile>
40 #include <QtCore/QTextStream>
41 
42 using namespace KCal;
43 
44 // Minimum repetition counts for VTIMEZONE RRULEs
45 static const int minRuleCount = 5; // for any RRULE
46 static const int minPhaseCount = 8; // for separate STANDARD/DAYLIGHT component
47 
48 // Convert an ical time to QDateTime, preserving the UTC indicator
49 static QDateTime toQDateTime( const icaltimetype &t )
50 {
51  return QDateTime( QDate( t.year, t.month, t.day ),
52  QTime( t.hour, t.minute, t.second ),
53  ( t.is_utc ? Qt::UTC : Qt::LocalTime ) );
54 }
55 
56 // Maximum date for time zone data.
57 // It's not sensible to try to predict them very far in advance, because
58 // they can easily change. Plus, it limits the processing required.
59 static QDateTime MAX_DATE()
60 {
61  static QDateTime dt;
62  if ( !dt.isValid() ) {
63  dt = QDateTime( QDate::currentDate().addYears( 20 ), QTime( 0, 0, 0 ) );
64  }
65  return dt;
66 }
67 
68 static icaltimetype writeLocalICalDateTime( const QDateTime &utc, int offset )
69 {
70  QDateTime local = utc.addSecs( offset );
71  icaltimetype t = icaltime_null_time();
72  t.year = local.date().year();
73  t.month = local.date().month();
74  t.day = local.date().day();
75  t.hour = local.time().hour();
76  t.minute = local.time().minute();
77  t.second = local.time().second();
78  t.is_date = 0;
79  t.zone = 0;
80  t.is_utc = 0;
81  return t;
82 }
83 
84 namespace KCal {
85 
86 /******************************************************************************/
87 
88 //@cond PRIVATE
89 class ICalTimeZonesPrivate
90 {
91  public:
92  ICalTimeZonesPrivate() {}
93  ICalTimeZones::ZoneMap zones;
94 };
95 //@endcond
96 
97 ICalTimeZones::ICalTimeZones()
98  : d( new ICalTimeZonesPrivate )
99 {
100 }
101 
102 ICalTimeZones::~ICalTimeZones()
103 {
104  delete d;
105 }
106 
107 const ICalTimeZones::ZoneMap ICalTimeZones::zones() const
108 {
109  return d->zones;
110 }
111 
112 bool ICalTimeZones::add( const ICalTimeZone &zone )
113 {
114  if ( !zone.isValid() ) {
115  return false;
116  }
117  if ( d->zones.find( zone.name() ) != d->zones.end() ) {
118  return false; // name already exists
119  }
120 
121  d->zones.insert( zone.name(), zone );
122  return true;
123 }
124 
125 ICalTimeZone ICalTimeZones::remove( const ICalTimeZone &zone )
126 {
127  if ( zone.isValid() ) {
128  for ( ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it ) {
129  if ( it.value() == zone ) {
130  d->zones.erase( it );
131  return ( zone == ICalTimeZone::utc() ) ? ICalTimeZone() : zone;
132  }
133  }
134  }
135  return ICalTimeZone();
136 }
137 
138 ICalTimeZone ICalTimeZones::remove( const QString &name )
139 {
140  if ( !name.isEmpty() ) {
141  ZoneMap::Iterator it = d->zones.find( name );
142  if ( it != d->zones.end() ) {
143  ICalTimeZone zone = it.value();
144  d->zones.erase(it);
145  return ( zone == ICalTimeZone::utc() ) ? ICalTimeZone() : zone;
146  }
147  }
148  return ICalTimeZone();
149 }
150 
151 void ICalTimeZones::clear()
152 {
153  d->zones.clear();
154 }
155 
156 ICalTimeZone ICalTimeZones::zone( const QString &name ) const
157 {
158  if ( !name.isEmpty() ) {
159  ZoneMap::ConstIterator it = d->zones.constFind( name );
160  if ( it != d->zones.constEnd() ) {
161  return it.value();
162  }
163  }
164  return ICalTimeZone(); // error
165 }
166 
167 /******************************************************************************/
168 
169 ICalTimeZoneBackend::ICalTimeZoneBackend()
170  : KTimeZoneBackend()
171 {}
172 
173 ICalTimeZoneBackend::ICalTimeZoneBackend( ICalTimeZoneSource *source,
174  const QString &name,
175  const QString &countryCode,
176  float latitude, float longitude,
177  const QString &comment )
178  : KTimeZoneBackend( source, name, countryCode, latitude, longitude, comment )
179 {}
180 
181 ICalTimeZoneBackend::ICalTimeZoneBackend( const KTimeZone &tz, const QDate &earliest )
182  : KTimeZoneBackend( 0, tz.name(), tz.countryCode(), tz.latitude(), tz.longitude(), tz.comment() )
183 {
184  Q_UNUSED( earliest );
185 }
186 
187 ICalTimeZoneBackend::~ICalTimeZoneBackend()
188 {}
189 
190 KTimeZoneBackend *ICalTimeZoneBackend::clone() const
191 {
192  return new ICalTimeZoneBackend( *this );
193 }
194 
195 QByteArray ICalTimeZoneBackend::type() const
196 {
197  return "ICalTimeZone";
198 }
199 
200 bool ICalTimeZoneBackend::hasTransitions( const KTimeZone *caller ) const
201 {
202  Q_UNUSED( caller );
203  return true;
204 }
205 
206 /******************************************************************************/
207 
208 ICalTimeZone::ICalTimeZone()
209  : KTimeZone( new ICalTimeZoneBackend() )
210 {}
211 
212 ICalTimeZone::ICalTimeZone( ICalTimeZoneSource *source, const QString &name,
213  ICalTimeZoneData *data )
214  : KTimeZone( new ICalTimeZoneBackend( source, name ) )
215 {
216  setData( data );
217 }
218 
219 ICalTimeZone::ICalTimeZone( const KTimeZone &tz, const QDate &earliest )
220  : KTimeZone( new ICalTimeZoneBackend( 0, tz.name(), tz.countryCode(),
221  tz.latitude(), tz.longitude(),
222  tz.comment() ) )
223 {
224  const KTimeZoneData *data = tz.data( true );
225  if ( data ) {
226  const ICalTimeZoneData *icaldata = dynamic_cast<const ICalTimeZoneData*>( data );
227  if ( icaldata ) {
228  setData( new ICalTimeZoneData( *icaldata ) );
229  } else {
230  setData( new ICalTimeZoneData( *data, tz, earliest ) );
231  }
232  }
233 }
234 
235 ICalTimeZone::~ICalTimeZone()
236 {}
237 
238 QString ICalTimeZone::city() const
239 {
240  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
241  return dat ? dat->city() : QString();
242 }
243 
244 QByteArray ICalTimeZone::url() const
245 {
246  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
247  return dat ? dat->url() : QByteArray();
248 }
249 
250 QDateTime ICalTimeZone::lastModified() const
251 {
252  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
253  return dat ? dat->lastModified() : QDateTime();
254 }
255 
256 QByteArray ICalTimeZone::vtimezone() const
257 {
258  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
259  return dat ? dat->vtimezone() : QByteArray();
260 }
261 
262 icaltimezone *ICalTimeZone::icalTimezone() const
263 {
264  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
265  return dat ? dat->icalTimezone() : 0;
266 }
267 
268 bool ICalTimeZone::update( const ICalTimeZone &other )
269 {
270  if ( !updateBase( other ) ) {
271  return false;
272  }
273 
274  KTimeZoneData *otherData = other.data() ? other.data()->clone() : 0;
275  setData( otherData, other.source() );
276  return true;
277 }
278 
279 ICalTimeZone ICalTimeZone::utc()
280 {
281  static ICalTimeZone utcZone;
282  if ( !utcZone.isValid() ) {
283  ICalTimeZoneSource tzs;
284  utcZone = tzs.parse( icaltimezone_get_utc_timezone() );
285  }
286  return utcZone;
287 }
288 
289 /******************************************************************************/
290 
291 //@cond PRIVATE
292 class ICalTimeZoneDataPrivate
293 {
294  public:
295  ICalTimeZoneDataPrivate() : icalComponent(0) {}
296  ~ICalTimeZoneDataPrivate()
297  {
298  if ( icalComponent ) {
299  icalcomponent_free( icalComponent );
300  }
301  }
302  icalcomponent *component() const { return icalComponent; }
303  void setComponent( icalcomponent *c )
304  {
305  if ( icalComponent ) {
306  icalcomponent_free( icalComponent );
307  }
308  icalComponent = c;
309  }
310  QString location; // name of city for this time zone
311  QByteArray url; // URL of published VTIMEZONE definition (optional)
312  QDateTime lastModified; // time of last modification of the VTIMEZONE component (optional)
313  private:
314  icalcomponent *icalComponent; // ical component representing this time zone
315 };
316 //@endcond
317 
318 ICalTimeZoneData::ICalTimeZoneData()
319  : d ( new ICalTimeZoneDataPrivate() )
320 {
321 }
322 
323 ICalTimeZoneData::ICalTimeZoneData( const ICalTimeZoneData &rhs )
324  : KTimeZoneData( rhs ),
325  d( new ICalTimeZoneDataPrivate() )
326 {
327  d->location = rhs.d->location;
328  d->url = rhs.d->url;
329  d->lastModified = rhs.d->lastModified;
330  d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
331 }
332 
333 ICalTimeZoneData::ICalTimeZoneData( const KTimeZoneData &rhs,
334  const KTimeZone &tz, const QDate &earliest )
335  : KTimeZoneData( rhs ),
336  d( new ICalTimeZoneDataPrivate() )
337 {
338  // VTIMEZONE RRULE types
339  enum {
340  DAY_OF_MONTH = 0x01,
341  WEEKDAY_OF_MONTH = 0x02,
342  LAST_WEEKDAY_OF_MONTH = 0x04
343  };
344 
345  if ( tz.type() == "KSystemTimeZone" ) {
346  // Try to fetch a system time zone in preference, on the grounds
347  // that system time zones are more likely to be up to date than
348  // built-in libical ones.
349  icalcomponent *c = 0;
350  KTimeZone ktz = KSystemTimeZones::readZone( tz.name() );
351  if ( ktz.isValid() ) {
352  if ( ktz.data(true) ) {
353  ICalTimeZone icaltz( ktz, earliest );
354  icaltimezone *itz = icaltz.icalTimezone();
355  c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
356  icaltimezone_free( itz, 1 );
357  }
358  }
359  if ( !c ) {
360  // Try to fetch a built-in libical time zone.
361  icaltimezone *itz = icaltimezone_get_builtin_timezone( tz.name().toUtf8() );
362  c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
363  }
364  if ( c ) {
365  // TZID in built-in libical time zones has a standard prefix.
366  // To make the VTIMEZONE TZID match TZID references in incidences
367  // (as required by RFC2445), strip off the prefix.
368  icalproperty *prop = icalcomponent_get_first_property( c, ICAL_TZID_PROPERTY );
369  if ( prop ) {
370  icalvalue *value = icalproperty_get_value( prop );
371  const char *tzid = icalvalue_get_text( value );
372  QByteArray icalprefix = ICalTimeZoneSource::icalTzidPrefix();
373  int len = icalprefix.size();
374  if ( !strncmp( icalprefix, tzid, len ) ) {
375  const char *s = strchr( tzid + len, '/' ); // find third '/'
376  if ( s ) {
377  QByteArray tzidShort( s + 1 ); // deep copy of string (needed by icalvalue_set_text())
378  icalvalue_set_text( value, tzidShort );
379 
380  // Remove the X-LIC-LOCATION property, which is only used by libical
381  prop = icalcomponent_get_first_property( c, ICAL_X_PROPERTY );
382  const char *xname = icalproperty_get_x_name( prop );
383  if ( xname && !strcmp( xname, "X-LIC-LOCATION" ) ) {
384  icalcomponent_remove_property( c, prop );
385  }
386  }
387  }
388  }
389  }
390  d->setComponent( c );
391  } else {
392  // Write the time zone data into an iCal component
393  icalcomponent *tzcomp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
394  icalcomponent_add_property( tzcomp, icalproperty_new_tzid( tz.name().toUtf8() ) );
395 // icalcomponent_add_property(tzcomp, icalproperty_new_location( tz.name().toUtf8() ));
396 
397  // Compile an ordered list of transitions so that we can know the phases
398  // which occur before and after each transition.
399  QList<KTimeZone::Transition> transits = transitions();
400  if ( earliest.isValid() ) {
401  // Remove all transitions earlier than those we are interested in
402  for ( int i = 0, end = transits.count(); i < end; ++i ) {
403  if ( transits[i].time().date() >= earliest ) {
404  if ( i > 0 ) {
405  transits.erase( transits.begin(), transits.begin() + i );
406  }
407  break;
408  }
409  }
410  }
411  int trcount = transits.count();
412  QVector<bool> transitionsDone(trcount);
413  transitionsDone.fill(false);
414 
415  // Go through the list of transitions and create an iCal component for each
416  // distinct combination of phase after and UTC offset before the transition.
417  icaldatetimeperiodtype dtperiod;
418  dtperiod.period = icalperiodtype_null_period();
419  for ( ; ; ) {
420  int i = 0;
421  for ( ; i < trcount && transitionsDone[i]; ++i ) {
422  ;
423  }
424  if ( i >= trcount ) {
425  break;
426  }
427  // Found a phase combination which hasn't yet been processed
428  int preOffset = ( i > 0 ) ? transits[i - 1].phase().utcOffset() : rhs.previousUtcOffset();
429  KTimeZone::Phase phase = transits[i].phase();
430  if ( phase.utcOffset() == preOffset ) {
431  transitionsDone[i] = true;
432  while ( ++i < trcount ) {
433  if ( transitionsDone[i] ||
434  transits[i].phase() != phase ||
435  transits[i - 1].phase().utcOffset() != preOffset ) {
436  continue;
437  }
438  transitionsDone[i] = true;
439  }
440  continue;
441  }
442  icalcomponent *phaseComp =
443  icalcomponent_new( phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT );
444  QList<QByteArray> abbrevs = phase.abbreviations();
445  for ( int a = 0, aend = abbrevs.count(); a < aend; ++a ) {
446  icalcomponent_add_property( phaseComp,
447  icalproperty_new_tzname(
448  static_cast<const char*>( abbrevs[a]) ) );
449  }
450  if ( !phase.comment().isEmpty() ) {
451  icalcomponent_add_property( phaseComp,
452  icalproperty_new_comment( phase.comment().toUtf8() ) );
453  }
454  icalcomponent_add_property( phaseComp,
455  icalproperty_new_tzoffsetfrom( preOffset ) );
456  icalcomponent_add_property( phaseComp,
457  icalproperty_new_tzoffsetto( phase.utcOffset() ) );
458  // Create a component to hold initial RRULE if any, plus all RDATEs
459  icalcomponent *phaseComp1 = icalcomponent_new_clone( phaseComp );
460  icalcomponent_add_property( phaseComp1,
461  icalproperty_new_dtstart(
462  writeLocalICalDateTime( transits[i].time(), preOffset ) ) );
463  bool useNewRRULE = false;
464 
465  // Compile the list of UTC transition dates/times, and check
466  // if the list can be reduced to an RRULE instead of multiple RDATEs.
467  QTime time;
468  QDate date;
469  int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0; // avoid compiler warnings
470  int dayOfWeek = 0; // Monday = 1
471  int nthFromStart = 0; // nth (weekday) of month
472  int nthFromEnd = 0; // nth last (weekday) of month
473  int newRule;
474  int rule = 0;
475  QList<QDateTime> rdates;// dates which (probably) need to be written as RDATEs
476  QList<QDateTime> times;
477  QDateTime qdt = transits[i].time(); // set 'qdt' for start of loop
478  times += qdt;
479  transitionsDone[i] = true;
480  do {
481  if ( !rule ) {
482  // Initialise data for detecting a new rule
483  rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
484  time = qdt.time();
485  date = qdt.date();
486  year = date.year();
487  month = date.month();
488  daysInMonth = date.daysInMonth();
489  dayOfWeek = date.dayOfWeek(); // Monday = 1
490  dayOfMonth = date.day();
491  nthFromStart = ( dayOfMonth - 1 ) / 7 + 1; // nth (weekday) of month
492  nthFromEnd = ( daysInMonth - dayOfMonth ) / 7 + 1; // nth last (weekday) of month
493  }
494  if ( ++i >= trcount ) {
495  newRule = 0;
496  times += QDateTime(); // append a dummy value since last value in list is ignored
497  } else {
498  if ( transitionsDone[i] ||
499  transits[i].phase() != phase ||
500  transits[i - 1].phase().utcOffset() != preOffset ) {
501  continue;
502  }
503  transitionsDone[i] = true;
504  qdt = transits[i].time();
505  if ( !qdt.isValid() ) {
506  continue;
507  }
508  newRule = rule;
509  times += qdt;
510  date = qdt.date();
511  if ( qdt.time() != time ||
512  date.month() != month ||
513  date.year() != ++year ) {
514  newRule = 0;
515  } else {
516  int day = date.day();
517  if ( ( newRule & DAY_OF_MONTH ) && day != dayOfMonth ) {
518  newRule &= ~DAY_OF_MONTH;
519  }
520  if ( newRule & ( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH ) ) {
521  if ( date.dayOfWeek() != dayOfWeek ) {
522  newRule &= ~( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH );
523  } else {
524  if ( ( newRule & WEEKDAY_OF_MONTH ) &&
525  ( day - 1 ) / 7 + 1 != nthFromStart ) {
526  newRule &= ~WEEKDAY_OF_MONTH;
527  }
528  if ( ( newRule & LAST_WEEKDAY_OF_MONTH ) &&
529  ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) {
530  newRule &= ~LAST_WEEKDAY_OF_MONTH;
531  }
532  }
533  }
534  }
535  }
536  if ( !newRule ) {
537  // The previous rule (if any) no longer applies.
538  // Write all the times up to but not including the current one.
539  // First check whether any of the last RDATE values fit this rule.
540  int yr = times[0].date().year();
541  while ( !rdates.isEmpty() ) {
542  qdt = rdates.last();
543  date = qdt.date();
544  if ( qdt.time() != time ||
545  date.month() != month ||
546  date.year() != --yr ) {
547  break;
548  }
549  int day = date.day();
550  if ( rule & DAY_OF_MONTH ) {
551  if ( day != dayOfMonth ) {
552  break;
553  }
554  } else {
555  if ( date.dayOfWeek() != dayOfWeek ||
556  ( ( rule & WEEKDAY_OF_MONTH ) &&
557  ( day - 1 ) / 7 + 1 != nthFromStart ) ||
558  ( ( rule & LAST_WEEKDAY_OF_MONTH ) &&
559  ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) ) {
560  break;
561  }
562  }
563  times.prepend( qdt );
564  rdates.pop_back();
565  }
566  if ( times.count() > ( useNewRRULE ? minPhaseCount : minRuleCount ) ) {
567  // There are enough dates to combine into an RRULE
568  icalrecurrencetype r;
569  icalrecurrencetype_clear( &r );
570  r.freq = ICAL_YEARLY_RECURRENCE;
571  r.count = ( year >= 2030 ) ? 0 : times.count() - 1;
572  r.by_month[0] = month;
573  if ( rule & DAY_OF_MONTH ) {
574  r.by_month_day[0] = dayOfMonth;
575  } else if ( rule & WEEKDAY_OF_MONTH ) {
576  r.by_day[0] = ( dayOfWeek % 7 + 1 ) + ( nthFromStart * 8 ); // Sunday = 1
577  } else if ( rule & LAST_WEEKDAY_OF_MONTH ) {
578  r.by_day[0] = -( dayOfWeek % 7 + 1 ) - ( nthFromEnd * 8 ); // Sunday = 1
579  }
580  icalproperty *prop = icalproperty_new_rrule( r );
581  if ( useNewRRULE ) {
582  // This RRULE doesn't start from the phase start date, so set it into
583  // a new STANDARD/DAYLIGHT component in the VTIMEZONE.
584  icalcomponent *c = icalcomponent_new_clone( phaseComp );
585  icalcomponent_add_property(
586  c, icalproperty_new_dtstart( writeLocalICalDateTime( times[0], preOffset ) ) );
587  icalcomponent_add_property( c, prop );
588  icalcomponent_add_component( tzcomp, c );
589  } else {
590  icalcomponent_add_property( phaseComp1, prop );
591  }
592  } else {
593  // Save dates for writing as RDATEs
594  for ( int t = 0, tend = times.count() - 1; t < tend; ++t ) {
595  rdates += times[t];
596  }
597  }
598  useNewRRULE = true;
599  // All date/time values but the last have been added to the VTIMEZONE.
600  // Remove them from the list.
601  qdt = times.last(); // set 'qdt' for start of loop
602  times.clear();
603  times += qdt;
604  }
605  rule = newRule;
606  } while ( i < trcount );
607 
608  // Write remaining dates as RDATEs
609  for ( int rd = 0, rdend = rdates.count(); rd < rdend; ++rd ) {
610  dtperiod.time = writeLocalICalDateTime( rdates[rd], preOffset );
611  icalcomponent_add_property( phaseComp1, icalproperty_new_rdate( dtperiod ) );
612  }
613  icalcomponent_add_component( tzcomp, phaseComp1 );
614  icalcomponent_free( phaseComp );
615  }
616 
617  d->setComponent( tzcomp );
618  }
619 }
620 
621 ICalTimeZoneData::~ICalTimeZoneData()
622 {
623  delete d;
624 }
625 
626 ICalTimeZoneData &ICalTimeZoneData::operator=( const ICalTimeZoneData &rhs )
627 {
628  // check for self assignment
629  if ( &rhs == this ) {
630  return *this;
631  }
632 
633  KTimeZoneData::operator=( rhs );
634  d->location = rhs.d->location;
635  d->url = rhs.d->url;
636  d->lastModified = rhs.d->lastModified;
637  d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
638  return *this;
639 }
640 
641 KTimeZoneData *ICalTimeZoneData::clone() const
642 {
643  return new ICalTimeZoneData( *this );
644 }
645 
646 QString ICalTimeZoneData::city() const
647 {
648  return d->location;
649 }
650 
651 QByteArray ICalTimeZoneData::url() const
652 {
653  return d->url;
654 }
655 
656 QDateTime ICalTimeZoneData::lastModified() const
657 {
658  return d->lastModified;
659 }
660 
661 QByteArray ICalTimeZoneData::vtimezone() const
662 {
663  QByteArray result( icalcomponent_as_ical_string( d->component() ) );
664  icalmemory_free_ring();
665  return result;
666 }
667 
668 icaltimezone *ICalTimeZoneData::icalTimezone() const
669 {
670  icaltimezone *icaltz = icaltimezone_new();
671  if ( !icaltz ) {
672  return 0;
673  }
674  icalcomponent *c = icalcomponent_new_clone( d->component() );
675  if ( !icaltimezone_set_component( icaltz, c ) ) {
676  icalcomponent_free( c );
677  icaltimezone_free( icaltz, 1 );
678  return 0;
679  }
680  return icaltz;
681 }
682 
683 bool ICalTimeZoneData::hasTransitions() const
684 {
685  return true;
686 }
687 
688 /******************************************************************************/
689 
690 //@cond PRIVATE
691 class ICalTimeZoneSourcePrivate
692 {
693  public:
694  static QList<QDateTime> parsePhase( icalcomponent *, bool daylight,
695  int &prevOffset, KTimeZone::Phase & );
696  static QByteArray icalTzidPrefix;
697 };
698 
699 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
700 //@endcond
701 
702 ICalTimeZoneSource::ICalTimeZoneSource()
703  : KTimeZoneSource( false ),
704  d( 0 )
705 {
706 }
707 
708 ICalTimeZoneSource::~ICalTimeZoneSource()
709 {
710 }
711 
712 bool ICalTimeZoneSource::parse( const QString &fileName, ICalTimeZones &zones )
713 {
714  QFile file( fileName );
715  if ( !file.open( QIODevice::ReadOnly ) ) {
716  return false;
717  }
718  QTextStream ts( &file );
719  ts.setCodec( "ISO 8859-1" );
720  QByteArray text = ts.readAll().trimmed().toLatin1();
721  file.close();
722 
723  bool result = false;
724  icalcomponent *calendar = icalcomponent_new_from_string( text.data() );
725  if ( calendar ) {
726  if ( icalcomponent_isa( calendar ) == ICAL_VCALENDAR_COMPONENT ) {
727  result = parse( calendar, zones );
728  }
729  icalcomponent_free( calendar );
730  }
731  return result;
732 }
733 
734 bool ICalTimeZoneSource::parse( icalcomponent *calendar, ICalTimeZones &zones )
735 {
736  for ( icalcomponent *c = icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
737  c; c = icalcomponent_get_next_component( calendar, ICAL_VTIMEZONE_COMPONENT ) ) {
738  ICalTimeZone zone = parse( c );
739  if ( !zone.isValid() ) {
740  return false;
741  }
742  ICalTimeZone oldzone = zones.zone( zone.name() );
743  if ( oldzone.isValid() ) {
744  // The zone already exists in the collection, so update the definition
745  // of the zone rather than using a newly created one.
746  oldzone.update( zone );
747  } else if ( !zones.add( zone ) ) {
748  return false;
749  }
750  }
751  return true;
752 }
753 
754 ICalTimeZone ICalTimeZoneSource::parse( icalcomponent *vtimezone )
755 {
756  QString name;
757  QString xlocation;
758  ICalTimeZoneData *data = new ICalTimeZoneData();
759 
760  // Read the fixed properties which can only appear once in VTIMEZONE
761  icalproperty *p = icalcomponent_get_first_property( vtimezone, ICAL_ANY_PROPERTY );
762  while ( p ) {
763  icalproperty_kind kind = icalproperty_isa( p );
764  switch ( kind ) {
765 
766  case ICAL_TZID_PROPERTY:
767  name = QString::fromUtf8( icalproperty_get_tzid( p ) );
768  break;
769 
770  case ICAL_TZURL_PROPERTY:
771  data->d->url = icalproperty_get_tzurl( p );
772  break;
773 
774  case ICAL_LOCATION_PROPERTY:
775  // This isn't mentioned in RFC2445, but libical reads it ...
776  data->d->location = QString::fromUtf8( icalproperty_get_location( p ) );
777  break;
778 
779  case ICAL_X_PROPERTY:
780  { // use X-LIC-LOCATION if LOCATION is missing
781  const char *xname = icalproperty_get_x_name( p );
782  if ( xname && !strcmp( xname, "X-LIC-LOCATION" ) ) {
783  xlocation = QString::fromUtf8( icalproperty_get_x( p ) );
784  }
785  break;
786  }
787  case ICAL_LASTMODIFIED_PROPERTY:
788  {
789  icaltimetype t = icalproperty_get_lastmodified(p);
790  if ( t.is_utc ) {
791  data->d->lastModified = toQDateTime( t );
792  } else {
793  kDebug() << "LAST-MODIFIED not UTC";
794  }
795  break;
796  }
797  default:
798  break;
799  }
800  p = icalcomponent_get_next_property( vtimezone, ICAL_ANY_PROPERTY );
801  }
802 
803  if ( name.isEmpty() ) {
804  kDebug() << "TZID missing";
805  delete data;
806  return ICalTimeZone();
807  }
808  if ( data->d->location.isEmpty() && !xlocation.isEmpty() ) {
809  data->d->location = xlocation;
810  }
811  QString prefix = QString::fromUtf8( icalTzidPrefix() );
812  if ( name.startsWith( prefix ) ) {
813  // Remove the prefix from libical built in time zone TZID
814  int i = name.indexOf( '/', prefix.length() );
815  if ( i > 0 ) {
816  name = name.mid( i + 1 );
817  }
818  }
819  //kDebug() << "---zoneId: \"" << name << '"';
820 
821  /*
822  * Iterate through all time zone rules for this VTIMEZONE,
823  * and create a Phase object containing details for each one.
824  */
825  int prevOffset = 0;
826  QList<KTimeZone::Transition> transitions;
827  QDateTime earliest;
828  QList<KTimeZone::Phase> phases;
829  for ( icalcomponent *c = icalcomponent_get_first_component( vtimezone, ICAL_ANY_COMPONENT );
830  c; c = icalcomponent_get_next_component( vtimezone, ICAL_ANY_COMPONENT ) )
831  {
832  int prevoff = 0;
833  KTimeZone::Phase phase;
834  QList<QDateTime> times;
835  icalcomponent_kind kind = icalcomponent_isa( c );
836  switch ( kind ) {
837 
838  case ICAL_XSTANDARD_COMPONENT:
839  //kDebug() << "---standard phase: found";
840  times = ICalTimeZoneSourcePrivate::parsePhase( c, false, prevoff, phase );
841  break;
842 
843  case ICAL_XDAYLIGHT_COMPONENT:
844  //kDebug() << "---daylight phase: found";
845  times = ICalTimeZoneSourcePrivate::parsePhase( c, true, prevoff, phase );
846  break;
847 
848  default:
849  kDebug() << "Unknown component:" << kind;
850  break;
851  }
852  int tcount = times.count();
853  if ( tcount ) {
854  phases += phase;
855  for ( int t = 0; t < tcount; ++t ) {
856  transitions += KTimeZone::Transition( times[t], phase );
857  }
858  if ( !earliest.isValid() || times[0] < earliest ) {
859  prevOffset = prevoff;
860  earliest = times[0];
861  }
862  }
863  }
864  data->setPhases( phases, prevOffset );
865  // Remove any "duplicate" transitions, i.e. those where two consecutive
866  // transitions have the same phase.
867  qSort( transitions );
868  for ( int t = 1, tend = transitions.count(); t < tend; ) {
869  if ( transitions[t].phase() == transitions[t - 1].phase() ) {
870  transitions.removeAt( t );
871  --tend;
872  } else {
873  ++t;
874  }
875  }
876  data->setTransitions( transitions );
877 
878  data->d->setComponent( icalcomponent_new_clone( vtimezone ) );
879  kDebug() << "VTIMEZONE" << name;
880  return ICalTimeZone( this, name, data );
881 }
882 
883 ICalTimeZone ICalTimeZoneSource::parse( icaltimezone *tz )
884 {
885  /* Parse the VTIMEZONE component stored in the icaltimezone structure.
886  * This is both easier and provides more complete information than
887  * extracting already parsed data from icaltimezone.
888  */
889  return tz ? parse( icaltimezone_get_component( tz ) ) : ICalTimeZone();
890 }
891 
892 //@cond PRIVATE
893 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase( icalcomponent *c,
894  bool daylight,
895  int &prevOffset,
896  KTimeZone::Phase &phase )
897 {
898  QList<QDateTime> transitions;
899 
900  // Read the observance data for this standard/daylight savings phase
901  QList<QByteArray> abbrevs;
902  QString comment;
903  prevOffset = 0;
904  int utcOffset = 0;
905  bool recurs = false;
906  bool found_dtstart = false;
907  bool found_tzoffsetfrom = false;
908  bool found_tzoffsetto = false;
909  icaltimetype dtstart = icaltime_null_time();
910 
911  // Now do the ical reading.
912  icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
913  while ( p ) {
914  icalproperty_kind kind = icalproperty_isa( p );
915  switch ( kind ) {
916 
917  case ICAL_TZNAME_PROPERTY: // abbreviated name for this time offset
918  {
919  // TZNAME can appear multiple times in order to provide language
920  // translations of the time zone offset name.
921 
922  // TODO: Does this cope with multiple language specifications?
923  QByteArray tzname = icalproperty_get_tzname( p );
924  // Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME
925  // strings, which is totally useless. So ignore those.
926  if ( ( !daylight && tzname == "Standard Time" ) ||
927  ( daylight && tzname == "Daylight Time" ) ) {
928  break;
929  }
930  if ( !abbrevs.contains( tzname ) ) {
931  abbrevs += tzname;
932  }
933  break;
934  }
935  case ICAL_DTSTART_PROPERTY: // local time at which phase starts
936  dtstart = icalproperty_get_dtstart( p );
937  found_dtstart = true;
938  break;
939 
940  case ICAL_TZOFFSETFROM_PROPERTY: // UTC offset immediately before start of phase
941  prevOffset = icalproperty_get_tzoffsetfrom( p );
942  found_tzoffsetfrom = true;
943  break;
944 
945  case ICAL_TZOFFSETTO_PROPERTY:
946  utcOffset = icalproperty_get_tzoffsetto( p );
947  found_tzoffsetto = true;
948  break;
949 
950  case ICAL_COMMENT_PROPERTY:
951  comment = QString::fromUtf8( icalproperty_get_comment( p ) );
952  break;
953 
954  case ICAL_RDATE_PROPERTY:
955  case ICAL_RRULE_PROPERTY:
956  recurs = true;
957  break;
958 
959  default:
960  kDebug() << "Unknown property:" << kind;
961  break;
962  }
963  p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
964  }
965 
966  // Validate the phase data
967  if ( !found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto ) {
968  kDebug() << "DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
969  return transitions;
970  }
971 
972  // Convert DTSTART to QDateTime, and from local time to UTC
973  QDateTime localStart = toQDateTime( dtstart ); // local time
974  dtstart.second -= prevOffset;
975  dtstart.is_utc = 1;
976  QDateTime utcStart = toQDateTime( icaltime_normalize( dtstart ) ); // UTC
977 
978  transitions += utcStart;
979  if ( recurs ) {
980  /* RDATE or RRULE is specified. There should only be one or the other, but
981  * it doesn't really matter - the code can cope with both.
982  * Note that we had to get DTSTART, TZOFFSETFROM, TZOFFSETTO before reading
983  * recurrences.
984  */
985  KDateTime klocalStart( localStart, KDateTime::Spec::ClockTime() );
986  KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
987  Recurrence recur;
988  icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
989  while ( p ) {
990  icalproperty_kind kind = icalproperty_isa( p );
991  switch ( kind ) {
992 
993  case ICAL_RDATE_PROPERTY:
994  {
995  icaltimetype t = icalproperty_get_rdate(p).time;
996  if ( icaltime_is_date( t ) ) {
997  // RDATE with a DATE value inherits the (local) time from DTSTART
998  t.hour = dtstart.hour;
999  t.minute = dtstart.minute;
1000  t.second = dtstart.second;
1001  t.is_date = 0;
1002  t.is_utc = 0; // dtstart is in local time
1003  }
1004  // RFC2445 states that RDATE must be in local time,
1005  // but we support UTC as well to be safe.
1006  if ( !t.is_utc ) {
1007  t.second -= prevOffset; // convert to UTC
1008  t.is_utc = 1;
1009  t = icaltime_normalize( t );
1010  }
1011  transitions += toQDateTime( t );
1012  break;
1013  }
1014  case ICAL_RRULE_PROPERTY:
1015  {
1016  RecurrenceRule r;
1017  ICalFormat icf;
1018  ICalFormatImpl impl( &icf );
1019  impl.readRecurrence( icalproperty_get_rrule( p ), &r );
1020  r.setStartDt( klocalStart );
1021  // The end date time specified in an RRULE should be in UTC.
1022  // Convert to local time to avoid timesInInterval() getting things wrong.
1023  if ( r.duration() == 0 ) {
1024  KDateTime end( r.endDt() );
1025  if ( end.timeSpec() == KDateTime::Spec::UTC() ) {
1026  end.setTimeSpec( KDateTime::Spec::ClockTime() );
1027  r.setEndDt( end.addSecs( prevOffset ) );
1028  }
1029  }
1030  DateTimeList dts = r.timesInInterval( klocalStart, maxTime );
1031  for ( int i = 0, end = dts.count(); i < end; ++i ) {
1032  QDateTime utc = dts[i].dateTime();
1033  utc.setTimeSpec( Qt::UTC );
1034  transitions += utc.addSecs( -prevOffset );
1035  }
1036  break;
1037  }
1038  default:
1039  break;
1040  }
1041  p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1042  }
1043  qSortUnique( transitions );
1044  }
1045 
1046  phase = KTimeZone::Phase( utcOffset, abbrevs, daylight, comment );
1047  return transitions;
1048 }
1049 //@endcond
1050 
1051 ICalTimeZone ICalTimeZoneSource::standardZone( const QString &zone, bool icalBuiltIn )
1052 {
1053  if ( !icalBuiltIn ) {
1054  // Try to fetch a system time zone in preference, on the grounds
1055  // that system time zones are more likely to be up to date than
1056  // built-in libical ones.
1057  QString tzid = zone;
1058  QString prefix = QString::fromUtf8( icalTzidPrefix() );
1059  if ( zone.startsWith( prefix ) ) {
1060  int i = zone.indexOf( '/', prefix.length() );
1061  if ( i > 0 ) {
1062  tzid = zone.mid( i + 1 ); // strip off the libical prefix
1063  }
1064  }
1065  KTimeZone ktz = KSystemTimeZones::readZone( tzid );
1066  if ( ktz.isValid() ) {
1067  if ( ktz.data( true ) ) {
1068  ICalTimeZone icaltz( ktz );
1069  //kDebug() << zone << " read from system database";
1070  return icaltz;
1071  }
1072  }
1073  }
1074  // Try to fetch a built-in libical time zone.
1075  // First try to look it up as a geographical location (e.g. Europe/London)
1076  QByteArray zoneName = zone.toUtf8();
1077  icaltimezone *icaltz = icaltimezone_get_builtin_timezone( zoneName );
1078  if ( !icaltz ) {
1079  // This will find it if it includes the libical prefix
1080  icaltz = icaltimezone_get_builtin_timezone_from_tzid( zoneName );
1081  if ( !icaltz ) {
1082  return ICalTimeZone();
1083  }
1084  }
1085  return parse( icaltz );
1086 }
1087 
1088 QByteArray ICalTimeZoneSource::icalTzidPrefix()
1089 {
1090  if ( ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty() ) {
1091  icaltimezone *icaltz = icaltimezone_get_builtin_timezone( "Europe/London" );
1092  QByteArray tzid = icaltimezone_get_tzid( icaltz );
1093  if ( tzid.right( 13 ) == "Europe/London" ) {
1094  int i = tzid.indexOf( '/', 1 );
1095  if ( i > 0 ) {
1096  ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left( i + 1 );
1097  return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1098  }
1099  }
1100  kError() << "failed to get libical TZID prefix";
1101  }
1102  return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1103 }
1104 
1105 } // namespace KCal
KCal::ICalTimeZone::icalTimezone
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
Definition: icaltimezones.cpp:262
KCal::ICalTimeZoneBackend::clone
virtual KTimeZoneBackend * clone() const
Creates a copy of this instance.
Definition: icaltimezones.cpp:190
KCal::ICalTimeZoneData::url
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Definition: icaltimezones.cpp:651
KCal::ICalTimeZoneData::~ICalTimeZoneData
virtual ~ICalTimeZoneData()
Destructor.
Definition: icaltimezones.cpp:621
KCal::ICalTimeZone::ICalTimeZone
ICalTimeZone()
Constructs a null time zone.
Definition: icaltimezones.cpp:208
KCal::ICalTimeZoneBackend::ICalTimeZoneBackend
ICalTimeZoneBackend()
Implements ICalTimeZone::ICalTimeZone().
Definition: icaltimezones.cpp:169
KCal::ICalTimeZone::url
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Definition: icaltimezones.cpp:244
KCal::ICalTimeZoneBackend::type
virtual QByteArray type() const
Returns the class name of the data represented by this instance.
Definition: icaltimezones.cpp:195
KCal::ICalTimeZoneSource::parse
ICalTimeZone parse(icalcomponent *vtimezone)
Creates an ICalTimeZone instance containing the detailed information parsed from a VTIMEZONE componen...
Definition: icaltimezones.cpp:754
KCal::ICalTimeZone::update
bool update(const ICalTimeZone &other)
Update the definition of the time zone to be identical to another ICalTimeZone instance.
Definition: icaltimezones.cpp:268
KCal::ICalTimeZoneSource::icalTzidPrefix
static QByteArray icalTzidPrefix()
Returns the prefix string used in the TZID field in built-in libical time zones.
Definition: icaltimezones.cpp:1088
KCal::ICalTimeZoneData::icalTimezone
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
Definition: icaltimezones.cpp:668
KCal::ICalTimeZoneSource::ICalTimeZoneSource
ICalTimeZoneSource()
Constructs an iCalendar time zone source.
Definition: icaltimezones.cpp:702
icalformat_p.h
This file is part of the API for handling calendar data and defines the internal ICalFormatImpl class...
KCal::ICalTimeZone::~ICalTimeZone
virtual ~ICalTimeZone()
Destructor.
Definition: icaltimezones.cpp:235
KCal::ICalTimeZoneData::clone
virtual KTimeZoneData * clone() const
Creates a new copy of this object.
Definition: icaltimezones.cpp:641
KCal::ICalTimeZones::zone
ICalTimeZone zone(const QString &name) const
Returns the time zone with the given name.
Definition: icaltimezones.cpp:156
KCal::ICalFormatImpl
This class provides the libical dependent functions for ICalFormat.
Definition: icalformat_p.h:67
KCal::ICalTimeZones::~ICalTimeZones
~ICalTimeZones()
Destructor.
Definition: icaltimezones.cpp:102
KCal::ICalFormat
iCalendar format implementation.
Definition: icalformat.h:52
KCal::ICalTimeZones::add
bool add(const ICalTimeZone &zone)
Adds a time zone to the collection.
Definition: icaltimezones.cpp:112
KCal::ICalTimeZones::clear
void clear()
Clears the collection.
Definition: icaltimezones.cpp:151
KCal::ICalTimeZones::zones
const ZoneMap zones() const
Returns all the time zones defined in this collection.
Definition: icaltimezones.cpp:107
KCal::ICalTimeZone::lastModified
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
Definition: icaltimezones.cpp:250
KCal::ICalTimeZones::ICalTimeZones
ICalTimeZones()
Constructs an empty time zone collection.
Definition: icaltimezones.cpp:97
KCal::ICalTimeZoneBackend
Backend class for KICalTimeZone class.
Definition: icaltimezones.h:258
KCal::ICalTimeZoneSource::~ICalTimeZoneSource
virtual ~ICalTimeZoneSource()
Destructor.
Definition: icaltimezones.cpp:708
KCal::ICalTimeZones
The ICalTimeZones class represents a time zone database which consists of a collection of individual ...
Definition: icaltimezones.h:64
KCal::RecurrenceRule::timesInInterval
DateTimeList timesInInterval(const KDateTime &start, const KDateTime &end) const
Returns a list of all the times at which the recurrence will occur between two specified times...
Definition: recurrencerule.cpp:1717
KCal::ICalTimeZones::remove
ICalTimeZone remove(const ICalTimeZone &zone)
Removes a time zone from the collection.
Definition: icaltimezones.cpp:125
KCal::ICalTimeZoneData::hasTransitions
virtual bool hasTransitions() const
Return whether daylight saving transitions are available for the time zone.
Definition: icaltimezones.cpp:683
KCal::ICalTimeZoneData::ICalTimeZoneData
ICalTimeZoneData()
Default constructor.
Definition: icaltimezones.cpp:318
KCal::SortableList
A QList which can be sorted.
Definition: sortablelist.h:86
KCal::ICalTimeZoneSource
A class which reads and parses iCalendar VTIMEZONE components, and accesses libical time zone data...
Definition: icaltimezones.h:328
KCal::ICalTimeZoneData::operator=
ICalTimeZoneData & operator=(const ICalTimeZoneData &rhs)
Assignment operator.
Definition: icaltimezones.cpp:626
KCal::ICalTimeZoneBackend::hasTransitions
virtual bool hasTransitions(const KTimeZone *caller) const
Implements ICalTimeZone::hasTransitions().
Definition: icaltimezones.cpp:200
KCal::ICalTimeZoneData
Parsed iCalendar VTIMEZONE data.
Definition: icaltimezones.h:434
KCal::RecurrenceRule::endDt
KDateTime endDt(bool *result=0) const
Returns the date and time of the last recurrence.
Definition: recurrencerule.cpp:937
KCal::ICalTimeZone::city
QString city() const
Returns the name of the city for this time zone, if any.
Definition: icaltimezones.cpp:238
KCal::ICalTimeZoneData::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:661
KCal::RecurrenceRule::setEndDt
void setEndDt(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
Definition: recurrencerule.cpp:968
KCal::ICalTimeZone::utc
static ICalTimeZone utc()
Returns a standard UTC time zone, with name "UTC".
Definition: icaltimezones.cpp:279
KCal::ICalTimeZone::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:256
KCal::RecurrenceRule::duration
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
Definition: recurrencerule.cpp:2124
KCal::ICalTimeZoneData::city
QString city() const
Returns the name of the city for this time zone, if any.
Definition: icaltimezones.cpp:646
KCal::ICalTimeZoneData::lastModified
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
Definition: icaltimezones.cpp:656
icalformat.h
This file is part of the API for handling calendar data and defines the ICalFormat class...
KCal::Recurrence
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:91
KCal::ICalTimeZoneSource::standardZone
ICalTimeZone standardZone(const QString &zone, bool icalBuiltIn=false)
Creates an ICalTimeZone instance for a standard time zone.
Definition: icaltimezones.cpp:1051
KCal::ICalTimeZone
The ICalTimeZone class represents an iCalendar VTIMEZONE component.
Definition: icaltimezones.h:142
KCal::RecurrenceRule::setStartDt
void setStartDt(const KDateTime &start)
Sets the recurrence start date/time.
Definition: recurrencerule.cpp:1006
KCal::RecurrenceRule
This class represents a recurrence rule for a calendar incidence.
Definition: recurrencerule.h:46
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:57 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal