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;