diff --git a/kmime/kmime_dateformatter.cpp b/kmime/kmime_dateformatter.cpp index 66f15b613..296dba9e6 100644 --- a/kmime/kmime_dateformatter.cpp +++ b/kmime/kmime_dateformatter.cpp @@ -1,344 +1,336 @@ /* kmime_dateformatter.cpp KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details 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 @ref MIME data and defines the DateFormatter class. @brief Defines the DateFormatter class. @authors the KMime authors (see AUTHORS file) */ #include "kmime_dateformatter.h" #include #include // for abs() #include #include #include #include using namespace KMime; //@cond PRIVATE int DateFormatter::mDaylight = -1; //@endcond DateFormatter::DateFormatter( FormatType ftype ) - : mFormat( ftype ), mCurrentTime( 0 ) + : mFormat( ftype ), mTodayOneSecondBeforeMidnight( 0 ) { } DateFormatter::~DateFormatter() { } DateFormatter::FormatType DateFormatter::format() const { return mFormat; } void DateFormatter::setFormat( FormatType ftype ) { mFormat = ftype; } QString DateFormatter::dateString( time_t t , const QString &lang , bool shortFormat, bool includeSecs ) const { switch ( mFormat ) { case Fancy: return fancy( t ); break; case Localized: return localized( t, shortFormat, includeSecs, lang ); break; case CTime: return cTime( t ); break; case Iso: return isoDate( t ); break; case Rfc: return rfc2822( t ); break; case Custom: return custom( t ); break; } return QString(); } QString DateFormatter::dateString( const QDateTime &dt, const QString &lang, bool shortFormat, bool includeSecs ) const { return dateString( qdateToTimeT( dt ), lang, shortFormat, includeSecs ); } QString DateFormatter::rfc2822( time_t t ) const { QDateTime tmp; QString ret; tmp.setTime_t( t ); ret = tmp.toString( "ddd, dd MMM yyyy hh:mm:ss " ).toLatin1(); ret += zone( t ); return ret; } QString DateFormatter::custom( time_t t ) const { if ( mCustomFormat.isEmpty() ) { return QString(); } int z = mCustomFormat.indexOf( 'Z' ); QDateTime d; QString ret = mCustomFormat; d.setTime_t( t ); if ( z != -1 ) { ret.replace( z, 1, zone( t ) ); } ret = d.toString( ret ); return ret; } void DateFormatter::setCustomFormat( const QString &format ) { mCustomFormat = format; mFormat = Custom; } QString DateFormatter::customFormat() const { return mCustomFormat; } QByteArray DateFormatter::zone( time_t t ) const { #if defined(HAVE_TIMEZONE) || defined(HAVE_TM_GMTOFF) struct tm *local = localtime( &t ); #endif #if defined(HAVE_TIMEZONE) //hmm, could make hours & mins static int secs = abs( timezone ); int neg = (timezone > 0) ? 1 : 0; int hours = secs / 3600; int mins = (secs - hours*3600) / 60; // adjust to daylight if ( local->tm_isdst > 0 ) { mDaylight = 1; if ( neg ) { --hours; } else { ++hours; } } else { mDaylight = 0; } #elif defined(HAVE_TM_GMTOFF) int secs = abs( local->tm_gmtoff ); int neg = (local->tm_gmtoff < 0) ? 1 : 0; int hours = secs / 3600; int mins = (secs - hours * 3600) / 60; if ( local->tm_isdst > 0 ) { mDaylight = 1; } else { mDaylight = 0; } #else QDateTime d1 = QDateTime::fromString( asctime( gmtime( &t ) ) ); QDateTime d2 = QDateTime::fromString( asctime( localtime( &t ) ) ); int secs = d1.secsTo( d2 ); int neg = ( secs < 0 ) ? 1 : 0; secs = abs( secs ); int hours = secs / 3600; int mins = (secs - hours * 3600) / 60; // daylight should be already taken care of here #endif /* HAVE_TIMEZONE */ QByteArray ret; QTextStream s( &ret, QIODevice::WriteOnly ); s << ( neg ? '-' : '+' ) << qSetFieldWidth( 2 ) << qSetPadChar( '0' ) << right << hours << mins; //old code: ret.sprintf( "%c%.2d%.2d", (neg) ? '-' : '+', hours, mins ); return ret; } time_t DateFormatter::qdateToTimeT( const QDateTime &dt ) const { QDateTime epoch( QDate( 1970, 1, 1 ), QTime( 00, 00, 00 ) ); time_t t; time( &t ); QDateTime d1 = QDateTime::fromString( asctime( gmtime( &t ) ) ); QDateTime d2 = QDateTime::fromString( asctime( localtime( &t ) ) ); time_t drf = epoch.secsTo( dt ) - d1.secsTo( d2 ); return drf; } QString DateFormatter::fancy( time_t t ) const { KLocale *locale = KGlobal::locale(); if ( t <= 0 ) { return i18nc( "invalid time specified", "unknown" ); } - if ( !mCurrentTime ) { - time( &mCurrentTime ); - mDate.setTime_t( mCurrentTime ); + if ( mTodayOneSecondBeforeMidnight == 0 ) { + // determine time_t value of today 23:59:59 + const QDateTime today( QDate::currentDate(), QTime( 23, 59, 59 ) ); + mTodayOneSecondBeforeMidnight = today.toTime_t(); } QDateTime old; old.setTime_t( t ); - // not more than an hour in the future - if ( mCurrentTime + 60 * 60 >= t ) { - time_t diff = mCurrentTime - t; - - if ( diff < 24 * 60 * 60 ) { - if ( old.date().year() == mDate.date().year() && - old.date().dayOfYear() == mDate.date().dayOfYear() ) - return i18n( "Today %1", locale-> - formatTime( old.time(), true ) ); - } - if ( diff < 2 * 24 * 60 * 60 ) { - QDateTime yesterday( mDate.addDays( -1 ) ); - if ( old.date().year() == yesterday.date().year() && - old.date().dayOfYear() == yesterday.date().dayOfYear() ) - return i18n( "Yesterday %1", locale-> - formatTime( old.time(), true) ); - } - for ( int i = 3; i < 7; i++ ) { - if ( diff < i * 24 * 60 * 60 ) { - QDateTime weekday( mDate.addDays( -i + 1 ) ); - if ( old.date().year() == weekday.date().year() && - old.date().dayOfYear() == weekday.date().dayOfYear() ) + if ( mTodayOneSecondBeforeMidnight >= t ) { + const time_t diff = mTodayOneSecondBeforeMidnight - t; + if ( diff < 7 * 24 * 60 * 60 ) { + if ( diff < 24 * 60 * 60 ) { + return i18n( "Today %1", + locale->formatTime( old.time(), true ) ); + } + if ( diff < 2 * 24 * 60 * 60 ) { + return i18n( "Yesterday %1", + locale->formatTime( old.time(), true ) ); + } + for ( int i = 3; i < 8; i++ ) { + if ( diff < i * 24 * 60 * 60 ) { return i18nc( "1. weekday, 2. time", "%1 %2" , locale->calendar()->weekDayName( old.date() ) , - locale->formatTime( old.time(), true) ); + locale->formatTime( old.time(), true ) ); + } } } } return locale->formatDateTime( old ); - } QString DateFormatter::localized( time_t t, bool shortFormat, bool includeSecs, const QString &lang ) const { QDateTime tmp; QString ret; KLocale *locale = KGlobal::locale(); tmp.setTime_t( t ); if ( !lang.isEmpty() ) { locale = new KLocale( lang, lang, lang); ret = locale->formatDateTime( tmp, (shortFormat ? KLocale::ShortDate : KLocale::LongDate), includeSecs ); delete locale; } else { ret = locale->formatDateTime( tmp, (shortFormat ? KLocale::ShortDate : KLocale::LongDate), includeSecs ); } return ret; } QString DateFormatter::cTime( time_t t ) const { return QString::fromLatin1( ctime( &t ) ).trimmed(); } QString DateFormatter::isoDate( time_t t ) const { char cstr[64]; strftime( cstr, 63, "%Y-%m-%d %H:%M:%S", localtime( &t ) ); return QString( cstr ); } void DateFormatter::reset() { - mCurrentTime = 0; + mTodayOneSecondBeforeMidnight = 0; } QString DateFormatter::formatDate( FormatType ftype, time_t t, const QString &data, bool shortFormat, bool includeSecs ) { DateFormatter f( ftype ); if ( ftype == Custom ) { f.setCustomFormat( data ); } return f.dateString( t, data, shortFormat, includeSecs ); } QString DateFormatter::formatCurrentDate( FormatType ftype, const QString &data, bool shortFormat, bool includeSecs ) { DateFormatter f( ftype ); if ( ftype == Custom ) { f.setCustomFormat( data ); } return f.dateString( time( 0 ), data, shortFormat, includeSecs ); } bool DateFormatter::isDaylight() { if ( mDaylight == -1 ) { time_t ntime = time( 0 ); struct tm *local = localtime( &ntime ); if ( local->tm_isdst > 0 ) { mDaylight = 1; return true; } else { mDaylight = 0; return false; } } else if ( mDaylight != 0 ) { return true; } else { return false; } } diff --git a/kmime/kmime_dateformatter.h b/kmime/kmime_dateformatter.h index 93949fdeb..5fb0e475f 100644 --- a/kmime/kmime_dateformatter.h +++ b/kmime/kmime_dateformatter.h @@ -1,293 +1,294 @@ /* -*- c++ -*- kmime_dateformatter.h KMime, the KDE internet mail/usenet news message library. Copyright (c) 2001 the KMime authors. See file AUTHORS for details 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 @ref MIME data and defines the DateFormatter class. @brief Defines the DateFormatter class. @authors the KMime authors (see AUTHORS file) @glossary @anchor RFC2822 @anchor rfc2822 @b RFC @b 2822: RFC that defines the Internet Message Format. @glossary @anchor ISO8601 @anchor iso8601 @b ISO @b 8601: International Standards Organization (ISO) standard that defines the international standard for date and time representations. @glossary @anchor ctime @b ctime: a Unix library call which returns the local time as a human readable ASCII string of the form "Sun Mar 31 02:08:35 2002". */ #ifndef __KMIME_DATEFORMATTER_H__ #define __KMIME_DATEFORMATTER_H__ #include #include #include #include "kmime_export.h" namespace KMime { /** @brief A class for abstracting date formatting. This class deals with different kinds of date display formats. The formats supported include: - @b fancy "Today 02:08:35" - @b ctime as with the @ref ctime function, eg. "Sun Mar 31 02:08:35 2002" - @b localized according to the control center setting, eg. "2002-03-31 02:08" - @b iso according to the @ref ISO8601 standard, eg. "2002-03-31 02:08:35" - @b rfc according to @ref RFC2822 (Section 3.3), eg. "Sun, 31 Mar 2002 02:08:35 -0500" - @b custom "whatever you like" */ class KMIME_EXPORT DateFormatter { public: /** The different types of date formats. */ enum FormatType { CTime, /**< ctime "Sun Mar 31 02:08:35 2002" */ Localized, /**< localized "2002-03-31 02:08" */ Fancy, /**< fancy "Today 02:08:35" */ Iso, /**< iso "2002-03-31 02:08:35" */ Rfc, /**< rfc "Sun, 31 Mar 2002 02:08:35 -0500" */ Custom /**< custom "whatever you like" */ }; /** Constructs a date formatter with a default #FormatType. @param ftype is the default #FormatType to use. */ explicit DateFormatter( FormatType ftype=DateFormatter::Fancy ); /** Destroys the date formatter. */ ~DateFormatter(); /** Returns the #FormatType currently set. @see setFormat(). */ FormatType format() const; /** Sets the date format to @p ftype. @param ftype is the #FormatType. @see format(). */ void setFormat( FormatType ftype ); /** Constructs a formatted date string from time_t @p t. @param t is the time_t to use for formatting. @param lang is the language, only used if #FormatType is #Localized. @param shortFormat if true, create the short version of the date string, only used if #FormatType is #Localized. @param includeSecs if true, include the seconds field in the date string, only used if #FormatType is #Localized. @return a QString containing the formatted date. */ QString dateString( time_t t, const QString &lang=QString(), bool shortFormat=true, bool includeSecs=false ) const; /** Constructs a formatted date string from QDateTime @p dtime. @param dtime is the QDateTime to use for formatting. @param lang is the language, only used if #FormatType is #Localized. @param shortFormat if true, create the short version of the date string, only used if #FormatType is #Localized. @param includeSecs if true, include the seconds field in the date string, only used if #FormatType is #Localized. @return a QString containing the formatted date. */ QString dateString( const QDateTime &dtime, const QString &lang=QString(), bool shortFormat=true, bool includeSecs=false ) const; /** Sets the custom format for date to string conversions to @p format. This method accepts the same arguments as QDateTime::toString(), but also supports the "Z" expression which is substituted with the @ref RFC2822 (Section 3.3) style numeric timezone (-0500). @param format is a QString containing the custom format. @see QDateTime::toString(), customFormat(). */ void setCustomFormat( const QString &format ); /** Returns the custom format string. @see setCustomFormat(). */ QString customFormat() const; /** - Resets the internal clock. + Resets the cached current date used for calculating the fancy date. + This should be called whenever the current date changed, i.e. on midnight. */ void reset(); //static methods /** Convenience function dateString @param ftype is the #FormatType to use. @param t is the time_t to use for formatting. @param data is either the format when #FormatType is Custom, or language when #FormatType is #Localized. @param shortFormat if true, create the short version of the date string, only used if #FormatType is #Localized. @param includeSecs if true, include the seconds field in the date string, only used if #FormatType is #Localized. @return a QString containing the formatted date. */ static QString formatDate( DateFormatter::FormatType ftype, time_t t, const QString &data=QString(), bool shortFormat=true, bool includeSecs=false ); /** Convenience function, same as formatDate() but returns the current time formatted. @param ftype is the #FormatType to use. @param data is either the format when #FormatType is Custom, or language when #FormatType is #Localized. @param shortFormat if true, create the short version of the date string, only used if #FormatType is #Localized. @param includeSecs if true, include the seconds field in the date string, only used if #FormatType is #Localized. @return a QString containing the formatted date. */ static QString formatCurrentDate( DateFormatter::FormatType ftype, const QString &data=QString(), bool shortFormat=true, bool includeSecs=false ); /** Returns true if the current time is on daylight savings time; else false. */ static bool isDaylight(); protected: /** Returns a QString containing the specified time_t @p t formatted using the #Fancy #FormatType. @param t is the time_t to use for formatting. */ QString fancy( time_t t ) const ; /** Returns a QString containing the specified time_t @p t formatted using the #Localized #FormatType. @param t is the time_t to use for formatting. @param shortFormat if true, create the short version of the date string. @param includeSecs if true, include the seconds field in the date string. @param lang is a QString containing the language to use. */ QString localized( time_t t, bool shortFormat=true, bool includeSecs=false, const QString &lang=QString() ) const; /** Returns a QString containing the specified time_t @p t formatted with the ctime() function. @param t is the time_t to use for formatting. */ QString cTime( time_t t ) const; /** Returns a QString containing the specified time_t @p t in the "%Y-%m-%d %H:%M:%S" #Iso #FormatType. @param t is the time_t to use for formatting. */ QString isoDate( time_t t ) const; /** Returns a QString containing the specified time_t @p t in the #Rfc #FormatType. @param t is the time_t to use for formatting. */ QString rfc2822( time_t t ) const; /** Returns a QString containing the specified time_t @p t formatted with a previously specified custom format. @param t time used for formatting */ QString custom( time_t t ) const; /** Returns a QString that identifies the timezone (eg."-0500") of the specified time_t @p t. @param t time to compute timezone from. */ QByteArray zone( time_t t ) const; /** Converts QDateTime @p dt to a time_t value. @param dt is the QDateTime to be converted. @return the time_t equivalent of the specified QDateTime. */ time_t qdateToTimeT( const QDateTime &dt ) const; private: //@cond PRIVATE FormatType mFormat; - mutable time_t mCurrentTime; - mutable QDateTime mDate; + mutable time_t mTodayOneSecondBeforeMidnight; + mutable QDateTime mUnused; // KDE5: remove QString mCustomFormat; static int mDaylight; //@endcond }; } // namespace KMime #endif /* __KMIME_DATEFORMATTER_H__ */