• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

KCal Library

  • kcal
icaltimezones.cpp
1/*
2 This file is part of the kcal library.
3
4 Copyright (c) 2005-2007 David Jarvie <djarvie@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "icaltimezones.h"
23#include "icalformat.h"
24#include "icalformat_p.h"
25
26extern "C" {
27 #include <libical/ical.h>
28 #include <libical/icaltimezone.h>
29}
30#include <ksystemtimezone.h>
31#include <kdatetime.h>
32#include <kdebug.h>
33
34#include <QtCore/QDateTime>
35#include <QtCore/QString>
36#include <QtCore/QList>
37#include <QtCore/QVector>
38#include <QtCore/QSet>
39#include <QtCore/QFile>
40#include <QtCore/QTextStream>
41
42using namespace KCal;
43
44// Minimum repetition counts for VTIMEZONE RRULEs
45static const int minRuleCount = 5; // for any RRULE
46static const int minPhaseCount = 8; // for separate STANDARD/DAYLIGHT component
47
48// Convert an ical time to QDateTime, preserving the UTC indicator
49static QDateTime toQDateTime( const icaltimetype &t )
50{
51 return QDateTime( QDate( t.year, t.month, t.day ),
52 QTime( t.hour, t.minute, t.second ),
53 ( icaltime_is_utc( t ) ? Qt::UTC : Qt::LocalTime ) );
54}
55
56// Maximum date for time zone data.
57// It's not sensible to try to predict them very far in advance, because
58// they can easily change. Plus, it limits the processing required.
59static QDateTime MAX_DATE()
60{
61 static QDateTime dt;
62 if ( !dt.isValid() ) {
63 dt = QDateTime( QDate::currentDate().addYears( 20 ), QTime( 0, 0, 0 ) );
64 }
65 return dt;
66}
67
68static icaltimetype writeLocalICalDateTime( const QDateTime &utc, int offset )
69{
70 QDateTime local = utc.addSecs( offset );
71 icaltimetype t = icaltime_null_time();
72 t.year = local.date().year();
73 t.month = local.date().month();
74 t.day = local.date().day();
75 t.hour = local.time().hour();
76 t.minute = local.time().minute();
77 t.second = local.time().second();
78 t.is_date = 0;
79 t.zone = 0;
80 return t;
81}
82
83namespace KCal {
84
85/******************************************************************************/
86
87//@cond PRIVATE
88class ICalTimeZonesPrivate
89{
90 public:
91 ICalTimeZonesPrivate() {}
92 ICalTimeZones::ZoneMap zones;
93};
94//@endcond
95
96ICalTimeZones::ICalTimeZones()
97 : d( new ICalTimeZonesPrivate )
98{
99}
100
101ICalTimeZones::~ICalTimeZones()
102{
103 delete d;
104}
105
106const ICalTimeZones::ZoneMap ICalTimeZones::zones() const
107{
108 return d->zones;
109}
110
111bool ICalTimeZones::add( const ICalTimeZone &zone )
112{
113 if ( !zone.isValid() ) {
114 return false;
115 }
116 if ( d->zones.find( zone.name() ) != d->zones.end() ) {
117 return false; // name already exists
118 }
119
120 d->zones.insert( zone.name(), zone );
121 return true;
122}
123
124ICalTimeZone ICalTimeZones::remove( const ICalTimeZone &zone )
125{
126 if ( zone.isValid() ) {
127 for ( ZoneMap::Iterator it = d->zones.begin(), end = d->zones.end(); it != end; ++it ) {
128 if ( it.value() == zone ) {
129 d->zones.erase( it );
130 return ( zone == ICalTimeZone::utc() ) ? ICalTimeZone() : zone;
131 }
132 }
133 }
134 return ICalTimeZone();
135}
136
137ICalTimeZone ICalTimeZones::remove( const QString &name )
138{
139 if ( !name.isEmpty() ) {
140 ZoneMap::Iterator it = d->zones.find( name );
141 if ( it != d->zones.end() ) {
142 ICalTimeZone zone = it.value();
143 d->zones.erase(it);
144 return ( zone == ICalTimeZone::utc() ) ? ICalTimeZone() : zone;
145 }
146 }
147 return ICalTimeZone();
148}
149
150void ICalTimeZones::clear()
151{
152 d->zones.clear();
153}
154
155ICalTimeZone ICalTimeZones::zone( const QString &name ) const
156{
157 if ( !name.isEmpty() ) {
158 ZoneMap::ConstIterator it = d->zones.constFind( name );
159 if ( it != d->zones.constEnd() ) {
160 return it.value();
161 }
162 }
163 return ICalTimeZone(); // error
164}
165
166/******************************************************************************/
167
168ICalTimeZoneBackend::ICalTimeZoneBackend()
169 : KTimeZoneBackend()
170{}
171
172ICalTimeZoneBackend::ICalTimeZoneBackend( ICalTimeZoneSource *source,
173 const QString &name,
174 const QString &countryCode,
175 float latitude, float longitude,
176 const QString &comment )
177 : KTimeZoneBackend( source, name, countryCode, latitude, longitude, comment )
178{}
179
180ICalTimeZoneBackend::ICalTimeZoneBackend( const KTimeZone &tz, const QDate &earliest )
181 : KTimeZoneBackend( 0, tz.name(), tz.countryCode(), tz.latitude(), tz.longitude(), tz.comment() )
182{
183 Q_UNUSED( earliest );
184}
185
186ICalTimeZoneBackend::~ICalTimeZoneBackend()
187{}
188
189KTimeZoneBackend *ICalTimeZoneBackend::clone() const
190{
191 return new ICalTimeZoneBackend( *this );
192}
193
194QByteArray ICalTimeZoneBackend::type() const
195{
196 return "ICalTimeZone";
197}
198
199bool ICalTimeZoneBackend::hasTransitions( const KTimeZone *caller ) const
200{
201 Q_UNUSED( caller );
202 return true;
203}
204
205/******************************************************************************/
206
207ICalTimeZone::ICalTimeZone()
208 : KTimeZone( new ICalTimeZoneBackend() )
209{}
210
211ICalTimeZone::ICalTimeZone( ICalTimeZoneSource *source, const QString &name,
212 ICalTimeZoneData *data )
213 : KTimeZone( new ICalTimeZoneBackend( source, name ) )
214{
215 setData( data );
216}
217
218ICalTimeZone::ICalTimeZone( const KTimeZone &tz, const QDate &earliest )
219 : KTimeZone( new ICalTimeZoneBackend( 0, tz.name(), tz.countryCode(),
220 tz.latitude(), tz.longitude(),
221 tz.comment() ) )
222{
223 const KTimeZoneData *data = tz.data( true );
224 if ( data ) {
225 const ICalTimeZoneData *icaldata = dynamic_cast<const ICalTimeZoneData*>( data );
226 if ( icaldata ) {
227 setData( new ICalTimeZoneData( *icaldata ) );
228 } else {
229 setData( new ICalTimeZoneData( *data, tz, earliest ) );
230 }
231 }
232}
233
234ICalTimeZone::~ICalTimeZone()
235{}
236
237QString ICalTimeZone::city() const
238{
239 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
240 return dat ? dat->city() : QString();
241}
242
243QByteArray ICalTimeZone::url() const
244{
245 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
246 return dat ? dat->url() : QByteArray();
247}
248
249QDateTime ICalTimeZone::lastModified() const
250{
251 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
252 return dat ? dat->lastModified() : QDateTime();
253}
254
255QByteArray ICalTimeZone::vtimezone() const
256{
257 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
258 return dat ? dat->vtimezone() : QByteArray();
259}
260
261icaltimezone *ICalTimeZone::icalTimezone() const
262{
263 const ICalTimeZoneData *dat = static_cast<const ICalTimeZoneData*>( data() );
264 return dat ? dat->icalTimezone() : 0;
265}
266
267bool ICalTimeZone::update( const ICalTimeZone &other )
268{
269 if ( !updateBase( other ) ) {
270 return false;
271 }
272
273 KTimeZoneData *otherData = other.data() ? other.data()->clone() : 0;
274 setData( otherData, other.source() );
275 return true;
276}
277
278ICalTimeZone ICalTimeZone::utc()
279{
280 static ICalTimeZone utcZone;
281 if ( !utcZone.isValid() ) {
282 ICalTimeZoneSource tzs;
283 utcZone = tzs.parse( icaltimezone_get_utc_timezone() );
284 }
285 return utcZone;
286}
287
288/******************************************************************************/
289
290//@cond PRIVATE
291class ICalTimeZoneDataPrivate
292{
293 public:
294 ICalTimeZoneDataPrivate() : icalComponent(0) {}
295 ~ICalTimeZoneDataPrivate()
296 {
297 if ( icalComponent ) {
298 icalcomponent_free( icalComponent );
299 }
300 }
301 icalcomponent *component() const { return icalComponent; }
302 void setComponent( icalcomponent *c )
303 {
304 if ( icalComponent ) {
305 icalcomponent_free( icalComponent );
306 }
307 icalComponent = c;
308 }
309 QString location; // name of city for this time zone
310 QByteArray url; // URL of published VTIMEZONE definition (optional)
311 QDateTime lastModified; // time of last modification of the VTIMEZONE component (optional)
312 private:
313 icalcomponent *icalComponent; // ical component representing this time zone
314};
315//@endcond
316
317ICalTimeZoneData::ICalTimeZoneData()
318 : d ( new ICalTimeZoneDataPrivate() )
319{
320}
321
322ICalTimeZoneData::ICalTimeZoneData( const ICalTimeZoneData &rhs )
323 : KTimeZoneData( rhs ),
324 d( new ICalTimeZoneDataPrivate() )
325{
326 d->location = rhs.d->location;
327 d->url = rhs.d->url;
328 d->lastModified = rhs.d->lastModified;
329 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
330}
331
332ICalTimeZoneData::ICalTimeZoneData( const KTimeZoneData &rhs,
333 const KTimeZone &tz, const QDate &earliest )
334 : KTimeZoneData( rhs ),
335 d( new ICalTimeZoneDataPrivate() )
336{
337 // VTIMEZONE RRULE types
338 enum {
339 DAY_OF_MONTH = 0x01,
340 WEEKDAY_OF_MONTH = 0x02,
341 LAST_WEEKDAY_OF_MONTH = 0x04
342 };
343
344 if ( tz.type() == "KSystemTimeZone" ) {
345 // Try to fetch a system time zone in preference, on the grounds
346 // that system time zones are more likely to be up to date than
347 // built-in libical ones.
348 icalcomponent *c = 0;
349 KTimeZone ktz = KSystemTimeZones::readZone( tz.name() );
350 if ( ktz.isValid() ) {
351 if ( ktz.data(true) ) {
352 ICalTimeZone icaltz( ktz, earliest );
353 icaltimezone *itz = icaltz.icalTimezone();
354 c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
355 icaltimezone_free( itz, 1 );
356 }
357 }
358 if ( !c ) {
359 // Try to fetch a built-in libical time zone.
360 icaltimezone *itz = icaltimezone_get_builtin_timezone( tz.name().toUtf8() );
361 c = icalcomponent_new_clone( icaltimezone_get_component( itz ) );
362 }
363 if ( c ) {
364 // TZID in built-in libical time zones has a standard prefix.
365 // To make the VTIMEZONE TZID match TZID references in incidences
366 // (as required by RFC2445), strip off the prefix.
367 icalproperty *prop = icalcomponent_get_first_property( c, ICAL_TZID_PROPERTY );
368 if ( prop ) {
369 icalvalue *value = icalproperty_get_value( prop );
370 const char *tzid = icalvalue_get_text( value );
371 QByteArray icalprefix = ICalTimeZoneSource::icalTzidPrefix();
372 int len = icalprefix.size();
373 if ( !strncmp( icalprefix, tzid, len ) ) {
374 const char *s = strchr( tzid + len, '/' ); // find third '/'
375 if ( s ) {
376 QByteArray tzidShort( s + 1 ); // deep copy of string (needed by icalvalue_set_text())
377 icalvalue_set_text( value, tzidShort );
378
379 // Remove the X-LIC-LOCATION property, which is only used by libical
380 prop = icalcomponent_get_first_property( c, ICAL_X_PROPERTY );
381 const char *xname = icalproperty_get_x_name( prop );
382 if ( xname && !strcmp( xname, "X-LIC-LOCATION" ) ) {
383 icalcomponent_remove_property( c, prop );
384 }
385 }
386 }
387 }
388 }
389 d->setComponent( c );
390 } else {
391 // Write the time zone data into an iCal component
392 icalcomponent *tzcomp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
393 icalcomponent_add_property( tzcomp, icalproperty_new_tzid( tz.name().toUtf8() ) );
394// icalcomponent_add_property(tzcomp, icalproperty_new_location( tz.name().toUtf8() ));
395
396 // Compile an ordered list of transitions so that we can know the phases
397 // which occur before and after each transition.
398 QList<KTimeZone::Transition> transits = transitions();
399 if ( earliest.isValid() ) {
400 // Remove all transitions earlier than those we are interested in
401 for ( int i = 0, end = transits.count(); i < end; ++i ) {
402 if ( transits[i].time().date() >= earliest ) {
403 if ( i > 0 ) {
404 transits.erase( transits.begin(), transits.begin() + i );
405 }
406 break;
407 }
408 }
409 }
410 int trcount = transits.count();
411 QVector<bool> transitionsDone(trcount);
412 transitionsDone.fill(false);
413
414 // Go through the list of transitions and create an iCal component for each
415 // distinct combination of phase after and UTC offset before the transition.
416 icaldatetimeperiodtype dtperiod;
417 dtperiod.period = icalperiodtype_null_period();
418 for ( ; ; ) {
419 int i = 0;
420 for ( ; i < trcount && transitionsDone[i]; ++i ) {
421 ;
422 }
423 if ( i >= trcount ) {
424 break;
425 }
426 // Found a phase combination which hasn't yet been processed
427 int preOffset = ( i > 0 ) ? transits[i - 1].phase().utcOffset() : rhs.previousUtcOffset();
428 KTimeZone::Phase phase = transits[i].phase();
429 if ( phase.utcOffset() == preOffset ) {
430 transitionsDone[i] = true;
431 while ( ++i < trcount ) {
432 if ( transitionsDone[i] ||
433 transits[i].phase() != phase ||
434 transits[i - 1].phase().utcOffset() != preOffset ) {
435 continue;
436 }
437 transitionsDone[i] = true;
438 }
439 continue;
440 }
441 icalcomponent *phaseComp =
442 icalcomponent_new( phase.isDst() ? ICAL_XDAYLIGHT_COMPONENT : ICAL_XSTANDARD_COMPONENT );
443 QList<QByteArray> abbrevs = phase.abbreviations();
444 for ( int a = 0, aend = abbrevs.count(); a < aend; ++a ) {
445 icalcomponent_add_property( phaseComp,
446 icalproperty_new_tzname(
447 static_cast<const char*>( abbrevs[a]) ) );
448 }
449 if ( !phase.comment().isEmpty() ) {
450 icalcomponent_add_property( phaseComp,
451 icalproperty_new_comment( phase.comment().toUtf8() ) );
452 }
453 icalcomponent_add_property( phaseComp,
454 icalproperty_new_tzoffsetfrom( preOffset ) );
455 icalcomponent_add_property( phaseComp,
456 icalproperty_new_tzoffsetto( phase.utcOffset() ) );
457 // Create a component to hold initial RRULE if any, plus all RDATEs
458 icalcomponent *phaseComp1 = icalcomponent_new_clone( phaseComp );
459 icalcomponent_add_property( phaseComp1,
460 icalproperty_new_dtstart(
461 writeLocalICalDateTime( transits[i].time(), preOffset ) ) );
462 bool useNewRRULE = false;
463
464 // Compile the list of UTC transition dates/times, and check
465 // if the list can be reduced to an RRULE instead of multiple RDATEs.
466 QTime time;
467 QDate date;
468 int year = 0, month = 0, daysInMonth = 0, dayOfMonth = 0; // avoid compiler warnings
469 int dayOfWeek = 0; // Monday = 1
470 int nthFromStart = 0; // nth (weekday) of month
471 int nthFromEnd = 0; // nth last (weekday) of month
472 int newRule;
473 int rule = 0;
474 QList<QDateTime> rdates;// dates which (probably) need to be written as RDATEs
475 QList<QDateTime> times;
476 QDateTime qdt = transits[i].time(); // set 'qdt' for start of loop
477 times += qdt;
478 transitionsDone[i] = true;
479 do {
480 if ( !rule ) {
481 // Initialise data for detecting a new rule
482 rule = DAY_OF_MONTH | WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH;
483 time = qdt.time();
484 date = qdt.date();
485 year = date.year();
486 month = date.month();
487 daysInMonth = date.daysInMonth();
488 dayOfWeek = date.dayOfWeek(); // Monday = 1
489 dayOfMonth = date.day();
490 nthFromStart = ( dayOfMonth - 1 ) / 7 + 1; // nth (weekday) of month
491 nthFromEnd = ( daysInMonth - dayOfMonth ) / 7 + 1; // nth last (weekday) of month
492 }
493 if ( ++i >= trcount ) {
494 newRule = 0;
495 times += QDateTime(); // append a dummy value since last value in list is ignored
496 } else {
497 if ( transitionsDone[i] ||
498 transits[i].phase() != phase ||
499 transits[i - 1].phase().utcOffset() != preOffset ) {
500 continue;
501 }
502 transitionsDone[i] = true;
503 qdt = transits[i].time();
504 if ( !qdt.isValid() ) {
505 continue;
506 }
507 newRule = rule;
508 times += qdt;
509 date = qdt.date();
510 if ( qdt.time() != time ||
511 date.month() != month ||
512 date.year() != ++year ) {
513 newRule = 0;
514 } else {
515 int day = date.day();
516 if ( ( newRule & DAY_OF_MONTH ) && day != dayOfMonth ) {
517 newRule &= ~DAY_OF_MONTH;
518 }
519 if ( newRule & ( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH ) ) {
520 if ( date.dayOfWeek() != dayOfWeek ) {
521 newRule &= ~( WEEKDAY_OF_MONTH | LAST_WEEKDAY_OF_MONTH );
522 } else {
523 if ( ( newRule & WEEKDAY_OF_MONTH ) &&
524 ( day - 1 ) / 7 + 1 != nthFromStart ) {
525 newRule &= ~WEEKDAY_OF_MONTH;
526 }
527 if ( ( newRule & LAST_WEEKDAY_OF_MONTH ) &&
528 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) {
529 newRule &= ~LAST_WEEKDAY_OF_MONTH;
530 }
531 }
532 }
533 }
534 }
535 if ( !newRule ) {
536 // The previous rule (if any) no longer applies.
537 // Write all the times up to but not including the current one.
538 // First check whether any of the last RDATE values fit this rule.
539 int yr = times[0].date().year();
540 while ( !rdates.isEmpty() ) {
541 qdt = rdates.last();
542 date = qdt.date();
543 if ( qdt.time() != time ||
544 date.month() != month ||
545 date.year() != --yr ) {
546 break;
547 }
548 int day = date.day();
549 if ( rule & DAY_OF_MONTH ) {
550 if ( day != dayOfMonth ) {
551 break;
552 }
553 } else {
554 if ( date.dayOfWeek() != dayOfWeek ||
555 ( ( rule & WEEKDAY_OF_MONTH ) &&
556 ( day - 1 ) / 7 + 1 != nthFromStart ) ||
557 ( ( rule & LAST_WEEKDAY_OF_MONTH ) &&
558 ( daysInMonth - day ) / 7 + 1 != nthFromEnd ) ) {
559 break;
560 }
561 }
562 times.prepend( qdt );
563 rdates.pop_back();
564 }
565 if ( times.count() > ( useNewRRULE ? minPhaseCount : minRuleCount ) ) {
566 // There are enough dates to combine into an RRULE
567 icalrecurrencetype r;
568 icalrecurrencetype_clear( &r );
569 r.freq = ICAL_YEARLY_RECURRENCE;
570 r.count = ( year >= 2030 ) ? 0 : times.count() - 1;
571 r.by_month[0] = month;
572 if ( rule & DAY_OF_MONTH ) {
573 r.by_month_day[0] = dayOfMonth;
574 } else if ( rule & WEEKDAY_OF_MONTH ) {
575 r.by_day[0] = ( dayOfWeek % 7 + 1 ) + ( nthFromStart * 8 ); // Sunday = 1
576 } else if ( rule & LAST_WEEKDAY_OF_MONTH ) {
577 r.by_day[0] = -( dayOfWeek % 7 + 1 ) - ( nthFromEnd * 8 ); // Sunday = 1
578 }
579 icalproperty *prop = icalproperty_new_rrule( r );
580 if ( useNewRRULE ) {
581 // This RRULE doesn't start from the phase start date, so set it into
582 // a new STANDARD/DAYLIGHT component in the VTIMEZONE.
583 icalcomponent *c = icalcomponent_new_clone( phaseComp );
584 icalcomponent_add_property(
585 c, icalproperty_new_dtstart( writeLocalICalDateTime( times[0], preOffset ) ) );
586 icalcomponent_add_property( c, prop );
587 icalcomponent_add_component( tzcomp, c );
588 } else {
589 icalcomponent_add_property( phaseComp1, prop );
590 }
591 } else {
592 // Save dates for writing as RDATEs
593 for ( int t = 0, tend = times.count() - 1; t < tend; ++t ) {
594 rdates += times[t];
595 }
596 }
597 useNewRRULE = true;
598 // All date/time values but the last have been added to the VTIMEZONE.
599 // Remove them from the list.
600 qdt = times.last(); // set 'qdt' for start of loop
601 times.clear();
602 times += qdt;
603 }
604 rule = newRule;
605 } while ( i < trcount );
606
607 // Write remaining dates as RDATEs
608 for ( int rd = 0, rdend = rdates.count(); rd < rdend; ++rd ) {
609 dtperiod.time = writeLocalICalDateTime( rdates[rd], preOffset );
610 icalcomponent_add_property( phaseComp1, icalproperty_new_rdate( dtperiod ) );
611 }
612 icalcomponent_add_component( tzcomp, phaseComp1 );
613 icalcomponent_free( phaseComp );
614 }
615
616 d->setComponent( tzcomp );
617 }
618}
619
620ICalTimeZoneData::~ICalTimeZoneData()
621{
622 delete d;
623}
624
625ICalTimeZoneData &ICalTimeZoneData::operator=( const ICalTimeZoneData &rhs )
626{
627 // check for self assignment
628 if ( &rhs == this ) {
629 return *this;
630 }
631
632 KTimeZoneData::operator=( rhs );
633 d->location = rhs.d->location;
634 d->url = rhs.d->url;
635 d->lastModified = rhs.d->lastModified;
636 d->setComponent( icalcomponent_new_clone( rhs.d->component() ) );
637 return *this;
638}
639
640KTimeZoneData *ICalTimeZoneData::clone() const
641{
642 return new ICalTimeZoneData( *this );
643}
644
645QString ICalTimeZoneData::city() const
646{
647 return d->location;
648}
649
650QByteArray ICalTimeZoneData::url() const
651{
652 return d->url;
653}
654
655QDateTime ICalTimeZoneData::lastModified() const
656{
657 return d->lastModified;
658}
659
660QByteArray ICalTimeZoneData::vtimezone() const
661{
662 QByteArray result( icalcomponent_as_ical_string( d->component() ) );
663 icalmemory_free_ring();
664 return result;
665}
666
667icaltimezone *ICalTimeZoneData::icalTimezone() const
668{
669 icaltimezone *icaltz = icaltimezone_new();
670 if ( !icaltz ) {
671 return 0;
672 }
673 icalcomponent *c = icalcomponent_new_clone( d->component() );
674 if ( !icaltimezone_set_component( icaltz, c ) ) {
675 icalcomponent_free( c );
676 icaltimezone_free( icaltz, 1 );
677 return 0;
678 }
679 return icaltz;
680}
681
682bool ICalTimeZoneData::hasTransitions() const
683{
684 return true;
685}
686
687/******************************************************************************/
688
689//@cond PRIVATE
690class ICalTimeZoneSourcePrivate
691{
692 public:
693 static QList<QDateTime> parsePhase( icalcomponent *, bool daylight,
694 int &prevOffset, KTimeZone::Phase & );
695 static QByteArray icalTzidPrefix;
696};
697
698QByteArray ICalTimeZoneSourcePrivate::icalTzidPrefix;
699//@endcond
700
701ICalTimeZoneSource::ICalTimeZoneSource()
702 : KTimeZoneSource( false ),
703 d( 0 )
704{
705}
706
707ICalTimeZoneSource::~ICalTimeZoneSource()
708{
709}
710
711bool ICalTimeZoneSource::parse( const QString &fileName, ICalTimeZones &zones )
712{
713 QFile file( fileName );
714 if ( !file.open( QIODevice::ReadOnly ) ) {
715 return false;
716 }
717 QTextStream ts( &file );
718 ts.setCodec( "ISO 8859-1" );
719 QByteArray text = ts.readAll().trimmed().toLatin1();
720 file.close();
721
722 bool result = false;
723 icalcomponent *calendar = icalcomponent_new_from_string( text.data() );
724 if ( calendar ) {
725 if ( icalcomponent_isa( calendar ) == ICAL_VCALENDAR_COMPONENT ) {
726 result = parse( calendar, zones );
727 }
728 icalcomponent_free( calendar );
729 }
730 return result;
731}
732
733bool ICalTimeZoneSource::parse( icalcomponent *calendar, ICalTimeZones &zones )
734{
735 for ( icalcomponent *c = icalcomponent_get_first_component( calendar, ICAL_VTIMEZONE_COMPONENT );
736 c; c = icalcomponent_get_next_component( calendar, ICAL_VTIMEZONE_COMPONENT ) ) {
737 ICalTimeZone zone = parse( c );
738 if ( !zone.isValid() ) {
739 return false;
740 }
741 ICalTimeZone oldzone = zones.zone( zone.name() );
742 if ( oldzone.isValid() ) {
743 // The zone already exists in the collection, so update the definition
744 // of the zone rather than using a newly created one.
745 oldzone.update( zone );
746 } else if ( !zones.add( zone ) ) {
747 return false;
748 }
749 }
750 return true;
751}
752
753ICalTimeZone ICalTimeZoneSource::parse( icalcomponent *vtimezone )
754{
755 QString name;
756 QString xlocation;
757 ICalTimeZoneData *data = new ICalTimeZoneData();
758
759 // Read the fixed properties which can only appear once in VTIMEZONE
760 icalproperty *p = icalcomponent_get_first_property( vtimezone, ICAL_ANY_PROPERTY );
761 while ( p ) {
762 icalproperty_kind kind = icalproperty_isa( p );
763 switch ( kind ) {
764
765 case ICAL_TZID_PROPERTY:
766 name = QString::fromUtf8( icalproperty_get_tzid( p ) );
767 break;
768
769 case ICAL_TZURL_PROPERTY:
770 data->d->url = icalproperty_get_tzurl( p );
771 break;
772
773 case ICAL_LOCATION_PROPERTY:
774 // This isn't mentioned in RFC2445, but libical reads it ...
775 data->d->location = QString::fromUtf8( icalproperty_get_location( p ) );
776 break;
777
778 case ICAL_X_PROPERTY:
779 { // use X-LIC-LOCATION if LOCATION is missing
780 const char *xname = icalproperty_get_x_name( p );
781 if ( xname && !strcmp( xname, "X-LIC-LOCATION" ) ) {
782 xlocation = QString::fromUtf8( icalproperty_get_x( p ) );
783 }
784 break;
785 }
786 case ICAL_LASTMODIFIED_PROPERTY:
787 {
788 icaltimetype t = icalproperty_get_lastmodified(p);
789 if ( icaltime_is_utc( t ) ) {
790 data->d->lastModified = toQDateTime( t );
791 } else {
792 kDebug() << "LAST-MODIFIED not UTC";
793 }
794 break;
795 }
796 default:
797 break;
798 }
799 p = icalcomponent_get_next_property( vtimezone, ICAL_ANY_PROPERTY );
800 }
801
802 if ( name.isEmpty() ) {
803 kDebug() << "TZID missing";
804 delete data;
805 return ICalTimeZone();
806 }
807 if ( data->d->location.isEmpty() && !xlocation.isEmpty() ) {
808 data->d->location = xlocation;
809 }
810 QString prefix = QString::fromUtf8( icalTzidPrefix() );
811 if ( name.startsWith( prefix ) ) {
812 // Remove the prefix from libical built in time zone TZID
813 int i = name.indexOf( '/', prefix.length() );
814 if ( i > 0 ) {
815 name = name.mid( i + 1 );
816 }
817 }
818 //kDebug() << "---zoneId: \"" << name << '"';
819
820 /*
821 * Iterate through all time zone rules for this VTIMEZONE,
822 * and create a Phase object containing details for each one.
823 */
824 int prevOffset = 0;
825 QList<KTimeZone::Transition> transitions;
826 QDateTime earliest;
827 QList<KTimeZone::Phase> phases;
828 for ( icalcomponent *c = icalcomponent_get_first_component( vtimezone, ICAL_ANY_COMPONENT );
829 c; c = icalcomponent_get_next_component( vtimezone, ICAL_ANY_COMPONENT ) )
830 {
831 int prevoff = 0;
832 KTimeZone::Phase phase;
833 QList<QDateTime> times;
834 icalcomponent_kind kind = icalcomponent_isa( c );
835 switch ( kind ) {
836
837 case ICAL_XSTANDARD_COMPONENT:
838 //kDebug() << "---standard phase: found";
839 times = ICalTimeZoneSourcePrivate::parsePhase( c, false, prevoff, phase );
840 break;
841
842 case ICAL_XDAYLIGHT_COMPONENT:
843 //kDebug() << "---daylight phase: found";
844 times = ICalTimeZoneSourcePrivate::parsePhase( c, true, prevoff, phase );
845 break;
846
847 default:
848 kDebug() << "Unknown component:" << kind;
849 break;
850 }
851 int tcount = times.count();
852 if ( tcount ) {
853 phases += phase;
854 for ( int t = 0; t < tcount; ++t ) {
855 transitions += KTimeZone::Transition( times[t], phase );
856 }
857 if ( !earliest.isValid() || times[0] < earliest ) {
858 prevOffset = prevoff;
859 earliest = times[0];
860 }
861 }
862 }
863 data->setPhases( phases, prevOffset );
864 // Remove any "duplicate" transitions, i.e. those where two consecutive
865 // transitions have the same phase.
866 qSort( transitions );
867 for ( int t = 1, tend = transitions.count(); t < tend; ) {
868 if ( transitions[t].phase() == transitions[t - 1].phase() ) {
869 transitions.removeAt( t );
870 --tend;
871 } else {
872 ++t;
873 }
874 }
875 data->setTransitions( transitions );
876
877 data->d->setComponent( icalcomponent_new_clone( vtimezone ) );
878 kDebug() << "VTIMEZONE" << name;
879 return ICalTimeZone( this, name, data );
880}
881
882ICalTimeZone ICalTimeZoneSource::parse( icaltimezone *tz )
883{
884 /* Parse the VTIMEZONE component stored in the icaltimezone structure.
885 * This is both easier and provides more complete information than
886 * extracting already parsed data from icaltimezone.
887 */
888 return tz ? parse( icaltimezone_get_component( tz ) ) : ICalTimeZone();
889}
890
891//@cond PRIVATE
892QList<QDateTime> ICalTimeZoneSourcePrivate::parsePhase( icalcomponent *c,
893 bool daylight,
894 int &prevOffset,
895 KTimeZone::Phase &phase )
896{
897 QList<QDateTime> transitions;
898
899 // Read the observance data for this standard/daylight savings phase
900 QList<QByteArray> abbrevs;
901 QString comment;
902 prevOffset = 0;
903 int utcOffset = 0;
904 bool recurs = false;
905 bool found_dtstart = false;
906 bool found_tzoffsetfrom = false;
907 bool found_tzoffsetto = false;
908 icaltimetype dtstart = icaltime_null_time();
909
910 // Now do the ical reading.
911 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
912 while ( p ) {
913 icalproperty_kind kind = icalproperty_isa( p );
914 switch ( kind ) {
915
916 case ICAL_TZNAME_PROPERTY: // abbreviated name for this time offset
917 {
918 // TZNAME can appear multiple times in order to provide language
919 // translations of the time zone offset name.
920
921 // TODO: Does this cope with multiple language specifications?
922 QByteArray tzname = icalproperty_get_tzname( p );
923 // Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME
924 // strings, which is totally useless. So ignore those.
925 if ( ( !daylight && tzname == "Standard Time" ) ||
926 ( daylight && tzname == "Daylight Time" ) ) {
927 break;
928 }
929 if ( !abbrevs.contains( tzname ) ) {
930 abbrevs += tzname;
931 }
932 break;
933 }
934 case ICAL_DTSTART_PROPERTY: // local time at which phase starts
935 dtstart = icalproperty_get_dtstart( p );
936 found_dtstart = true;
937 break;
938
939 case ICAL_TZOFFSETFROM_PROPERTY: // UTC offset immediately before start of phase
940 prevOffset = icalproperty_get_tzoffsetfrom( p );
941 found_tzoffsetfrom = true;
942 break;
943
944 case ICAL_TZOFFSETTO_PROPERTY:
945 utcOffset = icalproperty_get_tzoffsetto( p );
946 found_tzoffsetto = true;
947 break;
948
949 case ICAL_COMMENT_PROPERTY:
950 comment = QString::fromUtf8( icalproperty_get_comment( p ) );
951 break;
952
953 case ICAL_RDATE_PROPERTY:
954 case ICAL_RRULE_PROPERTY:
955 recurs = true;
956 break;
957
958 default:
959 kDebug() << "Unknown property:" << kind;
960 break;
961 }
962 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
963 }
964
965 // Validate the phase data
966 if ( !found_dtstart || !found_tzoffsetfrom || !found_tzoffsetto ) {
967 kDebug() << "DTSTART/TZOFFSETFROM/TZOFFSETTO missing";
968 return transitions;
969 }
970
971 // Convert DTSTART to QDateTime, and from local time to UTC
972 QDateTime localStart = toQDateTime( dtstart ); // local time
973 dtstart.second -= prevOffset;
974 dtstart.zone = icaltimezone_get_utc_timezone();
975 QDateTime utcStart = toQDateTime( icaltime_normalize( dtstart ) ); // UTC
976
977 transitions += utcStart;
978 if ( recurs ) {
979 /* RDATE or RRULE is specified. There should only be one or the other, but
980 * it doesn't really matter - the code can cope with both.
981 * Note that we had to get DTSTART, TZOFFSETFROM, TZOFFSETTO before reading
982 * recurrences.
983 */
984 KDateTime klocalStart( localStart, KDateTime::Spec::ClockTime() );
985 KDateTime maxTime( MAX_DATE(), KDateTime::Spec::ClockTime() );
986 Recurrence recur;
987 icalproperty *p = icalcomponent_get_first_property( c, ICAL_ANY_PROPERTY );
988 while ( p ) {
989 icalproperty_kind kind = icalproperty_isa( p );
990 switch ( kind ) {
991
992 case ICAL_RDATE_PROPERTY:
993 {
994 icaltimetype t = icalproperty_get_rdate(p).time;
995 if ( icaltime_is_date( t ) ) {
996 // RDATE with a DATE value inherits the (local) time from DTSTART
997 t.hour = dtstart.hour;
998 t.minute = dtstart.minute;
999 t.second = dtstart.second;
1000 t.is_date = 0;
1001 t.zone = 0; // dtstart is in local time
1002 }
1003 // RFC2445 states that RDATE must be in local time,
1004 // but we support UTC as well to be safe.
1005 if ( !icaltime_is_utc( t ) ) {
1006 t.second -= prevOffset; // convert to UTC
1007 t.zone = icaltimezone_get_utc_timezone();
1008 t = icaltime_normalize( t );
1009 }
1010 transitions += toQDateTime( t );
1011 break;
1012 }
1013 case ICAL_RRULE_PROPERTY:
1014 {
1015 RecurrenceRule r;
1016 ICalFormat icf;
1017 ICalFormatImpl impl( &icf );
1018 impl.readRecurrence( icalproperty_get_rrule( p ), &r );
1019 r.setStartDt( klocalStart );
1020 // The end date time specified in an RRULE should be in UTC.
1021 // Convert to local time to avoid timesInInterval() getting things wrong.
1022 if ( r.duration() == 0 ) {
1023 KDateTime end( r.endDt() );
1024 if ( end.timeSpec() == KDateTime::Spec::UTC() ) {
1025 end.setTimeSpec( KDateTime::Spec::ClockTime() );
1026 r.setEndDt( end.addSecs( prevOffset ) );
1027 }
1028 }
1029 DateTimeList dts = r.timesInInterval( klocalStart, maxTime );
1030 for ( int i = 0, end = dts.count(); i < end; ++i ) {
1031 QDateTime utc = dts[i].dateTime();
1032 utc.setTimeSpec( Qt::UTC );
1033 transitions += utc.addSecs( -prevOffset );
1034 }
1035 break;
1036 }
1037 default:
1038 break;
1039 }
1040 p = icalcomponent_get_next_property( c, ICAL_ANY_PROPERTY );
1041 }
1042 qSortUnique( transitions );
1043 }
1044
1045 phase = KTimeZone::Phase( utcOffset, abbrevs, daylight, comment );
1046 return transitions;
1047}
1048//@endcond
1049
1050ICalTimeZone ICalTimeZoneSource::standardZone( const QString &zone, bool icalBuiltIn )
1051{
1052 if ( !icalBuiltIn ) {
1053 // Try to fetch a system time zone in preference, on the grounds
1054 // that system time zones are more likely to be up to date than
1055 // built-in libical ones.
1056 QString tzid = zone;
1057 QString prefix = QString::fromUtf8( icalTzidPrefix() );
1058 if ( zone.startsWith( prefix ) ) {
1059 int i = zone.indexOf( '/', prefix.length() );
1060 if ( i > 0 ) {
1061 tzid = zone.mid( i + 1 ); // strip off the libical prefix
1062 }
1063 }
1064 KTimeZone ktz = KSystemTimeZones::readZone( tzid );
1065 if ( ktz.isValid() ) {
1066 if ( ktz.data( true ) ) {
1067 ICalTimeZone icaltz( ktz );
1068 //kDebug() << zone << " read from system database";
1069 return icaltz;
1070 }
1071 }
1072 }
1073 // Try to fetch a built-in libical time zone.
1074 // First try to look it up as a geographical location (e.g. Europe/London)
1075 QByteArray zoneName = zone.toUtf8();
1076 icaltimezone *icaltz = icaltimezone_get_builtin_timezone( zoneName );
1077 if ( !icaltz ) {
1078 // This will find it if it includes the libical prefix
1079 icaltz = icaltimezone_get_builtin_timezone_from_tzid( zoneName );
1080 if ( !icaltz ) {
1081 return ICalTimeZone();
1082 }
1083 }
1084 return parse( icaltz );
1085}
1086
1087QByteArray ICalTimeZoneSource::icalTzidPrefix()
1088{
1089 if ( ICalTimeZoneSourcePrivate::icalTzidPrefix.isEmpty() ) {
1090 icaltimezone *icaltz = icaltimezone_get_builtin_timezone( "Europe/London" );
1091 QByteArray tzid = icaltimezone_get_tzid( icaltz );
1092 if ( tzid.right( 13 ) == "Europe/London" ) {
1093 int i = tzid.indexOf( '/', 1 );
1094 if ( i > 0 ) {
1095 ICalTimeZoneSourcePrivate::icalTzidPrefix = tzid.left( i + 1 );
1096 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1097 }
1098 }
1099 kError() << "failed to get libical TZID prefix";
1100 }
1101 return ICalTimeZoneSourcePrivate::icalTzidPrefix;
1102}
1103
1104} // namespace KCal
KCal::ICalFormatImpl
This class provides the libical dependent functions for ICalFormat.
Definition: icalformat_p.h:68
KCal::ICalFormat
iCalendar format implementation.
Definition: icalformat.h:53
KCal::ICalTimeZoneBackend
Backend class for KICalTimeZone class.
Definition: icaltimezones.h:259
KCal::ICalTimeZoneBackend::hasTransitions
virtual bool hasTransitions(const KTimeZone *caller) const
Implements ICalTimeZone::hasTransitions().
Definition: icaltimezones.cpp:199
KCal::ICalTimeZoneBackend::ICalTimeZoneBackend
ICalTimeZoneBackend()
Implements ICalTimeZone::ICalTimeZone().
Definition: icaltimezones.cpp:168
KCal::ICalTimeZoneBackend::type
virtual QByteArray type() const
Returns the class name of the data represented by this instance.
Definition: icaltimezones.cpp:194
KCal::ICalTimeZoneBackend::clone
virtual KTimeZoneBackend * clone() const
Creates a copy of this instance.
Definition: icaltimezones.cpp:189
KCal::ICalTimeZoneData
Parsed iCalendar VTIMEZONE data.
Definition: icaltimezones.h:435
KCal::ICalTimeZoneData::ICalTimeZoneData
ICalTimeZoneData()
Default constructor.
Definition: icaltimezones.cpp:317
KCal::ICalTimeZoneData::~ICalTimeZoneData
virtual ~ICalTimeZoneData()
Destructor.
Definition: icaltimezones.cpp:620
KCal::ICalTimeZoneData::city
QString city() const
Returns the name of the city for this time zone, if any.
Definition: icaltimezones.cpp:645
KCal::ICalTimeZoneData::clone
virtual KTimeZoneData * clone() const
Creates a new copy of this object.
Definition: icaltimezones.cpp:640
KCal::ICalTimeZoneData::url
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Definition: icaltimezones.cpp:650
KCal::ICalTimeZoneData::lastModified
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
Definition: icaltimezones.cpp:655
KCal::ICalTimeZoneData::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:660
KCal::ICalTimeZoneData::hasTransitions
virtual bool hasTransitions() const
Return whether daylight saving transitions are available for the time zone.
Definition: icaltimezones.cpp:682
KCal::ICalTimeZoneData::operator=
ICalTimeZoneData & operator=(const ICalTimeZoneData &rhs)
Assignment operator.
Definition: icaltimezones.cpp:625
KCal::ICalTimeZoneData::icalTimezone
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
Definition: icaltimezones.cpp:667
KCal::ICalTimeZoneSource
A class which reads and parses iCalendar VTIMEZONE components, and accesses libical time zone data.
Definition: icaltimezones.h:329
KCal::ICalTimeZoneSource::parse
ICalTimeZone parse(icalcomponent *vtimezone)
Creates an ICalTimeZone instance containing the detailed information parsed from a VTIMEZONE componen...
Definition: icaltimezones.cpp:753
KCal::ICalTimeZoneSource::ICalTimeZoneSource
ICalTimeZoneSource()
Constructs an iCalendar time zone source.
Definition: icaltimezones.cpp:701
KCal::ICalTimeZoneSource::icalTzidPrefix
static QByteArray icalTzidPrefix()
Returns the prefix string used in the TZID field in built-in libical time zones.
Definition: icaltimezones.cpp:1087
KCal::ICalTimeZoneSource::standardZone
ICalTimeZone standardZone(const QString &zone, bool icalBuiltIn=false)
Creates an ICalTimeZone instance for a standard time zone.
Definition: icaltimezones.cpp:1050
KCal::ICalTimeZoneSource::~ICalTimeZoneSource
virtual ~ICalTimeZoneSource()
Destructor.
Definition: icaltimezones.cpp:707
KCal::ICalTimeZone
The ICalTimeZone class represents an iCalendar VTIMEZONE component.
Definition: icaltimezones.h:144
KCal::ICalTimeZone::city
QString city() const
Returns the name of the city for this time zone, if any.
Definition: icaltimezones.cpp:237
KCal::ICalTimeZone::ICalTimeZone
ICalTimeZone()
Constructs a null time zone.
Definition: icaltimezones.cpp:207
KCal::ICalTimeZone::url
QByteArray url() const
Returns the URL of the published VTIMEZONE definition, if any.
Definition: icaltimezones.cpp:243
KCal::ICalTimeZone::icalTimezone
icaltimezone * icalTimezone() const
Returns the ICal timezone structure which represents this time zone.
Definition: icaltimezones.cpp:261
KCal::ICalTimeZone::utc
static ICalTimeZone utc()
Returns a standard UTC time zone, with name "UTC".
Definition: icaltimezones.cpp:278
KCal::ICalTimeZone::vtimezone
QByteArray vtimezone() const
Returns the VTIMEZONE string which represents this time zone.
Definition: icaltimezones.cpp:255
KCal::ICalTimeZone::lastModified
QDateTime lastModified() const
Returns the LAST-MODIFIED time of the VTIMEZONE, if any.
Definition: icaltimezones.cpp:249
KCal::ICalTimeZone::~ICalTimeZone
virtual ~ICalTimeZone()
Destructor.
Definition: icaltimezones.cpp:234
KCal::ICalTimeZone::update
bool update(const ICalTimeZone &other)
Update the definition of the time zone to be identical to another ICalTimeZone instance.
Definition: icaltimezones.cpp:267
KCal::ICalTimeZones
The ICalTimeZones class represents a time zone database which consists of a collection of individual ...
Definition: icaltimezones.h:65
KCal::ICalTimeZones::ICalTimeZones
ICalTimeZones()
Constructs an empty time zone collection.
Definition: icaltimezones.cpp:96
KCal::ICalTimeZones::zones
const ZoneMap zones() const
Returns all the time zones defined in this collection.
Definition: icaltimezones.cpp:106
KCal::ICalTimeZones::~ICalTimeZones
~ICalTimeZones()
Destructor.
Definition: icaltimezones.cpp:101
KCal::ICalTimeZones::clear
void clear()
Clears the collection.
Definition: icaltimezones.cpp:150
KCal::ICalTimeZones::remove
ICalTimeZone remove(const ICalTimeZone &zone)
Removes a time zone from the collection.
Definition: icaltimezones.cpp:124
KCal::ICalTimeZones::zone
ICalTimeZone zone(const QString &name) const
Returns the time zone with the given name.
Definition: icaltimezones.cpp:155
KCal::ICalTimeZones::add
bool add(const ICalTimeZone &zone)
Adds a time zone to the collection.
Definition: icaltimezones.cpp:111
KCal::RecurrenceRule
This class represents a recurrence rule for a calendar incidence.
Definition: recurrencerule.h:47
KCal::RecurrenceRule::duration
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
Definition: recurrencerule.cpp:2124
KCal::RecurrenceRule::setEndDt
void setEndDt(const KDateTime &endDateTime)
Sets the date and time of the last recurrence.
Definition: recurrencerule.cpp:968
KCal::RecurrenceRule::endDt
KDateTime endDt(bool *result=0) const
Returns the date and time of the last recurrence.
Definition: recurrencerule.cpp:937
KCal::RecurrenceRule::setStartDt
void setStartDt(const KDateTime &start)
Sets the recurrence start date/time.
Definition: recurrencerule.cpp:1006
KCal::RecurrenceRule::timesInInterval
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.
Definition: recurrencerule.cpp:1717
KCal::Recurrence
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:92
KCal::SortableList
A QList which can be sorted.
Definition: sortablelist.h:87
icalformat.h
This file is part of the API for handling calendar data and defines the ICalFormat class.
icalformat_p.h
This file is part of the API for handling calendar data and defines the internal ICalFormatImpl class...
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jul 21 2022 00:00:00 by doxygen 1.9.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal