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

KCalCore Library

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

Skip menu "kdepimlibs 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

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