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 (icaltime_is_utc( t ) ? Qt::UTC : Qt::LocalTime));
66 static QDateTime MAX_DATE()
69 if ( !dt.isValid() ) {
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();
95 class ICalTimeZonesPrivate
98 ICalTimeZonesPrivate() {}
99 ICalTimeZones::ZoneMap zones;
104 : d( new ICalTimeZonesPrivate )
109 : d( new ICalTimeZonesPrivate() )
111 d->zones = rhs.d->
zones;
117 if ( &rhs ==
this ) {
136 if ( !zone.isValid() ) {
139 if ( d->zones.find( zone.name() ) != d->zones.end() ) {
143 d->zones.insert( zone.name(),
zone );
149 if ( zone.isValid() ) {
150 for ( ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it ) {
151 if ( it.value() ==
zone ) {
152 d->zones.erase( it );
162 if ( !name.isEmpty() ) {
163 ZoneMap::Iterator it = d->zones.find( name );
164 if ( it != d->zones.end() ) {
180 return d->zones.count();
185 if ( !name.isEmpty() ) {
186 ZoneMap::ConstIterator it = d->zones.constFind( name );
187 if ( it != d->zones.constEnd() ) {
196 if ( zone.isValid() ) {
197 QMapIterator<QString, ICalTimeZone> it(d->zones);
198 while ( it.hasNext() ) {
201 const QList<KTimeZone::Transition> list1 = tz.transitions();
202 const QList<KTimeZone::Transition> list2 = zone.transitions();
203 if ( list1.size() == list2.size() ) {
206 for ( ; i < list1.size(); ++i ) {
207 const KTimeZone::Transition t1 = list1[ i ];
208 const KTimeZone::Transition t2 = list2[ i ];
209 if ( ( t1.time() == t2.time() ) &&
210 ( t1.phase().utcOffset() == t2.phase().utcOffset() ) &&
211 ( t1.phase().isDst() == t2.phase().isDst() ) ) {
215 if ( matches == i ) {
233 const QString &countryCode,
234 float latitude,
float longitude,
235 const QString &comment )
236 : KTimeZoneBackend( source, name, countryCode, latitude, longitude, comment )
240 : KTimeZoneBackend( 0, tz.name(), tz.countryCode(), tz.latitude(), tz.longitude(), tz.comment() )
242 Q_UNUSED( earliest );
245 ICalTimeZoneBackend::~ICalTimeZoneBackend()
255 return "ICalTimeZone";
285 tz.latitude(), tz.longitude(),
288 const KTimeZoneData *data = tz.data(
true );
305 return dat ? dat->
city() : QString();
311 return dat ? dat->
url() : QByteArray();
323 return dat ? dat->
vtimezone() : QByteArray();
334 if ( !updateBase( other ) ) {
338 KTimeZoneData *otherData = other.data() ? other.data()->clone() : 0;
339 setData( otherData, other.source() );
346 if ( !utcZone.isValid() ) {
348 utcZone = tzs.
parse( icaltimezone_get_utc_timezone() );
361 class ICalTimeZoneDataPrivate
364 ICalTimeZoneDataPrivate() : icalComponent( 0 ) {}
366 ~ICalTimeZoneDataPrivate()
368 if ( icalComponent ) {
369 icalcomponent_free( icalComponent );
373 icalcomponent *component()
const {
return icalComponent; }
374 void setComponent( icalcomponent *c )
376 if ( icalComponent ) {
377 icalcomponent_free( icalComponent );
384 QDateTime lastModified;
387 icalcomponent *icalComponent;
392 : d ( new ICalTimeZoneDataPrivate() )
397 : KTimeZoneData( rhs ),
398 d( new ICalTimeZoneDataPrivate() )
400 d->location = rhs.d->location;
403 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
408 static QDate find_nth_weekday_in_month_of_year(
int nth,
int dayOfWeek,
int month,
int year ) {
409 const QDate first( year, month, 1 );
410 const int actualDayOfWeek = first.dayOfWeek();
411 QDate candidate = first.addDays( ( nth - 1 ) * 7 + dayOfWeek - actualDayOfWeek );
413 if ( candidate.month() != month ) {
414 candidate = candidate.addDays( -7 );
422 const KTimeZone &tz,
const QDate &earliest )
423 : KTimeZoneData( rhs ),
424 d( new ICalTimeZoneDataPrivate() )
429 WEEKDAY_OF_MONTH = 0x02,
430 LAST_WEEKDAY_OF_MONTH = 0x04
433 if ( tz.type() ==
"KSystemTimeZone" ) {
437 icalcomponent *c = 0;
438 const KTimeZone ktz = KSystemTimeZones::readZone( tz.name() );
439 if ( ktz.isValid() ) {
440 if ( ktz.data(
true ) ) {
444 c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
445 icaltimezone_free( itz, 1 );
451 icaltimezone *itz = icaltimezone_get_builtin_timezone( tz.name().toUtf8() );
452 c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
458 icalproperty *prop = icalcomponent_get_first_property( c, ICAL_TZID_PROPERTY );
460 icalvalue *value = icalproperty_get_value( prop );
461 const char *tzid = icalvalue_get_text( value );
463 const int len = icalprefix.size();
464 if ( !strncmp( icalprefix, tzid, len ) ) {
465 const char *s = strchr( tzid + len,
'/' );
467 const QByteArray tzidShort( s + 1 );
468 icalvalue_set_text( value, tzidShort );
471 prop = icalcomponent_get_first_property( c, ICAL_X_PROPERTY );
472 const char *xname = icalproperty_get_x_name( prop );
473 if ( xname && !strcmp( xname,
"X-LIC-LOCATION" ) ) {
474 icalcomponent_remove_property( c, prop );
480 d->setComponent( c );
483 icalcomponent *tzcomp = icalcomponent_new( ICAL_VTIMEZONE_COMPONENT );
484 icalcomponent_add_property( tzcomp, icalproperty_new_tzid( tz.name().toUtf8() ) );
489 QList<KTimeZone::Transition> transits = transitions();
490 if ( transits.isEmpty() ) {
495 TIME_ZONE_INFORMATION currentTimeZone;
496 GetTimeZoneInformation( ¤tTimeZone );
497 if ( QString::fromWCharArray( currentTimeZone.StandardName ) != tz.name() ) {
498 kDebug() <<
"VTIMEZONE entry will be invalid for: " << tz.name();
500 const SYSTEMTIME std = currentTimeZone.StandardDate;
501 const SYSTEMTIME dlt = currentTimeZone.DaylightDate;
504 const KTimeZone::Phase standardPhase =
505 KTimeZone::Phase( ( currentTimeZone.Bias +
506 currentTimeZone.StandardBias ) * -60,
507 QByteArray(),
false );
508 const KTimeZone::Phase daylightPhase =
509 KTimeZone::Phase( ( currentTimeZone.Bias +
510 currentTimeZone.DaylightBias ) * -60,
511 QByteArray(),
true );
514 for (
int i = 2000; i <= 2050; i++ ) {
515 const QDateTime standardTime =
516 QDateTime( find_nth_weekday_in_month_of_year(
518 std.wDayOfWeek ? std.wDayOfWeek : 7,
520 QTime( std.wHour, std.wMinute,
521 std.wSecond, std.wMilliseconds ) );
523 const QDateTime daylightTime =
524 QDateTime( find_nth_weekday_in_month_of_year(
526 dlt.wDayOfWeek ? dlt.wDayOfWeek : 7,
528 QTime( dlt.wHour, dlt.wMinute,
529 dlt.wSecond, dlt.wMilliseconds ) );
531 transits << KTimeZone::Transition( standardTime, standardPhase )
532 << KTimeZone::Transition( daylightTime, daylightPhase );
536 if ( transits.isEmpty() ) {
537 kDebug() <<
"No transition information available VTIMEZONE will be invalid.";
540 if ( earliest.isValid() ) {
542 for (
int i = 0, end = transits.count(); i < end; ++i ) {
543 if ( transits.at( i ).time().date() >= earliest ) {
545 transits.erase( transits.begin(), transits.begin() + i );
551 int trcount = transits.count();
552 QVector<bool> transitionsDone(trcount);
553 transitionsDone.fill(
false );
557 icaldatetimeperiodtype dtperiod;
558 dtperiod.period = icalperiodtype_null_period();
561 for ( ; i < trcount && transitionsDone[i]; ++i ) {
564 if ( i >= trcount ) {
568 const int preOffset = ( i > 0 ) ?
569 transits.at( i - 1 ).phase().utcOffset() :
570 rhs.previousUtcOffset();
571 const KTimeZone::Phase phase = transits.at( i ).phase();
572 if ( phase.utcOffset() == preOffset ) {
573 transitionsDone[i] =
true;
574 while ( ++i < trcount ) {
575 if ( transitionsDone[i] ||
576 transits.at( i ).phase() != phase ||
577 transits.at( i - 1 ).phase().utcOffset() != preOffset ) {
580 transitionsDone[i] =
true;
584 icalcomponent *phaseComp =
585 icalcomponent_new( phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT );
586 const QList<QByteArray> abbrevs = phase.abbreviations();
587 for (
int a = 0, aend = abbrevs.count(); a < aend; ++a ) {
588 icalcomponent_add_property( phaseComp,
589 icalproperty_new_tzname(
590 static_cast<const char*>( abbrevs[a]) ) );
592 if ( !phase.comment().isEmpty() ) {
593 icalcomponent_add_property( phaseComp,
594 icalproperty_new_comment( phase.comment().toUtf8() ) );
596 icalcomponent_add_property( phaseComp,
597 icalproperty_new_tzoffsetfrom( preOffset ) );
598 icalcomponent_add_property( phaseComp,
599 icalproperty_new_tzoffsetto( phase.utcOffset() ) );
601 icalcomponent *phaseComp1 = icalcomponent_new_clone( phaseComp );
602 icalcomponent_add_property( phaseComp1,
603 icalproperty_new_dtstart(
604 writeLocalICalDateTime( transits.at( i ).time(),
606 bool useNewRRULE =
false;
612 int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0;
614 int nthFromStart = 0;
618 QList<QDateTime> rdates;
619 QList<QDateTime> times;
620 QDateTime qdt = transits.at( i ).time();
622 transitionsDone[i] =
true;
626 rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
630 month = date.month();
631 daysInMonth = date.daysInMonth();
632 dayOfWeek = date.dayOfWeek();
633 dayOfMonth = date.day();
634 nthFromStart = ( dayOfMonth - 1 ) / 7 + 1;
635 nthFromEnd = ( daysInMonth - dayOfMonth ) / 7 + 1;
637 if ( ++i >= trcount ) {
639 times += QDateTime();
641 if ( transitionsDone[i] ||
642 transits.at( i ).phase() != phase ||
643 transits.at( i - 1 ).phase().utcOffset() != preOffset ) {
646 transitionsDone[i] =
true;
647 qdt = transits.at( i ).time();
648 if ( !qdt.isValid() ) {
654 if ( qdt.time() != time ||
655 date.month() != month ||
656 date.year() != ++year ) {
659 const int day = date.day();
660 if ( ( newRule & DAY_OF_MONTH ) && day != dayOfMonth ) {
661 newRule &= ~DAY_OF_MONTH;
663 if ( newRule & ( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH ) ) {
664 if ( date.dayOfWeek() != dayOfWeek ) {
665 newRule &= ~( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH );
667 if ( ( newRule & WEEKDAY_OF_MONTH ) &&
668 ( day - 1 ) / 7 + 1 != nthFromStart ) {
669 newRule &= ~WEEKDAY_OF_MONTH;
671 if ( ( newRule & LAST_WEEKDAY_OF_MONTH ) &&
672 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) {
673 newRule &= ~LAST_WEEKDAY_OF_MONTH;
683 int yr = times[0].date().year();
684 while ( !rdates.isEmpty() ) {
687 if ( qdt.time() != time ||
688 date.month() != month ||
689 date.year() != --yr ) {
692 const int day = date.day();
693 if ( rule & DAY_OF_MONTH ) {
694 if ( day != dayOfMonth ) {
698 if ( date.dayOfWeek() != dayOfWeek ||
699 ( ( rule & WEEKDAY_OF_MONTH ) &&
700 ( day - 1 ) / 7 + 1 != nthFromStart ) ||
701 ( ( rule & LAST_WEEKDAY_OF_MONTH ) &&
702 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) ) {
706 times.prepend( qdt );
709 if ( times.count() > ( useNewRRULE ? minPhaseCount : minRuleCount ) ) {
711 icalrecurrencetype r;
712 icalrecurrencetype_clear( &r );
713 r.freq = ICAL_YEARLY_RECURRENCE;
714 r.count = ( year >= 2030 ) ? 0 : times.count() - 1;
715 r.by_month[0] = month;
716 if ( rule & DAY_OF_MONTH ) {
717 r.by_month_day[0] = dayOfMonth;
718 }
else if ( rule & WEEKDAY_OF_MONTH ) {
719 r.by_day[0] = ( dayOfWeek % 7 + 1 ) + ( nthFromStart * 8 );
720 }
else if ( rule & LAST_WEEKDAY_OF_MONTH ) {
721 r.by_day[0] = -( dayOfWeek % 7 + 1 ) - ( nthFromEnd * 8 );
723 icalproperty *prop = icalproperty_new_rrule( r );
727 icalcomponent *c = icalcomponent_new_clone( phaseComp );
728 icalcomponent_add_property(
729 c, icalproperty_new_dtstart( writeLocalICalDateTime( times[0], preOffset ) ) );
730 icalcomponent_add_property( c, prop );
731 icalcomponent_add_component( tzcomp, c );
733 icalcomponent_add_property( phaseComp1, prop );
737 for (
int t = 0, tend = times.count() - 1; t < tend; ++t ) {
749 }
while ( i < trcount );
752 for (
int rd = 0, rdend = rdates.count(); rd < rdend; ++rd ) {
753 dtperiod.time = writeLocalICalDateTime( rdates[rd], preOffset );
754 icalcomponent_add_property( phaseComp1, icalproperty_new_rdate( dtperiod ) );
756 icalcomponent_add_component( tzcomp, phaseComp1 );
757 icalcomponent_free( phaseComp );
760 d->setComponent( tzcomp );
772 if ( &rhs ==
this ) {
776 KTimeZoneData::operator=( rhs );
777 d->location = rhs.d->location;
780 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
801 return d->lastModified;
806 const QByteArray result( icalcomponent_as_ical_string( d->component() ) );
807 icalmemory_free_ring();
813 icaltimezone *icaltz = icaltimezone_new();
817 icalcomponent *c = icalcomponent_new_clone( d->component() );
818 if ( !icaltimezone_set_component( icaltz, c ) ) {
819 icalcomponent_free( c );
820 icaltimezone_free( icaltz, 1 );
840 class ICalTimeZoneSourcePrivate
843 static QList<QDateTime> parsePhase( icalcomponent *,
bool daylight,
844 int &prevOffset, KTimeZone::Phase & );
845 static QByteArray icalTzidPrefix;
847 #if defined(HAVE_UUID_UUID_H)
848 static void parseTransitions(
const MSSystemTime &date,
const KTimeZone::Phase &phase,
849 int prevOffset, QList<KTimeZone::Transition> &transitions );
853 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
857 : KTimeZoneSource( false ),
868 QFile file( fileName );
869 if ( !file.open( QIODevice::ReadOnly ) ) {
872 QTextStream ts( &file );
873 ts.setCodec(
"ISO 8859-1" );
874 const QByteArray text = ts.readAll().trimmed().toLatin1();
878 icalcomponent *calendar = icalcomponent_new_from_string( text.data() );
880 if ( icalcomponent_isa( calendar ) == ICAL_VCALENDAR_COMPONENT ) {
881 result =
parse( calendar, zones );
883 icalcomponent_free( calendar );
890 for ( icalcomponent *c = icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
891 c; c = icalcomponent_get_next_component( calendar, ICAL_VTIMEZONE_COMPONENT ) ) {
893 if ( !zone.isValid() ) {
897 if ( oldzone.isValid() ) {
901 }
else if ( !zones.
add( zone ) ) {
915 icalproperty *p = icalcomponent_get_first_property( vtimezone, ICAL_ANY_PROPERTY );
917 icalproperty_kind kind = icalproperty_isa( p );
920 case ICAL_TZID_PROPERTY:
921 name = QString::fromUtf8( icalproperty_get_tzid( p ) );
924 case ICAL_TZURL_PROPERTY:
925 data->d->
url = icalproperty_get_tzurl( p );
928 case ICAL_LOCATION_PROPERTY:
930 data->d->location = QString::fromUtf8( icalproperty_get_location( p ) );
933 case ICAL_X_PROPERTY:
935 const char *xname = icalproperty_get_x_name( p );
936 if ( xname && !strcmp( xname,
"X-LIC-LOCATION" ) ) {
937 xlocation = QString::fromUtf8( icalproperty_get_x( p ) );
941 case ICAL_LASTMODIFIED_PROPERTY:
943 const icaltimetype t = icalproperty_get_lastmodified(p);
944 if (icaltime_is_utc( t )) {
947 kDebug() <<
"LAST-MODIFIED not UTC";
954 p = icalcomponent_get_next_property( vtimezone, ICAL_ANY_PROPERTY );
957 if ( name.isEmpty() ) {
958 kDebug() <<
"TZID missing";
962 if ( data->d->location.isEmpty() && !xlocation.isEmpty() ) {
963 data->d->location = xlocation;
966 if ( name.startsWith( prefix ) ) {
968 const int i = name.indexOf(
'/', prefix.length() );
970 name = name.mid( i + 1 );
980 QList<KTimeZone::Transition> transitions;
982 QList<KTimeZone::Phase> phases;
983 for ( icalcomponent *c = icalcomponent_get_first_component( vtimezone, ICAL_ANY_COMPONENT );
984 c; c = icalcomponent_get_next_component( vtimezone, ICAL_ANY_COMPONENT ) ) {
986 KTimeZone::Phase phase;
987 QList<QDateTime> times;
988 icalcomponent_kind kind = icalcomponent_isa( c );
991 case ICAL_XSTANDARD_COMPONENT:
993 times = ICalTimeZoneSourcePrivate::parsePhase( c,
false, prevoff, phase );
996 case ICAL_XDAYLIGHT_COMPONENT:
998 times = ICalTimeZoneSourcePrivate::parsePhase( c,
true, prevoff, phase );
1002 kDebug() <<
"Unknown component:" << int( kind );
1005 const int tcount = times.count();
1008 for (
int t = 0; t < tcount; ++t ) {
1009 transitions += KTimeZone::Transition( times[t], phase );
1011 if ( !earliest.isValid() || times[0] < earliest ) {
1012 prevOffset = prevoff;
1013 earliest = times[0];
1019 data->setPhases( phases, prevOffset );
1022 qSort( transitions );
1023 for (
int t = 1, tend = transitions.count(); t < tend; ) {
1024 if ( transitions[t].phase() == transitions[t - 1].phase() ) {
1025 transitions.removeAt( t );
1031 data->setTransitions( transitions );
1033 data->d->setComponent( icalcomponent_new_clone( vtimezone ) );
1038 #if defined(HAVE_UUID_UUID_H)
1042 if ( !zone.isValid() ) {
1046 if ( oldzone.isValid() ) {
1050 }
else if ( zones.
add( zone ) ) {
1064 uuid_generate_random( uuid );
1065 uuid_unparse( uuid, suuid );
1066 QString name = QString( suuid );
1069 QList<KTimeZone::Phase> phases;
1071 QList<QByteArray> standardAbbrevs;
1072 standardAbbrevs += tz->StandardName.toLatin1();
1073 const KTimeZone::Phase standardPhase(
1074 ( tz->Bias + tz->StandardBias ) * -60,
1075 standardAbbrevs,
false,
1076 "Microsoft TIME_ZONE_INFORMATION" );
1077 phases += standardPhase;
1079 QList<QByteArray> daylightAbbrevs;
1080 daylightAbbrevs += tz->DaylightName.toLatin1();
1081 const KTimeZone::Phase daylightPhase(
1082 ( tz->Bias + tz->DaylightBias ) * -60,
1083 daylightAbbrevs,
true,
1084 "Microsoft TIME_ZONE_INFORMATION" );
1085 phases += daylightPhase;
1089 const int prevOffset = tz->Bias * -60;
1090 kdata.setPhases( phases, prevOffset );
1093 QList<KTimeZone::Transition> transitions;
1094 ICalTimeZoneSourcePrivate::parseTransitions(
1095 tz->StandardDate, standardPhase, prevOffset, transitions );
1096 ICalTimeZoneSourcePrivate::parseTransitions(
1097 tz->DaylightDate, daylightPhase, prevOffset, transitions );
1099 qSort( transitions );
1100 kdata.setTransitions( transitions );
1106 #endif // HAVE_UUID_UUID_H
1112 if ( !zone.isValid() ) {
1118 if ( oldzone.isValid() ) {
1122 oldzone = zones.
zone( name );
1123 if ( oldzone.isValid() ) {
1127 }
else if ( zones.
add( zone ) ) {
1137 QList<KTimeZone::Phase> phases;
1138 QList<KTimeZone::Transition> transitions;
1141 for ( QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it ) {
1142 QString value = *it;
1144 const QString tzName = value.mid( 0, value.indexOf(
";" ) );
1145 value = value.mid( ( value.indexOf(
";" ) + 1 ) );
1146 const QString tzOffset = value.mid( 0, value.indexOf(
";" ) );
1147 value = value.mid( ( value.indexOf(
";" ) + 1 ) );
1148 const QString tzDaylight = value.mid( 0, value.indexOf(
";" ) );
1149 const KDateTime tzDate = KDateTime::fromString( value.mid( ( value.lastIndexOf(
";" ) + 1 ) ) );
1150 if ( tzDaylight ==
"true" ) {
1154 const KTimeZone::Phase tzPhase(
1156 QByteArray( tzName.toLatin1() ), daylight,
"VCAL_TZ_INFORMATION" );
1158 transitions += KTimeZone::Transition( tzDate.dateTime(), tzPhase );
1161 kdata.setPhases( phases, 0 );
1162 qSort( transitions );
1163 kdata.setTransitions( transitions );
1169 #if defined(HAVE_UUID_UUID_H)
1171 void ICalTimeZoneSourcePrivate::parseTransitions(
const MSSystemTime &date,
1172 const KTimeZone::Phase &phase,
int prevOffset,
1173 QList<KTimeZone::Transition> &transitions )
1177 const KDateTime klocalStart( QDateTime( QDate( 2000, 1, 1 ), QTime( 0, 0, 0 ) ),
1178 KDateTime::Spec::ClockTime() );
1179 const KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
1183 if ( date.wYear >= 1601 && date.wYear <= 30827 &&
1184 date.wMonth >= 1 && date.wMonth <= 12 &&
1185 date.wDay >= 1 && date.wDay <= 31 ) {
1186 const QDate dt( date.wYear, date.wMonth, date.wDay );
1187 const QTime tm( date.wHour, date.wMinute, date.wSecond, date.wMilliseconds );
1188 const QDateTime datetime( dt, tm );
1189 if ( datetime.isValid() ) {
1190 transitions += KTimeZone::Transition( datetime, phase );
1195 if ( date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1196 date.wMonth >= 1 && date.wMonth <= 12 &&
1197 date.wDay >= 1 && date.wDay <= 5 ) {
1199 r.setRecurrenceType( RecurrenceRule::rYearly );
1203 lst.append( date.wMonth );
1204 r.setByMonths( lst );
1205 QList<RecurrenceRule::WDayPos> wdlst;
1207 pos.setDay( date.wDayOfWeek ? date.wDayOfWeek : 7 );
1208 pos.setPos( date.wDay < 5 ? date.wDay : -1 );
1209 wdlst.append( pos );
1210 r.setByDays( wdlst );
1212 r.setWeekStart( 1 );
1214 for (
int i = 0, end = dtl.count(); i < end; ++i ) {
1215 QDateTime utc = dtl[i].dateTime();
1216 utc.setTimeSpec( Qt::UTC );
1217 transitions += KTimeZone::Transition( utc.addSecs( -prevOffset ), phase );
1223 #endif // HAVE_UUID_UUID_H
1235 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase( icalcomponent *c,
1238 KTimeZone::Phase &phase )
1240 QList<QDateTime> transitions;
1243 QList<QByteArray> abbrevs;
1247 bool recurs =
false;
1248 bool found_dtstart =
false;
1249 bool found_tzoffsetfrom =
false;
1250 bool found_tzoffsetto =
false;
1251 icaltimetype dtstart = icaltime_null_time();
1254 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
1256 icalproperty_kind kind = icalproperty_isa( p );
1259 case ICAL_TZNAME_PROPERTY:
1265 QByteArray tzname = icalproperty_get_tzname( p );
1268 if ( ( !daylight && tzname ==
"Standard Time" ) ||
1269 ( daylight && tzname ==
"Daylight Time" ) ) {
1272 if ( !abbrevs.contains( tzname ) ) {
1277 case ICAL_DTSTART_PROPERTY:
1278 dtstart = icalproperty_get_dtstart( p );
1279 found_dtstart =
true;
1282 case ICAL_TZOFFSETFROM_PROPERTY:
1283 prevOffset = icalproperty_get_tzoffsetfrom( p );
1284 found_tzoffsetfrom =
true;
1287 case ICAL_TZOFFSETTO_PROPERTY:
1288 utcOffset = icalproperty_get_tzoffsetto( p );
1289 found_tzoffsetto =
true;
1292 case ICAL_COMMENT_PROPERTY:
1293 comment = QString::fromUtf8( icalproperty_get_comment( p ) );
1296 case ICAL_RDATE_PROPERTY:
1297 case ICAL_RRULE_PROPERTY:
1302 kDebug() <<
"Unknown property:" << int( kind );
1305 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1309 if ( !found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto ) {
1310 kDebug() <<
"DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1315 const QDateTime localStart = toQDateTime( dtstart );
1316 dtstart.second -= prevOffset;
1317 dtstart.zone = icaltimezone_get_utc_timezone();
1318 const QDateTime utcStart = toQDateTime( icaltime_normalize( dtstart ) );
1320 transitions += utcStart;
1327 const KDateTime klocalStart( localStart, KDateTime::Spec::ClockTime() );
1328 const KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
1330 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
1332 icalproperty_kind kind = icalproperty_isa( p );
1335 case ICAL_RDATE_PROPERTY:
1337 icaltimetype t = icalproperty_get_rdate( p ).time;
1338 if ( icaltime_is_date( t ) ) {
1340 t.hour = dtstart.hour;
1341 t.minute = dtstart.minute;
1342 t.second = dtstart.second;
1348 if (!icaltime_is_utc( t )) {
1349 t.second -= prevOffset;
1350 t.zone = icaltimezone_get_utc_timezone();
1351 t = icaltime_normalize( t );
1353 transitions += toQDateTime( t );
1356 case ICAL_RRULE_PROPERTY:
1361 impl.readRecurrence( icalproperty_get_rrule( p ), &r );
1366 KDateTime end( r.
endDt() );
1367 if ( end.timeSpec() == KDateTime::Spec::UTC() ) {
1368 end.setTimeSpec( KDateTime::Spec::ClockTime() );
1369 r.
setEndDt( end.addSecs( prevOffset ) );
1373 for (
int i = 0, end = dts.count(); i < end; ++i ) {
1374 QDateTime utc = dts[i].dateTime();
1375 utc.setTimeSpec( Qt::UTC );
1376 transitions += utc.addSecs( -prevOffset );
1383 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1385 qSortUnique( transitions );
1388 phase = KTimeZone::Phase( utcOffset, abbrevs, daylight, comment );
1395 if ( !icalBuiltIn ) {
1399 QString tzid = zone;
1401 if ( zone.startsWith( prefix ) ) {
1402 const int i = zone.indexOf(
'/', prefix.length() );
1404 tzid = zone.mid( i + 1 );
1407 const KTimeZone ktz = KSystemTimeZones::readZone( tzid );
1408 if ( ktz.isValid() ) {
1409 if ( ktz.data(
true ) ) {
1418 const QByteArray zoneName = zone.toUtf8();
1419 icaltimezone *icaltz = icaltimezone_get_builtin_timezone( zoneName );
1422 icaltz = icaltimezone_get_builtin_timezone_from_tzid( zoneName );
1427 return parse( icaltz );
1432 if ( ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty() ) {
1433 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(
"Europe/London" );
1434 const QByteArray tzid = icaltimezone_get_tzid( icaltz );
1435 if ( tzid.right( 13 ) ==
"Europe/London" ) {
1436 int i = tzid.indexOf(
'/', 1 );
1438 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left( i + 1 );
1439 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1442 kError() <<
"failed to get libical TZID prefix";
1444 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.