mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 10:52:52 +00:00
641 lines
19 KiB
C++
641 lines
19 KiB
C++
/*
|
|
This file is part of the kcalcore library.
|
|
|
|
Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
/**
|
|
@file
|
|
This file is part of the API for handling calendar data and
|
|
defines the ICalFormat class.
|
|
|
|
@brief
|
|
iCalendar format implementation: a layer of abstraction for libical.
|
|
|
|
@author Cornelius Schumacher \<schumacher@kde.org\>
|
|
*/
|
|
#include "icalformat.h"
|
|
#include "icalformat_p.h"
|
|
#include "icaltimezones.h"
|
|
#include "freebusy.h"
|
|
#include "memorycalendar.h"
|
|
|
|
#include <KDebug>
|
|
#include <KSaveFile>
|
|
|
|
#include <QtCore/QFile>
|
|
|
|
extern "C" {
|
|
#include <libical/ical.h>
|
|
#include <libical/icalss.h>
|
|
#include <libical/icalparser.h>
|
|
#include <libical/icalrestriction.h>
|
|
#include <libical/icalmemory.h>
|
|
}
|
|
|
|
using namespace KCalCore;
|
|
|
|
//@cond PRIVATE
|
|
class KCalCore::ICalFormat::Private
|
|
{
|
|
public:
|
|
Private(ICalFormat *parent)
|
|
: mImpl(new ICalFormatImpl(parent)),
|
|
mTimeSpec(KDateTime::UTC)
|
|
{}
|
|
~Private() {
|
|
delete mImpl;
|
|
}
|
|
ICalFormatImpl *mImpl;
|
|
KDateTime::Spec mTimeSpec;
|
|
};
|
|
//@endcond
|
|
|
|
ICalFormat::ICalFormat()
|
|
: d(new Private(this))
|
|
{
|
|
}
|
|
|
|
ICalFormat::~ICalFormat()
|
|
{
|
|
icalmemory_free_ring();
|
|
delete d;
|
|
}
|
|
|
|
bool ICalFormat::load(const Calendar::Ptr &calendar, const QString &fileName)
|
|
{
|
|
kDebug() << fileName;
|
|
|
|
clearException();
|
|
|
|
QFile file(fileName);
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
kError() << "load error";
|
|
setException(new Exception(Exception::LoadError));
|
|
return false;
|
|
}
|
|
QTextStream ts(&file);
|
|
ts.setCodec("UTF-8");
|
|
QByteArray text = ts.readAll().trimmed().toUtf8();
|
|
file.close();
|
|
|
|
if (text.isEmpty()) {
|
|
// empty files are valid
|
|
return true;
|
|
} else {
|
|
return fromRawString(calendar, text, false, fileName);
|
|
}
|
|
}
|
|
|
|
bool ICalFormat::save(const Calendar::Ptr &calendar, const QString &fileName)
|
|
{
|
|
kDebug() << fileName;
|
|
|
|
clearException();
|
|
|
|
QString text = toString(calendar);
|
|
if (text.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
// Write backup file
|
|
KSaveFile::backupFile(fileName);
|
|
|
|
KSaveFile file(fileName);
|
|
if (!file.open()) {
|
|
kError() << "file open error: " << file.errorString() << ";filename=" << fileName;
|
|
setException(new Exception(Exception::SaveErrorOpenFile,
|
|
QStringList(fileName)));
|
|
|
|
return false;
|
|
}
|
|
|
|
// Convert to UTF8 and save
|
|
QByteArray textUtf8 = text.toUtf8();
|
|
file.write(textUtf8.data(), textUtf8.size());
|
|
|
|
if (!file.finalize()) {
|
|
kDebug() << "file finalize error:" << file.errorString();
|
|
setException(new Exception(Exception::SaveErrorSaveFile,
|
|
QStringList(fileName)));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ICalFormat::fromString(const Calendar::Ptr &cal, const QString &string,
|
|
bool deleted, const QString ¬ebook)
|
|
{
|
|
return fromRawString(cal, string.toUtf8(), deleted, notebook);
|
|
}
|
|
|
|
bool ICalFormat::fromRawString(const Calendar::Ptr &cal, const QByteArray &string,
|
|
bool deleted, const QString ¬ebook)
|
|
{
|
|
Q_UNUSED(notebook);
|
|
// Get first VCALENDAR component.
|
|
// TODO: Handle more than one VCALENDAR or non-VCALENDAR top components
|
|
icalcomponent *calendar;
|
|
|
|
// Let's defend const correctness until the very gates of hell^Wlibical
|
|
calendar = icalcomponent_new_from_string(const_cast<char*>((const char *)string));
|
|
if (!calendar) {
|
|
kError() << "parse error ; string is empty?" << string.isEmpty();
|
|
setException(new Exception(Exception::ParseErrorIcal));
|
|
return false;
|
|
}
|
|
|
|
bool success = true;
|
|
|
|
if (icalcomponent_isa(calendar) == ICAL_XROOT_COMPONENT) {
|
|
icalcomponent *comp;
|
|
for (comp = icalcomponent_get_first_component(calendar, ICAL_VCALENDAR_COMPONENT);
|
|
comp; comp = icalcomponent_get_next_component(calendar, ICAL_VCALENDAR_COMPONENT)) {
|
|
// put all objects into their proper places
|
|
if (!d->mImpl->populate(cal, comp, deleted)) {
|
|
kError() << "Could not populate calendar";
|
|
if (!exception()) {
|
|
setException(new Exception(Exception::ParseErrorKcal));
|
|
}
|
|
success = false;
|
|
} else {
|
|
setLoadedProductId(d->mImpl->loadedProductId());
|
|
}
|
|
}
|
|
} else if (icalcomponent_isa(calendar) != ICAL_VCALENDAR_COMPONENT) {
|
|
kDebug() << "No VCALENDAR component found";
|
|
setException(new Exception(Exception::NoCalendar));
|
|
success = false;
|
|
} else {
|
|
// put all objects into their proper places
|
|
if (!d->mImpl->populate(cal, calendar, deleted)) {
|
|
kDebug() << "Could not populate calendar";
|
|
if (!exception()) {
|
|
setException(new Exception(Exception::ParseErrorKcal));
|
|
}
|
|
success = false;
|
|
} else {
|
|
setLoadedProductId(d->mImpl->loadedProductId());
|
|
}
|
|
}
|
|
|
|
icalcomponent_free(calendar);
|
|
icalmemory_free_ring();
|
|
|
|
return success;
|
|
}
|
|
|
|
Incidence::Ptr ICalFormat::fromString(const QString &string)
|
|
{
|
|
MemoryCalendar::Ptr cal(new MemoryCalendar(d->mTimeSpec));
|
|
fromString(cal, string);
|
|
|
|
const Incidence::List list = cal->incidences();
|
|
return !list.isEmpty() ? list.first() : Incidence::Ptr();
|
|
}
|
|
|
|
QString ICalFormat::toString(const Calendar::Ptr &cal,
|
|
const QString ¬ebook, bool deleted)
|
|
{
|
|
icalcomponent *calendar = d->mImpl->createCalendarComponent(cal);
|
|
icalcomponent *component;
|
|
|
|
ICalTimeZones *tzlist = cal->timeZones(); // time zones possibly used in the calendar
|
|
ICalTimeZones tzUsedList; // time zones actually used in the calendar
|
|
|
|
// todos
|
|
Todo::List todoList = deleted ? cal->deletedTodos() : cal->rawTodos();
|
|
Todo::List::ConstIterator it;
|
|
for (it = todoList.constBegin(); it != todoList.constEnd(); ++it) {
|
|
if (!deleted || !cal->todo((*it)->uid(), (*it)->recurrenceId())) {
|
|
// use existing ones, or really deleted ones
|
|
if (notebook.isEmpty() ||
|
|
(!cal->notebook(*it).isEmpty() && notebook.endsWith(cal->notebook(*it)))) {
|
|
component = d->mImpl->writeTodo(*it, tzlist, &tzUsedList);
|
|
icalcomponent_add_component(calendar, component);
|
|
}
|
|
}
|
|
}
|
|
// events
|
|
Event::List events = deleted ? cal->deletedEvents() : cal->rawEvents();
|
|
Event::List::ConstIterator it2;
|
|
for (it2 = events.constBegin(); it2 != events.constEnd(); ++it2) {
|
|
if (!deleted || !cal->event((*it2)->uid(), (*it2)->recurrenceId())) {
|
|
// use existing ones, or really deleted ones
|
|
if (notebook.isEmpty() ||
|
|
(!cal->notebook(*it2).isEmpty() && notebook.endsWith(cal->notebook(*it2)))) {
|
|
component = d->mImpl->writeEvent(*it2, tzlist, &tzUsedList);
|
|
icalcomponent_add_component(calendar, component);
|
|
}
|
|
}
|
|
}
|
|
|
|
// journals
|
|
Journal::List journals = deleted ? cal->deletedJournals() : cal->rawJournals();
|
|
Journal::List::ConstIterator it3;
|
|
for (it3 = journals.constBegin(); it3 != journals.constEnd(); ++it3) {
|
|
if (!deleted || !cal->journal((*it3)->uid(), (*it3)->recurrenceId())) {
|
|
// use existing ones, or really deleted ones
|
|
if (notebook.isEmpty() ||
|
|
(!cal->notebook(*it3).isEmpty() && notebook.endsWith(cal->notebook(*it3)))) {
|
|
component = d->mImpl->writeJournal(*it3, tzlist, &tzUsedList);
|
|
icalcomponent_add_component(calendar, component);
|
|
}
|
|
}
|
|
}
|
|
|
|
// time zones
|
|
ICalTimeZones::ZoneMap zones = tzUsedList.zones();
|
|
if (todoList.isEmpty() && events.isEmpty() && journals.isEmpty()) {
|
|
// no incidences means no used timezones, use all timezones
|
|
// this will export a calendar having only timezone definitions
|
|
zones = tzlist->zones();
|
|
}
|
|
for (ICalTimeZones::ZoneMap::ConstIterator it = zones.constBegin();
|
|
it != zones.constEnd(); ++it) {
|
|
icaltimezone *tz = (*it).icalTimezone();
|
|
if (!tz) {
|
|
kError() << "bad time zone";
|
|
} else {
|
|
component = icalcomponent_new_clone(icaltimezone_get_component(tz));
|
|
icalcomponent_add_component(calendar, component);
|
|
icaltimezone_free(tz, 1);
|
|
}
|
|
}
|
|
|
|
char *const componentString = icalcomponent_as_ical_string_r(calendar);
|
|
const QString &text = QString::fromUtf8(componentString);
|
|
free(componentString);
|
|
|
|
icalcomponent_free(calendar);
|
|
icalmemory_free_ring();
|
|
|
|
if (text.isEmpty()) {
|
|
setException(new Exception(Exception::LibICalError));
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
QString ICalFormat::toICalString(const Incidence::Ptr &incidence)
|
|
{
|
|
MemoryCalendar::Ptr cal(new MemoryCalendar(d->mTimeSpec));
|
|
cal->addIncidence(Incidence::Ptr(incidence->clone()));
|
|
return toString(cal.staticCast<Calendar>());
|
|
}
|
|
|
|
QString ICalFormat::toString(const Incidence::Ptr &incidence)
|
|
{
|
|
return QString::fromUtf8(toRawString(incidence));
|
|
}
|
|
|
|
QByteArray ICalFormat::toRawString(const Incidence::Ptr &incidence)
|
|
{
|
|
icalcomponent *component;
|
|
ICalTimeZones tzlist;
|
|
ICalTimeZones tzUsedList;
|
|
|
|
component = d->mImpl->writeIncidence(incidence, iTIPRequest, &tzlist, &tzUsedList);
|
|
|
|
QByteArray text = icalcomponent_as_ical_string(component);
|
|
|
|
// time zones
|
|
ICalTimeZones::ZoneMap zones = tzUsedList.zones();
|
|
for (ICalTimeZones::ZoneMap::ConstIterator it = zones.constBegin();
|
|
it != zones.constEnd(); ++it) {
|
|
icaltimezone *tz = (*it).icalTimezone();
|
|
if (!tz) {
|
|
kError() << "bad time zone";
|
|
} else {
|
|
icalcomponent *tzcomponent = icaltimezone_get_component(tz);
|
|
icalcomponent_add_component(component, component);
|
|
text.append(icalcomponent_as_ical_string(tzcomponent));
|
|
icaltimezone_free(tz, 1);
|
|
}
|
|
}
|
|
|
|
icalcomponent_free(component);
|
|
|
|
return text;
|
|
}
|
|
|
|
QString ICalFormat::toString(RecurrenceRule *recurrence)
|
|
{
|
|
icalproperty *property;
|
|
property = icalproperty_new_rrule(d->mImpl->writeRecurrenceRule(recurrence));
|
|
QString text = QString::fromUtf8(icalproperty_as_ical_string(property));
|
|
icalproperty_free(property);
|
|
return text;
|
|
}
|
|
|
|
bool ICalFormat::fromString(RecurrenceRule *recurrence, const QString &rrule)
|
|
{
|
|
if (!recurrence) {
|
|
return false;
|
|
}
|
|
bool success = true;
|
|
icalerror_clear_errno();
|
|
struct icalrecurrencetype recur = icalrecurrencetype_from_string(rrule.toLatin1());
|
|
if (icalerrno != ICAL_NO_ERROR) {
|
|
kDebug() << "Recurrence parsing error:" << icalerror_strerror(icalerrno);
|
|
success = false;
|
|
}
|
|
|
|
if (success) {
|
|
d->mImpl->readRecurrence(recur, recurrence);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
QString ICalFormat::createScheduleMessage(const IncidenceBase::Ptr &incidence,
|
|
iTIPMethod method)
|
|
{
|
|
icalcomponent *message = 0;
|
|
|
|
if (incidence->type() == Incidence::TypeEvent ||
|
|
incidence->type() == Incidence::TypeTodo) {
|
|
|
|
Incidence::Ptr i = incidence.staticCast<Incidence>();
|
|
|
|
// Recurring events need timezone information to allow proper calculations
|
|
// across timezones with different DST.
|
|
const bool useUtcTimes = !i->recurs();
|
|
|
|
const bool hasSchedulingId = (i->schedulingID() != i->uid());
|
|
|
|
const bool incidenceNeedChanges = (useUtcTimes || hasSchedulingId);
|
|
|
|
if (incidenceNeedChanges) {
|
|
// The incidence need changes, so clone it before we continue
|
|
i = Incidence::Ptr(i->clone());
|
|
|
|
// Handle conversion to UTC times
|
|
if (useUtcTimes) {
|
|
i->shiftTimes(KDateTime::Spec::UTC(), KDateTime::Spec::UTC());
|
|
}
|
|
|
|
// Handle scheduling ID being present
|
|
if (hasSchedulingId) {
|
|
// We have a separation of scheduling ID and UID
|
|
i->setSchedulingID(QString(), i->schedulingID());
|
|
|
|
}
|
|
|
|
// Build the message with the cloned incidence
|
|
message = d->mImpl->createScheduleComponent(i, method);
|
|
}
|
|
}
|
|
|
|
if (message == 0) {
|
|
message = d->mImpl->createScheduleComponent(incidence, method);
|
|
}
|
|
|
|
QString messageText = QString::fromUtf8(icalcomponent_as_ical_string(message));
|
|
|
|
icalcomponent_free(message);
|
|
return messageText;
|
|
}
|
|
|
|
FreeBusy::Ptr ICalFormat::parseFreeBusy(const QString &str)
|
|
{
|
|
clearException();
|
|
|
|
icalcomponent *message;
|
|
message = icalparser_parse_string(str.toUtf8());
|
|
|
|
if (!message) {
|
|
return FreeBusy::Ptr();
|
|
}
|
|
|
|
FreeBusy::Ptr freeBusy;
|
|
|
|
icalcomponent *c;
|
|
for (c = icalcomponent_get_first_component(message, ICAL_VFREEBUSY_COMPONENT);
|
|
c != 0; c = icalcomponent_get_next_component(message, ICAL_VFREEBUSY_COMPONENT)) {
|
|
FreeBusy::Ptr fb = d->mImpl->readFreeBusy(c);
|
|
|
|
if (freeBusy) {
|
|
freeBusy->merge(fb);
|
|
} else {
|
|
freeBusy = fb;
|
|
}
|
|
}
|
|
|
|
if (!freeBusy) {
|
|
kDebug() << "object is not a freebusy.";
|
|
}
|
|
|
|
icalcomponent_free(message);
|
|
|
|
return freeBusy;
|
|
}
|
|
|
|
ScheduleMessage::Ptr ICalFormat::parseScheduleMessage(const Calendar::Ptr &cal,
|
|
const QString &messageText)
|
|
{
|
|
setTimeSpec(cal->timeSpec());
|
|
clearException();
|
|
|
|
if (messageText.isEmpty()) {
|
|
setException(
|
|
new Exception(Exception::ParseErrorEmptyMessage));
|
|
return ScheduleMessage::Ptr();
|
|
}
|
|
|
|
icalcomponent *message;
|
|
message = icalparser_parse_string(messageText.toUtf8());
|
|
|
|
if (!message) {
|
|
setException(
|
|
new Exception(Exception::ParseErrorUnableToParse));
|
|
|
|
return ScheduleMessage::Ptr();
|
|
}
|
|
|
|
icalproperty *m =
|
|
icalcomponent_get_first_property(message, ICAL_METHOD_PROPERTY);
|
|
if (!m) {
|
|
setException(
|
|
new Exception(Exception::ParseErrorMethodProperty));
|
|
|
|
return ScheduleMessage::Ptr();
|
|
}
|
|
|
|
// Populate the message's time zone collection with all VTIMEZONE components
|
|
ICalTimeZones tzlist;
|
|
ICalTimeZoneSource tzs;
|
|
tzs.parse(message, tzlist);
|
|
|
|
icalcomponent *c;
|
|
|
|
IncidenceBase::Ptr incidence;
|
|
c = icalcomponent_get_first_component(message, ICAL_VEVENT_COMPONENT);
|
|
if (c) {
|
|
incidence = d->mImpl->readEvent(c, &tzlist).staticCast<IncidenceBase>();
|
|
}
|
|
|
|
if (!incidence) {
|
|
c = icalcomponent_get_first_component(message, ICAL_VTODO_COMPONENT);
|
|
if (c) {
|
|
incidence = d->mImpl->readTodo(c, &tzlist).staticCast<IncidenceBase>();
|
|
}
|
|
}
|
|
|
|
if (!incidence) {
|
|
c = icalcomponent_get_first_component(message, ICAL_VJOURNAL_COMPONENT);
|
|
if (c) {
|
|
incidence = d->mImpl->readJournal(c, &tzlist).staticCast<IncidenceBase>();
|
|
}
|
|
}
|
|
|
|
if (!incidence) {
|
|
c = icalcomponent_get_first_component(message, ICAL_VFREEBUSY_COMPONENT);
|
|
if (c) {
|
|
incidence = d->mImpl->readFreeBusy(c).staticCast<IncidenceBase>();
|
|
}
|
|
}
|
|
|
|
if (!incidence) {
|
|
kDebug() << "object is not a freebusy, event, todo or journal";
|
|
setException(new Exception(Exception::ParseErrorNotIncidence));
|
|
|
|
return ScheduleMessage::Ptr();
|
|
}
|
|
|
|
icalproperty_method icalmethod = icalproperty_get_method(m);
|
|
iTIPMethod method;
|
|
|
|
switch (icalmethod) {
|
|
case ICAL_METHOD_PUBLISH:
|
|
method = iTIPPublish;
|
|
break;
|
|
case ICAL_METHOD_REQUEST:
|
|
method = iTIPRequest;
|
|
break;
|
|
case ICAL_METHOD_REFRESH:
|
|
method = iTIPRefresh;
|
|
break;
|
|
case ICAL_METHOD_CANCEL:
|
|
method = iTIPCancel;
|
|
break;
|
|
case ICAL_METHOD_ADD:
|
|
method = iTIPAdd;
|
|
break;
|
|
case ICAL_METHOD_REPLY:
|
|
method = iTIPReply;
|
|
break;
|
|
case ICAL_METHOD_COUNTER:
|
|
method = iTIPCounter;
|
|
break;
|
|
case ICAL_METHOD_DECLINECOUNTER:
|
|
method = iTIPDeclineCounter;
|
|
break;
|
|
default:
|
|
method = iTIPNoMethod;
|
|
kDebug() << "Unknown method";
|
|
break;
|
|
}
|
|
|
|
if (!icalrestriction_check(message)) {
|
|
kWarning() << endl
|
|
<< "kcalcore library reported a problem while parsing:";
|
|
kWarning() << ScheduleMessage::methodName(method) << ":" //krazy:exclude=kdebug
|
|
<< d->mImpl->extractErrorProperty(c);
|
|
}
|
|
|
|
Incidence::Ptr existingIncidence = cal->incidence(incidence->uid());
|
|
|
|
icalcomponent *calendarComponent = 0;
|
|
if (existingIncidence) {
|
|
calendarComponent = d->mImpl->createCalendarComponent(cal);
|
|
|
|
// TODO: check, if cast is required, or if it can be done by virtual funcs.
|
|
// TODO: Use a visitor for this!
|
|
if (existingIncidence->type() == Incidence::TypeTodo) {
|
|
Todo::Ptr todo = existingIncidence.staticCast<Todo>();
|
|
icalcomponent_add_component(calendarComponent,
|
|
d->mImpl->writeTodo(todo));
|
|
}
|
|
if (existingIncidence->type() == Incidence::TypeEvent) {
|
|
Event::Ptr event = existingIncidence.staticCast<Event>();
|
|
icalcomponent_add_component(calendarComponent,
|
|
d->mImpl->writeEvent(event));
|
|
}
|
|
} else {
|
|
icalcomponent_free(message);
|
|
return ScheduleMessage::Ptr(new ScheduleMessage(incidence, method,
|
|
ScheduleMessage::Unknown));
|
|
}
|
|
|
|
icalproperty_xlicclass result =
|
|
icalclassify(message, calendarComponent, static_cast<const char *>(""));
|
|
|
|
ScheduleMessage::Status status;
|
|
|
|
switch (result) {
|
|
case ICAL_XLICCLASS_PUBLISHNEW:
|
|
status = ScheduleMessage::PublishNew;
|
|
break;
|
|
case ICAL_XLICCLASS_PUBLISHUPDATE:
|
|
status = ScheduleMessage::PublishUpdate;
|
|
break;
|
|
case ICAL_XLICCLASS_OBSOLETE:
|
|
status = ScheduleMessage::Obsolete;
|
|
break;
|
|
case ICAL_XLICCLASS_REQUESTNEW:
|
|
status = ScheduleMessage::RequestNew;
|
|
break;
|
|
case ICAL_XLICCLASS_REQUESTUPDATE:
|
|
status = ScheduleMessage::RequestUpdate;
|
|
break;
|
|
case ICAL_XLICCLASS_UNKNOWN:
|
|
default:
|
|
status = ScheduleMessage::Unknown;
|
|
break;
|
|
}
|
|
|
|
icalcomponent_free(message);
|
|
icalcomponent_free(calendarComponent);
|
|
|
|
return ScheduleMessage::Ptr(new ScheduleMessage(incidence, method, status));
|
|
}
|
|
|
|
void ICalFormat::setTimeSpec(const KDateTime::Spec &timeSpec)
|
|
{
|
|
d->mTimeSpec = timeSpec;
|
|
}
|
|
|
|
KDateTime::Spec ICalFormat::timeSpec() const
|
|
{
|
|
return d->mTimeSpec;
|
|
}
|
|
|
|
QString ICalFormat::timeZoneId() const
|
|
{
|
|
KTimeZone tz = d->mTimeSpec.timeZone();
|
|
return tz.isValid() ? tz.name() : QString();
|
|
}
|
|
|
|
void ICalFormat::virtual_hook(int id, void *data)
|
|
{
|
|
Q_UNUSED(id);
|
|
Q_UNUSED(data);
|
|
Q_ASSERT(false);
|
|
}
|