[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-pim
Subject: Re: [Kde-pim] PATCH, RFC: Korganizer and timezones (bug 68345)
From: Shaheed <srhaque () iee ! org>
Date: 2003-12-06 17:04:09
[Download RAW message or body]
Hi all,
Here is an updated version of this patch. It eliminates all usage of the
icalrecur_xxx() routines which pretend to do semantically useful things since
they seem horribly broken and impossible to read. As a result:
1. A memory leak due to not freeing the iterator has gone
2. Timezone adjustments work even if the date of interest is not in the last
phase of the definition.
Can I please check this in?
Thanks, Shaheed
["timezone2.diff" (text/x-diff)]
? timezone.diff
? timezone2.diff
Index: icalformatimpl.cpp
===================================================================
RCS file: /home/kde/kdepim/libkcal/icalformatimpl.cpp,v
retrieving revision 1.93
diff -u -p -r1.93 icalformatimpl.cpp
--- icalformatimpl.cpp 18 Nov 2003 23:37:26 -0000 1.93
+++ icalformatimpl.cpp 6 Dec 2003 16:50:28 -0000
@@ -23,6 +23,7 @@
#include <qstring.h>
#include <qptrlist.h>
#include <qfile.h>
+#include <cstdlib>
#include <kdebug.h>
#include <klocale.h>
@@ -44,6 +45,281 @@ extern "C" {
using namespace KCal;
+namespace KCal {
+
+// This macro reads any TZID setting for an icaltime. TBD: incorporate
+// this into icalproperty_get_dtend()/icalproperty_get_dtend()?
+#define GET_TZID(p,icaltime) \
+{ \
+ icalproperty *tzp = icalproperty_get_first_parameter(p,ICAL_TZID_PARAMETER); \
+ if (tzp) { \
+ icaltime.zone = icalparameter_get_tzid(tzp); \
+ } \
+}
+
+/**
+ * A TimezonePhase represents a setting within a timezone, e.g. standard or
+ * daylight savings.
+ */
+typedef struct icaltimezonephase icaltimezonephase;
+class TimezonePhase : private icaltimezonephase {
+ public:
+ /**
+ * Contructor for a timezone phase.
+ */
+ TimezonePhase(ICalFormatImpl *parent, icalcomponent *c)
+ {
+ tzname = (const char *)0;
+ is_stdandard = 1;
+ mIsStandard = 1;
+ dtstart = icaltime_null_time();
+ offsetto = 0;
+ tzoffsetfrom = 0;
+ comment = (const char *)0;
+ rdate.time = icaltime_null_time();
+ rdate.period = icalperiodtype_null_period();
+ rrule = (const char *)0;
+ mRrule = new Recurrence((Incidence *)0);
+
+ // Now do the ical reading.
+ icalproperty *p = icalcomponent_get_first_property(c,ICAL_ANY_PROPERTY);
+ while (p) {
+ icalproperty_kind kind = icalproperty_isa(p);
+ switch (kind) {
+
+ case ICAL_TZNAME_PROPERTY:
+ tzname = icalproperty_get_tzname(p);
+ break;
+
+ case ICAL_DTSTART_PROPERTY:
+ dtstart = icalproperty_get_dtstart(p);
+ break;
+
+ case ICAL_TZOFFSETTO_PROPERTY:
+ offsetto = icalproperty_get_tzoffsetto(p);
+ break;
+
+ case ICAL_TZOFFSETFROM_PROPERTY:
+ tzoffsetfrom = icalproperty_get_tzoffsetfrom(p);
+ break;
+
+ case ICAL_COMMENT_PROPERTY:
+ comment = icalproperty_get_comment(p);
+ break;
+
+ case ICAL_RDATE_PROPERTY:
+ rdate = icalproperty_get_rdate(p);
+ break;
+
+ case ICAL_RRULE_PROPERTY:
+ {
+ struct icalrecurrencetype r = icalproperty_get_rrule(p);
+
+ parent->readRecurrence(r,mRrule);
+ }
+ break;
+
+ default:
+ kdDebug(5800) << "TimezonePhase::TimezonePhase(): Unknown property: " << \
kind + << endl;
+ break;
+ }
+ p = icalcomponent_get_next_property(c,ICAL_ANY_PROPERTY);
+ }
+ }
+
+ /**
+ * Destructor for a timezone phase.
+ */
+ ~TimezonePhase()
+ {
+ delete mRrule;
+ }
+
+ /**
+ * Find the nearest start time of this phase just before a given time.
+ */
+ QDateTime nearestStart(const QDateTime &t) const
+ {
+ QDateTime tmp(QDate(dtstart.year,dtstart.month,dtstart.day), \
QTime(dtstart.hour,dtstart.minute,dtstart.second)); + // If this phase was \
not valid at the given time, give up. + if (tmp > t) {
+ kdDebug(5800) << "TimezonePhase::nearestStart(): Phase not valid" << endl;
+ return QDateTime();
+ }
+
+ // The Recurrance class's getPreviousDateTime() logic was not designed for
+ // start times which are not aligned with a reference time, but a little
+ // magic is sufficient to work around that...
+ QDateTime previous = mRrule->getPreviousDateTime(tmp);
+ if (mRrule->getNextDateTime(previous) < tmp)
+ previous = mRrule->getNextDateTime(previous);
+ return previous;
+ }
+
+ /**
+ * Offset of this phase in seconds.
+ */
+ int offset() const
+ {
+ return offsetto;
+ }
+
+ // Hide the missnamed "is_stdandard" variable in the base class.
+ int mIsStandard;
+
+ // Supplement the "rrule" in the base class.
+ Recurrence *mRrule;
+};
+
+/**
+ * A Timezone.
+ */
+typedef struct icaltimezonetype icaltimezonetype;
+class Timezone : private icaltimezonetype {
+ public:
+ /**
+ * Contructor for a timezone.
+ */
+ Timezone(ICalFormatImpl *parent, icalcomponent *vtimezone)
+ {
+ tzid = (const char *)0;
+ last_mod = icaltime_null_time();
+ tzurl = (const char *)0;
+
+ // The phases list is defined to be terminated by a phase with a
+ // null name.
+ phases = (icaltimezonephase *)malloc(sizeof(*phases));
+ phases[0].tzname = (const char *)0;
+ mPhases = new QPtrList<TimezonePhase>;
+ mPhases->setAutoDelete(true);
+
+ // Now do the ical reading.
+ icalproperty *p = \
icalcomponent_get_first_property(vtimezone,ICAL_ANY_PROPERTY); + while (p) {
+ icalproperty_kind kind = icalproperty_isa(p);
+ switch (kind) {
+
+ case ICAL_TZID_PROPERTY:
+ // The timezone id is basically a unique string which is used to
+ // identify this timezone. Note that if it begins with a "/", then it
+ // is suppsed to have some externally specified meaning, but we are
+ // just after its unique value.
+ tzid = icalproperty_get_tzid(p);
+ break;
+
+ case ICAL_TZURL_PROPERTY:
+ tzurl = icalproperty_get_tzurl(p);
+ break;
+
+ default:
+ kdDebug(5800) << "Timezone::Timezone(): Unknown property: " << kind
+ << endl;
+ break;
+ }
+ p = icalcomponent_get_next_property(vtimezone,ICAL_ANY_PROPERTY);
+ }
+ kdDebug(5800) << "---zoneId: \"" << tzid << '"' << endl;
+
+ icalcomponent *c;
+
+ TimezonePhase *phase;
+
+ // Iterate through all timezones before we do anything else. That way, the
+ // information needed to interpret times in actually usefulobject is
+ // available below.
+ c = icalcomponent_get_first_component(vtimezone,ICAL_ANY_COMPONENT);
+ while (c) {
+ icalcomponent_kind kind = icalcomponent_isa(c);
+ switch (kind) {
+
+ case ICAL_XSTANDARD_COMPONENT:
+ kdDebug(5800) << "---standard phase: found" << endl;
+ phase = new TimezonePhase(parent,c);
+ phase->mIsStandard = 1;
+ mPhases->append(phase);
+ break;
+
+ case ICAL_XDAYLIGHT_COMPONENT:
+ kdDebug(5800) << "---daylight phase: found" << endl;
+ phase = new TimezonePhase(parent,c);
+ phase->mIsStandard = 0;
+ mPhases->append(phase);
+ break;
+
+ default:
+ kdDebug(5800) << "Timezone::Timezone(): Unknown component: " << kind
+ << endl;
+ break;
+ }
+ c = icalcomponent_get_next_component(vtimezone,ICAL_ANY_COMPONENT);
+ }
+ }
+
+ /**
+ * Destructor for a timezone.
+ */
+ ~Timezone()
+ {
+ delete mPhases;
+ free(phases);
+ }
+
+ /**
+ * The string id of this timezone. Make sure we always have quotes!
+ */
+ QString id() const
+ {
+ if (tzid[0] != '"') {
+ return QString("\"") + tzid + '"';
+ } else {
+ return tzid;
+ }
+ }
+
+ /**
+ * Find the nearest timezone phase just before a given time.
+ */
+ const TimezonePhase *nearestStart(const QDateTime &t) const
+ {
+ unsigned i;
+ unsigned result = 0;
+ QDateTime previous;
+ QDateTime next;
+
+ // Main loop. Find the phase with the latest start date before t.
+ for (i = 0; i < mPhases->count(); i++) {
+ next = mPhases->at(i)->nearestStart(t);
+ if (previous.isNull() || previous < next) {
+ previous = next;
+ result = i;
+ }
+ }
+ return mPhases->at(result);
+ }
+
+ /**
+ * Convert the given time to UTC.
+ */
+ int offset(icaltimetype t)
+ {
+ QDateTime tmp(QDate(t.year,t.month,t.day), QTime(t.hour,t.minute,t.second)); \
+ const TimezonePhase *phase = nearestStart(tmp);
+
+ if (phase) {
+ return phase->offset();
+ } else {
+ kdError(5800) << "Timezone::offset() cannot find phase for " << tmp << endl;
+ return 0;
+ }
+ }
+
+ // Phases we have seen.
+ QPtrList<TimezonePhase> *mPhases;
+};
+
+}
+
const int gSecondsPerMinute = 60;
const int gSecondsPerHour = gSecondsPerMinute * 60;
const int gSecondsPerDay = gSecondsPerHour * 24;
@@ -53,10 +329,12 @@ ICalFormatImpl::ICalFormatImpl( ICalForm
mParent( parent ), mCalendarVersion( 0 )
{
mCompat = new Compat;
+ mTimezones = new QDict<class Timezone>;
}
ICalFormatImpl::~ICalFormatImpl()
{
+ delete mTimezones;
delete mCompat;
}
@@ -107,10 +385,10 @@ icalcomponent *ICalFormatImpl::writeTodo
if (todo->hasStartDate()) {
icaltimetype start;
if (todo->doesFloat()) {
-// kdDebug(5800) << "§§ Incidence " << todo->summary() << " floats." << endl;
+// kdDebug(5800) << " Incidence " << todo->summary() << " floats." << endl;
start = writeICalDate(todo->dtStart().date());
} else {
-// kdDebug(5800) << "§§ incidence " << todo->summary() << " has time." << endl;
+// kdDebug(5800) << " incidence " << todo->summary() << " has time." << endl;
start = writeICalDateTime(todo->dtStart());
}
icalcomponent_add_property(vtodo,icalproperty_new_dtstart(start));
@@ -148,10 +426,10 @@ icalcomponent *ICalFormatImpl::writeEven
// start time
icaltimetype start;
if (event->doesFloat()) {
-// kdDebug(5800) << "§§ Incidence " << event->summary() << " floats." << endl;
+// kdDebug(5800) << " Incidence " << event->summary() << " floats." << endl;
start = writeICalDate(event->dtStart().date());
} else {
-// kdDebug(5800) << "§§ incidence " << event->summary() << " has time." << endl;
+// kdDebug(5800) << " incidence " << event->summary() << " has time." << endl;
start = writeICalDateTime(event->dtStart());
}
icalcomponent_add_property(vevent,icalproperty_new_dtstart(start));
@@ -160,11 +438,11 @@ icalcomponent *ICalFormatImpl::writeEven
// end time
icaltimetype end;
if (event->doesFloat()) {
-// kdDebug(5800) << "§§ Event " << event->summary() << " floats." << endl;
+// kdDebug(5800) << " Event " << event->summary() << " floats." << endl;
// +1 day because end date is non-inclusive.
end = writeICalDate( event->dtEnd().date().addDays( 1 ) );
} else {
-// kdDebug(5800) << "§§ Event " << event->summary() << " has time." << endl;
+// kdDebug(5800) << " Event " << event->summary() << " has time." << endl;
end = writeICalDateTime(event->dtEnd());
}
icalcomponent_add_property(vevent,icalproperty_new_dtend(end));
@@ -240,10 +518,10 @@ icalcomponent *ICalFormatImpl::writeJour
if (journal->dtStart().isValid()) {
icaltimetype start;
if (journal->doesFloat()) {
-// kdDebug(5800) << "§§ Incidence " << event->summary() << " floats." << endl;
+// kdDebug(5800) << " Incidence " << event->summary() << " floats." << endl;
start = writeICalDate(journal->dtStart().date());
} else {
-// kdDebug(5800) << "§§ incidence " << event->summary() << " has time." << \
endl; +// kdDebug(5800) << " incidence " << event->summary() << " has time." << \
endl; start = writeICalDateTime(journal->dtStart());
}
icalcomponent_add_property(vjournal,icalproperty_new_dtstart(start));
@@ -781,6 +1059,16 @@ icalcomponent *ICalFormatImpl::writeAlar
return a;
}
+// Read a timezone and store it in a list where it can be accessed as needed
+// by the other readXXX() routines. Note that no writeTimezone is needed
+// because we always store in UTC.
+void ICalFormatImpl::readTimezone(icalcomponent *vtimezone)
+{
+ Timezone *timezone = new Timezone(this, vtimezone);
+
+ mTimezones->insert(timezone->id(), timezone);
+}
+
Todo *ICalFormatImpl::readTodo(icalcomponent *vtodo)
{
Todo *todo = new Todo;
@@ -865,6 +1153,7 @@ Event *ICalFormatImpl::readEvent(icalcom
case ICAL_DTEND_PROPERTY: // start date and time
icaltime = icalproperty_get_dtend(p);
+ GET_TZID(p,icaltime);
if (icaltime.is_date) {
event->setFloats( true );
// End date is non-inclusive
@@ -984,11 +1273,13 @@ FreeBusy *ICalFormatImpl::readFreeBusy(i
case ICAL_DTSTART_PROPERTY: // start date and time
icaltime = icalproperty_get_dtstart(p);
+ GET_TZID(p,icaltime);
freebusy->setDtStart(readICalDateTime(icaltime));
break;
case ICAL_DTEND_PROPERTY: // start End Date and Time
icaltime = icalproperty_get_dtend(p);
+ GET_TZID(p,icaltime);
freebusy->setDtEnd(readICalDateTime(icaltime));
break;
@@ -1169,6 +1460,7 @@ void ICalFormatImpl::readIncidence(icalc
case ICAL_DTSTART_PROPERTY: // start date and time
icaltime = icalproperty_get_dtstart(p);
+ GET_TZID(p,icaltime);
if (icaltime.is_date) {
incidence->setDtStart(QDateTime(readICalDate(icaltime),QTime(0,0,0)));
incidence->setFloats(true);
@@ -1700,8 +1992,31 @@ QDateTime ICalFormatImpl::readICalDateTi
kdDebug(5800) << "--- H: " << t.hour << " M: " << t.minute << " S: " << t.second
<< endl;
kdDebug(5800) << "--- isDate: " << t.is_date << endl;
+ kdDebug(5800) << "--- isUtc: " << t.is_utc << endl;
+ kdDebug(5800) << "--- zoneId: " << t.zone << endl;
*/
+ // First convert the time into UTC if required.
+ if ( !t.is_utc && t.zone ) {
+ Timezone *timezone;
+
+ // Always lookup with quotes.
+ if (t.zone[0] != '"') {
+ timezone = mTimezones->find(QString("\"") + t.zone + '"');
+ } else {
+ timezone = mTimezones->find(t.zone);
+ }
+ if (timezone) {
+ // Apply the offset, and mark the structure as UTC!
+ t.second -= timezone->offset(t);
+ t = icaltime_normalize(t);
+ t.is_utc = 1;
+ } else {
+ kdError(5800) << "ICalFormatImpl::readICalDateTime() cannot find timezone "
+ << t.zone << endl;
+ }
+ }
+
if ( t.is_utc && mCompat->useTimeZoneShift() ) {
// kdDebug(5800) << "--- Converting time to zone '" << cal->timeZoneId() << "'." \
<< endl; if (mParent->timeZoneId().isEmpty())
@@ -1895,6 +2210,16 @@ bool ICalFormatImpl::populate( Calendar
icalcomponent *c;
+ // Iterate through all timezones before we do anything else. That way, the
+ // information needed to interpret times in actually useful objects is
+ // available below.
+ c = icalcomponent_get_first_component(calendar,ICAL_VTIMEZONE_COMPONENT);
+ while (c) {
+// kdDebug(5800) << "----Timezone found" << endl;
+ readTimezone(c);
+ c = icalcomponent_get_next_component(calendar,ICAL_VTIMEZONE_COMPONENT);
+ }
+
// Iterate through all todos
c = icalcomponent_get_first_component(calendar,ICAL_VTODO_COMPONENT);
while (c) {
Index: icalformatimpl.h
===================================================================
RCS file: /home/kde/kdepim/libkcal/icalformatimpl.h,v
retrieving revision 1.22
diff -u -p -r1.22 icalformatimpl.h
--- icalformatimpl.h 22 Jul 2003 18:56:46 -0000 1.22
+++ icalformatimpl.h 6 Dec 2003 16:50:28 -0000
@@ -86,9 +86,11 @@ class ICalFormatImpl {
void writeCustomProperties(icalcomponent *parent,CustomProperties *);
void readCustomProperties(icalcomponent *parent,CustomProperties *);
void dumpIcalRecurrence(icalrecurrencetype);
+ void readTimezone(icalcomponent *vtimezone);
ICalFormat *mParent;
Calendar *mCalendar;
+ QDict<class Timezone> *mTimezones;
QString mLoadedProductId; // PRODID string loaded from calendar file
int mCalendarVersion; // determines backward compatibility mode on \
read
_______________________________________________
kde-pim mailing list
kde-pim@mail.kde.org
https://mail.kde.org/mailman/listinfo/kde-pim
kde-pim home page at http://pim.kde.org/
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic