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>
39 #include <icaltimezone.h>
42 #if defined(HAVE_UUID_UUID_H)
43 #include <uuid/uuid.h>
46 #if defined(Q_OS_WINCE)
49 using namespace KCalCore;
52 static const int minRuleCount = 5;
53 static const int minPhaseCount = 8;
56 static QDateTime toQDateTime(
const icaltimetype &t)
58 return QDateTime(QDate(t.year, t.month, t.day),
59 QTime(t.hour, t.minute, t.second),
60 (t.is_utc ? Qt::UTC : Qt::LocalTime));
66 static QDateTime MAX_DATE()
70 dt = QDateTime(QDate::currentDate().addYears(20), QTime(0, 0, 0));
75 static icaltimetype writeLocalICalDateTime(
const QDateTime &utc,
int offset)
77 const QDateTime local = utc.addSecs(offset);
78 icaltimetype t = icaltime_null_time();
79 t.year = local.date().year();
80 t.month = local.date().month();
81 t.day = local.date().day();
82 t.hour = local.time().hour();
83 t.minute = local.time().minute();
84 t.second = local.time().second();
96 class ICalTimeZonesPrivate
99 ICalTimeZonesPrivate() {}
100 ICalTimeZones::ZoneMap zones;
105 : d(new ICalTimeZonesPrivate)
110 : d(new ICalTimeZonesPrivate())
112 d->zones = rhs.d->
zones;
137 if (!zone.isValid()) {
140 if (d->zones.find(zone.name()) != d->zones.end()) {
144 d->zones.insert(zone.name(),
zone);
150 if (zone.isValid()) {
151 for (ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it) {
152 if (it.value() ==
zone) {
163 if (!name.isEmpty()) {
164 ZoneMap::Iterator it = d->zones.find(name);
165 if (it != d->zones.end()) {
181 return d->zones.count();
186 if (!name.isEmpty()) {
187 ZoneMap::ConstIterator it = d->zones.constFind(name);
188 if (it != d->zones.constEnd()) {
197 if (zone.isValid()) {
198 QMapIterator<QString, ICalTimeZone> it(d->zones);
199 while (it.hasNext()) {
202 const QList<KTimeZone::Transition> list1 = tz.transitions();
203 const QList<KTimeZone::Transition> list2 = zone.transitions();
204 if (list1.size() == list2.size()) {
207 for (; i < list1.size(); ++i) {
208 const KTimeZone::Transition t1 = list1[ i ];
209 const KTimeZone::Transition t2 = list2[ i ];
210 if ((t1.time() == t2.time()) &&
211 (t1.phase().utcOffset() == t2.phase().utcOffset()) &&
212 (t1.phase().isDst() == t2.phase().isDst())) {
234 const QString &countryCode,
235 float latitude,
float longitude,
236 const QString &comment)
237 : KTimeZoneBackend(source, name, countryCode, latitude, longitude, comment)
241 : KTimeZoneBackend(0, tz.name(), tz.countryCode(), tz.latitude(), tz.longitude(), tz.comment())
246 ICalTimeZoneBackend::~ICalTimeZoneBackend()
256 return "ICalTimeZone";
286 tz.latitude(), tz.longitude(),
289 const KTimeZoneData *data = tz.data(
true);
306 return dat ? dat->
city() : QString();
312 return dat ? dat->
url() : QByteArray();
324 return dat ? dat->
vtimezone() : QByteArray();
335 if (!updateBase(other)) {
339 KTimeZoneData *otherData = other.data() ? other.data()->clone() : 0;
340 setData(otherData, other.source());
347 if (!utcZone.isValid()) {
349 utcZone = tzs.
parse(icaltimezone_get_utc_timezone());
362 class ICalTimeZoneDataPrivate
365 ICalTimeZoneDataPrivate() : icalComponent(0) {}
367 ~ICalTimeZoneDataPrivate()
370 icalcomponent_free(icalComponent);
374 icalcomponent *component()
const {
375 return icalComponent;
377 void setComponent(icalcomponent *c)
380 icalcomponent_free(icalComponent);
387 QDateTime lastModified;
390 icalcomponent *icalComponent;
395 : d(new ICalTimeZoneDataPrivate())
400 : KTimeZoneData(rhs),
401 d(new ICalTimeZoneDataPrivate())
403 d->location = rhs.d->location;
406 d->setComponent(icalcomponent_new_clone(rhs.d->component()));
411 static QDate find_nth_weekday_in_month_of_year(
int nth,
int dayOfWeek,
int month,
int year) {
412 const QDate first(year, month, 1);
413 const int actualDayOfWeek = first.dayOfWeek();
414 QDate candidate = first.addDays((nth - 1) * 7 + dayOfWeek - actualDayOfWeek);
416 if (candidate.month() != month) {
417 candidate = candidate.addDays(-7);
425 const KTimeZone &tz,
const QDate &earliest)
426 : KTimeZoneData(rhs),
427 d(new ICalTimeZoneDataPrivate())
432 WEEKDAY_OF_MONTH = 0x02,
433 LAST_WEEKDAY_OF_MONTH = 0x04
436 if (tz.type() ==
"KSystemTimeZone") {
440 icalcomponent *c = 0;
441 const KTimeZone ktz = KSystemTimeZones::readZone(tz.name());
443 if (ktz.data(
true)) {
447 c = icalcomponent_new_clone(icaltimezone_get_component(itz));
448 icaltimezone_free(itz, 1);
454 icaltimezone *itz = icaltimezone_get_builtin_timezone(tz.name().toUtf8());
455 c = icalcomponent_new_clone(icaltimezone_get_component(itz));
461 icalproperty *prop = icalcomponent_get_first_property(c, ICAL_TZID_PROPERTY);
463 icalvalue *value = icalproperty_get_value(prop);
464 const char *tzid = icalvalue_get_text(value);
466 const int len = icalprefix.size();
467 if (!strncmp(icalprefix, tzid, len)) {
468 const char *s = strchr(tzid + len,
'/');
470 const QByteArray tzidShort(s + 1);
471 icalvalue_set_text(value, tzidShort);
474 prop = icalcomponent_get_first_property(c, ICAL_X_PROPERTY);
475 const char *xname = icalproperty_get_x_name(prop);
476 if (xname && !strcmp(xname,
"X-LIC-LOCATION")) {
477 icalcomponent_remove_property(c, prop);
478 icalproperty_free(prop);
487 icalcomponent *tzcomp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
488 icalcomponent_add_property(tzcomp, icalproperty_new_tzid(tz.name().toUtf8()));
493 QList<KTimeZone::Transition> transits = transitions();
494 if (transits.isEmpty()) {
499 TIME_ZONE_INFORMATION currentTimeZone;
500 GetTimeZoneInformation(¤tTimeZone);
501 if (QString::fromWCharArray(currentTimeZone.StandardName) != tz.name()) {
502 kDebug() <<
"VTIMEZONE entry will be invalid for: " << tz.name();
504 const SYSTEMTIME std = currentTimeZone.StandardDate;
505 const SYSTEMTIME dlt = currentTimeZone.DaylightDate;
508 const KTimeZone::Phase standardPhase =
509 KTimeZone::Phase((currentTimeZone.Bias +
510 currentTimeZone.StandardBias) * -60,
511 QByteArray(),
false);
512 const KTimeZone::Phase daylightPhase =
513 KTimeZone::Phase((currentTimeZone.Bias +
514 currentTimeZone.DaylightBias) * -60,
518 for (
int i = 2000; i <= 2050; i++) {
519 const QDateTime standardTime =
520 QDateTime(find_nth_weekday_in_month_of_year(
522 std.wDayOfWeek ? std.wDayOfWeek : 7,
524 QTime(std.wHour, std.wMinute,
525 std.wSecond, std.wMilliseconds));
527 const QDateTime daylightTime =
528 QDateTime(find_nth_weekday_in_month_of_year(
530 dlt.wDayOfWeek ? dlt.wDayOfWeek : 7,
532 QTime(dlt.wHour, dlt.wMinute,
533 dlt.wSecond, dlt.wMilliseconds));
535 transits << KTimeZone::Transition(standardTime, standardPhase)
536 << KTimeZone::Transition(daylightTime, daylightPhase);
540 if (transits.isEmpty()) {
541 kDebug() <<
"No transition information available VTIMEZONE will be invalid.";
544 if (earliest.isValid()) {
546 for (
int i = 0, end = transits.count(); i < end; ++i) {
547 if (transits.at(i).time().date() >= earliest) {
549 transits.erase(transits.begin(), transits.begin() + i);
555 int trcount = transits.count();
556 QVector<bool> transitionsDone(trcount);
557 transitionsDone.fill(
false);
561 icaldatetimeperiodtype dtperiod;
562 dtperiod.period = icalperiodtype_null_period();
565 for (; i < trcount && transitionsDone[i]; ++i) {
572 const int preOffset = (i > 0) ?
573 transits.at(i - 1).phase().utcOffset() :
574 rhs.previousUtcOffset();
575 const KTimeZone::Phase phase = transits.at(i).phase();
576 if (phase.utcOffset() == preOffset) {
577 transitionsDone[i] =
true;
578 while (++i < trcount) {
579 if (transitionsDone[i] ||
580 transits.at(i).phase() != phase ||
581 transits.at(i - 1).phase().utcOffset() != preOffset) {
584 transitionsDone[i] =
true;
588 icalcomponent *phaseComp =
589 icalcomponent_new(phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT);
590 const QList<QByteArray> abbrevs = phase.abbreviations();
591 for (
int a = 0, aend = abbrevs.count(); a < aend; ++a) {
592 icalcomponent_add_property(phaseComp,
593 icalproperty_new_tzname(
594 static_cast<const char*>(abbrevs[a])));
596 if (!phase.comment().isEmpty()) {
597 icalcomponent_add_property(phaseComp,
598 icalproperty_new_comment(phase.comment().toUtf8()));
600 icalcomponent_add_property(phaseComp,
601 icalproperty_new_tzoffsetfrom(preOffset));
602 icalcomponent_add_property(phaseComp,
603 icalproperty_new_tzoffsetto(phase.utcOffset()));
605 icalcomponent *phaseComp1 = icalcomponent_new_clone(phaseComp);
606 icalcomponent_add_property(phaseComp1,
607 icalproperty_new_dtstart(
608 writeLocalICalDateTime(transits.at(i).time(),
610 bool useNewRRULE =
false;
616 int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0;
618 int nthFromStart = 0;
622 QList<QDateTime> rdates;
623 QList<QDateTime> times;
624 QDateTime qdt = transits.at(i).time();
626 transitionsDone[i] =
true;
630 rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
634 month = date.month();
635 daysInMonth = date.daysInMonth();
636 dayOfWeek = date.dayOfWeek();
637 dayOfMonth = date.day();
638 nthFromStart = (dayOfMonth - 1) / 7 + 1;
639 nthFromEnd = (daysInMonth - dayOfMonth) / 7 + 1;
641 if (++i >= trcount) {
643 times += QDateTime();
645 if (transitionsDone[i] ||
646 transits.at(i).phase() != phase ||
647 transits.at(i - 1).phase().utcOffset() != preOffset) {
650 transitionsDone[i] =
true;
651 qdt = transits.at(i).time();
652 if (!qdt.isValid()) {
658 if (qdt.time() != time ||
659 date.month() != month ||
660 date.year() != ++year) {
663 const int day = date.day();
664 if ((newRule & DAY_OF_MONTH) && day != dayOfMonth) {
665 newRule &= ~DAY_OF_MONTH;
667 if (newRule & (WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH)) {
668 if (date.dayOfWeek() != dayOfWeek) {
669 newRule &= ~(WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH);
671 if ((newRule & WEEKDAY_OF_MONTH) &&
672 (day - 1) / 7 + 1 != nthFromStart) {
673 newRule &= ~WEEKDAY_OF_MONTH;
675 if ((newRule & LAST_WEEKDAY_OF_MONTH) &&
676 (daysInMonth - day) / 7 + 1 != nthFromEnd) {
677 newRule &= ~LAST_WEEKDAY_OF_MONTH;
687 int yr = times[0].date().year();
688 while (!rdates.isEmpty()) {
691 if (qdt.time() != time ||
692 date.month() != month ||
693 date.year() != --yr) {
696 const int day = date.day();
697 if (rule & DAY_OF_MONTH) {
698 if (day != dayOfMonth) {
702 if (date.dayOfWeek() != dayOfWeek ||
703 ((rule & WEEKDAY_OF_MONTH) &&
704 (day - 1) / 7 + 1 != nthFromStart) ||
705 ((rule & LAST_WEEKDAY_OF_MONTH) &&
706 (daysInMonth - day) / 7 + 1 != nthFromEnd)) {
713 if (times.count() > (useNewRRULE ? minPhaseCount : minRuleCount)) {
715 icalrecurrencetype r;
716 icalrecurrencetype_clear(&r);
717 r.freq = ICAL_YEARLY_RECURRENCE;
718 r.count = (year >= 2030) ? 0 : times.count() - 1;
719 r.by_month[0] = month;
720 if (rule & DAY_OF_MONTH) {
721 r.by_month_day[0] = dayOfMonth;
722 }
else if (rule & WEEKDAY_OF_MONTH) {
723 r.by_day[0] = (dayOfWeek % 7 + 1) + (nthFromStart * 8);
724 }
else if (rule & LAST_WEEKDAY_OF_MONTH) {
725 r.by_day[0] = -(dayOfWeek % 7 + 1) - (nthFromEnd * 8);
727 icalproperty *prop = icalproperty_new_rrule(r);
731 icalcomponent *c = icalcomponent_new_clone(phaseComp);
732 icalcomponent_add_property(
733 c, icalproperty_new_dtstart(writeLocalICalDateTime(times[0], preOffset)));
734 icalcomponent_add_property(c, prop);
735 icalcomponent_add_component(tzcomp, c);
737 icalcomponent_add_property(phaseComp1, prop);
741 for (
int t = 0, tend = times.count() - 1; t < tend; ++t) {
753 }
while (i < trcount);
756 for (
int rd = 0, rdend = rdates.count(); rd < rdend; ++rd) {
757 dtperiod.time = writeLocalICalDateTime(rdates[rd], preOffset);
758 icalcomponent_add_property(phaseComp1, icalproperty_new_rdate(dtperiod));
760 icalcomponent_add_component(tzcomp, phaseComp1);
761 icalcomponent_free(phaseComp);
764 d->setComponent(tzcomp);
780 KTimeZoneData::operator=(rhs);
781 d->location = rhs.d->location;
784 d->setComponent(icalcomponent_new_clone(rhs.d->component()));
805 return d->lastModified;
810 const QByteArray result(icalcomponent_as_ical_string(d->component()));
811 icalmemory_free_ring();
817 icaltimezone *icaltz = icaltimezone_new();
821 icalcomponent *c = icalcomponent_new_clone(d->component());
822 if (!icaltimezone_set_component(icaltz, c)) {
823 icalcomponent_free(c);
824 icaltimezone_free(icaltz, 1);
844 class ICalTimeZoneSourcePrivate
847 static QList<QDateTime> parsePhase(icalcomponent *,
bool daylight,
848 int &prevOffset, KTimeZone::Phase &);
849 static QByteArray icalTzidPrefix;
851 #if defined(HAVE_UUID_UUID_H)
852 static void parseTransitions(
const MSSystemTime &date,
const KTimeZone::Phase &phase,
853 int prevOffset, QList<KTimeZone::Transition> &transitions);
857 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
861 : KTimeZoneSource(false),
872 QFile file(fileName);
873 if (!file.open(QIODevice::ReadOnly)) {
876 QTextStream ts(&file);
877 ts.setCodec(
"ISO 8859-1");
878 const QByteArray text = ts.readAll().trimmed().toLatin1();
882 icalcomponent *calendar = icalcomponent_new_from_string(text.data());
884 if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT) {
885 result =
parse(calendar, zones);
887 icalcomponent_free(calendar);
894 for (icalcomponent *c = icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT);
895 c; c = icalcomponent_get_next_component(calendar, ICAL_VTIMEZONE_COMPONENT)) {
897 if (!zone.isValid()) {
901 if (oldzone.isValid()) {
905 }
else if (!zones.
add(zone)) {
919 icalproperty *p = icalcomponent_get_first_property(vtimezone, ICAL_ANY_PROPERTY);
921 icalproperty_kind kind = icalproperty_isa(p);
924 case ICAL_TZID_PROPERTY:
925 name = QString::fromUtf8(icalproperty_get_tzid(p));
928 case ICAL_TZURL_PROPERTY:
929 data->d->
url = icalproperty_get_tzurl(p);
932 case ICAL_LOCATION_PROPERTY:
934 data->d->location = QString::fromUtf8(icalproperty_get_location(p));
937 case ICAL_X_PROPERTY:
939 const char *xname = icalproperty_get_x_name(p);
940 if (xname && !strcmp(xname,
"X-LIC-LOCATION")) {
941 xlocation = QString::fromUtf8(icalproperty_get_x(p));
945 case ICAL_LASTMODIFIED_PROPERTY:
947 const icaltimetype t = icalproperty_get_lastmodified(p);
951 kDebug() <<
"LAST-MODIFIED not UTC";
958 p = icalcomponent_get_next_property(vtimezone, ICAL_ANY_PROPERTY);
961 if (name.isEmpty()) {
962 kDebug() <<
"TZID missing";
966 if (data->d->location.isEmpty() && !xlocation.isEmpty()) {
967 data->d->location = xlocation;
970 if (name.startsWith(prefix)) {
972 const int i = name.indexOf(
'/', prefix.length());
974 name = name.mid(i + 1);
984 QList<KTimeZone::Transition> transitions;
986 QList<KTimeZone::Phase> phases;
987 for (icalcomponent *c = icalcomponent_get_first_component(vtimezone, ICAL_ANY_COMPONENT);
988 c; c = icalcomponent_get_next_component(vtimezone, ICAL_ANY_COMPONENT)) {
990 KTimeZone::Phase phase;
991 QList<QDateTime> times;
992 icalcomponent_kind kind = icalcomponent_isa(c);
995 case ICAL_XSTANDARD_COMPONENT:
997 times = ICalTimeZoneSourcePrivate::parsePhase(c,
false, prevoff, phase);
1000 case ICAL_XDAYLIGHT_COMPONENT:
1002 times = ICalTimeZoneSourcePrivate::parsePhase(c,
true, prevoff, phase);
1006 kDebug() <<
"Unknown component:" << int(kind);
1009 const int tcount = times.count();
1012 for (
int t = 0; t < tcount; ++t) {
1013 transitions += KTimeZone::Transition(times[t], phase);
1015 if (!earliest.isValid() || times[0] < earliest) {
1016 prevOffset = prevoff;
1017 earliest = times[0];
1023 data->setPhases(phases, prevOffset);
1027 for (
int t = 1, tend = transitions.count(); t < tend;) {
1028 if (transitions[t].phase() == transitions[t - 1].phase()) {
1029 transitions.removeAt(t);
1035 data->setTransitions(transitions);
1037 data->d->setComponent(icalcomponent_new_clone(vtimezone));
1042 #if defined(HAVE_UUID_UUID_H)
1046 if (!zone.isValid()) {
1050 if (oldzone.isValid()) {
1054 }
else if (zones.
add(zone)) {
1068 uuid_generate_random(uuid);
1069 uuid_unparse(uuid, suuid);
1070 QString name = QString(suuid);
1073 QList<KTimeZone::Phase> phases;
1075 QList<QByteArray> standardAbbrevs;
1076 standardAbbrevs += tz->StandardName.toLatin1();
1077 const KTimeZone::Phase standardPhase(
1078 (tz->Bias + tz->StandardBias) * -60,
1079 standardAbbrevs,
false,
1080 "Microsoft TIME_ZONE_INFORMATION");
1081 phases += standardPhase;
1083 QList<QByteArray> daylightAbbrevs;
1084 daylightAbbrevs += tz->DaylightName.toLatin1();
1085 const KTimeZone::Phase daylightPhase(
1086 (tz->Bias + tz->DaylightBias) * -60,
1087 daylightAbbrevs,
true,
1088 "Microsoft TIME_ZONE_INFORMATION");
1089 phases += daylightPhase;
1093 const int prevOffset = tz->Bias * -60;
1094 kdata.setPhases(phases, prevOffset);
1097 QList<KTimeZone::Transition> transitions;
1098 ICalTimeZoneSourcePrivate::parseTransitions(
1099 tz->StandardDate, standardPhase, prevOffset, transitions);
1100 ICalTimeZoneSourcePrivate::parseTransitions(
1101 tz->DaylightDate, daylightPhase, prevOffset, transitions);
1104 kdata.setTransitions(transitions);
1110 #endif // HAVE_UUID_UUID_H
1116 if (!zone.isValid()) {
1122 if (oldzone.isValid()) {
1126 oldzone = zones.
zone(name);
1127 if (oldzone.isValid()) {
1131 }
else if (zones.
add(zone)) {
1141 QList<KTimeZone::Phase> phases;
1142 QList<KTimeZone::Transition> transitions;
1145 for (QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it) {
1146 QString value = *it;
1148 const QString tzName = value.mid(0, value.indexOf(
";"));
1149 value = value.mid((value.indexOf(
";") + 1));
1150 const QString tzOffset = value.mid(0, value.indexOf(
";"));
1151 value = value.mid((value.indexOf(
";") + 1));
1152 const QString tzDaylight = value.mid(0, value.indexOf(
";"));
1153 const KDateTime tzDate = KDateTime::fromString(value.mid((value.lastIndexOf(
";") + 1)));
1154 if (tzDaylight ==
"true") {
1158 const KTimeZone::Phase tzPhase(
1160 QByteArray(tzName.toLatin1()), daylight,
"VCAL_TZ_INFORMATION");
1162 transitions += KTimeZone::Transition(tzDate.dateTime(), tzPhase);
1165 kdata.setPhases(phases, 0);
1167 kdata.setTransitions(transitions);
1173 #if defined(HAVE_UUID_UUID_H)
1175 void ICalTimeZoneSourcePrivate::parseTransitions(
const MSSystemTime &date,
1176 const KTimeZone::Phase &phase,
int prevOffset,
1177 QList<KTimeZone::Transition> &transitions)
1181 const KDateTime klocalStart(QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0)),
1182 KDateTime::Spec::ClockTime());
1183 const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1187 if (date.wYear >= 1601 && date.wYear <= 30827 &&
1188 date.wMonth >= 1 && date.wMonth <= 12 &&
1189 date.wDay >= 1 && date.wDay <= 31) {
1190 const QDate dt(date.wYear, date.wMonth, date.wDay);
1191 const QTime tm(date.wHour, date.wMinute, date.wSecond, date.wMilliseconds);
1192 const QDateTime datetime(dt, tm);
1193 if (datetime.isValid()) {
1194 transitions += KTimeZone::Transition(datetime, phase);
1199 if (date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1200 date.wMonth >= 1 && date.wMonth <= 12 &&
1201 date.wDay >= 1 && date.wDay <= 5) {
1203 r.setRecurrenceType(RecurrenceRule::rYearly);
1207 lst.append(date.wMonth);
1209 QList<RecurrenceRule::WDayPos> wdlst;
1211 pos.setDay(date.wDayOfWeek ? date.wDayOfWeek : 7);
1212 pos.setPos(date.wDay < 5 ? date.wDay : -1);
1218 for (
int i = 0, end = dtl.count(); i < end; ++i) {
1219 QDateTime utc = dtl[i].dateTime();
1220 utc.setTimeSpec(Qt::UTC);
1221 transitions += KTimeZone::Transition(utc.addSecs(-prevOffset), phase);
1227 #endif // HAVE_UUID_UUID_H
1239 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase(icalcomponent *c,
1242 KTimeZone::Phase &phase)
1244 QList<QDateTime> transitions;
1247 QList<QByteArray> abbrevs;
1251 bool recurs =
false;
1252 bool found_dtstart =
false;
1253 bool found_tzoffsetfrom =
false;
1254 bool found_tzoffsetto =
false;
1255 icaltimetype dtstart = icaltime_null_time();
1258 icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1260 icalproperty_kind kind = icalproperty_isa(p);
1263 case ICAL_TZNAME_PROPERTY:
1269 QByteArray tzname = icalproperty_get_tzname(p);
1272 if ((!daylight && tzname ==
"Standard Time") ||
1273 (daylight && tzname ==
"Daylight Time")) {
1276 if (!abbrevs.contains(tzname)) {
1281 case ICAL_DTSTART_PROPERTY:
1282 dtstart = icalproperty_get_dtstart(p);
1283 found_dtstart =
true;
1286 case ICAL_TZOFFSETFROM_PROPERTY:
1287 prevOffset = icalproperty_get_tzoffsetfrom(p);
1288 found_tzoffsetfrom =
true;
1291 case ICAL_TZOFFSETTO_PROPERTY:
1292 utcOffset = icalproperty_get_tzoffsetto(p);
1293 found_tzoffsetto =
true;
1296 case ICAL_COMMENT_PROPERTY:
1297 comment = QString::fromUtf8(icalproperty_get_comment(p));
1300 case ICAL_RDATE_PROPERTY:
1301 case ICAL_RRULE_PROPERTY:
1306 kDebug() <<
"Unknown property:" << int(kind);
1309 p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1313 if (!found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto) {
1314 kDebug() <<
"DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1319 const QDateTime localStart = toQDateTime(dtstart);
1320 dtstart.second -= prevOffset;
1322 const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart));
1324 transitions += utcStart;
1331 const KDateTime klocalStart(localStart, KDateTime::Spec::ClockTime());
1332 const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1334 icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1336 icalproperty_kind kind = icalproperty_isa(p);
1339 case ICAL_RDATE_PROPERTY:
1341 icaltimetype t = icalproperty_get_rdate(p).time;
1342 if (icaltime_is_date(t)) {
1344 t.hour = dtstart.hour;
1345 t.minute = dtstart.minute;
1346 t.second = dtstart.second;
1353 t.second -= prevOffset;
1355 t = icaltime_normalize(t);
1357 transitions += toQDateTime(t);
1360 case ICAL_RRULE_PROPERTY:
1365 impl.readRecurrence(icalproperty_get_rrule(p), &r);
1370 KDateTime end(r.
endDt());
1371 if (end.timeSpec() == KDateTime::Spec::UTC()) {
1372 end.setTimeSpec(KDateTime::Spec::ClockTime());
1373 r.
setEndDt(end.addSecs(prevOffset));
1377 for (
int i = 0, end = dts.count(); i < end; ++i) {
1378 QDateTime utc = dts[i].dateTime();
1379 utc.setTimeSpec(Qt::UTC);
1380 transitions += utc.addSecs(-prevOffset);
1387 p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1389 qSortUnique(transitions);
1392 phase = KTimeZone::Phase(utcOffset, abbrevs, daylight, comment);
1403 QString tzid = zone;
1405 if (zone.startsWith(prefix)) {
1406 const int i = zone.indexOf(
'/', prefix.length());
1408 tzid = zone.mid(i + 1);
1411 const KTimeZone ktz = KSystemTimeZones::readZone(tzid);
1412 if (ktz.isValid()) {
1413 if (ktz.data(
true)) {
1422 const QByteArray zoneName = zone.toUtf8();
1423 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(zoneName);
1426 icaltz = icaltimezone_get_builtin_timezone_from_tzid(zoneName);
1431 return parse(icaltz);
1436 if (ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty()) {
1437 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(
"Europe/London");
1438 const QByteArray tzid = icaltimezone_get_tzid(icaltz);
1439 if (tzid.right(13) ==
"Europe/London") {
1440 int i = tzid.indexOf(
'/', 1);
1442 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left(i + 1);
1443 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1446 kError() <<
"failed to get libical TZID prefix";
1448 return ICalTimeZoneSourcePrivate::icalTzidPrefix;