• 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
37extern "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
46using namespace KCalCore;
47
48// Minimum repetition counts for VTIMEZONE RRULEs
49static const int minRuleCount = 5; // for any RRULE
50static const int minPhaseCount = 8; // for separate STANDARD/DAYLIGHT component
51
52// Convert an ical time to QDateTime, preserving the UTC indicator
53static 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.
63static 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
72static 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
87namespace KCalCore {
88
89/******************************************************************************/
90
91//@cond PRIVATE
92class ICalTimeZonesPrivate
93{
94public:
95 ICalTimeZonesPrivate() {}
96 ICalTimeZones::ZoneMap zones;
97};
98//@endcond
99
100ICalTimeZones::ICalTimeZones()
101 : d(new ICalTimeZonesPrivate)
102{
103}
104
105ICalTimeZones::ICalTimeZones(const ICalTimeZones &rhs)
106 : d(new ICalTimeZonesPrivate())
107{
108 d->zones = rhs.d->zones;
109}
110
111ICalTimeZones &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
121ICalTimeZones::~ICalTimeZones()
122{
123 delete d;
124}
125
126const ICalTimeZones::ZoneMap ICalTimeZones::zones() const
127{
128 return d->zones;
129}
130
131bool 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
144ICalTimeZone 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
157ICalTimeZone 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
170void ICalTimeZones::clear()
171{
172 d->zones.clear();
173}
174
175int ICalTimeZones::count()
176{
177 return d->zones.count();
178}
179
180ICalTimeZone 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
191ICalTimeZone 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
224ICalTimeZoneBackend::ICalTimeZoneBackend()
225 : KTimeZoneBackend()
226{}
227
228ICalTimeZoneBackend::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
236ICalTimeZoneBackend::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
242ICalTimeZoneBackend::~ICalTimeZoneBackend()
243{}
244
245KTimeZoneBackend *ICalTimeZoneBackend::clone() const
246{
247 return new ICalTimeZoneBackend(*this);
248}
249
250QByteArray ICalTimeZoneBackend::type() const
251{
252 return "ICalTimeZone";
253}
254
255bool ICalTimeZoneBackend::hasTransitions(const KTimeZone *caller) const
256{
257 Q_UNUSED(caller);
258 return true;
259}
260
261void ICalTimeZoneBackend::virtual_hook(int id, void *data)
262{
263 Q_UNUSED(id);
264 Q_UNUSED(data);
265}
266
267/******************************************************************************/
268
269ICalTimeZone::ICalTimeZone()
270 : KTimeZone(new ICalTimeZoneBackend())
271{}
272
273ICalTimeZone::ICalTimeZone(ICalTimeZoneSource *source, const QString &name,
274 ICalTimeZoneData *data)
275 : KTimeZone(new ICalTimeZoneBackend(source, name))
276{
277 setData(data);
278}
279
280ICalTimeZone::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
296ICalTimeZone::~ICalTimeZone()
297{}
298
299QString ICalTimeZone::city() const
300{
301 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
302 return dat ? dat->city() : QString();
303}
304
305QByteArray ICalTimeZone::url() const
306{
307 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
308 return dat ? dat->url() : QByteArray();
309}
310
311QDateTime ICalTimeZone::lastModified() const
312{
313 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
314 return dat ? dat->lastModified() : QDateTime();
315}
316
317QByteArray ICalTimeZone::vtimezone() const
318{
319 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
320 return dat ? dat->vtimezone() : QByteArray();
321}
322
323icaltimezone *ICalTimeZone::icalTimezone() const
324{
325 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>(data());
326 return dat ? dat->icalTimezone() : 0;
327}
328
329bool 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
340ICalTimeZone 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
350void ICalTimeZone::virtual_hook(int id, void *data)
351{
352 Q_UNUSED(id);
353 Q_UNUSED(data);
354}
355/******************************************************************************/
356
357//@cond PRIVATE
358class ICalTimeZoneDataPrivate
359{
360public:
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
385private:
386 icalcomponent *icalComponent; // ical component representing this time zone
387};
388//@endcond
389
390ICalTimeZoneData::ICalTimeZoneData()
391 : d(new ICalTimeZoneDataPrivate())
392{
393}
394
395ICalTimeZoneData::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
405ICalTimeZoneData::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
707ICalTimeZoneData::~ICalTimeZoneData()
708{
709 delete d;
710}
711
712ICalTimeZoneData &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
727KTimeZoneData *ICalTimeZoneData::clone() const
728{
729 return new ICalTimeZoneData(*this);
730}
731
732QString ICalTimeZoneData::city() const
733{
734 return d->location;
735}
736
737QByteArray ICalTimeZoneData::url() const
738{
739 return d->url;
740}
741
742QDateTime ICalTimeZoneData::lastModified() const
743{
744 return d->lastModified;
745}
746
747QByteArray ICalTimeZoneData::vtimezone() const
748{
749 const QByteArray result(icalcomponent_as_ical_string(d->component()));
750 icalmemory_free_ring();
751 return result;
752}
753
754icaltimezone *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
769bool ICalTimeZoneData::hasTransitions() const
770{
771 return true;
772}
773
774void ICalTimeZoneData::virtual_hook(int id, void *data)
775{
776 Q_UNUSED(id);
777 Q_UNUSED(data);
778}
779
780/******************************************************************************/
781
782//@cond PRIVATE
783class ICalTimeZoneSourcePrivate
784{
785public:
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
796QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
797//@endcond
798
799ICalTimeZoneSource::ICalTimeZoneSource()
800 : KTimeZoneSource(false),
801 d(0)
802{
803 Q_UNUSED(d);
804}
805
806ICalTimeZoneSource::~ICalTimeZoneSource()
807{
808}
809
810bool 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
832bool 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
852ICalTimeZone 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)
983ICalTimeZone 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
1001ICalTimeZone 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
1052ICalTimeZone 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
1078ICalTimeZone 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
1115void 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
1169ICalTimeZone 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
1179QList<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
1337ICalTimeZone 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
1374QByteArray 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
1391void 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-2022 The KDE developers.
Generated on Thu Jul 21 2022 00:00:00 by doxygen 1.9.5 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