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>
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),
873 QFile file(fileName);
874 if (!file.open(QIODevice::ReadOnly)) {
877 QTextStream ts(&file);
878 ts.setCodec(
"ISO 8859-1");
879 const QByteArray text = ts.readAll().trimmed().toLatin1();
883 icalcomponent *calendar = icalcomponent_new_from_string(text.data());
885 if (icalcomponent_isa(calendar) == ICAL_VCALENDAR_COMPONENT) {
886 result =
parse(calendar, zones);
888 icalcomponent_free(calendar);
895 for (icalcomponent *c = icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT);
896 c; c = icalcomponent_get_next_component(calendar, ICAL_VTIMEZONE_COMPONENT)) {
898 if (!zone.isValid()) {
902 if (oldzone.isValid()) {
906 }
else if (!zones.
add(zone)) {
920 icalproperty *p = icalcomponent_get_first_property(vtimezone, ICAL_ANY_PROPERTY);
922 icalproperty_kind kind = icalproperty_isa(p);
925 case ICAL_TZID_PROPERTY:
926 name = QString::fromUtf8(icalproperty_get_tzid(p));
929 case ICAL_TZURL_PROPERTY:
930 data->d->
url = icalproperty_get_tzurl(p);
933 case ICAL_LOCATION_PROPERTY:
935 data->d->location = QString::fromUtf8(icalproperty_get_location(p));
938 case ICAL_X_PROPERTY:
940 const char *xname = icalproperty_get_x_name(p);
941 if (xname && !strcmp(xname,
"X-LIC-LOCATION")) {
942 xlocation = QString::fromUtf8(icalproperty_get_x(p));
946 case ICAL_LASTMODIFIED_PROPERTY:
948 const icaltimetype t = icalproperty_get_lastmodified(p);
952 kDebug() <<
"LAST-MODIFIED not UTC";
959 p = icalcomponent_get_next_property(vtimezone, ICAL_ANY_PROPERTY);
962 if (name.isEmpty()) {
963 kDebug() <<
"TZID missing";
967 if (data->d->location.isEmpty() && !xlocation.isEmpty()) {
968 data->d->location = xlocation;
971 if (name.startsWith(prefix)) {
973 const int i = name.indexOf(QLatin1Char(
'/'), prefix.length());
975 name = name.mid(i + 1);
985 QList<KTimeZone::Transition> transitions;
987 QList<KTimeZone::Phase> phases;
988 for (icalcomponent *c = icalcomponent_get_first_component(vtimezone, ICAL_ANY_COMPONENT);
989 c; c = icalcomponent_get_next_component(vtimezone, ICAL_ANY_COMPONENT)) {
991 KTimeZone::Phase phase;
992 QList<QDateTime> times;
993 icalcomponent_kind kind = icalcomponent_isa(c);
996 case ICAL_XSTANDARD_COMPONENT:
998 times = ICalTimeZoneSourcePrivate::parsePhase(c,
false, prevoff, phase);
1001 case ICAL_XDAYLIGHT_COMPONENT:
1003 times = ICalTimeZoneSourcePrivate::parsePhase(c,
true, prevoff, phase);
1007 kDebug() <<
"Unknown component:" << int(kind);
1010 const int tcount = times.count();
1013 for (
int t = 0; t < tcount; ++t) {
1014 transitions += KTimeZone::Transition(times[t], phase);
1016 if (!earliest.isValid() || times[0] < earliest) {
1017 prevOffset = prevoff;
1018 earliest = times[0];
1024 data->setPhases(phases, prevOffset);
1028 for (
int t = 1, tend = transitions.count(); t < tend;) {
1029 if (transitions[t].phase() == transitions[t - 1].phase()) {
1030 transitions.removeAt(t);
1036 data->setTransitions(transitions);
1038 data->d->setComponent(icalcomponent_new_clone(vtimezone));
1043 #if defined(HAVE_UUID_UUID_H)
1047 if (!zone.isValid()) {
1051 if (oldzone.isValid()) {
1055 }
else if (zones.
add(zone)) {
1069 uuid_generate_random(uuid);
1070 uuid_unparse(uuid, suuid);
1071 QString name = QString::fromLatin1(suuid);
1074 QList<KTimeZone::Phase> phases;
1076 QList<QByteArray> standardAbbrevs;
1077 standardAbbrevs += tz->StandardName.toLatin1();
1078 const KTimeZone::Phase standardPhase(
1079 (tz->Bias + tz->StandardBias) * -60,
1080 standardAbbrevs,
false,
1081 QLatin1String(
"Microsoft TIME_ZONE_INFORMATION"));
1082 phases += standardPhase;
1084 QList<QByteArray> daylightAbbrevs;
1085 daylightAbbrevs += tz->DaylightName.toLatin1();
1086 const KTimeZone::Phase daylightPhase(
1087 (tz->Bias + tz->DaylightBias) * -60,
1088 daylightAbbrevs,
true,
1089 QLatin1String(
"Microsoft TIME_ZONE_INFORMATION"));
1090 phases += daylightPhase;
1094 const int prevOffset = tz->Bias * -60;
1095 kdata.setPhases(phases, prevOffset);
1098 QList<KTimeZone::Transition> transitions;
1099 ICalTimeZoneSourcePrivate::parseTransitions(
1100 tz->StandardDate, standardPhase, prevOffset, transitions);
1101 ICalTimeZoneSourcePrivate::parseTransitions(
1102 tz->DaylightDate, daylightPhase, prevOffset, transitions);
1105 kdata.setTransitions(transitions);
1111 #endif // HAVE_UUID_UUID_H
1117 if (!zone.isValid()) {
1123 if (oldzone.isValid()) {
1127 oldzone = zones.
zone(name);
1128 if (oldzone.isValid()) {
1132 }
else if (zones.
add(zone)) {
1142 QList<KTimeZone::Phase> phases;
1143 QList<KTimeZone::Transition> transitions;
1146 for (QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it) {
1147 QString value = *it;
1149 const QString tzName = value.mid(0, value.indexOf(QLatin1String(
";")));
1150 value = value.mid((value.indexOf(QLatin1String(
";")) + 1));
1151 const QString tzOffset = value.mid(0, value.indexOf(QLatin1String(
";")));
1152 value = value.mid((value.indexOf(QLatin1String(
";")) + 1));
1153 const QString tzDaylight = value.mid(0, value.indexOf(QLatin1String(
";")));
1154 const KDateTime tzDate = KDateTime::fromString(value.mid((value.lastIndexOf(QLatin1String(
";")) + 1)));
1155 if (tzDaylight == QLatin1String(
"true")) {
1159 const KTimeZone::Phase tzPhase(
1161 QByteArray(tzName.toLatin1()), daylight, QLatin1String(
"VCAL_TZ_INFORMATION"));
1163 transitions += KTimeZone::Transition(tzDate.dateTime(), tzPhase);
1166 kdata.setPhases(phases, 0);
1168 kdata.setTransitions(transitions);
1174 #if defined(HAVE_UUID_UUID_H)
1176 void ICalTimeZoneSourcePrivate::parseTransitions(
const MSSystemTime &date,
1177 const KTimeZone::Phase &phase,
int prevOffset,
1178 QList<KTimeZone::Transition> &transitions)
1182 const KDateTime klocalStart(QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0)),
1183 KDateTime::Spec::ClockTime());
1184 const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1188 if (date.wYear >= 1601 && date.wYear <= 30827 &&
1189 date.wMonth >= 1 && date.wMonth <= 12 &&
1190 date.wDay >= 1 && date.wDay <= 31) {
1191 const QDate dt(date.wYear, date.wMonth, date.wDay);
1192 const QTime tm(date.wHour, date.wMinute, date.wSecond, date.wMilliseconds);
1193 const QDateTime datetime(dt, tm);
1194 if (datetime.isValid()) {
1195 transitions += KTimeZone::Transition(datetime, phase);
1200 if (date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1201 date.wMonth >= 1 && date.wMonth <= 12 &&
1202 date.wDay >= 1 && date.wDay <= 5) {
1204 r.setRecurrenceType(RecurrenceRule::rYearly);
1208 lst.append(date.wMonth);
1210 QList<RecurrenceRule::WDayPos> wdlst;
1212 pos.setDay(date.wDayOfWeek ? date.wDayOfWeek : 7);
1213 pos.setPos(date.wDay < 5 ? date.wDay : -1);
1219 for (
int i = 0, end = dtl.count(); i < end; ++i) {
1220 QDateTime utc = dtl[i].dateTime();
1221 utc.setTimeSpec(Qt::UTC);
1222 transitions += KTimeZone::Transition(utc.addSecs(-prevOffset), phase);
1228 #endif // HAVE_UUID_UUID_H
1240 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase(icalcomponent *c,
1243 KTimeZone::Phase &phase)
1245 QList<QDateTime> transitions;
1248 QList<QByteArray> abbrevs;
1252 bool recurs =
false;
1253 bool found_dtstart =
false;
1254 bool found_tzoffsetfrom =
false;
1255 bool found_tzoffsetto =
false;
1256 icaltimetype dtstart = icaltime_null_time();
1259 icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1261 icalproperty_kind kind = icalproperty_isa(p);
1264 case ICAL_TZNAME_PROPERTY:
1270 QByteArray tzname = icalproperty_get_tzname(p);
1273 if ((!daylight && tzname ==
"Standard Time") ||
1274 (daylight && tzname ==
"Daylight Time")) {
1277 if (!abbrevs.contains(tzname)) {
1282 case ICAL_DTSTART_PROPERTY:
1283 dtstart = icalproperty_get_dtstart(p);
1284 found_dtstart =
true;
1287 case ICAL_TZOFFSETFROM_PROPERTY:
1288 prevOffset = icalproperty_get_tzoffsetfrom(p);
1289 found_tzoffsetfrom =
true;
1292 case ICAL_TZOFFSETTO_PROPERTY:
1293 utcOffset = icalproperty_get_tzoffsetto(p);
1294 found_tzoffsetto =
true;
1297 case ICAL_COMMENT_PROPERTY:
1298 comment = QString::fromUtf8(icalproperty_get_comment(p));
1301 case ICAL_RDATE_PROPERTY:
1302 case ICAL_RRULE_PROPERTY:
1307 kDebug() <<
"Unknown property:" << int(kind);
1310 p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1314 if (!found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto) {
1315 kDebug() <<
"DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1320 const QDateTime localStart = toQDateTime(dtstart);
1321 dtstart.second -= prevOffset;
1323 const QDateTime utcStart = toQDateTime(icaltime_normalize(dtstart));
1325 transitions += utcStart;
1332 const KDateTime klocalStart(localStart, KDateTime::Spec::ClockTime());
1333 const KDateTime maxTime(MAX_DATE(), KDateTime::Spec::ClockTime());
1335 icalproperty *p = icalcomponent_get_first_property(c, ICAL_ANY_PROPERTY);
1337 icalproperty_kind kind = icalproperty_isa(p);
1340 case ICAL_RDATE_PROPERTY:
1342 icaltimetype t = icalproperty_get_rdate(p).time;
1343 if (icaltime_is_date(t)) {
1345 t.hour = dtstart.hour;
1346 t.minute = dtstart.minute;
1347 t.second = dtstart.second;
1354 t.second -= prevOffset;
1356 t = icaltime_normalize(t);
1358 transitions += toQDateTime(t);
1361 case ICAL_RRULE_PROPERTY:
1366 impl.readRecurrence(icalproperty_get_rrule(p), &r);
1371 KDateTime end(r.
endDt());
1372 if (end.timeSpec() == KDateTime::Spec::UTC()) {
1373 end.setTimeSpec(KDateTime::Spec::ClockTime());
1374 r.
setEndDt(end.addSecs(prevOffset));
1378 for (
int i = 0, end = dts.count(); i < end; ++i) {
1379 QDateTime utc = dts[i].dateTime();
1380 utc.setTimeSpec(Qt::UTC);
1381 transitions += utc.addSecs(-prevOffset);
1388 p = icalcomponent_get_next_property(c, ICAL_ANY_PROPERTY);
1390 qSortUnique(transitions);
1393 phase = KTimeZone::Phase(utcOffset, abbrevs, daylight, comment);
1404 QString tzid = zone;
1406 if (zone.startsWith(prefix)) {
1407 const int i = zone.indexOf(QLatin1Char(
'/'), prefix.length());
1409 tzid = zone.mid(i + 1);
1412 const KTimeZone ktz = KSystemTimeZones::readZone(tzid);
1413 if (ktz.isValid()) {
1414 if (ktz.data(
true)) {
1423 const QByteArray zoneName = zone.toUtf8();
1424 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(zoneName);
1427 icaltz = icaltimezone_get_builtin_timezone_from_tzid(zoneName);
1432 return parse(icaltz);
1437 if (ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty()) {
1438 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(
"Europe/London");
1439 const QByteArray tzid = icaltimezone_get_tzid(icaltz);
1440 if (tzid.right(13) ==
"Europe/London") {
1441 int i = tzid.indexOf(
'/', 1);
1443 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left(i + 1);
1444 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1447 kError() <<
"failed to get libical TZID prefix";
1449 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
virtual ~ICalTimeZone()
Destructor.
QString city() const
Returns the name of the city for this time zone, if any.
void clear()
Clears the collection.
ICalTimeZone remove(const ICalTimeZone &zone)
Removes a time zone from the collection.
virtual void virtual_hook(int id, void *data)
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
ICalTimeZone()
Constructs a null time zone.
structure for describing the n-th weekday of the month/year.
virtual void virtual_hook(int id, void *data)
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
virtual void virtual_hook(int id, void *data)
ICalTimeZoneBackend()
Implements ICalTimeZone::ICalTimeZone().
QString city() const
Returns the name of the city for this time zone, if any.
A class which reads and parses iCalendar VTIMEZONE components, and accesses libical time zone data...
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last...
ICalTimeZoneData()
Default constructor.
ICalTimeZoneSource()
Constructs an iCalendar time zone source.
virtual QByteArray type() const
Returns the class name of the data represented by this instance.
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
ICalTimeZoneData & operator=(const ICalTimeZoneData &rhs)
Assignment operator.
ICalTimeZone standardZone(const QString &zone, bool icalBuiltIn=false)
Creates an ICalTimeZone instance for a standard time zone.
~ICalTimeZones()
Destructor.
static ICalTimeZone utc()
Returns a standard UTC time zone, with name "UTC".
virtual bool hasTransitions() const
Return whether daylight saving transitions are available for the time zone.
void setEndDt(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
virtual ~ICalTimeZoneSource()
Destructor.
bool add(const ICalTimeZone &zone)
Adds a time zone to the collection.
This class represents a recurrence rule for a calendar incidence.
virtual bool hasTransitions(const KTimeZone *caller) const
Implements ICalTimeZone::hasTransitions().
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
A QList which can be sorted.
Parsed iCalendar VTIMEZONE data.
static QByteArray icalTzidPrefix()
Returns the prefix string used in the TZID field in built-in libical time zones.
ICalTimeZone parse(icalcomponent *vtimezone)
Creates an ICalTimeZone instance containing the detailed information parsed from an iCalendar VTIMEZO...
int count()
Returns the number of zones kept in memory.
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
virtual KTimeZoneBackend * clone() const
Creates a copy of this instance.
KDateTime endDt(bool *result=0) const
Returns the date and time of the last recurrence.
virtual KTimeZoneData * clone() const
Creates a new copy of this object.
bool update(const ICalTimeZone &other)
Update the definition of the time zone to be identical to another ICalTimeZone instance.
virtual ~ICalTimeZoneData()
Destructor.
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Backend class for KICalTimeZone class.
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
ICalTimeZones()
Constructs an empty time zone collection.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
The ICalTimeZones class represents a time zone database which consists of a collection of individual ...
The ICalTimeZone class represents an iCalendar VTIMEZONE component.
void setStartDt(const KDateTime &start)
Sets the recurrence start date/time.
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...
const ZoneMap zones() const
Returns all the time zones defined in this collection.
ICalTimeZones & operator=(const ICalTimeZones &rhs)
Assignment operator.
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
virtual void virtual_hook(int id, void *data)
ICalTimeZone zone(const QString &name) const
Returns the time zone with the given name.
Placeholhers for Microsoft and ActiveSync timezone data.
This class represents a recurrence rule for a calendar incidence.