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

KCalCore Library

  • kcalcore
icaltimezones.cpp
1 /*
2  This file is part of the kcalcore 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 #include <config-kcalcore.h>
22 
23 #include "icaltimezones.h"
24 #include "icalformat.h"
25 #include "icalformat_p.h"
26 #include "recurrence.h"
27 #include "recurrencerule.h"
28 
29 #include <KDebug>
30 #include <KDateTime>
31 #include <KSystemTimeZone>
32 
33 #include <QtCore/QDateTime>
34 #include <QtCore/QFile>
35 #include <QtCore/QTextStream>
36 
37 extern "C" {
38 #include <libical/ical.h>
39 #include <icaltimezone.h>
40 }
41 
42 #if defined(HAVE_UUID_UUID_H)
43 #include <uuid/uuid.h>
44 #endif
45 
46 using namespace KCalCore;
47 
48 // Minimum repetition counts for VTIMEZONE RRULEs
49 static const int minRuleCount = 5; // for any RRULE
50 static const int minPhaseCount = 8; // for separate STANDARD/DAYLIGHT component
51 
52 // Convert an ical time to QDateTime, preserving the UTC indicator
53 static QDateTime toQDateTime(const icaltimetype &t)
54 {
55  return QDateTime(QDate(t.year, t.month, t.day),
56  QTime(t.hour, t.minute, t.second),
57  (icaltime_is_utc( t ) ? Qt::UTC : Qt::LocalTime));
58 }
59 
60 // Maximum date for time zone data.
61 // It's not sensible to try to predict them very far in advance, because
62 // they can easily change. Plus, it limits the processing required.
63 static QDateTime MAX_DATE()
64 {
65  static QDateTime dt;
66  if (!dt.isValid()) {
67  dt = QDateTime(QDate::currentDate().addYears(20), QTime(0, 0, 0));
68  }
69  return dt;
70 }
71 
72 static icaltimetype writeLocalICalDateTime(const QDateTime &utc, int offset)
73 {
74  const QDateTime local = utc.addSecs(offset);
75  icaltimetype t = icaltime_null_time();
76  t.year = local.date().year();
77  t.month = local.date().month();
78  t.day = local.date().day();
79  t.hour = local.time().hour();
80  t.minute = local.time().minute();
81  t.second = local.time().second();
82  t.is_date = 0;
83  t.zone = 0;
84  return t;
85 }
86 
87 namespace KCalCore {
88 
89 /******************************************************************************/
90 
91 //@cond PRIVATE
92 class ICalTimeZonesPrivate
93 {
94 public:
95  ICalTimeZonesPrivate() {}
96  ICalTimeZones::ZoneMap zones;
97 };
98 //@endcond
99 
100 ICalTimeZones::ICalTimeZones()
101  : d(new ICalTimeZonesPrivate)
102 {
103 }
104 
105 ICalTimeZones::ICalTimeZones(const ICalTimeZones &rhs)
106  : d(new ICalTimeZonesPrivate())
107 {
108  d->zones = rhs.d->zones;
109 }
110 
111 ICalTimeZones &ICalTimeZones::operator=(const ICalTimeZones &rhs)
112 {
113  // check for self assignment
114  if (&rhs == this) {
115  return *this;
116  }
117  d->zones = rhs.d->zones;
118  return *this;
119 }
120 
121 ICalTimeZones::~ICalTimeZones()
122 {
123  delete d;
124 }
125 
126 const ICalTimeZones::ZoneMap ICalTimeZones::zones() const
127 {
128  return d->zones;
129 }
130 
131 bool ICalTimeZones::add(const ICalTimeZone &zone)
132 {
133  if (!zone.isValid()) {
134  return false;
135  }
136  if (d->zones.find(zone.name()) != d->zones.end()) {
137  return false; // name already exists
138  }
139 
140  d->zones.insert(zone.name(), zone);
141  return true;
142 }
143 
144 ICalTimeZone ICalTimeZones::remove(const ICalTimeZone &zone)
145 {
146  if (zone.isValid()) {
147  for (ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it) {
148  if (it.value() == zone) {
149  d->zones.erase(it);
150  return (zone == ICalTimeZone::utc()) ? ICalTimeZone() : zone;
151  }
152  }
153  }
154  return ICalTimeZone();
155 }
156 
157 ICalTimeZone ICalTimeZones::remove(const QString &name)
158 {
159  if (!name.isEmpty()) {
160  ZoneMap::Iterator it = d->zones.find(name);
161  if (it != d->zones.end()) {
162  const ICalTimeZone zone = it.value();
163  d->zones.erase(it);
164  return (zone == ICalTimeZone::utc()) ? ICalTimeZone() : zone;
165  }
166  }
167  return ICalTimeZone();
168 }
169 
170 void ICalTimeZones::clear()
171 {
172  d->zones.clear();
173 }
174 
175 int ICalTimeZones::count()
176 {
177  return d->zones.count();
178 }
179 
180 ICalTimeZone ICalTimeZones::zone(const QString &name) const
181 {
182  if (!name.isEmpty()) {
183  ZoneMap::ConstIterator it = d->zones.constFind(name);
184  if (it != d->zones.constEnd()) {
185  return it.value();
186  }
187  }
188  return ICalTimeZone(); // error
189 }
190 
191 ICalTimeZone ICalTimeZones::zone(const ICalTimeZone &zone) const
192 {
193  if (zone.isValid()) {
194  QMapIterator<QString, ICalTimeZone> it(d->zones);
195  while (it.hasNext()) {
196  it.next();
197  const ICalTimeZone tz = it.value();
198  const QList<KTimeZone::Transition> list1 = tz.transitions();
199  const QList<KTimeZone::Transition> list2 = zone.transitions();
200  if (list1.size() == list2.size()) {
201  int i = 0;
202  int matches = 0;
203  for (; i < list1.size(); ++i) {
204  const KTimeZone::Transition t1 = list1[ i ];
205  const KTimeZone::Transition t2 = list2[ i ];
206  if ((t1.time() == t2.time()) &&
207  (t1.phase().utcOffset() == t2.phase().utcOffset()) &&
208  (t1.phase().isDst() == t2.phase().isDst())) {
209  matches++;
210  }
211  }
212  if (matches == i) {
213  // Existing zone has all the transitions of the given zone.
214  return tz;
215  }
216  }
217  }
218  }
219  return ICalTimeZone(); // not found
220 }
221 
222 /******************************************************************************/
223 
224 ICalTimeZoneBackend::ICalTimeZoneBackend()
225  : KTimeZoneBackend()
226 {}
227 
228 ICalTimeZoneBackend::ICalTimeZoneBackend(ICalTimeZoneSource *source,
229  const QString &name,
230  const QString &countryCode,
231  float latitude, float longitude,
232  const QString &comment)
233  : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
234 {}
235 
236 ICalTimeZoneBackend::ICalTimeZoneBackend(const KTimeZone &tz, const QDate &earliest)
237  : KTimeZoneBackend(0, tz.name(), tz.countryCode(), tz.latitude(), tz.longitude(), tz.comment())
238 {
239  Q_UNUSED(earliest);
240 }
241 
242 ICalTimeZoneBackend::~ICalTimeZoneBackend()
243 {}
244 
245 KTimeZoneBackend *ICalTimeZoneBackend::clone() const
246 {
247  return new ICalTimeZoneBackend(*this);
248 }
249 
250 QByteArray ICalTimeZoneBackend::type() const
251 {
252  return "ICalTimeZone";
253 }
254 
255 bool ICalTimeZoneBackend::hasTransitions(const KTimeZone *caller) const
256 {
257  Q_UNUSED(caller);
258  return true;
259 }
260 
261 void ICalTimeZoneBackend::virtual_hook(int id, void *data)
262 {
263  Q_UNUSED(id);
264  Q_UNUSED(data);
265 }
266 
267 /******************************************************************************/
268 
269 ICalTimeZone::ICalTimeZone()
270  : KTimeZone(new ICalTimeZoneBackend())
271 {}
272 
273 ICalTimeZone::ICalTimeZone(ICalTimeZoneSource *source, const QString &name,
274  ICalTimeZoneData *data)
275  : KTimeZone(new ICalTimeZoneBackend(source, name))
276 {
277  setData(data);
278 }
279 
280 ICalTimeZone::ICalTimeZone(const KTimeZone &tz, const QDate &earliest)
281  : KTimeZone(new ICalTimeZoneBackend(0, tz.name(), tz.countryCode(),
282  tz.latitude(), tz.longitude(),
283  tz.comment()))
284 {
285  const KTimeZoneData *data = tz.data(true);
286  if (data) {
287  const ICalTimeZoneData *icaldata = dynamic_cast<const ICalTimeZoneData*>(data);
288  if (icaldata) {
289  setData(new ICalTimeZoneData(*icaldata));
290  } else {
291  setData(new ICalTimeZoneData(*data, tz, earliest));
292  }
293  }
294 }
295 
296 ICalTimeZone::~ICalTimeZone()
297 {}
298 
299 QString ICalTimeZone::city() const
300 {
301  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
302  return dat ? dat->city() : QString();
303 }
304 
305 QByteArray ICalTimeZone::url() const
306 {
307  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
308  return dat ? dat->url() : QByteArray();
309 }
310 
311 QDateTime ICalTimeZone::lastModified() const
312 {
313  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
314  return dat ? dat->lastModified() : QDateTime();
315 }
316 
317 QByteArray ICalTimeZone::vtimezone() const
318 {
319  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
320  return dat ? dat->vtimezone() : QByteArray();
321 }
322 
323 icaltimezone *ICalTimeZone::icalTimezone() const
324 {
325  const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
326  return dat ? dat->icalTimezone() : 0;
327 }
328 
329 bool ICalTimeZone::update(const ICalTimeZone &other)
330 {
331  if (!updateBase(other)) {
332  return false;
333  }
334 
335  KTimeZoneData *otherData = other.data() ? other.data()->clone() : 0;
336  setData(otherData, other.source());
337  return true;
338 }
339 
340 ICalTimeZone ICalTimeZone::utc()
341 {
342  static ICalTimeZone utcZone;
343  if (!utcZone.isValid()) {
344  ICalTimeZoneSource tzs;
345  utcZone = tzs.parse(icaltimezone_get_utc_timezone());
346  }
347  return utcZone;
348 }
349 
350 void ICalTimeZone::virtual_hook(int id, void *data)
351 {
352  Q_UNUSED(id);
353  Q_UNUSED(data);
354 }
355 /******************************************************************************/
356 
357 //@cond PRIVATE
358 class ICalTimeZoneDataPrivate
359 {
360 public:
361  ICalTimeZoneDataPrivate() : icalComponent(0) {}
362 
363  ~ICalTimeZoneDataPrivate()
364  {
365  if (icalComponent) {
366  icalcomponent_free(icalComponent);
367  }
368  }
369 
370  icalcomponent *component() const {
371  return icalComponent;
372  }
373  void setComponent(icalcomponent *c)
374  {
375  if (icalComponent) {
376  icalcomponent_free(icalComponent);
377  }
378  icalComponent = c;
379  }
380 
381  QString location; // name of city for this time zone
382  QByteArray url; // URL of published VTIMEZONE definition (optional)
383  QDateTime lastModified; // time of last modification of the VTIMEZONE component (optional)
384 
385 private:
386  icalcomponent *icalComponent; // ical component representing this time zone
387 };
388 //@endcond
389 
390 ICalTimeZoneData::ICalTimeZoneData()
391  : d(new ICalTimeZoneDataPrivate())
392 {
393 }
394 
395 ICalTimeZoneData::ICalTimeZoneData(const ICalTimeZoneData &rhs)
396  : KTimeZoneData(rhs),
397  d(new ICalTimeZoneDataPrivate())
398 {
399  d->location = rhs.d->location;
400  d->url = rhs.d->url;
401  d->lastModified = rhs.d->lastModified;
402  d->setComponent(icalcomponent_new_clone(rhs.d->component()));
403 }
404 
405 ICalTimeZoneData::ICalTimeZoneData(const KTimeZoneData &rhs,
406  const KTimeZone &tz, const QDate &earliest)
407  : KTimeZoneData(rhs),
408  d(new ICalTimeZoneDataPrivate())
409 {
410  // VTIMEZONE RRULE types
411  enum {
412  DAY_OF_MONTH = 0x01,
413  WEEKDAY_OF_MONTH = 0x02,
414  LAST_WEEKDAY_OF_MONTH = 0x04
415  };
416 
417  if (tz.type() == "KSystemTimeZone") {
418  // Try to fetch a system time zone in preference, on the grounds
419  // that system time zones are more likely to be up to date than
420  // built-in libical ones.
421  icalcomponent *c = 0;
422  const KTimeZone ktz = KSystemTimeZones::readZone(tz.name());
423  if (ktz.isValid()) {
424  if (ktz.data(true)) {
425  const ICalTimeZone icaltz(ktz, earliest);
426  icaltimezone *itz = icaltz.icalTimezone();
427  if (itz) {
428  c = icalcomponent_new_clone(icaltimezone_get_component(itz));
429  icaltimezone_free(itz, 1);
430  }
431  }
432  }
433  if (!c) {
434  // Try to fetch a built-in libical time zone.
435  icaltimezone *itz = icaltimezone_get_builtin_timezone(tz.name().toUtf8());
436  c = icalcomponent_new_clone(icaltimezone_get_component(itz));
437  }
438  if (c) {
439  // TZID in built-in libical time zones has a standard prefix.
440  // To make the VTIMEZONE TZID match TZID references in incidences
441  // (as required by RFC2445), strip off the prefix.
442  icalproperty *prop = icalcomponent_get_first_property(c, ICAL_TZID_PROPERTY);
443  if (prop) {
444  icalvalue *value = icalproperty_get_value(prop);
445  const char *tzid = icalvalue_get_text(value);
446  const QByteArray icalprefix = ICalTimeZoneSource::icalTzidPrefix();
447  const int len = icalprefix.size();
448  if (!strncmp(icalprefix, tzid, len)) {
449  const char *s = strchr(tzid + len, '/'); // find third '/'
450  if (s) {
451  const QByteArray tzidShort(s + 1); // deep copy (needed by icalvalue_set_text())
452  icalvalue_set_text(value, tzidShort);
453 
454  // Remove the X-LIC-LOCATION property, which is only used by libical
455  prop = icalcomponent_get_first_property(c, ICAL_X_PROPERTY);
456  const char *xname = icalproperty_get_x_name(prop);
457  if (xname && !strcmp(xname, "X-LIC-LOCATION")) {
458  icalcomponent_remove_property(c, prop);
459  icalproperty_free(prop);
460  }
461  }
462  }
463  }
464  }
465  d->setComponent(c);
466  } else {
467  // Write the time zone data into an iCal component
468  icalcomponent *tzcomp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
469  icalcomponent_add_property(tzcomp, icalproperty_new_tzid(tz.name().toUtf8()));
470 // icalcomponent_add_property(tzcomp, icalproperty_new_location( tz.name().toUtf8() ));
471 
472  // Compile an ordered list of transitions so that we can know the phases
473  // which occur before and after each transition.
474  QList<KTimeZone::Transition> transits = transitions();
475  if (transits.isEmpty()) {
476  // If there is no way to compile a complete list of transitions
477  // transitions() can return an empty list
478  // In that case try get one transition to write a valid VTIMEZONE entry.
479  if (transits.isEmpty()) {
480  kDebug() << "No transition information available VTIMEZONE will be invalid.";
481  }
482  }
483  if (earliest.isValid()) {
484  // Remove all transitions earlier than those we are interested in
485  for (int i = 0, end = transits.count(); i < end; ++i) {
486  if (transits.at(i).time().date() >= earliest) {
487  if (i > 0) {
488  transits.erase(transits.begin(), transits.begin() + i);
489  }
490  break;
491  }
492  }
493  }
494  int trcount = transits.count();
495  QVector<bool> transitionsDone(trcount);
496  transitionsDone.fill(false);
497 
498  // Go through the list of transitions and create an iCal component for each
499  // distinct combination of phase after and UTC offset before the transition.
500  icaldatetimeperiodtype dtperiod;
501  dtperiod.period = icalperiodtype_null_period();
502  for (; ;) {
503  int i = 0;
504  for (; i < trcount && transitionsDone[i]; ++i) {
505  ;
506  }
507  if (i >= trcount) {
508  break;
509  }
510  // Found a phase combination which hasn't yet been processed
511  const int preOffset = (i > 0) ?
512  transits.at(i - 1).phase().utcOffset() :
513  rhs.previousUtcOffset();
514  const KTimeZone::Phase phase = transits.at(i).phase();
515  if (phase.utcOffset() == preOffset) {
516  transitionsDone[i] = true;
517  while (++i < trcount) {
518  if (transitionsDone[i] ||
519  transits.at(i).phase() != phase ||
520  transits.at(i - 1).phase().utcOffset() != preOffset) {
521  continue;
522  }
523  transitionsDone[i] = true;
524  }
525  continue;
526  }
527  icalcomponent *phaseComp =
528  icalcomponent_new(phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT);
529  const QList<QByteArray> abbrevs = phase.abbreviations();
530  for (int a = 0, aend = abbrevs.count(); a < aend; ++a) {
531  icalcomponent_add_property(phaseComp,
532  icalproperty_new_tzname(
533  static_cast<const char*>(abbrevs[a])));
534  }
535  if (!phase.comment().isEmpty()) {
536  icalcomponent_add_property(phaseComp,
537  icalproperty_new_comment(phase.comment().toUtf8()));
538  }
539  icalcomponent_add_property(phaseComp,
540  icalproperty_new_tzoffsetfrom(preOffset));
541  icalcomponent_add_property(phaseComp,
542  icalproperty_new_tzoffsetto(phase.utcOffset()));
543  // Create a component to hold initial RRULE if any, plus all RDATEs
544  icalcomponent *phaseComp1 = icalcomponent_new_clone(phaseComp);
545  icalcomponent_add_property(phaseComp1,
546  icalproperty_new_dtstart(
547  writeLocalICalDateTime(transits.at(i).time(),
548  preOffset)));
549  bool useNewRRULE = false;
550 
551  // Compile the list of UTC transition dates/times, and check
552  // if the list can be reduced to an RRULE instead of multiple RDATEs.
553  QTime time;
554  QDate date;
555  int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0; // avoid compiler warnings
556  int dayOfWeek = 0; // Monday = 1
557  int nthFromStart = 0; // nth (weekday) of month
558  int nthFromEnd = 0; // nth last (weekday) of month
559  int newRule;
560  int rule = 0;
561  QList<QDateTime> rdates;// dates which (probably) need to be written as RDATEs
562  QList<QDateTime> times;
563  QDateTime qdt = transits.at(i).time(); // set 'qdt' for start of loop
564  times += qdt;
565  transitionsDone[i] = true;
566  do {
567  if (!rule) {
568  // Initialise data for detecting a new rule
569  rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
570  time = qdt.time();
571  date = qdt.date();
572  year = date.year();
573  month = date.month();
574  daysInMonth = date.daysInMonth();
575  dayOfWeek = date.dayOfWeek(); // Monday = 1
576  dayOfMonth = date.day();
577  nthFromStart = (dayOfMonth - 1) / 7 + 1; // nth (weekday) of month
578  nthFromEnd = (daysInMonth - dayOfMonth) / 7 + 1; // nth last (weekday) of month
579  }
580  if (++i >= trcount) {
581  newRule = 0;
582  times += QDateTime(); // append a dummy value since last value in list is ignored
583  } else {
584  if (transitionsDone[i] ||
585  transits.at(i).phase() != phase ||
586  transits.at(i - 1).phase().utcOffset() != preOffset) {
587  continue;
588  }
589  transitionsDone[i] = true;
590  qdt = transits.at(i).time();
591  if (!qdt.isValid()) {
592  continue;
593  }
594  newRule = rule;
595  times += qdt;
596  date = qdt.date();
597  if (qdt.time() != time ||
598  date.month() != month ||
599  date.year() != ++year) {
600  newRule = 0;
601  } else {
602  const int day = date.day();
603  if ((newRule & DAY_OF_MONTH) && day != dayOfMonth) {
604  newRule &= ~DAY_OF_MONTH;
605  }
606  if (newRule & (WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH)) {
607  if (date.dayOfWeek() != dayOfWeek) {
608  newRule &= ~(WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH);
609  } else {
610  if ((newRule & WEEKDAY_OF_MONTH) &&
611  (day - 1) / 7 + 1 != nthFromStart) {
612  newRule &= ~WEEKDAY_OF_MONTH;
613  }
614  if ((newRule & LAST_WEEKDAY_OF_MONTH) &&
615  (daysInMonth - day) / 7 + 1 != nthFromEnd) {
616  newRule &= ~LAST_WEEKDAY_OF_MONTH;
617  }
618  }
619  }
620  }
621  }
622  if (!newRule) {
623  // The previous rule (if any) no longer applies.
624  // Write all the times up to but not including the current one.
625  // First check whether any of the last RDATE values fit this rule.
626  int yr = times[0].date().year();
627  while (!rdates.isEmpty()) {
628  qdt = rdates.last();
629  date = qdt.date();
630  if (qdt.time() != time ||
631  date.month() != month ||
632  date.year() != --yr) {
633  break;
634  }
635  const int day = date.day();
636  if (rule & DAY_OF_MONTH) {
637  if (day != dayOfMonth) {
638  break;
639  }
640  } else {
641  if (date.dayOfWeek() != dayOfWeek ||
642  ((rule & WEEKDAY_OF_MONTH) &&
643  (day - 1) / 7 + 1 != nthFromStart) ||
644  ((rule & LAST_WEEKDAY_OF_MONTH) &&
645  (daysInMonth - day) / 7 + 1 != nthFromEnd)) {
646  break;
647  }
648  }
649  times.prepend(qdt);
650  rdates.pop_back();
651  }
652  if (times.count() > (useNewRRULE ? minPhaseCount : minRuleCount)) {
653  // There are enough dates to combine into an RRULE
654  icalrecurrencetype r;
655  icalrecurrencetype_clear(&r);
656  r.freq = ICAL_YEARLY_RECURRENCE;
657  r.count = (year >= 2030) ? 0 : times.count() - 1;
658  r.by_month[0] = month;
659  if (rule & DAY_OF_MONTH) {
660  r.by_month_day[0] = dayOfMonth;
661  } else if (rule & WEEKDAY_OF_MONTH) {
662  r.by_day[0] = (dayOfWeek % 7 + 1) + (nthFromStart * 8); // Sunday = 1
663  } else if (rule & LAST_WEEKDAY_OF_MONTH) {
664  r.by_day[0] = -(dayOfWeek % 7 + 1) - (nthFromEnd * 8); // Sunday = 1
665  }
666  icalproperty *prop = icalproperty_new_rrule(r);
667  if (useNewRRULE) {
668  // This RRULE doesn't start from the phase start date, so set it into
669  // a new STANDARD/DAYLIGHT component in the VTIMEZONE.
670  icalcomponent *c = icalcomponent_new_clone(phaseComp);
671  icalcomponent_add_property(
672  c, icalproperty_new_dtstart(writeLocalICalDateTime(times[0], preOffset)));
673  icalcomponent_add_property(c, prop);
674  icalcomponent_add_component(tzcomp, c);
675  } else {
676  icalcomponent_add_property(phaseComp1, prop);
677  }
678  } else {
679  // Save dates for writing as RDATEs
680  for (int t = 0, tend = times.count() - 1; t < tend; ++t) {
681  rdates += times[t];
682  }
683  }
684  useNewRRULE = true;
685  // All date/time values but the last have been added to the VTIMEZONE.
686  // Remove them from the list.
687  qdt = times.last(); // set 'qdt' for start of loop
688  times.clear();
689  times += qdt;
690  }
691  rule = newRule;
692  } while (i < trcount);
693 
694  // Write remaining dates as RDATEs
695  for (int rd = 0, rdend = rdates.count(); rd < rdend; ++rd) {
696  dtperiod.time = writeLocalICalDateTime(rdates[rd], preOffset);
697  icalcomponent_add_property(phaseComp1, icalproperty_new_rdate(dtperiod));
698  }
699  icalcomponent_add_component(tzcomp, phaseComp1);
700  icalcomponent_free(phaseComp);
701  }
702 
703  d->setComponent(tzcomp);
704  }
705 }
706 
707 ICalTimeZoneData::~ICalTimeZoneData()
708 {
709  delete d;
710 }
711 
712 ICalTimeZoneData &ICalTimeZoneData::operator=(const ICalTimeZoneData &rhs)
713 {
714  // check for self assignment
715  if (&rhs == this) {
716  return *this;
717  }
718 
719  KTimeZoneData::operator=(rhs);
720  d->location = rhs.d->location;
721  d->url = rhs.d->url;
722  d->lastModified = rhs.d->lastModified;
723  d->setComponent(icalcomponent_new_clone(rhs.d->component()));
724  return *this;
725 }
726 
727 KTimeZoneData *ICalTimeZoneData::clone() const
728 {
729  return new ICalTimeZoneData(*this);
730 }
731 
732 QString ICalTimeZoneData::city() const
733 {
734  return d->location;
735 }
736 
737 QByteArray ICalTimeZoneData::url() const
738 {
739  return d->url;
740 }
741 
742 QDateTime ICalTimeZoneData::lastModified() const
743 {
744  return d->lastModified;
745 }
746 
747 QByteArray ICalTimeZoneData::vtimezone() const
748 {
749  const QByteArray result(icalcomponent_as_ical_string(d->component()));
750  icalmemory_free_ring();
751  return result;
752 }
753 
754 icaltimezone *ICalTimeZoneData::icalTimezone() const
755 {
756  icaltimezone *icaltz = icaltimezone_new();
757  if (!icaltz) {
758  return 0;
759  }
760  icalcomponent *c = icalcomponent_new_clone(d->component());
761  if (!icaltimezone_set_component(icaltz, c)) {
762  icalcomponent_free(c);
763  icaltimezone_free(icaltz, 1);
764  return 0;
765  }
766  return icaltz;
767 }
768 
769 bool ICalTimeZoneData::hasTransitions() const
770 {
771  return true;
772 }
773 
774 void ICalTimeZoneData::virtual_hook(int id, void *data)
775 {
776  Q_UNUSED(id);
777  Q_UNUSED(data);
778 }
779 
780 /******************************************************************************/
781 
782 //@cond PRIVATE
783 class ICalTimeZoneSourcePrivate
784 {
785 public:
786  static QList<QDateTime> parsePhase(icalcomponent *, bool daylight,
787  int &prevOffset, KTimeZone::Phase &);
788  static QByteArray icalTzidPrefix;
789 
790 #if defined(HAVE_UUID_UUID_H)
791  static void parseTransitions(const MSSystemTime &date, const KTimeZone::Phase &phase,
792  int prevOffset, QList<KTimeZone::Transition> &transitions);
793 #endif
794 };
795 
796 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
797 //@endcond
798 
799 ICalTimeZoneSource::ICalTimeZoneSource()
800  : KTimeZoneSource(false),
801  d(0)
802 {
803  Q_UNUSED(d);
804 }
805 
806 ICalTimeZoneSource::~ICalTimeZoneSource()
807 {
808 }
809 
810 bool ICalTimeZoneSource::parse(const QString &fileName, ICalTimeZones &zones)
811 {
812  QFile file(fileName);
813  if (!file.open(QIODevice::ReadOnly)) {
814  return false;
815  }
816  QTextStream ts(&file);
817  ts.setCodec("ISO 8859-1");
818  const QByteArray text = ts.readAll().trimmed().toLatin1();
819  file.close();
820 
821  bool result = false;
822  icalcomponent *calendar = icalcomponent_new_from_string(text.data());
823  if (calendar) {
824  if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT) {
825  result = parse(calendar, zones);
826  }
827  icalcomponent_free(calendar);
828  }
829  return result;
830 }
831 
832 bool ICalTimeZoneSource::parse(icalcomponent *calendar, ICalTimeZones &zones)
833 {
834  for (icalcomponent *c = icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT);
835  c; c = icalcomponent_get_next_component(calendar, ICAL_VTIMEZONE_COMPONENT)) {
836  const ICalTimeZone zone = parse(c);
837  if (!zone.isValid()) {
838  return false;
839  }
840  ICalTimeZone oldzone = zones.zone(zone.name());
841  if (oldzone.isValid()) {
842  // The zone already exists in the collection, so update the definition
843  // of the zone rather than using a newly created one.
844  oldzone.update(zone);
845  } else if (!zones.add(zone)) {
846  return false;
847  }
848  }
849  return true;
850 }
851 
852 ICalTimeZone ICalTimeZoneSource::parse(icalcomponent *vtimezone)
853 {
854  QString name;
855  QString xlocation;
856  ICalTimeZoneData *data = new ICalTimeZoneData();
857 
858  // Read the fixed properties which can only appear once in VTIMEZONE
859  icalproperty *p = icalcomponent_get_first_property(vtimezone, ICAL_ANY_PROPERTY);
860  while (p) {
861  icalproperty_kind kind = icalproperty_isa(p);
862  switch (kind) {
863 
864  case ICAL_TZID_PROPERTY:
865  name = QString::fromUtf8(icalproperty_get_tzid(p));
866  break;
867 
868  case ICAL_TZURL_PROPERTY:
869  data->d->url = icalproperty_get_tzurl(p);
870  break;
871 
872  case ICAL_LOCATION_PROPERTY:
873  // This isn't mentioned in RFC2445, but libical reads it ...
874  data->d->location = QString::fromUtf8(icalproperty_get_location(p));
875  break;
876 
877  case ICAL_X_PROPERTY:
878  { // use X-LIC-LOCATION if LOCATION is missing
879  const char *xname = icalproperty_get_x_name(p);
880  if (xname && !strcmp(xname, "X-LIC-LOCATION")) {
881  xlocation = QString::fromUtf8(icalproperty_get_x(p));
882  }
883  break;
884  }
885  case ICAL_LASTMODIFIED_PROPERTY:
886  {
887  const icaltimetype t = icalproperty_get_lastmodified(p);
888  if (icaltime_is_utc( t )) {
889  data->d->lastModified = toQDateTime(t);
890  } else {
891  kDebug() << "LAST-MODIFIED not UTC";
892  }
893  break;
894  }
895  default:
896  break;
897  }
898  p = icalcomponent_get_next_property(vtimezone, ICAL_ANY_PROPERTY);
899  }
900 
901  if (name.isEmpty()) {
902  kDebug() << "TZID missing";
903  delete data;
904  return ICalTimeZone();
905  }
906  if (data->d->location.isEmpty() && !xlocation.isEmpty()) {
907  data->d->location = xlocation;
908  }
909  const QString prefix = QString::fromUtf8(icalTzidPrefix());
910  if (name.startsWith(prefix)) {
911  // Remove the prefix from libical built in time zone TZID
912  const int i = name.indexOf(QLatin1Char('/'), prefix.length());
913  if (i > 0) {
914  name = name.mid(i + 1);
915  }
916  }
917  //kDebug() << "---zoneId: \"" << name << '"';
918 
919  /*
920  * Iterate through all time zone rules for this VTIMEZONE,
921  * and create a Phase object containing details for each one.
922  */
923  int prevOffset = 0;
924  QList<KTimeZone::Transition> transitions;
925  QDateTime earliest;
926  QList<KTimeZone::Phase> phases;
927  for (icalcomponent *c = icalcomponent_get_first_component(vtimezone, ICAL_ANY_COMPONENT);
928  c; c = icalcomponent_get_next_component(vtimezone, ICAL_ANY_COMPONENT)) {
929  int prevoff = 0;
930  KTimeZone::Phase phase;
931  QList<QDateTime> times;
932  icalcomponent_kind kind = icalcomponent_isa(c);
933  switch (kind) {
934 
935  case ICAL_XSTANDARD_COMPONENT:
936  //kDebug() << "---standard phase: found";
937  times = ICalTimeZoneSourcePrivate::parsePhase(c, false, prevoff, phase);
938  break;
939 
940  case ICAL_XDAYLIGHT_COMPONENT:
941  //kDebug() << "---daylight phase: found";
942  times = ICalTimeZoneSourcePrivate::parsePhase(c, true, prevoff, phase);
943  break;
944 
945  default:
946  kDebug() << "Unknown component:" << int(kind);
947  break;
948  }
949  const int tcount = times.count();
950  if (tcount) {
951  phases += phase;
952  for (int t = 0; t < tcount; ++t) {
953  transitions += KTimeZone::Transition(times[t], phase);
954  }
955  if (!earliest.isValid() || times[0] < earliest) {
956  prevOffset = prevoff;
957  earliest = times[0];
958  }
959  }
960  }
961  // Set phases used by the time zone, but note that VTIMEZONE doesn't contain
962  // time zone abbreviation before first transition.
963  data->setPhases(phases, prevOffset);
964  // Remove any "duplicate" transitions, i.e. those where two consecutive
965  // transitions have the same phase.
966  qSort(transitions);
967  for (int t = 1, tend = transitions.count(); t < tend;) {
968  if (transitions[t].phase() == transitions[t - 1].phase()) {
969  transitions.removeAt(t);
970  --tend;
971  } else {
972  ++t;
973  }
974  }
975  data->setTransitions(transitions);
976 
977  data->d->setComponent(icalcomponent_new_clone(vtimezone));
978  //kDebug() << "VTIMEZONE" << name;
979  return ICalTimeZone(this, name, data);
980 }
981 
982 #if defined(HAVE_UUID_UUID_H)
983 ICalTimeZone ICalTimeZoneSource::parse(MSTimeZone *tz, ICalTimeZones &zones)
984 {
985  const ICalTimeZone zone = parse(tz);
986  if (!zone.isValid()) {
987  return ICalTimeZone(); // error
988  }
989  const ICalTimeZone oldzone = zones.zone(zone);
990  if (oldzone.isValid()) {
991  // A similar zone already exists in the collection, so don't add this
992  // new zone, return old zone instead.
993  return oldzone;
994  } else if (zones.add(zone)) {
995  // No similar zone, add and return new one.
996  return zone;
997  }
998  return ICalTimeZone(); // error
999 }
1000 
1001 ICalTimeZone ICalTimeZoneSource::parse(MSTimeZone *tz)
1002 {
1003  ICalTimeZoneData kdata;
1004 
1005  // General properties.
1006  uuid_t uuid;
1007  char suuid[64];
1008  uuid_generate_random(uuid);
1009  uuid_unparse(uuid, suuid);
1010  QString name = QString::fromLatin1(suuid);
1011 
1012  // Create phases.
1013  QList<KTimeZone::Phase> phases;
1014 
1015  QList<QByteArray> standardAbbrevs;
1016  standardAbbrevs += tz->StandardName.toLatin1();
1017  const KTimeZone::Phase standardPhase(
1018  (tz->Bias + tz->StandardBias) * -60,
1019  standardAbbrevs, false,
1020  QLatin1String("Microsoft TIME_ZONE_INFORMATION"));
1021  phases += standardPhase;
1022 
1023  QList<QByteArray> daylightAbbrevs;
1024  daylightAbbrevs += tz->DaylightName.toLatin1();
1025  const KTimeZone::Phase daylightPhase(
1026  (tz->Bias + tz->DaylightBias) * -60,
1027  daylightAbbrevs, true,
1028  QLatin1String("Microsoft TIME_ZONE_INFORMATION"));
1029  phases += daylightPhase;
1030 
1031  // Set phases used by the time zone, but note that previous time zone
1032  // abbreviation is not known.
1033  const int prevOffset = tz->Bias * -60;
1034  kdata.setPhases(phases, prevOffset);
1035 
1036  // Create transitions
1037  QList<KTimeZone::Transition> transitions;
1038  ICalTimeZoneSourcePrivate::parseTransitions(
1039  tz->StandardDate, standardPhase, prevOffset, transitions);
1040  ICalTimeZoneSourcePrivate::parseTransitions(
1041  tz->DaylightDate, daylightPhase, prevOffset, transitions);
1042 
1043  qSort(transitions);
1044  kdata.setTransitions(transitions);
1045 
1046  ICalTimeZoneData *idata = new ICalTimeZoneData(kdata, KTimeZone(name), QDate());
1047 
1048  return ICalTimeZone(this, name, idata);
1049 }
1050 #endif // HAVE_UUID_UUID_H
1051 
1052 ICalTimeZone ICalTimeZoneSource::parse(const QString &name, const QStringList &tzList,
1053  ICalTimeZones &zones)
1054 {
1055  const ICalTimeZone zone = parse(name, tzList);
1056  if (!zone.isValid()) {
1057  return ICalTimeZone(); // error
1058  }
1059 
1060  ICalTimeZone oldzone = zones.zone(zone);
1061  // First off see if the zone is same as oldzone - _exactly_ same
1062  if (oldzone.isValid()) {
1063  return oldzone;
1064  }
1065 
1066  oldzone = zones.zone(name);
1067  if (oldzone.isValid()) {
1068  // The zone already exists, so update
1069  oldzone.update(zone);
1070  return zone;
1071  } else if (zones.add(zone)) {
1072  // No similar zone, add and return new one.
1073  return zone;
1074  }
1075  return ICalTimeZone(); // error
1076 }
1077 
1078 ICalTimeZone ICalTimeZoneSource::parse(const QString &name, const QStringList &tzList)
1079 {
1080  ICalTimeZoneData kdata;
1081  QList<KTimeZone::Phase> phases;
1082  QList<KTimeZone::Transition> transitions;
1083  bool daylight;
1084 
1085  for (QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it) {
1086  QString value = *it;
1087  daylight = false;
1088  const QString tzName = value.mid(0, value.indexOf(QLatin1String(";")));
1089  value = value.mid((value.indexOf(QLatin1String(";")) + 1));
1090  const QString tzOffset = value.mid(0, value.indexOf(QLatin1String(";")));
1091  value = value.mid((value.indexOf(QLatin1String(";")) + 1));
1092  const QString tzDaylight = value.mid(0, value.indexOf(QLatin1String(";")));
1093  const KDateTime tzDate = KDateTime::fromString(value.mid((value.lastIndexOf(QLatin1String(";")) + 1)));
1094  if (tzDaylight == QLatin1String("true")) {
1095  daylight = true;
1096  }
1097 
1098  const KTimeZone::Phase tzPhase(
1099  tzOffset.toInt(),
1100  QByteArray(tzName.toLatin1()), daylight, QLatin1String("VCAL_TZ_INFORMATION"));
1101  phases += tzPhase;
1102  transitions += KTimeZone::Transition(tzDate.dateTime(), tzPhase);
1103  }
1104 
1105  kdata.setPhases(phases, 0);
1106  qSort(transitions);
1107  kdata.setTransitions(transitions);
1108 
1109  ICalTimeZoneData *idata = new ICalTimeZoneData(kdata, KTimeZone(name), QDate());
1110  return ICalTimeZone(this, name, idata);
1111 }
1112 
1113 #if defined(HAVE_UUID_UUID_H)
1114 //@cond PRIVATE
1115 void ICalTimeZoneSourcePrivate::parseTransitions(const MSSystemTime &date,
1116  const KTimeZone::Phase &phase, int prevOffset,
1117  QList<KTimeZone::Transition> &transitions)
1118 {
1119  // NOTE that we need to set start and end times and they cannot be
1120  // to far in either direction to avoid bloating the transitions list
1121  const KDateTime klocalStart(QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0)),
1122  KDateTime::Spec::ClockTime());
1123  const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1124 
1125  if (date.wYear) {
1126  // Absolute change time.
1127  if (date.wYear >= 1601 && date.wYear <= 30827 &&
1128  date.wMonth >= 1 && date.wMonth <= 12 &&
1129  date.wDay >= 1 && date.wDay <= 31) {
1130  const QDate dt(date.wYear, date.wMonth, date.wDay);
1131  const QTime tm(date.wHour, date.wMinute, date.wSecond, date.wMilliseconds);
1132  const QDateTime datetime(dt, tm);
1133  if (datetime.isValid()) {
1134  transitions += KTimeZone::Transition(datetime, phase);
1135  }
1136  }
1137  } else {
1138  // The normal way, for example: 'First Sunday in April at 02:00'.
1139  if (date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1140  date.wMonth >= 1 && date.wMonth <= 12 &&
1141  date.wDay >= 1 && date.wDay <= 5) {
1142  RecurrenceRule r;
1143  r.setRecurrenceType(RecurrenceRule::rYearly);
1144  r.setDuration(-1);
1145  r.setFrequency(1);
1146  QList<int> lst;
1147  lst.append(date.wMonth);
1148  r.setByMonths(lst);
1149  QList<RecurrenceRule::WDayPos> wdlst;
1150  RecurrenceRule::WDayPos pos;
1151  pos.setDay(date.wDayOfWeek ? date.wDayOfWeek : 7);
1152  pos.setPos(date.wDay < 5 ? date.wDay : -1);
1153  wdlst.append(pos);
1154  r.setByDays(wdlst);
1155  r.setStartDt(klocalStart);
1156  r.setWeekStart(1);
1157  const DateTimeList dtl = r.timesInInterval(klocalStart, maxTime);
1158  for (int i = 0, end = dtl.count(); i < end; ++i) {
1159  QDateTime utc = dtl[i].dateTime();
1160  utc.setTimeSpec(Qt::UTC);
1161  transitions += KTimeZone::Transition(utc.addSecs(-prevOffset), phase);
1162  }
1163  }
1164  }
1165 }
1166 //@endcond
1167 #endif // HAVE_UUID_UUID_H
1168 
1169 ICalTimeZone ICalTimeZoneSource::parse(icaltimezone *tz)
1170 {
1171  /* Parse the VTIMEZONE component stored in the icaltimezone structure.
1172  * This is both easier and provides more complete information than
1173  * extracting already parsed data from icaltimezone.
1174  */
1175  return tz ? parse(icaltimezone_get_component(tz)) : ICalTimeZone();
1176 }
1177 
1178 //@cond PRIVATE
1179 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase(icalcomponent *c,
1180  bool daylight,
1181  int &prevOffset,
1182  KTimeZone::Phase &phase)
1183 {
1184  QList<QDateTime> transitions;
1185 
1186  // Read the observance data for this standard/daylight savings phase
1187  QList<QByteArray> abbrevs;
1188  QString comment;
1189  prevOffset = 0;
1190  int utcOffset = 0;
1191  bool recurs = false;
1192  bool found_dtstart = false;
1193  bool found_tzoffsetfrom = false;
1194  bool found_tzoffsetto = false;
1195  icaltimetype dtstart = icaltime_null_time();
1196 
1197  // Now do the ical reading.
1198  icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1199  while (p) {
1200  icalproperty_kind kind = icalproperty_isa(p);
1201  switch (kind) {
1202 
1203  case ICAL_TZNAME_PROPERTY: // abbreviated name for this time offset
1204  {
1205  // TZNAME can appear multiple times in order to provide language
1206  // translations of the time zone offset name.
1207 
1208  // TODO: Does this cope with multiple language specifications?
1209  QByteArray tzname = icalproperty_get_tzname(p);
1210  // Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME
1211  // strings, which is totally useless. So ignore those.
1212  if ((!daylight && tzname == "Standard Time") ||
1213  (daylight && tzname == "Daylight Time")) {
1214  break;
1215  }
1216  if (!abbrevs.contains(tzname)) {
1217  abbrevs += tzname;
1218  }
1219  break;
1220  }
1221  case ICAL_DTSTART_PROPERTY: // local time at which phase starts
1222  dtstart = icalproperty_get_dtstart(p);
1223  found_dtstart = true;
1224  break;
1225 
1226  case ICAL_TZOFFSETFROM_PROPERTY: // UTC offset immediately before start of phase
1227  prevOffset = icalproperty_get_tzoffsetfrom(p);
1228  found_tzoffsetfrom = true;
1229  break;
1230 
1231  case ICAL_TZOFFSETTO_PROPERTY:
1232  utcOffset = icalproperty_get_tzoffsetto(p);
1233  found_tzoffsetto = true;
1234  break;
1235 
1236  case ICAL_COMMENT_PROPERTY:
1237  comment = QString::fromUtf8(icalproperty_get_comment(p));
1238  break;
1239 
1240  case ICAL_RDATE_PROPERTY:
1241  case ICAL_RRULE_PROPERTY:
1242  recurs = true;
1243  break;
1244 
1245  default:
1246  kDebug() << "Unknown property:" << int(kind);
1247  break;
1248  }
1249  p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1250  }
1251 
1252  // Validate the phase data
1253  if (!found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto) {
1254  kDebug() << "DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1255  return transitions;
1256  }
1257 
1258  // Convert DTSTART to QDateTime, and from local time to UTC
1259  const QDateTime localStart = toQDateTime(dtstart); // local time
1260  dtstart.second -= prevOffset;
1261  dtstart.zone = icaltimezone_get_utc_timezone();
1262  const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart)); // UTC
1263 
1264  transitions += utcStart;
1265  if (recurs) {
1266  /* RDATE or RRULE is specified. There should only be one or the other, but
1267  * it doesn't really matter - the code can cope with both.
1268  * Note that we had to get DTSTART, TZOFFSETFROM, TZOFFSETTO before reading
1269  * recurrences.
1270  */
1271  const KDateTime klocalStart(localStart, KDateTime::Spec::ClockTime());
1272  const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1273  Recurrence recur;
1274  icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1275  while (p) {
1276  icalproperty_kind kind = icalproperty_isa(p);
1277  switch (kind) {
1278 
1279  case ICAL_RDATE_PROPERTY:
1280  {
1281  icaltimetype t = icalproperty_get_rdate(p).time;
1282  if (icaltime_is_date(t)) {
1283  // RDATE with a DATE value inherits the (local) time from DTSTART
1284  t.hour = dtstart.hour;
1285  t.minute = dtstart.minute;
1286  t.second = dtstart.second;
1287  t.is_date = 0;
1288  t.zone = 0; // dtstart is in local time
1289  }
1290  // RFC2445 states that RDATE must be in local time,
1291  // but we support UTC as well to be safe.
1292  if (!icaltime_is_utc( t )) {
1293  t.second -= prevOffset; // convert to UTC
1294  t.zone = icaltimezone_get_utc_timezone();
1295  t = icaltime_normalize(t);
1296  }
1297  transitions += toQDateTime(t);
1298  break;
1299  }
1300  case ICAL_RRULE_PROPERTY:
1301  {
1302  RecurrenceRule r;
1303  ICalFormat icf;
1304  ICalFormatImpl impl(&icf);
1305  impl.readRecurrence(icalproperty_get_rrule(p), &r);
1306  r.setStartDt(klocalStart);
1307  // The end date time specified in an RRULE should be in UTC.
1308  // Convert to local time to avoid timesInInterval() getting things wrong.
1309  if (r.duration() == 0) {
1310  KDateTime end(r.endDt());
1311  if (end.timeSpec() == KDateTime::Spec::UTC()) {
1312  end.setTimeSpec(KDateTime::Spec::ClockTime());
1313  r.setEndDt(end.addSecs(prevOffset));
1314  }
1315  }
1316  const DateTimeList dts = r.timesInInterval(klocalStart, maxTime);
1317  for (int i = 0, end = dts.count(); i < end; ++i) {
1318  QDateTime utc = dts[i].dateTime();
1319  utc.setTimeSpec(Qt::UTC);
1320  transitions += utc.addSecs(-prevOffset);
1321  }
1322  break;
1323  }
1324  default:
1325  break;
1326  }
1327  p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1328  }
1329  qSortUnique(transitions);
1330  }
1331 
1332  phase = KTimeZone::Phase(utcOffset, abbrevs, daylight, comment);
1333  return transitions;
1334 }
1335 //@endcond
1336 
1337 ICalTimeZone ICalTimeZoneSource::standardZone(const QString &zone, bool icalBuiltIn)
1338 {
1339  if (!icalBuiltIn) {
1340  // Try to fetch a system time zone in preference, on the grounds
1341  // that system time zones are more likely to be up to date than
1342  // built-in libical ones.
1343  QString tzid = zone;
1344  const QString prefix = QString::fromUtf8(icalTzidPrefix());
1345  if (zone.startsWith(prefix)) {
1346  const int i = zone.indexOf(QLatin1Char('/'), prefix.length());
1347  if (i > 0) {
1348  tzid = zone.mid(i + 1); // strip off the libical prefix
1349  }
1350  }
1351  const KTimeZone ktz = KSystemTimeZones::readZone(tzid);
1352  if (ktz.isValid()) {
1353  if (ktz.data(true)) {
1354  const ICalTimeZone icaltz(ktz);
1355  //kDebug() << zone << " read from system database";
1356  return icaltz;
1357  }
1358  }
1359  }
1360  // Try to fetch a built-in libical time zone.
1361  // First try to look it up as a geographical location (e.g. Europe/London)
1362  const QByteArray zoneName = zone.toUtf8();
1363  icaltimezone *icaltz = icaltimezone_get_builtin_timezone(zoneName);
1364  if (!icaltz) {
1365  // This will find it if it includes the libical prefix
1366  icaltz = icaltimezone_get_builtin_timezone_from_tzid(zoneName);
1367  if (!icaltz) {
1368  return ICalTimeZone();
1369  }
1370  }
1371  return parse(icaltz);
1372 }
1373 
1374 QByteArray ICalTimeZoneSource::icalTzidPrefix()
1375 {
1376  if (ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty()) {
1377  icaltimezone *icaltz = icaltimezone_get_builtin_timezone("Europe/London");
1378  const QByteArray tzid = icaltimezone_get_tzid(icaltz);
1379  if (tzid.right(13) == "Europe/London") {
1380  int i = tzid.indexOf('/', 1);
1381  if (i > 0) {
1382  ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left(i + 1);
1383  return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1384  }
1385  }
1386  kError() << "failed to get libical TZID prefix";
1387  }
1388  return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1389 }
1390 
1391 void ICalTimeZoneSource::virtual_hook(int id, void *data)
1392 {
1393  Q_UNUSED(id);
1394  Q_UNUSED(data);
1395  Q_ASSERT(false);
1396 }
1397 
1398 } // namespace KCalCore
KCalCore::ICalFormatImpl
This class provides the libical dependent functions for ICalFormat.
Definition: icalformat_p.h:90
KCalCore::ICalFormat
iCalendar format implementation.
Definition: icalformat.h:59
KCalCore::ICalTimeZoneBackend
Backend class for KICalTimeZone class.
Definition: icaltimezones.h:300
KCalCore::ICalTimeZoneBackend::ICalTimeZoneBackend
ICalTimeZoneBackend()
Implements ICalTimeZone::ICalTimeZone().
Definition: icaltimezones.cpp:224
KCalCore::ICalTimeZoneBackend::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: icaltimezones.cpp:261
KCalCore::ICalTimeZoneBackend::clone
virtual KTimeZoneBackend * clone() const
Creates a copy of this instance.
Definition: icaltimezones.cpp:245
KCalCore::ICalTimeZoneBackend::type
virtual QByteArray type() const
Returns the class name of the data represented by this instance.
Definition: icaltimezones.cpp:250
KCalCore::ICalTimeZoneBackend::hasTransitions
virtual bool hasTransitions(const KTimeZone *caller) const
Implements ICalTimeZone::hasTransitions().
Definition: icaltimezones.cpp:255
KCalCore::ICalTimeZoneData
Parsed iCalendar VTIMEZONE data.
Definition: icaltimezones.h:565
KCalCore::ICalTimeZoneData::ICalTimeZoneData
ICalTimeZoneData()
Default constructor.
Definition: icaltimezones.cpp:390
KCalCore::ICalTimeZoneData::icalTimezone
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
Definition: icaltimezones.cpp:754
KCalCore::ICalTimeZoneData::clone
virtual KTimeZoneData * clone() const
Creates a new copy of this object.
Definition: icaltimezones.cpp:727
KCalCore::ICalTimeZoneData::hasTransitions
virtual bool hasTransitions() const
Return whether daylight saving transitions are available for the time zone.
Definition: icaltimezones.cpp:769
KCalCore::ICalTimeZoneData::url
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Definition: icaltimezones.cpp:737
KCalCore::ICalTimeZoneData::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:747
KCalCore::ICalTimeZoneData::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: icaltimezones.cpp:774
KCalCore::ICalTimeZoneData::~ICalTimeZoneData
virtual ~ICalTimeZoneData()
Destructor.
Definition: icaltimezones.cpp:707
KCalCore::ICalTimeZoneData::lastModified
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
Definition: icaltimezones.cpp:742
KCalCore::ICalTimeZoneData::operator=
ICalTimeZoneData & operator=(const ICalTimeZoneData &rhs)
Assignment operator.
Definition: icaltimezones.cpp:712
KCalCore::ICalTimeZoneData::city
QString city() const
Returns the name of the city for this time zone, if any.
Definition: icaltimezones.cpp:732
KCalCore::ICalTimeZoneSource
A class which reads and parses iCalendar VTIMEZONE components, and accesses libical time zone data.
Definition: icaltimezones.h:406
KCalCore::ICalTimeZoneSource::icalTzidPrefix
static QByteArray icalTzidPrefix()
Returns the prefix string used in the TZID field in built-in libical time zones.
Definition: icaltimezones.cpp:1374
KCalCore::ICalTimeZoneSource::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: icaltimezones.cpp:1391
KCalCore::ICalTimeZoneSource::standardZone
ICalTimeZone standardZone(const QString &zone, bool icalBuiltIn=false)
Creates an ICalTimeZone instance for a standard time zone.
Definition: icaltimezones.cpp:1337
KCalCore::ICalTimeZoneSource::parse
ICalTimeZone parse(icalcomponent *vtimezone)
Creates an ICalTimeZone instance containing the detailed information parsed from an iCalendar VTIMEZO...
Definition: icaltimezones.cpp:852
KCalCore::ICalTimeZoneSource::ICalTimeZoneSource
ICalTimeZoneSource()
Constructs an iCalendar time zone source.
Definition: icaltimezones.cpp:799
KCalCore::ICalTimeZoneSource::~ICalTimeZoneSource
virtual ~ICalTimeZoneSource()
Destructor.
Definition: icaltimezones.cpp:806
KCalCore::ICalTimeZone
The ICalTimeZone class represents an iCalendar VTIMEZONE component.
Definition: icaltimezones.h:178
KCalCore::ICalTimeZone::url
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Definition: icaltimezones.cpp:305
KCalCore::ICalTimeZone::~ICalTimeZone
virtual ~ICalTimeZone()
Destructor.
Definition: icaltimezones.cpp:296
KCalCore::ICalTimeZone::lastModified
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
Definition: icaltimezones.cpp:311
KCalCore::ICalTimeZone::city
QString city() const
Returns the name of the city for this time zone, if any.
Definition: icaltimezones.cpp:299
KCalCore::ICalTimeZone::ICalTimeZone
ICalTimeZone()
Constructs a null time zone.
Definition: icaltimezones.cpp:269
KCalCore::ICalTimeZone::utc
static ICalTimeZone utc()
Returns a standard UTC time zone, with name "UTC".
Definition: icaltimezones.cpp:340
KCalCore::ICalTimeZone::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:317
KCalCore::ICalTimeZone::icalTimezone
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
Definition: icaltimezones.cpp:323
KCalCore::ICalTimeZone::virtual_hook
virtual void virtual_hook(int id, void *data)
Definition: icaltimezones.cpp:350
KCalCore::ICalTimeZone::update
bool update(const ICalTimeZone &other)
Update the definition of the time zone to be identical to another ICalTimeZone instance.
Definition: icaltimezones.cpp:329
KCalCore::ICalTimeZones
The ICalTimeZones class represents a time zone database which consists of a collection of individual ...
Definition: icaltimezones.h:66
KCalCore::ICalTimeZones::add
bool add(const ICalTimeZone &zone)
Adds a time zone to the collection.
Definition: icaltimezones.cpp:131
KCalCore::ICalTimeZones::zone
ICalTimeZone zone(const QString &name) const
Returns the time zone with the given name.
Definition: icaltimezones.cpp:180
KCalCore::ICalTimeZones::clear
void clear()
Clears the collection.
Definition: icaltimezones.cpp:170
KCalCore::ICalTimeZones::ICalTimeZones
ICalTimeZones()
Constructs an empty time zone collection.
Definition: icaltimezones.cpp:100
KCalCore::ICalTimeZones::count
int count()
Returns the number of zones kept in memory.
Definition: icaltimezones.cpp:175
KCalCore::ICalTimeZones::operator=
ICalTimeZones & operator=(const ICalTimeZones &rhs)
Assignment operator.
Definition: icaltimezones.cpp:111
KCalCore::ICalTimeZones::~ICalTimeZones
~ICalTimeZones()
Destructor.
Definition: icaltimezones.cpp:121
KCalCore::ICalTimeZones::remove
ICalTimeZone remove(const ICalTimeZone &zone)
Removes a time zone from the collection.
Definition: icaltimezones.cpp:144
KCalCore::ICalTimeZones::zones
const ZoneMap zones() const
Returns all the time zones defined in this collection.
Definition: icaltimezones.cpp:126
KCalCore::RecurrenceRule::WDayPos
structure for describing the n-th weekday of the month/year.
Definition: recurrencerule.h:69
KCalCore::RecurrenceRule
This class represents a recurrence rule for a calendar incidence.
Definition: recurrencerule.h:44
KCalCore::RecurrenceRule::setDuration
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
Definition: recurrencerule.cpp:996
KCalCore::RecurrenceRule::setFrequency
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
Definition: recurrencerule.cpp:1033
KCalCore::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:2157
KCalCore::RecurrenceRule::setEndDt
void setEndDt(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
Definition: recurrencerule.cpp:986
KCalCore::RecurrenceRule::endDt
KDateTime endDt(bool *result=0) const
Returns the date and time of the last recurrence.
Definition: recurrencerule.cpp:955
KCalCore::RecurrenceRule::setStartDt
void setStartDt(const KDateTime &start)
Sets the recurrence start date/time.
Definition: recurrencerule.cpp:1024
KCalCore::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:1738
KCalCore::Recurrence
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:88
KCalCore::SortableList
A QList which can be sorted.
Definition: sortablelist.h:87
icalformat.h
This file is part of the API for handling calendar data and defines the ICalFormat class.
icalformat_p.h
This file is part of the API for handling calendar data and defines the internal ICalFormatImpl class...
KCalCore
TODO: KDE5:
Definition: alarm.h:47
KCalCore::_MSSystemTime
Placeholhers for Microsoft and ActiveSync timezone data.
Definition: icaltimezones.h:373
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Jan 26 2021 00:00:00 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalCore Library

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

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
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