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 );
481 d->setComponent( c );
484 icalcomponent *tzcomp = icalcomponent_new( ICAL_VTIMEZONE_COMPONENT );
485 icalcomponent_add_property( tzcomp, icalproperty_new_tzid( tz.name().toUtf8() ) );
490 QList<KTimeZone::Transition> transits = transitions();
491 if ( transits.isEmpty() ) {
496 TIME_ZONE_INFORMATION currentTimeZone;
497 GetTimeZoneInformation( ¤tTimeZone );
498 if ( QString::fromWCharArray( currentTimeZone.StandardName ) != tz.name() ) {
499 kDebug() <<
"VTIMEZONE entry will be invalid for: " << tz.name();
501 const SYSTEMTIME std = currentTimeZone.StandardDate;
502 const SYSTEMTIME dlt = currentTimeZone.DaylightDate;
505 const KTimeZone::Phase standardPhase =
506 KTimeZone::Phase( ( currentTimeZone.Bias +
507 currentTimeZone.StandardBias ) * -60,
508 QByteArray(),
false );
509 const KTimeZone::Phase daylightPhase =
510 KTimeZone::Phase( ( currentTimeZone.Bias +
511 currentTimeZone.DaylightBias ) * -60,
512 QByteArray(),
true );
515 for (
int i = 2000; i <= 2050; i++ ) {
516 const QDateTime standardTime =
517 QDateTime( find_nth_weekday_in_month_of_year(
519 std.wDayOfWeek ? std.wDayOfWeek : 7,
521 QTime( std.wHour, std.wMinute,
522 std.wSecond, std.wMilliseconds ) );
524 const QDateTime daylightTime =
525 QDateTime( find_nth_weekday_in_month_of_year(
527 dlt.wDayOfWeek ? dlt.wDayOfWeek : 7,
529 QTime( dlt.wHour, dlt.wMinute,
530 dlt.wSecond, dlt.wMilliseconds ) );
532 transits << KTimeZone::Transition( standardTime, standardPhase )
533 << KTimeZone::Transition( daylightTime, daylightPhase );
537 if ( transits.isEmpty() ) {
538 kDebug() <<
"No transition information available VTIMEZONE will be invalid.";
541 if ( earliest.isValid() ) {
543 for (
int i = 0, end = transits.count(); i < end; ++i ) {
544 if ( transits.at( i ).time().date() >= earliest ) {
546 transits.erase( transits.begin(), transits.begin() + i );
552 int trcount = transits.count();
553 QVector<bool> transitionsDone(trcount);
554 transitionsDone.fill(
false );
558 icaldatetimeperiodtype dtperiod;
559 dtperiod.period = icalperiodtype_null_period();
562 for ( ; i < trcount && transitionsDone[i]; ++i ) {
565 if ( i >= trcount ) {
569 const int preOffset = ( i > 0 ) ?
570 transits.at( i - 1 ).phase().utcOffset() :
571 rhs.previousUtcOffset();
572 const KTimeZone::Phase phase = transits.at( i ).phase();
573 if ( phase.utcOffset() == preOffset ) {
574 transitionsDone[i] =
true;
575 while ( ++i < trcount ) {
576 if ( transitionsDone[i] ||
577 transits.at( i ).phase() != phase ||
578 transits.at( i - 1 ).phase().utcOffset() != preOffset ) {
581 transitionsDone[i] =
true;
585 icalcomponent *phaseComp =
586 icalcomponent_new( phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT );
587 const QList<QByteArray> abbrevs = phase.abbreviations();
588 for (
int a = 0, aend = abbrevs.count(); a < aend; ++a ) {
589 icalcomponent_add_property( phaseComp,
590 icalproperty_new_tzname(
591 static_cast<const char*>( abbrevs[a]) ) );
593 if ( !phase.comment().isEmpty() ) {
594 icalcomponent_add_property( phaseComp,
595 icalproperty_new_comment( phase.comment().toUtf8() ) );
597 icalcomponent_add_property( phaseComp,
598 icalproperty_new_tzoffsetfrom( preOffset ) );
599 icalcomponent_add_property( phaseComp,
600 icalproperty_new_tzoffsetto( phase.utcOffset() ) );
602 icalcomponent *phaseComp1 = icalcomponent_new_clone( phaseComp );
603 icalcomponent_add_property( phaseComp1,
604 icalproperty_new_dtstart(
605 writeLocalICalDateTime( transits.at( i ).time(),
607 bool useNewRRULE =
false;
613 int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0;
615 int nthFromStart = 0;
619 QList<QDateTime> rdates;
620 QList<QDateTime> times;
621 QDateTime qdt = transits.at( i ).time();
623 transitionsDone[i] =
true;
627 rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
631 month = date.month();
632 daysInMonth = date.daysInMonth();
633 dayOfWeek = date.dayOfWeek();
634 dayOfMonth = date.day();
635 nthFromStart = ( dayOfMonth - 1 ) / 7 + 1;
636 nthFromEnd = ( daysInMonth - dayOfMonth ) / 7 + 1;
638 if ( ++i >= trcount ) {
640 times += QDateTime();
642 if ( transitionsDone[i] ||
643 transits.at( i ).phase() != phase ||
644 transits.at( i - 1 ).phase().utcOffset() != preOffset ) {
647 transitionsDone[i] =
true;
648 qdt = transits.at( i ).time();
649 if ( !qdt.isValid() ) {
655 if ( qdt.time() != time ||
656 date.month() != month ||
657 date.year() != ++year ) {
660 const int day = date.day();
661 if ( ( newRule & DAY_OF_MONTH ) && day != dayOfMonth ) {
662 newRule &= ~DAY_OF_MONTH;
664 if ( newRule & ( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH ) ) {
665 if ( date.dayOfWeek() != dayOfWeek ) {
666 newRule &= ~( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH );
668 if ( ( newRule & WEEKDAY_OF_MONTH ) &&
669 ( day - 1 ) / 7 + 1 != nthFromStart ) {
670 newRule &= ~WEEKDAY_OF_MONTH;
672 if ( ( newRule & LAST_WEEKDAY_OF_MONTH ) &&
673 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) {
674 newRule &= ~LAST_WEEKDAY_OF_MONTH;
684 int yr = times[0].date().year();
685 while ( !rdates.isEmpty() ) {
688 if ( qdt.time() != time ||
689 date.month() != month ||
690 date.year() != --yr ) {
693 const int day = date.day();
694 if ( rule & DAY_OF_MONTH ) {
695 if ( day != dayOfMonth ) {
699 if ( date.dayOfWeek() != dayOfWeek ||
700 ( ( rule & WEEKDAY_OF_MONTH ) &&
701 ( day - 1 ) / 7 + 1 != nthFromStart ) ||
702 ( ( rule & LAST_WEEKDAY_OF_MONTH ) &&
703 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) ) {
707 times.prepend( qdt );
710 if ( times.count() > ( useNewRRULE ? minPhaseCount : minRuleCount ) ) {
712 icalrecurrencetype r;
713 icalrecurrencetype_clear( &r );
714 r.freq = ICAL_YEARLY_RECURRENCE;
715 r.count = ( year >= 2030 ) ? 0 : times.count() - 1;
716 r.by_month[0] = month;
717 if ( rule & DAY_OF_MONTH ) {
718 r.by_month_day[0] = dayOfMonth;
719 }
else if ( rule & WEEKDAY_OF_MONTH ) {
720 r.by_day[0] = ( dayOfWeek % 7 + 1 ) + ( nthFromStart * 8 );
721 }
else if ( rule & LAST_WEEKDAY_OF_MONTH ) {
722 r.by_day[0] = -( dayOfWeek % 7 + 1 ) - ( nthFromEnd * 8 );
724 icalproperty *prop = icalproperty_new_rrule( r );
728 icalcomponent *c = icalcomponent_new_clone( phaseComp );
729 icalcomponent_add_property(
730 c, icalproperty_new_dtstart( writeLocalICalDateTime( times[0], preOffset ) ) );
731 icalcomponent_add_property( c, prop );
732 icalcomponent_add_component( tzcomp, c );
734 icalcomponent_add_property( phaseComp1, prop );
738 for (
int t = 0, tend = times.count() - 1; t < tend; ++t ) {
750 }
while ( i < trcount );
753 for (
int rd = 0, rdend = rdates.count(); rd < rdend; ++rd ) {
754 dtperiod.time = writeLocalICalDateTime( rdates[rd], preOffset );
755 icalcomponent_add_property( phaseComp1, icalproperty_new_rdate( dtperiod ) );
757 icalcomponent_add_component( tzcomp, phaseComp1 );
758 icalcomponent_free( phaseComp );
761 d->setComponent( tzcomp );
773 if ( &rhs ==
this ) {
777 KTimeZoneData::operator=( rhs );
778 d->location = rhs.d->location;
781 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
802 return d->lastModified;
807 const QByteArray result( icalcomponent_as_ical_string( d->component() ) );
808 icalmemory_free_ring();
814 icaltimezone *icaltz = icaltimezone_new();
818 icalcomponent *c = icalcomponent_new_clone( d->component() );
819 if ( !icaltimezone_set_component( icaltz, c ) ) {
820 icalcomponent_free( c );
821 icaltimezone_free( icaltz, 1 );
841 class ICalTimeZoneSourcePrivate
844 static QList<QDateTime> parsePhase( icalcomponent *,
bool daylight,
845 int &prevOffset, KTimeZone::Phase & );
846 static QByteArray icalTzidPrefix;
848 #if defined(HAVE_UUID_UUID_H)
849 static void parseTransitions(
const MSSystemTime &date,
const KTimeZone::Phase &phase,
850 int prevOffset, QList<KTimeZone::Transition> &transitions );
854 QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
858 : KTimeZoneSource( false ),
869 QFile file( fileName );
870 if ( !file.open( QIODevice::ReadOnly ) ) {
873 QTextStream ts( &file );
874 ts.setCodec(
"ISO 8859-1" );
875 const QByteArray text = ts.readAll().trimmed().toLatin1();
879 icalcomponent *calendar = icalcomponent_new_from_string( text.data() );
881 if ( icalcomponent_isa( calendar ) == ICAL_VCALENDAR_COMPONENT ) {
882 result =
parse( calendar, zones );
884 icalcomponent_free( calendar );
891 for ( icalcomponent *c = icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
892 c; c = icalcomponent_get_next_component( calendar, ICAL_VTIMEZONE_COMPONENT ) ) {
894 if ( !zone.isValid() ) {
898 if ( oldzone.isValid() ) {
902 }
else if ( !zones.
add( zone ) ) {
916 icalproperty *p = icalcomponent_get_first_property( vtimezone, ICAL_ANY_PROPERTY );
918 icalproperty_kind kind = icalproperty_isa( p );
921 case ICAL_TZID_PROPERTY:
922 name = QString::fromUtf8( icalproperty_get_tzid( p ) );
925 case ICAL_TZURL_PROPERTY:
926 data->d->
url = icalproperty_get_tzurl( p );
929 case ICAL_LOCATION_PROPERTY:
931 data->d->location = QString::fromUtf8( icalproperty_get_location( p ) );
934 case ICAL_X_PROPERTY:
936 const char *xname = icalproperty_get_x_name( p );
937 if ( xname && !strcmp( xname,
"X-LIC-LOCATION" ) ) {
938 xlocation = QString::fromUtf8( icalproperty_get_x( p ) );
942 case ICAL_LASTMODIFIED_PROPERTY:
944 const icaltimetype t = icalproperty_get_lastmodified(p);
948 kDebug() <<
"LAST-MODIFIED not UTC";
955 p = icalcomponent_get_next_property( vtimezone, ICAL_ANY_PROPERTY );
958 if ( name.isEmpty() ) {
959 kDebug() <<
"TZID missing";
963 if ( data->d->location.isEmpty() && !xlocation.isEmpty() ) {
964 data->d->location = xlocation;
967 if ( name.startsWith( prefix ) ) {
969 const int i = name.indexOf(
'/', prefix.length() );
971 name = name.mid( i + 1 );
981 QList<KTimeZone::Transition> transitions;
983 QList<KTimeZone::Phase> phases;
984 for ( icalcomponent *c = icalcomponent_get_first_component( vtimezone, ICAL_ANY_COMPONENT );
985 c; c = icalcomponent_get_next_component( vtimezone, ICAL_ANY_COMPONENT ) ) {
987 KTimeZone::Phase phase;
988 QList<QDateTime> times;
989 icalcomponent_kind kind = icalcomponent_isa( c );
992 case ICAL_XSTANDARD_COMPONENT:
994 times = ICalTimeZoneSourcePrivate::parsePhase( c,
false, prevoff, phase );
997 case ICAL_XDAYLIGHT_COMPONENT:
999 times = ICalTimeZoneSourcePrivate::parsePhase( c,
true, prevoff, phase );
1003 kDebug() <<
"Unknown component:" << int( kind );
1006 const int tcount = times.count();
1009 for (
int t = 0; t < tcount; ++t ) {
1010 transitions += KTimeZone::Transition( times[t], phase );
1012 if ( !earliest.isValid() || times[0] < earliest ) {
1013 prevOffset = prevoff;
1014 earliest = times[0];
1020 data->setPhases( phases, prevOffset );
1023 qSort( transitions );
1024 for (
int t = 1, tend = transitions.count(); t < tend; ) {
1025 if ( transitions[t].phase() == transitions[t - 1].phase() ) {
1026 transitions.removeAt( t );
1032 data->setTransitions( transitions );
1034 data->d->setComponent( icalcomponent_new_clone( vtimezone ) );
1039 #if defined(HAVE_UUID_UUID_H)
1043 if ( !zone.isValid() ) {
1047 if ( oldzone.isValid() ) {
1051 }
else if ( zones.
add( zone ) ) {
1065 uuid_generate_random( uuid );
1066 uuid_unparse( uuid, suuid );
1067 QString name = QString( suuid );
1070 QList<KTimeZone::Phase> phases;
1072 QList<QByteArray> standardAbbrevs;
1073 standardAbbrevs += tz->StandardName.toLatin1();
1074 const KTimeZone::Phase standardPhase(
1075 ( tz->Bias + tz->StandardBias ) * -60,
1076 standardAbbrevs,
false,
1077 "Microsoft TIME_ZONE_INFORMATION" );
1078 phases += standardPhase;
1080 QList<QByteArray> daylightAbbrevs;
1081 daylightAbbrevs += tz->DaylightName.toLatin1();
1082 const KTimeZone::Phase daylightPhase(
1083 ( tz->Bias + tz->DaylightBias ) * -60,
1084 daylightAbbrevs,
true,
1085 "Microsoft TIME_ZONE_INFORMATION" );
1086 phases += daylightPhase;
1090 const int prevOffset = tz->Bias * -60;
1091 kdata.setPhases( phases, prevOffset );
1094 QList<KTimeZone::Transition> transitions;
1095 ICalTimeZoneSourcePrivate::parseTransitions(
1096 tz->StandardDate, standardPhase, prevOffset, transitions );
1097 ICalTimeZoneSourcePrivate::parseTransitions(
1098 tz->DaylightDate, daylightPhase, prevOffset, transitions );
1100 qSort( transitions );
1101 kdata.setTransitions( transitions );
1107 #endif // HAVE_UUID_UUID_H
1113 if ( !zone.isValid() ) {
1119 if ( oldzone.isValid() ) {
1123 oldzone = zones.
zone( name );
1124 if ( oldzone.isValid() ) {
1128 }
else if ( zones.
add( zone ) ) {
1138 QList<KTimeZone::Phase> phases;
1139 QList<KTimeZone::Transition> transitions;
1142 for ( QStringList::ConstIterator it = tzList.begin(); it != tzList.end(); ++it ) {
1143 QString value = *it;
1145 const QString tzName = value.mid( 0, value.indexOf(
";" ) );
1146 value = value.mid( ( value.indexOf(
";" ) + 1 ) );
1147 const QString tzOffset = value.mid( 0, value.indexOf(
";" ) );
1148 value = value.mid( ( value.indexOf(
";" ) + 1 ) );
1149 const QString tzDaylight = value.mid( 0, value.indexOf(
";" ) );
1150 const KDateTime tzDate = KDateTime::fromString( value.mid( ( value.lastIndexOf(
";" ) + 1 ) ) );
1151 if ( tzDaylight ==
"true" ) {
1155 const KTimeZone::Phase tzPhase(
1157 QByteArray( tzName.toLatin1() ), daylight,
"VCAL_TZ_INFORMATION" );
1159 transitions += KTimeZone::Transition( tzDate.dateTime(), tzPhase );
1162 kdata.setPhases( phases, 0 );
1163 qSort( transitions );
1164 kdata.setTransitions( transitions );
1170 #if defined(HAVE_UUID_UUID_H)
1172 void ICalTimeZoneSourcePrivate::parseTransitions(
const MSSystemTime &date,
1173 const KTimeZone::Phase &phase,
int prevOffset,
1174 QList<KTimeZone::Transition> &transitions )
1178 const KDateTime klocalStart( QDateTime( QDate( 2000, 1, 1 ), QTime( 0, 0, 0 ) ),
1179 KDateTime::Spec::ClockTime() );
1180 const KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
1184 if ( date.wYear >= 1601 && date.wYear <= 30827 &&
1185 date.wMonth >= 1 && date.wMonth <= 12 &&
1186 date.wDay >= 1 && date.wDay <= 31 ) {
1187 const QDate dt( date.wYear, date.wMonth, date.wDay );
1188 const QTime tm( date.wHour, date.wMinute, date.wSecond, date.wMilliseconds );
1189 const QDateTime datetime( dt, tm );
1190 if ( datetime.isValid() ) {
1191 transitions += KTimeZone::Transition( datetime, phase );
1196 if ( date.wDayOfWeek >= 0 && date.wDayOfWeek <= 6 &&
1197 date.wMonth >= 1 && date.wMonth <= 12 &&
1198 date.wDay >= 1 && date.wDay <= 5 ) {
1200 r.setRecurrenceType( RecurrenceRule::rYearly );
1204 lst.append( date.wMonth );
1205 r.setByMonths( lst );
1206 QList<RecurrenceRule::WDayPos> wdlst;
1208 pos.setDay( date.wDayOfWeek ? date.wDayOfWeek : 7 );
1209 pos.setPos( date.wDay < 5 ? date.wDay : -1 );
1210 wdlst.append( pos );
1211 r.setByDays( wdlst );
1213 r.setWeekStart( 1 );
1215 for (
int i = 0, end = dtl.count(); i < end; ++i ) {
1216 QDateTime utc = dtl[i].dateTime();
1217 utc.setTimeSpec( Qt::UTC );
1218 transitions += KTimeZone::Transition( utc.addSecs( -prevOffset ), phase );
1224 #endif // HAVE_UUID_UUID_H
1236 QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase( icalcomponent *c,
1239 KTimeZone::Phase &phase )
1241 QList<QDateTime> transitions;
1244 QList<QByteArray> abbrevs;
1248 bool recurs =
false;
1249 bool found_dtstart =
false;
1250 bool found_tzoffsetfrom =
false;
1251 bool found_tzoffsetto =
false;
1252 icaltimetype dtstart = icaltime_null_time();
1255 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
1257 icalproperty_kind kind = icalproperty_isa( p );
1260 case ICAL_TZNAME_PROPERTY:
1266 QByteArray tzname = icalproperty_get_tzname( p );
1269 if ( ( !daylight && tzname ==
"Standard Time" ) ||
1270 ( daylight && tzname ==
"Daylight Time" ) ) {
1273 if ( !abbrevs.contains( tzname ) ) {
1278 case ICAL_DTSTART_PROPERTY:
1279 dtstart = icalproperty_get_dtstart( p );
1280 found_dtstart =
true;
1283 case ICAL_TZOFFSETFROM_PROPERTY:
1284 prevOffset = icalproperty_get_tzoffsetfrom( p );
1285 found_tzoffsetfrom =
true;
1288 case ICAL_TZOFFSETTO_PROPERTY:
1289 utcOffset = icalproperty_get_tzoffsetto( p );
1290 found_tzoffsetto =
true;
1293 case ICAL_COMMENT_PROPERTY:
1294 comment = QString::fromUtf8( icalproperty_get_comment( p ) );
1297 case ICAL_RDATE_PROPERTY:
1298 case ICAL_RRULE_PROPERTY:
1303 kDebug() <<
"Unknown property:" << int( kind );
1306 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1310 if ( !found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto ) {
1311 kDebug() <<
"DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
1316 const QDateTime localStart = toQDateTime( dtstart );
1317 dtstart.second -= prevOffset;
1319 const QDateTime utcStart = toQDateTime( icaltime_normalize( dtstart ) );
1321 transitions += utcStart;
1328 const KDateTime klocalStart( localStart, KDateTime::Spec::ClockTime() );
1329 const KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
1331 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
1333 icalproperty_kind kind = icalproperty_isa( p );
1336 case ICAL_RDATE_PROPERTY:
1338 icaltimetype t = icalproperty_get_rdate( p ).time;
1339 if ( icaltime_is_date( t ) ) {
1341 t.hour = dtstart.hour;
1342 t.minute = dtstart.minute;
1343 t.second = dtstart.second;
1350 t.second -= prevOffset;
1352 t = icaltime_normalize( t );
1354 transitions += toQDateTime( t );
1357 case ICAL_RRULE_PROPERTY:
1362 impl.readRecurrence( icalproperty_get_rrule( p ), &r );
1367 KDateTime end( r.
endDt() );
1368 if ( end.timeSpec() == KDateTime::Spec::UTC() ) {
1369 end.setTimeSpec( KDateTime::Spec::ClockTime() );
1370 r.
setEndDt( end.addSecs( prevOffset ) );
1374 for (
int i = 0, end = dts.count(); i < end; ++i ) {
1375 QDateTime utc = dts[i].dateTime();
1376 utc.setTimeSpec( Qt::UTC );
1377 transitions += utc.addSecs( -prevOffset );
1384 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1386 qSortUnique( transitions );
1389 phase = KTimeZone::Phase( utcOffset, abbrevs, daylight, comment );
1396 if ( !icalBuiltIn ) {
1400 QString tzid = zone;
1402 if ( zone.startsWith( prefix ) ) {
1403 const int i = zone.indexOf(
'/', prefix.length() );
1405 tzid = zone.mid( i + 1 );
1408 const KTimeZone ktz = KSystemTimeZones::readZone( tzid );
1409 if ( ktz.isValid() ) {
1410 if ( ktz.data(
true ) ) {
1419 const QByteArray zoneName = zone.toUtf8();
1420 icaltimezone *icaltz = icaltimezone_get_builtin_timezone( zoneName );
1423 icaltz = icaltimezone_get_builtin_timezone_from_tzid( zoneName );
1428 return parse( icaltz );
1433 if ( ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty() ) {
1434 icaltimezone *icaltz = icaltimezone_get_builtin_timezone(
"Europe/London" );
1435 const QByteArray tzid = icaltimezone_get_tzid( icaltz );
1436 if ( tzid.right( 13 ) ==
"Europe/London" ) {
1437 int i = tzid.indexOf(
'/', 1 );
1439 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left( i + 1 );
1440 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1443 kError() <<
"failed to get libical TZID prefix";
1445 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.