21 #include <config-kcalcore.h>
23 #include "icaltimezones.h"
26 #include "recurrence.h"
27 #include "recurrencerule.h"
31 #include <KSystemTimeZone>
33 #include <QtCore/QDateTime>
34 #include <QtCore/QFile>
35 #include <QtCore/QTextStream>
38 #include <libical/ical.h>
39 #include <icaltimezone.h>
42 #if defined(HAVE_UUID_UUID_H)
43 #include <uuid/uuid.h>
49 static const int minRuleCount = 5;
50 static const int minPhaseCount = 8;
53 static QDateTime toQDateTime(
const icaltimetype &t)
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));
63 static QDateTime MAX_DATE()
67 dt = QDateTime(QDate::currentDate().addYears(20), QTime(0, 0, 0));
72 static icaltimetype writeLocalICalDateTime(
const QDateTime &utc,
int offset)
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();
92 class ICalTimeZonesPrivate
95 ICalTimeZonesPrivate() {}
96 ICalTimeZones::ZoneMap zones;
101 : d(new ICalTimeZonesPrivate)
106 : d(new ICalTimeZonesPrivate())
108 d->zones = rhs.d->
zones;
133 if (!
zone.isValid()) {
136 if (d->zones.find(
zone.name()) != d->zones.end()) {
146 if (
zone.isValid()) {
147 for (ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it) {
148 if (it.value() ==
zone) {
159 if (!name.isEmpty()) {
160 ZoneMap::Iterator it = d->zones.find(name);
161 if (it != d->zones.end()) {
177 return d->zones.count();
182 if (!name.isEmpty()) {
183 ZoneMap::ConstIterator it = d->zones.constFind(name);
184 if (it != d->zones.constEnd()) {
193 if (
zone.isValid()) {
194 QMapIterator<QString, ICalTimeZone> it(d->zones);
195 while (it.hasNext()) {
198 const QList<KTimeZone::Transition> list1 = tz.transitions();
199 const QList<KTimeZone::Transition> list2 =
zone.transitions();
200 if (list1.size() == list2.size()) {
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())) {
230 const QString &countryCode,
231 float latitude,
float longitude,
232 const QString &comment)
233 : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
237 : KTimeZoneBackend(0, tz.name(), tz.countryCode(), tz.latitude(), tz.longitude(), tz.comment())
242 ICalTimeZoneBackend::~ICalTimeZoneBackend()
252 return "ICalTimeZone";
282 tz.latitude(), tz.longitude(),
285 const KTimeZoneData *data = tz.data(
true);
302 return dat ? dat->
city() : QString();
308 return dat ? dat->
url() : QByteArray();
320 return dat ? dat->
vtimezone() : QByteArray();
331 if (!updateBase(other)) {
335 KTimeZoneData *otherData = other.data() ? other.data()->clone() : 0;
336 setData(otherData, other.source());
343 if (!utcZone.isValid()) {
345 utcZone = tzs.
parse(icaltimezone_get_utc_timezone());
358 class ICalTimeZoneDataPrivate
361 ICalTimeZoneDataPrivate() : icalComponent(0) {}
363 ~ICalTimeZoneDataPrivate()
366 icalcomponent_free(icalComponent);
370 icalcomponent *component()
const {
371 return icalComponent;
373 void setComponent(icalcomponent *c)
376 icalcomponent_free(icalComponent);
383 QDateTime lastModified;
386 icalcomponent *icalComponent;
391 : d(new ICalTimeZoneDataPrivate())
396 : KTimeZoneData(rhs),
397 d(new ICalTimeZoneDataPrivate())
399 d->location = rhs.d->location;
402 d->setComponent(icalcomponent_new_clone(rhs.d->component()));
406 const KTimeZone &tz,
const QDate &earliest)
407 : KTimeZoneData(rhs),
408 d(new ICalTimeZoneDataPrivate())
413 WEEKDAY_OF_MONTH = 0x02,
414 LAST_WEEKDAY_OF_MONTH = 0x04
417 if (tz.type() ==
"KSystemTimeZone") {
421 icalcomponent *c = 0;
422 const KTimeZone ktz = KSystemTimeZones::readZone(tz.name());
424 if (ktz.data(
true)) {
428 c = icalcomponent_new_clone(icaltimezone_get_component(itz));
429 icaltimezone_free(itz, 1);
435 icaltimezone *itz = icaltimezone_get_builtin_timezone(tz.name().toUtf8());
436 c = icalcomponent_new_clone(icaltimezone_get_component(itz));
442 icalproperty *prop = icalcomponent_get_first_property(c, ICAL_TZID_PROPERTY);
444 icalvalue *value = icalproperty_get_value(prop);
445 const char *tzid = icalvalue_get_text(value);
447 const int len = icalprefix.size();
448 if (!strncmp(icalprefix, tzid, len)) {
449 const char *s = strchr(tzid + len,
'/');
451 const QByteArray tzidShort(s + 1);
452 icalvalue_set_text(value, tzidShort);
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);
468 icalcomponent *tzcomp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
469 icalcomponent_add_property(tzcomp, icalproperty_new_tzid(tz.name().toUtf8()));
474 QList<KTimeZone::Transition> transits = transitions();
475 if (transits.isEmpty()) {
479 if (transits.isEmpty()) {
480 kDebug() <<
"No transition information available VTIMEZONE will be invalid.";
483 if (earliest.isValid()) {
485 for (
int i = 0, end = transits.count(); i < end; ++i) {
486 if (transits.at(i).time().date() >= earliest) {
488 transits.erase(transits.begin(), transits.begin() + i);
494 int trcount = transits.count();
495 QVector<bool> transitionsDone(trcount);
496 transitionsDone.fill(
false);
500 icaldatetimeperiodtype dtperiod;
501 dtperiod.period = icalperiodtype_null_period();
504 for (; i < trcount && transitionsDone[i]; ++i) {
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) {
523 transitionsDone[i] =
true;
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])));
535 if (!phase.comment().isEmpty()) {
536 icalcomponent_add_property(phaseComp,
537 icalproperty_new_comment(phase.comment().toUtf8()));
539 icalcomponent_add_property(phaseComp,
540 icalproperty_new_tzoffsetfrom(preOffset));
541 icalcomponent_add_property(phaseComp,
542 icalproperty_new_tzoffsetto(phase.utcOffset()));
544 icalcomponent *phaseComp1 = icalcomponent_new_clone(phaseComp);
545 icalcomponent_add_property(phaseComp1,
546 icalproperty_new_dtstart(
547 writeLocalICalDateTime(transits.at(i).time(),
549 bool useNewRRULE =
false;
555 int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0;
557 int nthFromStart = 0;
561 QList<QDateTime> rdates;
562 QList<QDateTime> times;
563 QDateTime qdt = transits.at(i).time();
565 transitionsDone[i] =
true;
569 rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
573 month = date.month();
574 daysInMonth = date.daysInMonth();
575 dayOfWeek = date.dayOfWeek();
576 dayOfMonth = date.day();
577 nthFromStart = (dayOfMonth - 1) / 7 + 1;
578 nthFromEnd = (daysInMonth - dayOfMonth) / 7 + 1;
580 if (++i >= trcount) {
582 times += QDateTime();
584 if (transitionsDone[i] ||
585 transits.at(i).phase() != phase ||
586 transits.at(i - 1).phase().utcOffset() != preOffset) {
589 transitionsDone[i] =
true;
590 qdt = transits.at(i).time();
591 if (!qdt.isValid()) {
597 if (qdt.time() != time ||
598 date.month() != month ||
599 date.year() != ++year) {
602 const int day = date.day();
603 if ((newRule & DAY_OF_MONTH) && day != dayOfMonth) {
604 newRule &= ~DAY_OF_MONTH;
606 if (newRule & (WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH)) {
607 if (date.dayOfWeek() != dayOfWeek) {
608 newRule &= ~(WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH);
610 if ((newRule & WEEKDAY_OF_MONTH) &&
611 (day - 1) / 7 + 1 != nthFromStart) {
612 newRule &= ~WEEKDAY_OF_MONTH;
614 if ((newRule & LAST_WEEKDAY_OF_MONTH) &&
615 (daysInMonth - day) / 7 + 1 != nthFromEnd) {
616 newRule &= ~LAST_WEEKDAY_OF_MONTH;
626 int yr = times[0].date().year();
627 while (!rdates.isEmpty()) {
630 if (qdt.time() != time ||
631 date.month() != month ||
632 date.year() != --yr) {
635 const int day = date.day();
636 if (rule & DAY_OF_MONTH) {
637 if (day != dayOfMonth) {
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)) {
652 if (times.count() > (useNewRRULE ? minPhaseCount : minRuleCount)) {
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);
663 }
else if (rule & LAST_WEEKDAY_OF_MONTH) {
664 r.by_day[0] = -(dayOfWeek % 7 + 1) - (nthFromEnd * 8);
666 icalproperty *prop = icalproperty_new_rrule(r);
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);
676 icalcomponent_add_property(phaseComp1, prop);
680 for (
int t = 0, tend = times.count() - 1; t < tend; ++t) {
692 }
while (i < trcount);
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));
699 icalcomponent_add_component(tzcomp, phaseComp1);
700 icalcomponent_free(phaseComp);
703 d->setComponent(tzcomp);
719 KTimeZoneData::operator=(rhs);
720 d->location = rhs.d->location;
723 d->setComponent(icalcomponent_new_clone(rhs.d->component()));
744 return d->lastModified;
749 const QByteArray result(icalcomponent_as_ical_string(d->component()));
750 icalmemory_free_ring();
756 icaltimezone *icaltz = icaltimezone_new();
760 icalcomponent *c = icalcomponent_new_clone(d->component());
761 if (!icaltimezone_set_component(icaltz, c)) {
762 icalcomponent_free(c);
763 icaltimezone_free(icaltz, 1);
783 class ICalTimeZoneSourcePrivate
786 static QList<QDateTime> parsePhase(icalcomponent *,
bool daylight,
787 int &prevOffset, KTimeZone::Phase &);
788 static QByteArray icalTzidPrefix;
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);
796 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
800 : KTimeZoneSource(false),
812 QFile file(fileName);
813 if (!file.open(QIODevice::ReadOnly)) {
816 QTextStream ts(&file);
817 ts.setCodec(
"ISO 8859-1");
818 const QByteArray text = ts.readAll().trimmed().toLatin1();
822 icalcomponent *calendar = icalcomponent_new_from_string(text.data());
824 if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT) {
825 result =
parse(calendar, zones);
827 icalcomponent_free(calendar);
834 for (icalcomponent *c = icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT);
835 c; c = icalcomponent_get_next_component(calendar, ICAL_VTIMEZONE_COMPONENT)) {
837 if (!zone.isValid()) {
841 if (oldzone.isValid()) {
845 }
else if (!zones.
add(zone)) {
859 icalproperty *p = icalcomponent_get_first_property(vtimezone, ICAL_ANY_PROPERTY);
861 icalproperty_kind kind = icalproperty_isa(p);
864 case ICAL_TZID_PROPERTY:
865 name = QString::fromUtf8(icalproperty_get_tzid(p));
868 case ICAL_TZURL_PROPERTY:
869 data->d->
url = icalproperty_get_tzurl(p);
872 case ICAL_LOCATION_PROPERTY:
874 data->d->location = QString::fromUtf8(icalproperty_get_location(p));
877 case ICAL_X_PROPERTY:
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));
885 case ICAL_LASTMODIFIED_PROPERTY:
887 const icaltimetype t = icalproperty_get_lastmodified(p);
888 if (icaltime_is_utc( t )) {
891 kDebug() <<
"LAST-MODIFIED not UTC";
898 p = icalcomponent_get_next_property(vtimezone, ICAL_ANY_PROPERTY);
901 if (name.isEmpty()) {
902 kDebug() <<
"TZID missing";
906 if (data->d->location.isEmpty() && !xlocation.isEmpty()) {
907 data->d->location = xlocation;
910 if (name.startsWith(prefix)) {
912 const int i = name.indexOf(QLatin1Char(
'/'), prefix.length());
914 name = name.mid(i + 1);
924 QList<KTimeZone::Transition> transitions;
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)) {
930 KTimeZone::Phase phase;
931 QList<QDateTime> times;
932 icalcomponent_kind kind = icalcomponent_isa(c);
935 case ICAL_XSTANDARD_COMPONENT:
937 times = ICalTimeZoneSourcePrivate::parsePhase(c,
false, prevoff, phase);
940 case ICAL_XDAYLIGHT_COMPONENT:
942 times = ICalTimeZoneSourcePrivate::parsePhase(c,
true, prevoff, phase);
946 kDebug() <<
"Unknown component:" << int(kind);
949 const int tcount = times.count();
952 for (
int t = 0; t < tcount; ++t) {
953 transitions += KTimeZone::Transition(times[t], phase);
955 if (!earliest.isValid() || times[0] < earliest) {
956 prevOffset = prevoff;
963 data->setPhases(phases, prevOffset);
967 for (
int t = 1, tend = transitions.count(); t < tend;) {
968 if (transitions[t].phase() == transitions[t - 1].phase()) {
969 transitions.removeAt(t);
975 data->setTransitions(transitions);
977 data->d->setComponent(icalcomponent_new_clone(vtimezone));
982 #if defined(HAVE_UUID_UUID_H)
986 if (!zone.isValid()) {
990 if (oldzone.isValid()) {
994 }
else if (zones.
add(zone)) {
1008 uuid_generate_random(uuid);
1009 uuid_unparse(uuid, suuid);
1010 QString name = QString::fromLatin1(suuid);
1013 QList<KTimeZone::Phase> phases;
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;
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;
1033 const int prevOffset = tz->Bias * -60;
1034 kdata.setPhases(phases, prevOffset);
1037 QList<KTimeZone::Transition> transitions;
1038 ICalTimeZoneSourcePrivate::parseTransitions(
1039 tz->StandardDate, standardPhase, prevOffset, transitions);
1040 ICalTimeZoneSourcePrivate::parseTransitions(
1041 tz->DaylightDate, daylightPhase, prevOffset, transitions);
1044 kdata.setTransitions(transitions);
1050 #endif // HAVE_UUID_UUID_H
1056 if (!zone.isValid()) {
1062 if (oldzone.isValid()) {
1066 oldzone = zones.
zone(name);
1067 if (oldzone.isValid()) {
1071 }
else if (zones.
add(zone)) {
1081 QList<KTimeZone::Phase> phases;
1082 QList<KTimeZone::Transition> transitions;
1085 for (QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it) {
1086 QString value = *it;
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")) {
1098 const KTimeZone::Phase tzPhase(
1100 QByteArray(tzName.toLatin1()), daylight, QLatin1String(
"VCAL_TZ_INFORMATION"));
1102 transitions += KTimeZone::Transition(tzDate.dateTime(), tzPhase);
1105 kdata.setPhases(phases, 0);
1107 kdata.setTransitions(transitions);
1113 #if defined(HAVE_UUID_UUID_H)
1115 void ICalTimeZoneSourcePrivate::parseTransitions(
const MSSystemTime &date,
1116 const KTimeZone::Phase &phase,
int prevOffset,
1117 QList<KTimeZone::Transition> &transitions)
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());
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);
1139 if (date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1140 date.wMonth >= 1 && date.wMonth <= 12 &&
1141 date.wDay >= 1 && date.wDay <= 5) {
1143 r.setRecurrenceType(RecurrenceRule::rYearly);
1147 lst.append(date.wMonth);
1149 QList<RecurrenceRule::WDayPos> wdlst;
1151 pos.setDay(date.wDayOfWeek ? date.wDayOfWeek : 7);
1152 pos.setPos(date.wDay < 5 ? date.wDay : -1);
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);
1167 #endif // HAVE_UUID_UUID_H
1179 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase(icalcomponent *c,
1182 KTimeZone::Phase &phase)
1184 QList<QDateTime> transitions;
1187 QList<QByteArray> abbrevs;
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();
1198 icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1200 icalproperty_kind kind = icalproperty_isa(p);
1203 case ICAL_TZNAME_PROPERTY:
1209 QByteArray tzname = icalproperty_get_tzname(p);
1212 if ((!daylight && tzname ==
"Standard Time") ||
1213 (daylight && tzname ==
"Daylight Time")) {
1216 if (!abbrevs.contains(tzname)) {
1221 case ICAL_DTSTART_PROPERTY:
1222 dtstart = icalproperty_get_dtstart(p);
1223 found_dtstart =
true;
1226 case ICAL_TZOFFSETFROM_PROPERTY:
1227 prevOffset = icalproperty_get_tzoffsetfrom(p);
1228 found_tzoffsetfrom =
true;
1231 case ICAL_TZOFFSETTO_PROPERTY:
1232 utcOffset = icalproperty_get_tzoffsetto(p);
1233 found_tzoffsetto =
true;
1236 case ICAL_COMMENT_PROPERTY:
1237 comment = QString::fromUtf8(icalproperty_get_comment(p));
1240 case ICAL_RDATE_PROPERTY:
1241 case ICAL_RRULE_PROPERTY:
1246 kDebug() <<
"Unknown property:" << int(kind);
1249 p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1253 if (!found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto) {
1254 kDebug() <<
"DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1259 const QDateTime localStart = toQDateTime(dtstart);
1260 dtstart.second -= prevOffset;
1261 dtstart.zone = icaltimezone_get_utc_timezone();
1262 const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart));
1264 transitions += utcStart;
1271 const KDateTime klocalStart(localStart, KDateTime::Spec::ClockTime());
1272 const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1274 icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1276 icalproperty_kind kind = icalproperty_isa(p);
1279 case ICAL_RDATE_PROPERTY:
1281 icaltimetype t = icalproperty_get_rdate(p).time;
1282 if (icaltime_is_date(t)) {
1284 t.hour = dtstart.hour;
1285 t.minute = dtstart.minute;
1286 t.second = dtstart.second;
1292 if (!icaltime_is_utc( t )) {
1293 t.second -= prevOffset;
1294 t.zone = icaltimezone_get_utc_timezone();
1295 t = icaltime_normalize(t);
1297 transitions += toQDateTime(t);
1300 case ICAL_RRULE_PROPERTY:
1305 impl.readRecurrence(icalproperty_get_rrule(p), &r);
1310 KDateTime end(r.
endDt());
1311 if (end.timeSpec() == KDateTime::Spec::UTC()) {
1312 end.setTimeSpec(KDateTime::Spec::ClockTime());
1313 r.
setEndDt(end.addSecs(prevOffset));
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);
1327 p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1329 qSortUnique(transitions);
1332 phase = KTimeZone::Phase(utcOffset, abbrevs, daylight, comment);
1343 QString tzid = zone;
1345 if (zone.startsWith(prefix)) {
1346 const int i = zone.indexOf(QLatin1Char(
'/'), prefix.length());
1348 tzid = zone.mid(i + 1);
1351 const KTimeZone ktz = KSystemTimeZones::readZone(tzid);
1352 if (ktz.isValid()) {
1353 if (ktz.data(
true)) {
1362 const QByteArray zoneName = zone.toUtf8();
1363 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(zoneName);
1366 icaltz = icaltimezone_get_builtin_timezone_from_tzid(zoneName);
1371 return parse(icaltz);
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);
1382 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left(i + 1);
1383 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1386 kError() <<
"failed to get libical TZID prefix";
1388 return ICalTimeZoneSourcePrivate::icalTzidPrefix;