File indexing completed on 2022-10-04 16:37:43

0001 /*
0002   kmime_dateformatter.cpp
0003 
0004   KMime, the KDE Internet mail/usenet news message library.
0005   SPDX-FileCopyrightText: 2001 the KMime authors.
0006   See file AUTHORS for details
0007 
0008   SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 /**
0011   @file
0012   This file is part of the API for handling @ref MIME data and
0013   defines the DateFormatter class.
0014 
0015   @brief
0016   Defines the DateFormatter class.
0017 
0018   @authors the KMime authors (see AUTHORS file)
0019 */
0020 
0021 #include "kmime_dateformatter.h"
0022 
0023 #include <config-kmime.h>
0024 
0025 #include <QTextStream>
0026 #include <QIODevice>
0027 #include <KLocalizedString>
0028 
0029 using namespace KMime;
0030 
0031 namespace KMime {
0032 
0033 class DateFormatterPrivate {
0034 public:
0035     DateFormatterPrivate()
0036     {}
0037 
0038     /**
0039       Returns a QString containing the specified time_t @p t formatted
0040       using the #Fancy #FormatType.
0041 
0042       @param t is the time_t to use for formatting.
0043     */
0044     QString fancy(time_t t);
0045 
0046     /**
0047       Returns a QString containing the specified time_t @p t formatted
0048       using the #Localized #FormatType.
0049 
0050       @param t is the time_t to use for formatting.
0051       @param shortFormat if true, create the short version of the date string.
0052       @param lang is a QString containing the language to use.
0053     */
0054     static QString localized(time_t t, bool shortFormat = true, const QString &lang = QString());
0055 
0056     /**
0057       Returns a QString containing the specified time_t @p t formatted
0058       with the ctime() function.
0059 
0060       @param t is the time_t to use for formatting.
0061     */
0062     static QString cTime(time_t t);
0063 
0064     /**
0065       Returns a QString containing the specified time_t @p t in the
0066       "%Y-%m-%d %H:%M:%S" #Iso #FormatType.
0067 
0068       @param t is the time_t to use for formatting.
0069     */
0070     static QString isoDate(time_t t);
0071 
0072     /**
0073       Returns a QString containing the specified time_t @p t in the
0074       #Rfc #FormatType.
0075 
0076       @param t is the time_t to use for formatting.
0077     */
0078     static QString rfc2822(time_t t);
0079 
0080     /**
0081       Returns a QString containing the specified time_t @p t formatted
0082       with a previously specified custom format.
0083 
0084       @param t time used for formatting
0085     */
0086     QString custom(time_t t) const;
0087 
0088     /**
0089       Returns a QString that identifies the timezone (eg."-0500")
0090       of the specified time_t @p t.
0091 
0092       @param t time to compute timezone from.
0093     */
0094     static QByteArray zone(time_t t);
0095 
0096     DateFormatter::FormatType mFormat;
0097     time_t mTodayOneSecondBeforeMidnight = 0;
0098     QString mCustomFormat;
0099 };
0100 
0101 }
0102 
0103 DateFormatter::DateFormatter(FormatType ftype) :
0104     d(new DateFormatterPrivate)
0105 {
0106     d->mFormat = ftype;
0107 }
0108 
0109 DateFormatter::~DateFormatter() = default;
0110 
0111 DateFormatter::FormatType DateFormatter::format() const
0112 {
0113     return d->mFormat;
0114 }
0115 
0116 void DateFormatter::setFormat(FormatType ftype)
0117 {
0118     d->mFormat = ftype;
0119 }
0120 
0121 QString DateFormatter::dateString(time_t t, const QString &lang, bool shortFormat) const
0122 {
0123     switch (d->mFormat) {
0124     case Fancy:
0125         return d->fancy(t);
0126     case Localized:
0127         return d->localized(t, shortFormat, lang);
0128     case CTime:
0129         return d->cTime(t);
0130     case Iso:
0131         return d->isoDate(t);
0132     case Rfc:
0133         return d->rfc2822(t);
0134     case Custom:
0135         return d->custom(t);
0136     }
0137     return {};
0138 }
0139 
0140 QString DateFormatter::dateString(const QDateTime &dt, const QString &lang, bool shortFormat) const
0141 {
0142     return dateString(dt.toLocalTime().toSecsSinceEpoch(), lang, shortFormat);
0143 }
0144 
0145 QString DateFormatterPrivate::rfc2822(time_t t)
0146 {
0147     QDateTime tmp;
0148     QString ret;
0149 
0150     tmp.setSecsSinceEpoch(t);
0151 
0152     ret = tmp.toString(QStringLiteral("ddd, dd MMM yyyy hh:mm:ss "));
0153     ret += QLatin1String(zone(t));
0154 
0155     return ret;
0156 }
0157 
0158 QString DateFormatterPrivate::custom(time_t t) const
0159 {
0160     if (mCustomFormat.isEmpty()) {
0161       return {};
0162     }
0163 
0164     int z = mCustomFormat.indexOf(QLatin1Char('Z'));
0165     QDateTime dt;
0166     QString ret = mCustomFormat;
0167 
0168     dt.setSecsSinceEpoch(t);
0169     if (z != -1) {
0170         ret.replace(z, 1, QLatin1String(zone(t)));
0171     }
0172 
0173     ret = dt.toString(ret);
0174 
0175     return ret;
0176 }
0177 
0178 void DateFormatter::setCustomFormat(const QString &format)
0179 {
0180     d->mCustomFormat = format;
0181     d->mFormat = Custom;
0182 }
0183 
0184 QString DateFormatter::customFormat() const
0185 {
0186     return d->mCustomFormat;
0187 }
0188 
0189 QByteArray DateFormatterPrivate::zone(time_t t)
0190 {
0191 #if HAVE_TIMEZONE || HAVE_TM_GMTOFF
0192     struct tm *local = localtime(&t);
0193 #endif
0194 
0195 #if HAVE_TIMEZONE
0196 
0197     //hmm, could make hours & mins static
0198     int secs = qAbs(timezone);
0199     int neg  = (timezone > 0) ? 1 : 0;
0200     int hours = secs / 3600;
0201     int mins  = (secs - hours * 3600) / 60;
0202 
0203     // adjust to daylight
0204     if (local->tm_isdst > 0) {
0205         if (neg) {
0206             --hours;
0207         } else {
0208             ++hours;
0209         }
0210     }
0211 
0212 #elif HAVE_TM_GMTOFF
0213 
0214     int secs = qAbs(local->tm_gmtoff);
0215     int neg  = (local->tm_gmtoff < 0) ? 1 : 0;
0216     int hours = secs / 3600;
0217     int mins  = (secs - hours * 3600) / 60;
0218 
0219 #else
0220 
0221     QDateTime d1 = QDateTime::fromString(QString::fromLatin1(asctime(gmtime(&t))));
0222     QDateTime d2 = QDateTime::fromString(QString::fromLatin1(asctime(localtime(&t))));
0223     int secs = d1.secsTo(d2);
0224     int neg = (secs < 0) ? 1 : 0;
0225     secs = qAbs(secs);
0226     int hours = secs / 3600;
0227     int mins  = (secs - hours * 3600) / 60;
0228 
0229 #endif /* HAVE_TIMEZONE */
0230 
0231     QByteArray ret;
0232     QTextStream s(&ret, QIODevice::WriteOnly);
0233     s << (neg ? '-' : '+')         
0234       << qSetFieldWidth(2) << qSetPadChar(QLatin1Char('0'))
0235       << Qt::right
0236       << hours << mins;
0237     //old code: ret.sprintf( "%c%.2d%.2d", (neg) ? '-' : '+', hours, mins );
0238 
0239     return ret;
0240 }
0241 
0242 QString DateFormatterPrivate::fancy(time_t t)
0243 {
0244     auto locale = QLocale::system();
0245 
0246     if (t <= 0) {
0247         return i18nc("invalid time specified", "unknown");
0248     }
0249 
0250     if (mTodayOneSecondBeforeMidnight < time(nullptr)) {
0251         // determine time_t value of today 23:59:59
0252         const QDateTime today(QDate::currentDate(), QTime(23, 59, 59));
0253         mTodayOneSecondBeforeMidnight = today.toSecsSinceEpoch();
0254     }
0255 
0256     QDateTime old;
0257     old.setSecsSinceEpoch(t);
0258 
0259     if (mTodayOneSecondBeforeMidnight >= t) {
0260         const time_t diff = mTodayOneSecondBeforeMidnight - t;
0261         if (diff < 7 * 24 * 60 * 60) {
0262             if (diff < 24 * 60 * 60) {
0263                 return i18n("Today %1",
0264                             locale.toString(old.time(), QLocale::ShortFormat));
0265             }
0266             if (diff < 2 * 24 * 60 * 60) {
0267                 return i18n("Yesterday %1",
0268                             locale.toString(old.time(), QLocale::ShortFormat));
0269             }
0270             for (int i = 3; i < 8; i++) {
0271                 if (diff < i * 24 * 60 * 60) {
0272                     return i18nc("1. weekday, 2. time", "%1 %2" ,
0273                                  locale.dayName(old.date().dayOfWeek(), QLocale::LongFormat),
0274                                  locale.toString(old.time(), QLocale::ShortFormat));
0275                 }
0276             }
0277         }
0278     }
0279 
0280     return locale.toString(old, QLocale::ShortFormat);
0281 }
0282 
0283 QString DateFormatterPrivate::localized(time_t t, bool shortFormat, const QString &lang)
0284 {
0285     QDateTime tmp;
0286     QString ret;
0287     auto locale = QLocale::system();
0288 
0289     tmp.setSecsSinceEpoch(t);
0290 
0291     if (!lang.isEmpty()) {
0292         locale = QLocale(lang);
0293         ret = locale.toString(tmp, (shortFormat ? QLocale::ShortFormat : QLocale::LongFormat));
0294     } else {
0295         ret = locale.toString(tmp, (shortFormat ? QLocale::ShortFormat : QLocale::LongFormat));
0296     }
0297 
0298     return ret;
0299 }
0300 
0301 QString DateFormatterPrivate::cTime(time_t t)
0302 {
0303     return QString::fromLatin1(ctime(&t)).trimmed();
0304 }
0305 
0306 QString DateFormatterPrivate::isoDate(time_t t)
0307 {
0308     char cstr[64];
0309     strftime(cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&t));
0310     return QLatin1String(cstr);
0311 }
0312 
0313 QString DateFormatter::formatDate(FormatType ftype, time_t t,
0314                                   const QString &data, bool shortFormat)
0315 {
0316     DateFormatter f(ftype);
0317     if (ftype == Custom) {
0318         f.setCustomFormat(data);
0319     }
0320     return f.dateString(t, data, shortFormat);
0321 }
0322 
0323 QString DateFormatter::formatCurrentDate(FormatType ftype, const QString &data, bool shortFormat)
0324 {
0325     DateFormatter f(ftype);
0326     if (ftype == Custom) {
0327         f.setCustomFormat(data);
0328     }
0329     return f.dateString(time(nullptr), data, shortFormat);
0330 }