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()
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();
96 class ICalTimeZonesPrivate
99 ICalTimeZonesPrivate() {}
100 ICalTimeZones::ZoneMap zones;
105 : d( new ICalTimeZonesPrivate )
110 : d( new ICalTimeZonesPrivate() )
112 d->zones = rhs.d->
zones;
118 if ( &rhs ==
this ) {
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 ) {
153 d->zones.erase( it );
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() ) ) {
216 if ( matches == i ) {
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() )
243 Q_UNUSED( earliest );
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()
369 if ( icalComponent ) {
370 icalcomponent_free( icalComponent );
374 icalcomponent *component()
const {
return icalComponent; }
375 void setComponent( icalcomponent *c )
377 if ( icalComponent ) {
378 icalcomponent_free( icalComponent );
385 QDateTime lastModified;
388 icalcomponent *icalComponent;
393 : d ( new ICalTimeZoneDataPrivate() )
398 : KTimeZoneData( rhs ),
399 d( new ICalTimeZoneDataPrivate() )
401 d->location = rhs.d->location;
404 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
409 static QDate find_nth_weekday_in_month_of_year(
int nth,
int dayOfWeek,
int month,
int year ) {
410 const QDate first( year, month, 1 );
411 const int actualDayOfWeek = first.dayOfWeek();
412 QDate candidate = first.addDays( ( nth - 1 ) * 7 + dayOfWeek - actualDayOfWeek );
414 if ( candidate.month() != month ) {
415 candidate = candidate.addDays( -7 );
423 const KTimeZone &tz,
const QDate &earliest )
424 : KTimeZoneData( rhs ),
425 d( new ICalTimeZoneDataPrivate() )
430 WEEKDAY_OF_MONTH = 0x02,
431 LAST_WEEKDAY_OF_MONTH = 0x04
434 if ( tz.type() ==
"KSystemTimeZone" ) {
438 icalcomponent *c = 0;
439 const KTimeZone ktz = KSystemTimeZones::readZone( tz.name() );
440 if ( ktz.isValid() ) {
441 if ( ktz.data(
true ) ) {
445 c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
446 icaltimezone_free( itz, 1 );
452 icaltimezone *itz = icaltimezone_get_builtin_timezone( tz.name().toUtf8() );
453 c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
459 icalproperty *prop = icalcomponent_get_first_property( c, ICAL_TZID_PROPERTY );
461 icalvalue *value = icalproperty_get_value( prop );
462 const char *tzid = icalvalue_get_text( value );
464 const int len = icalprefix.size();
465 if ( !strncmp( icalprefix, tzid, len ) ) {
466 const char *s = strchr( tzid + len,
'/' );
468 const QByteArray tzidShort( s + 1 );
469 icalvalue_set_text( value, tzidShort );
472 prop = icalcomponent_get_first_property( c, ICAL_X_PROPERTY );
473 const char *xname = icalproperty_get_x_name( prop );
474 if ( xname && !strcmp( xname,
"X-LIC-LOCATION" ) ) {
475 icalcomponent_remove_property( c, prop );
476 icalproperty_free( prop );
482 d->setComponent( c );
485 icalcomponent *tzcomp = icalcomponent_new( ICAL_VTIMEZONE_COMPONENT );
486 icalcomponent_add_property( tzcomp, icalproperty_new_tzid( tz.name().toUtf8() ) );
491 QList<KTimeZone::Transition> transits = transitions();
492 if ( transits.isEmpty() ) {
497 TIME_ZONE_INFORMATION currentTimeZone;
498 GetTimeZoneInformation( ¤tTimeZone );
499 if ( QString::fromWCharArray( currentTimeZone.StandardName ) != tz.name() ) {
500 kDebug() <<
"VTIMEZONE entry will be invalid for: " << tz.name();
502 const SYSTEMTIME std = currentTimeZone.StandardDate;
503 const SYSTEMTIME dlt = currentTimeZone.DaylightDate;
506 const KTimeZone::Phase standardPhase =
507 KTimeZone::Phase( ( currentTimeZone.Bias +
508 currentTimeZone.StandardBias ) * -60,
509 QByteArray(),
false );
510 const KTimeZone::Phase daylightPhase =
511 KTimeZone::Phase( ( currentTimeZone.Bias +
512 currentTimeZone.DaylightBias ) * -60,
513 QByteArray(),
true );
516 for (
int i = 2000; i <= 2050; i++ ) {
517 const QDateTime standardTime =
518 QDateTime( find_nth_weekday_in_month_of_year(
520 std.wDayOfWeek ? std.wDayOfWeek : 7,
522 QTime( std.wHour, std.wMinute,
523 std.wSecond, std.wMilliseconds ) );
525 const QDateTime daylightTime =
526 QDateTime( find_nth_weekday_in_month_of_year(
528 dlt.wDayOfWeek ? dlt.wDayOfWeek : 7,
530 QTime( dlt.wHour, dlt.wMinute,
531 dlt.wSecond, dlt.wMilliseconds ) );
533 transits << KTimeZone::Transition( standardTime, standardPhase )
534 << KTimeZone::Transition( daylightTime, daylightPhase );
538 if ( transits.isEmpty() ) {
539 kDebug() <<
"No transition information available VTIMEZONE will be invalid.";
542 if ( earliest.isValid() ) {
544 for (
int i = 0, end = transits.count(); i < end; ++i ) {
545 if ( transits.at( i ).time().date() >= earliest ) {
547 transits.erase( transits.begin(), transits.begin() + i );
553 int trcount = transits.count();
554 QVector<bool> transitionsDone(trcount);
555 transitionsDone.fill(
false );
559 icaldatetimeperiodtype dtperiod;
560 dtperiod.period = icalperiodtype_null_period();
563 for ( ; i < trcount && transitionsDone[i]; ++i ) {
566 if ( i >= trcount ) {
570 const int preOffset = ( i > 0 ) ?
571 transits.at( i - 1 ).phase().utcOffset() :
572 rhs.previousUtcOffset();
573 const KTimeZone::Phase phase = transits.at( i ).phase();
574 if ( phase.utcOffset() == preOffset ) {
575 transitionsDone[i] =
true;
576 while ( ++i < trcount ) {
577 if ( transitionsDone[i] ||
578 transits.at( i ).phase() != phase ||
579 transits.at( i - 1 ).phase().utcOffset() != preOffset ) {
582 transitionsDone[i] =
true;
586 icalcomponent *phaseComp =
587 icalcomponent_new( phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT );
588 const QList<QByteArray> abbrevs = phase.abbreviations();
589 for (
int a = 0, aend = abbrevs.count(); a < aend; ++a ) {
590 icalcomponent_add_property( phaseComp,
591 icalproperty_new_tzname(
592 static_cast<const char*>( abbrevs[a]) ) );
594 if ( !phase.comment().isEmpty() ) {
595 icalcomponent_add_property( phaseComp,
596 icalproperty_new_comment( phase.comment().toUtf8() ) );
598 icalcomponent_add_property( phaseComp,
599 icalproperty_new_tzoffsetfrom( preOffset ) );
600 icalcomponent_add_property( phaseComp,
601 icalproperty_new_tzoffsetto( phase.utcOffset() ) );
603 icalcomponent *phaseComp1 = icalcomponent_new_clone( phaseComp );
604 icalcomponent_add_property( phaseComp1,
605 icalproperty_new_dtstart(
606 writeLocalICalDateTime( transits.at( i ).time(),
608 bool useNewRRULE =
false;
614 int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0;
616 int nthFromStart = 0;
620 QList<QDateTime> rdates;
621 QList<QDateTime> times;
622 QDateTime qdt = transits.at( i ).time();
624 transitionsDone[i] =
true;
628 rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
632 month = date.month();
633 daysInMonth = date.daysInMonth();
634 dayOfWeek = date.dayOfWeek();
635 dayOfMonth = date.day();
636 nthFromStart = ( dayOfMonth - 1 ) / 7 + 1;
637 nthFromEnd = ( daysInMonth - dayOfMonth ) / 7 + 1;
639 if ( ++i >= trcount ) {
641 times += QDateTime();
643 if ( transitionsDone[i] ||
644 transits.at( i ).phase() != phase ||
645 transits.at( i - 1 ).phase().utcOffset() != preOffset ) {
648 transitionsDone[i] =
true;
649 qdt = transits.at( i ).time();
650 if ( !qdt.isValid() ) {
656 if ( qdt.time() != time ||
657 date.month() != month ||
658 date.year() != ++year ) {
661 const int day = date.day();
662 if ( ( newRule & DAY_OF_MONTH ) && day != dayOfMonth ) {
663 newRule &= ~DAY_OF_MONTH;
665 if ( newRule & ( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH ) ) {
666 if ( date.dayOfWeek() != dayOfWeek ) {
667 newRule &= ~( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH );
669 if ( ( newRule & WEEKDAY_OF_MONTH ) &&
670 ( day - 1 ) / 7 + 1 != nthFromStart ) {
671 newRule &= ~WEEKDAY_OF_MONTH;
673 if ( ( newRule & LAST_WEEKDAY_OF_MONTH ) &&
674 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) {
675 newRule &= ~LAST_WEEKDAY_OF_MONTH;
685 int yr = times[0].date().year();
686 while ( !rdates.isEmpty() ) {
689 if ( qdt.time() != time ||
690 date.month() != month ||
691 date.year() != --yr ) {
694 const int day = date.day();
695 if ( rule & DAY_OF_MONTH ) {
696 if ( day != dayOfMonth ) {
700 if ( date.dayOfWeek() != dayOfWeek ||
701 ( ( rule & WEEKDAY_OF_MONTH ) &&
702 ( day - 1 ) / 7 + 1 != nthFromStart ) ||
703 ( ( rule & LAST_WEEKDAY_OF_MONTH ) &&
704 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) ) {
708 times.prepend( qdt );
711 if ( times.count() > ( useNewRRULE ? minPhaseCount : minRuleCount ) ) {
713 icalrecurrencetype r;
714 icalrecurrencetype_clear( &r );
715 r.freq = ICAL_YEARLY_RECURRENCE;
716 r.count = ( year >= 2030 ) ? 0 : times.count() - 1;
717 r.by_month[0] = month;
718 if ( rule & DAY_OF_MONTH ) {
719 r.by_month_day[0] = dayOfMonth;
720 }
else if ( rule & WEEKDAY_OF_MONTH ) {
721 r.by_day[0] = ( dayOfWeek % 7 + 1 ) + ( nthFromStart * 8 );
722 }
else if ( rule & LAST_WEEKDAY_OF_MONTH ) {
723 r.by_day[0] = -( dayOfWeek % 7 + 1 ) - ( nthFromEnd * 8 );
725 icalproperty *prop = icalproperty_new_rrule( r );
729 icalcomponent *c = icalcomponent_new_clone( phaseComp );
730 icalcomponent_add_property(
731 c, icalproperty_new_dtstart( writeLocalICalDateTime( times[0], preOffset ) ) );
732 icalcomponent_add_property( c, prop );
733 icalcomponent_add_component( tzcomp, c );
735 icalcomponent_add_property( phaseComp1, prop );
739 for (
int t = 0, tend = times.count() - 1; t < tend; ++t ) {
751 }
while ( i < trcount );
754 for (
int rd = 0, rdend = rdates.count(); rd < rdend; ++rd ) {
755 dtperiod.time = writeLocalICalDateTime( rdates[rd], preOffset );
756 icalcomponent_add_property( phaseComp1, icalproperty_new_rdate( dtperiod ) );
758 icalcomponent_add_component( tzcomp, phaseComp1 );
759 icalcomponent_free( phaseComp );
762 d->setComponent( tzcomp );
774 if ( &rhs ==
this ) {
778 KTimeZoneData::operator=( rhs );
779 d->location = rhs.d->location;
782 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
803 return d->lastModified;
808 const QByteArray result( icalcomponent_as_ical_string( d->component() ) );
809 icalmemory_free_ring();
815 icaltimezone *icaltz = icaltimezone_new();
819 icalcomponent *c = icalcomponent_new_clone( d->component() );
820 if ( !icaltimezone_set_component( icaltz, c ) ) {
821 icalcomponent_free( c );
822 icaltimezone_free( icaltz, 1 );
842 class ICalTimeZoneSourcePrivate
845 static QList<QDateTime> parsePhase( icalcomponent *,
bool daylight,
846 int &prevOffset, KTimeZone::Phase & );
847 static QByteArray icalTzidPrefix;
849 #if defined(HAVE_UUID_UUID_H)
850 static void parseTransitions(
const MSSystemTime &date,
const KTimeZone::Phase &phase,
851 int prevOffset, QList<KTimeZone::Transition> &transitions );
855 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
859 : KTimeZoneSource( false ),
870 QFile file( fileName );
871 if ( !file.open( QIODevice::ReadOnly ) ) {
874 QTextStream ts( &file );
875 ts.setCodec(
"ISO 8859-1" );
876 const QByteArray text = ts.readAll().trimmed().toLatin1();
880 icalcomponent *calendar = icalcomponent_new_from_string( text.data() );
882 if ( icalcomponent_isa( calendar ) == ICAL_VCALENDAR_COMPONENT ) {
883 result =
parse( calendar, zones );
885 icalcomponent_free( calendar );
892 for ( icalcomponent *c = icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
893 c; c = icalcomponent_get_next_component( calendar, ICAL_VTIMEZONE_COMPONENT ) ) {
895 if ( !zone.isValid() ) {
899 if ( oldzone.isValid() ) {
903 }
else if ( !zones.
add( zone ) ) {
917 icalproperty *p = icalcomponent_get_first_property( vtimezone, ICAL_ANY_PROPERTY );
919 icalproperty_kind kind = icalproperty_isa( p );
922 case ICAL_TZID_PROPERTY:
923 name = QString::fromUtf8( icalproperty_get_tzid( p ) );
926 case ICAL_TZURL_PROPERTY:
927 data->d->
url = icalproperty_get_tzurl( p );
930 case ICAL_LOCATION_PROPERTY:
932 data->d->location = QString::fromUtf8( icalproperty_get_location( p ) );
935 case ICAL_X_PROPERTY:
937 const char *xname = icalproperty_get_x_name( p );
938 if ( xname && !strcmp( xname,
"X-LIC-LOCATION" ) ) {
939 xlocation = QString::fromUtf8( icalproperty_get_x( p ) );
943 case ICAL_LASTMODIFIED_PROPERTY:
945 const icaltimetype t = icalproperty_get_lastmodified(p);
949 kDebug() <<
"LAST-MODIFIED not UTC";
956 p = icalcomponent_get_next_property( vtimezone, ICAL_ANY_PROPERTY );
959 if ( name.isEmpty() ) {
960 kDebug() <<
"TZID missing";
964 if ( data->d->location.isEmpty() && !xlocation.isEmpty() ) {
965 data->d->location = xlocation;
968 if ( name.startsWith( prefix ) ) {
970 const int i = name.indexOf(
'/', prefix.length() );
972 name = name.mid( i + 1 );
982 QList<KTimeZone::Transition> transitions;
984 QList<KTimeZone::Phase> phases;
985 for ( icalcomponent *c = icalcomponent_get_first_component( vtimezone, ICAL_ANY_COMPONENT );
986 c; c = icalcomponent_get_next_component( vtimezone, ICAL_ANY_COMPONENT ) ) {
988 KTimeZone::Phase phase;
989 QList<QDateTime> times;
990 icalcomponent_kind kind = icalcomponent_isa( c );
993 case ICAL_XSTANDARD_COMPONENT:
995 times = ICalTimeZoneSourcePrivate::parsePhase( c,
false, prevoff, phase );
998 case ICAL_XDAYLIGHT_COMPONENT:
1000 times = ICalTimeZoneSourcePrivate::parsePhase( c,
true, prevoff, phase );
1004 kDebug() <<
"Unknown component:" << int( kind );
1007 const int tcount = times.count();
1010 for (
int t = 0; t < tcount; ++t ) {
1011 transitions += KTimeZone::Transition( times[t], phase );
1013 if ( !earliest.isValid() || times[0] < earliest ) {
1014 prevOffset = prevoff;
1015 earliest = times[0];
1021 data->setPhases( phases, prevOffset );
1024 qSort( transitions );
1025 for (
int t = 1, tend = transitions.count(); t < tend; ) {
1026 if ( transitions[t].phase() == transitions[t - 1].phase() ) {
1027 transitions.removeAt( t );
1033 data->setTransitions( transitions );
1035 data->d->setComponent( icalcomponent_new_clone( vtimezone ) );
1040 #if defined(HAVE_UUID_UUID_H)
1044 if ( !zone.isValid() ) {
1048 if ( oldzone.isValid() ) {
1052 }
else if ( zones.
add( zone ) ) {
1066 uuid_generate_random( uuid );
1067 uuid_unparse( uuid, suuid );
1068 QString name = QString( suuid );
1071 QList<KTimeZone::Phase> phases;
1073 QList<QByteArray> standardAbbrevs;
1074 standardAbbrevs += tz->StandardName.toLatin1();
1075 const KTimeZone::Phase standardPhase(
1076 ( tz->Bias + tz->StandardBias ) * -60,
1077 standardAbbrevs,
false,
1078 "Microsoft TIME_ZONE_INFORMATION" );
1079 phases += standardPhase;
1081 QList<QByteArray> daylightAbbrevs;
1082 daylightAbbrevs += tz->DaylightName.toLatin1();
1083 const KTimeZone::Phase daylightPhase(
1084 ( tz->Bias + tz->DaylightBias ) * -60,
1085 daylightAbbrevs,
true,
1086 "Microsoft TIME_ZONE_INFORMATION" );
1087 phases += daylightPhase;
1091 const int prevOffset = tz->Bias * -60;
1092 kdata.setPhases( phases, prevOffset );
1095 QList<KTimeZone::Transition> transitions;
1096 ICalTimeZoneSourcePrivate::parseTransitions(
1097 tz->StandardDate, standardPhase, prevOffset, transitions );
1098 ICalTimeZoneSourcePrivate::parseTransitions(
1099 tz->DaylightDate, daylightPhase, prevOffset, transitions );
1101 qSort( transitions );
1102 kdata.setTransitions( transitions );
1108 #endif // HAVE_UUID_UUID_H
1114 if ( !zone.isValid() ) {
1120 if ( oldzone.isValid() ) {
1124 oldzone = zones.
zone( name );
1125 if ( oldzone.isValid() ) {
1129 }
else if ( zones.
add( zone ) ) {
1139 QList<KTimeZone::Phase> phases;
1140 QList<KTimeZone::Transition> transitions;
1143 for ( QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it ) {
1144 QString value = *it;
1146 const QString tzName = value.mid( 0, value.indexOf(
";" ) );
1147 value = value.mid( ( value.indexOf(
";" ) + 1 ) );
1148 const QString tzOffset = value.mid( 0, value.indexOf(
";" ) );
1149 value = value.mid( ( value.indexOf(
";" ) + 1 ) );
1150 const QString tzDaylight = value.mid( 0, value.indexOf(
";" ) );
1151 const KDateTime tzDate = KDateTime::fromString( value.mid( ( value.lastIndexOf(
";" ) + 1 ) ) );
1152 if ( tzDaylight ==
"true" ) {
1156 const KTimeZone::Phase tzPhase(
1158 QByteArray( tzName.toLatin1() ), daylight,
"VCAL_TZ_INFORMATION" );
1160 transitions += KTimeZone::Transition( tzDate.dateTime(), tzPhase );
1163 kdata.setPhases( phases, 0 );
1164 qSort( transitions );
1165 kdata.setTransitions( transitions );
1171 #if defined(HAVE_UUID_UUID_H)
1173 void ICalTimeZoneSourcePrivate::parseTransitions(
const MSSystemTime &date,
1174 const KTimeZone::Phase &phase,
int prevOffset,
1175 QList<KTimeZone::Transition> &transitions )
1179 const KDateTime klocalStart( QDateTime( QDate( 2000, 1, 1 ), QTime( 0, 0, 0 ) ),
1180 KDateTime::Spec::ClockTime() );
1181 const KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
1185 if ( date.wYear >= 1601 && date.wYear <= 30827 &&
1186 date.wMonth >= 1 && date.wMonth <= 12 &&
1187 date.wDay >= 1 && date.wDay <= 31 ) {
1188 const QDate dt( date.wYear, date.wMonth, date.wDay );
1189 const QTime tm( date.wHour, date.wMinute, date.wSecond, date.wMilliseconds );
1190 const QDateTime datetime( dt, tm );
1191 if ( datetime.isValid() ) {
1192 transitions += KTimeZone::Transition( datetime, phase );
1197 if ( date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1198 date.wMonth >= 1 && date.wMonth <= 12 &&
1199 date.wDay >= 1 && date.wDay <= 5 ) {
1201 r.setRecurrenceType( RecurrenceRule::rYearly );
1205 lst.append( date.wMonth );
1206 r.setByMonths( lst );
1207 QList<RecurrenceRule::WDayPos> wdlst;
1209 pos.setDay( date.wDayOfWeek ? date.wDayOfWeek : 7 );
1210 pos.setPos( date.wDay < 5 ? date.wDay : -1 );
1211 wdlst.append( pos );
1212 r.setByDays( wdlst );
1214 r.setWeekStart( 1 );
1216 for (
int i = 0, end = dtl.count(); i < end; ++i ) {
1217 QDateTime utc = dtl[i].dateTime();
1218 utc.setTimeSpec( Qt::UTC );
1219 transitions += KTimeZone::Transition( utc.addSecs( -prevOffset ), phase );
1225 #endif // HAVE_UUID_UUID_H
1237 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase( icalcomponent *c,
1240 KTimeZone::Phase &phase )
1242 QList<QDateTime> transitions;
1245 QList<QByteArray> abbrevs;
1249 bool recurs =
false;
1250 bool found_dtstart =
false;
1251 bool found_tzoffsetfrom =
false;
1252 bool found_tzoffsetto =
false;
1253 icaltimetype dtstart = icaltime_null_time();
1256 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
1258 icalproperty_kind kind = icalproperty_isa( p );
1261 case ICAL_TZNAME_PROPERTY:
1267 QByteArray tzname = icalproperty_get_tzname( p );
1270 if ( ( !daylight && tzname ==
"Standard Time" ) ||
1271 ( daylight && tzname ==
"Daylight Time" ) ) {
1274 if ( !abbrevs.contains( tzname ) ) {
1279 case ICAL_DTSTART_PROPERTY:
1280 dtstart = icalproperty_get_dtstart( p );
1281 found_dtstart =
true;
1284 case ICAL_TZOFFSETFROM_PROPERTY:
1285 prevOffset = icalproperty_get_tzoffsetfrom( p );
1286 found_tzoffsetfrom =
true;
1289 case ICAL_TZOFFSETTO_PROPERTY:
1290 utcOffset = icalproperty_get_tzoffsetto( p );
1291 found_tzoffsetto =
true;
1294 case ICAL_COMMENT_PROPERTY:
1295 comment = QString::fromUtf8( icalproperty_get_comment( p ) );
1298 case ICAL_RDATE_PROPERTY:
1299 case ICAL_RRULE_PROPERTY:
1304 kDebug() <<
"Unknown property:" << int( kind );
1307 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1311 if ( !found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto ) {
1312 kDebug() <<
"DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1317 const QDateTime localStart = toQDateTime( dtstart );
1318 dtstart.second -= prevOffset;
1320 const QDateTime utcStart = toQDateTime( icaltime_normalize( dtstart ) );
1322 transitions += utcStart;
1329 const KDateTime klocalStart( localStart, KDateTime::Spec::ClockTime() );
1330 const KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
1332 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
1334 icalproperty_kind kind = icalproperty_isa( p );
1337 case ICAL_RDATE_PROPERTY:
1339 icaltimetype t = icalproperty_get_rdate( p ).time;
1340 if ( icaltime_is_date( t ) ) {
1342 t.hour = dtstart.hour;
1343 t.minute = dtstart.minute;
1344 t.second = dtstart.second;
1351 t.second -= prevOffset;
1353 t = icaltime_normalize( t );
1355 transitions += toQDateTime( t );
1358 case ICAL_RRULE_PROPERTY:
1363 impl.readRecurrence( icalproperty_get_rrule( p ), &r );
1368 KDateTime end( r.
endDt() );
1369 if ( end.timeSpec() == KDateTime::Spec::UTC() ) {
1370 end.setTimeSpec( KDateTime::Spec::ClockTime() );
1371 r.
setEndDt( end.addSecs( prevOffset ) );
1375 for (
int i = 0, end = dts.count(); i < end; ++i ) {
1376 QDateTime utc = dts[i].dateTime();
1377 utc.setTimeSpec( Qt::UTC );
1378 transitions += utc.addSecs( -prevOffset );
1385 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1387 qSortUnique( transitions );
1390 phase = KTimeZone::Phase( utcOffset, abbrevs, daylight, comment );
1397 if ( !icalBuiltIn ) {
1401 QString tzid = zone;
1403 if ( zone.startsWith( prefix ) ) {
1404 const int i = zone.indexOf(
'/', prefix.length() );
1406 tzid = zone.mid( i + 1 );
1409 const KTimeZone ktz = KSystemTimeZones::readZone( tzid );
1410 if ( ktz.isValid() ) {
1411 if ( ktz.data(
true ) ) {
1420 const QByteArray zoneName = zone.toUtf8();
1421 icaltimezone *icaltz = icaltimezone_get_builtin_timezone( zoneName );
1424 icaltz = icaltimezone_get_builtin_timezone_from_tzid( zoneName );
1429 return parse( icaltz );
1434 if ( ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty() ) {
1435 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(
"Europe/London" );
1436 const QByteArray tzid = icaltimezone_get_tzid( icaltz );
1437 if ( tzid.right( 13 ) ==
"Europe/London" ) {
1438 int i = tzid.indexOf(
'/', 1 );
1440 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left( i + 1 );
1441 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1444 kError() <<
"failed to get libical TZID prefix";
1446 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.