Fix dnd email
[kdepim.git] / kalarm / alarmtime.cpp
blob432f0d3068ff87e64e5327e20ee5b30f228b2086
1 /*
2 * alarmtime.cpp - conversion functions for alarm times
3 * Program: kalarm
4 * Copyright © 2007-2012 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "alarmtime.h"
22 #include "preferences.h"
24 #include <kalarmcal/datetime.h>
26 #include <ksystemtimezone.h>
27 #include <KLocalizedString>
28 #include <KLocale>
29 #include <QApplication>
30 #include "kalarm_debug.h"
32 using namespace KAlarmCal;
34 int AlarmTime::mTimeHourPos = -2;
36 /******************************************************************************
37 * Return the alarm time text in the form "date time".
39 QString AlarmTime::alarmTimeText(const DateTime& dateTime)
41 if (!dateTime.isValid())
42 return i18nc("@info Alarm never occurs", "Never");
43 KLocale* locale = KLocale::global();
44 KDateTime kdt = dateTime.effectiveKDateTime().toTimeSpec(Preferences::timeZone());
45 QString dateTimeText = locale->formatDate(kdt.date(), KLocale::ShortDate);
46 if (!dateTime.isDateOnly()
47 || (!dateTime.isClockTime() && kdt.utcOffset() != dateTime.utcOffset()))
49 // Display the time of day if it's a date/time value, or if it's
50 // a date-only value but it's in a different time zone
51 dateTimeText += QLatin1Char(' ');
52 QString time = locale->formatTime(kdt.time());
53 if (mTimeHourPos == -2)
55 // Initialise the position of the hour within the time string, if leading
56 // zeroes are omitted, so that displayed times can be aligned with each other.
57 mTimeHourPos = -1; // default = alignment isn't possible/sensible
58 if (QApplication::isLeftToRight()) // don't try to align right-to-left languages
60 QString fmt = locale->timeFormat();
61 int i = fmt.indexOf(QRegExp(QLatin1String("%[kl]"))); // check if leading zeroes are omitted
62 if (i >= 0 && i == fmt.indexOf(QLatin1Char('%'))) // and whether the hour is first
63 mTimeHourPos = i; // yes, so need to align
66 if (mTimeHourPos >= 0 && (int)time.length() > mTimeHourPos + 1
67 && time[mTimeHourPos].isDigit() && !time[mTimeHourPos + 1].isDigit())
68 dateTimeText += QLatin1Char('~'); // improve alignment of times with no leading zeroes
69 dateTimeText += time;
71 return dateTimeText + QLatin1Char(' ');
74 /******************************************************************************
75 * Return the time-to-alarm text.
77 QString AlarmTime::timeToAlarmText(const DateTime& dateTime)
79 if (!dateTime.isValid())
80 return i18nc("@info Alarm never occurs", "Never");
81 KDateTime now = KDateTime::currentUtcDateTime();
82 if (dateTime.isDateOnly())
84 int days = now.date().daysTo(dateTime.date());
85 // xgettext: no-c-format
86 return i18nc("@info n days", "%1d", days);
88 int mins = (now.secsTo(dateTime.effectiveKDateTime()) + 59) / 60;
89 if (mins < 0)
90 return QString();
91 char minutes[3] = "00";
92 minutes[0] = (mins%60) / 10 + '0';
93 minutes[1] = (mins%60) % 10 + '0';
94 if (mins < 24*60)
95 return i18nc("@info hours:minutes", "%1:%2", mins/60, QLatin1String(minutes));
96 int days = mins / (24*60);
97 mins = mins % (24*60);
98 return i18nc("@info days hours:minutes", "%1d %2:%3", days, mins/60, QLatin1String(minutes));
101 /******************************************************************************
102 * Convert a date/time specification string into a local date/time or date value.
103 * Parameters:
104 * timeString = in the form [[[yyyy-]mm-]dd-]hh:mm [TZ] or yyyy-mm-dd [TZ].
105 * dateTime = receives converted date/time value.
106 * defaultDt = default date/time used for missing parts of timeString, or null
107 * to use current date/time.
108 * allowTZ = whether to allow a time zone specifier in timeString.
109 * Reply = true if successful.
111 bool AlarmTime::convertTimeString(const QByteArray& timeString, KDateTime& dateTime, const KDateTime& defaultDt, bool allowTZ)
113 #define MAX_DT_LEN 19
114 int i = timeString.indexOf(' ');
115 if (i > MAX_DT_LEN || (i >= 0 && !allowTZ))
116 return false;
117 QString zone = (i >= 0) ? QString::fromLatin1(timeString.mid(i)) : QString();
118 char timeStr[MAX_DT_LEN+1];
119 strcpy(timeStr, timeString.left(i >= 0 ? i : MAX_DT_LEN));
120 int dt[5] = { -1, -1, -1, -1, -1 };
121 char* s;
122 char* end;
123 bool noTime;
124 // Get the minute value
125 if ((s = strchr(timeStr, ':')) == Q_NULLPTR)
126 noTime = true;
127 else
129 noTime = false;
130 *s++ = 0;
131 dt[4] = strtoul(s, &end, 10);
132 if (end == s || *end || dt[4] >= 60)
133 return false;
134 // Get the hour value
135 if ((s = strrchr(timeStr, '-')) == Q_NULLPTR)
136 s = timeStr;
137 else
138 *s++ = 0;
139 dt[3] = strtoul(s, &end, 10);
140 if (end == s || *end || dt[3] >= 24)
141 return false;
143 bool noDate = true;
144 if (s != timeStr)
146 noDate = false;
147 // Get the day value
148 if ((s = strrchr(timeStr, '-')) == Q_NULLPTR)
149 s = timeStr;
150 else
151 *s++ = 0;
152 dt[2] = strtoul(s, &end, 10);
153 if (end == s || *end || dt[2] == 0 || dt[2] > 31)
154 return false;
155 if (s != timeStr)
157 // Get the month value
158 if ((s = strrchr(timeStr, '-')) == Q_NULLPTR)
159 s = timeStr;
160 else
161 *s++ = 0;
162 dt[1] = strtoul(s, &end, 10);
163 if (end == s || *end || dt[1] == 0 || dt[1] > 12)
164 return false;
165 if (s != timeStr)
167 // Get the year value
168 dt[0] = strtoul(timeStr, &end, 10);
169 if (end == timeStr || *end)
170 return false;
175 QDate date;
176 if (dt[0] >= 0)
177 date = QDate(dt[0], dt[1], dt[2]);
178 QTime time(0, 0, 0);
179 if (noTime)
181 // No time was specified, so the full date must have been specified
182 if (dt[0] < 0 || !date.isValid())
183 return false;
184 dateTime = applyTimeZone(zone, date, time, false, defaultDt);
186 else
188 // Compile the values into a date/time structure
189 time.setHMS(dt[3], dt[4], 0);
190 if (dt[0] < 0)
192 // Some or all of the date was omitted.
193 // Use the default date/time if provided.
194 if (defaultDt.isValid())
196 dt[0] = defaultDt.date().year();
197 date.setYMD(dt[0],
198 (dt[1] < 0 ? defaultDt.date().month() : dt[1]),
199 (dt[2] < 0 ? defaultDt.date().day() : dt[2]));
201 else
202 date.setYMD(2000, 1, 1); // temporary substitute for date
204 dateTime = applyTimeZone(zone, date, time, true, defaultDt);
205 if (!dateTime.isValid())
206 return false;
207 if (dt[0] < 0)
209 // Some or all of the date was omitted.
210 // Use the current date in the specified time zone as default.
211 KDateTime now = KDateTime::currentDateTime(dateTime.timeSpec());
212 date = dateTime.date();
213 date.setYMD(now.date().year(),
214 (dt[1] < 0 ? now.date().month() : dt[1]),
215 (dt[2] < 0 ? now.date().day() : dt[2]));
216 if (!date.isValid())
217 return false;
218 if (noDate && time < now.time())
219 date = date.addDays(1);
220 dateTime.setDate(date);
223 return dateTime.isValid();
226 /******************************************************************************
227 * Convert a time zone specifier string and apply it to a given date and/or time.
228 * The time zone specifier is a system time zone name, e.g. "Europe/London",
229 * "UTC" or "Clock". If no time zone is specified, it defaults to the local time
230 * zone.
231 * If 'defaultDt' is valid, it supplies the time spec and default date.
233 KDateTime AlarmTime::applyTimeZone(const QString& tzstring, const QDate& date, const QTime& time,
234 bool haveTime, const KDateTime& defaultDt)
236 bool error = false;
237 KDateTime::Spec spec = KDateTime::LocalZone;
238 const QString zone = tzstring.trimmed();
239 if (!zone.isEmpty())
241 if (zone == QStringLiteral("Clock"))
242 spec = KDateTime::ClockTime;
243 else if (zone == QStringLiteral("UTC"))
244 spec = KDateTime::UTC;
245 else
247 KTimeZone tz = KSystemTimeZones::zone(zone);
248 error = !tz.isValid();
249 if (!error)
250 spec = tz;
253 else if (defaultDt.isValid())
254 spec = defaultDt.timeSpec();
256 KDateTime result;
257 if (!error)
259 if (!date.isValid())
261 // It's a time without a date
262 if (defaultDt.isValid())
263 result = KDateTime(defaultDt.date(), time, spec);
264 else if (spec == KDateTime::LocalZone || spec == KDateTime::ClockTime)
265 result = KDateTime(KDateTime::currentLocalDate(), time, spec);
267 else if (haveTime)
269 // It's a date and time
270 result = KDateTime(date, time, spec);
272 else
274 // It's a date without a time
275 result = KDateTime(date, spec);
278 return result;
281 // vim: et sw=4: