[PATCH] Remove KDateTime limitation of 4712 BCE.

Mark markg85 at gmail.com
Mon Oct 8 10:05:19 UTC 2012


On Sun, Oct 7, 2012 at 10:30 PM, Jon Severinsson <jon at severinsson.net> wrote:
> QDate in Qt5 now supports dates before 4712 BCE (new limit is 12626108195557530 BCE, well outside the limit of a four digit year).
> ---
> This fixes test #12: kdecore-kdatetimetest when building on Qt5. I choose to
> just rip it out rather than to preserve behaviour for Qt4, as I figgured we
> could live with a bad return value from outOfRange() until we drop Qt4 support
> from the frameworks branch.
>
>  kdecore/date/kdatetime.cpp      |  233 +++++++++++++--------------------------
>  kdecore/date/kdatetime.h        |   33 ++----
>  kdecore/tests/kdatetimetest.cpp |    4 -
>  3 filer ändrade, 86 tillägg(+), 184 borttagningar(-)
>
> diff --git a/kdecore/date/kdatetime.cpp b/kdecore/date/kdatetime.cpp
> index df1fc3d..9dd1b26 100644
> --- a/kdecore/date/kdatetime.cpp
> +++ b/kdecore/date/kdatetime.cpp
> @@ -70,16 +70,8 @@ static const char longMonth[][10] = {
>      "October", "November", "December"
>  };
>
> -
> -// The reason for the KDateTime being invalid, returned from KDateTime::fromString()
> -enum Status {
> -    stValid = 0,   // either valid, or really invalid
> -    stTooEarly     // invalid (valid date before QDate range)
> -};
> -
> -
>  static QDateTime fromStr(const QString& string, const QString& format, int& utcOffset,
> -                         QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly, Status&);
> +                         QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly);
>  static int matchDay(const QString &string, int &offset, KCalendarSystem*);
>  static int matchMonth(const QString &string, int &offset, KCalendarSystem*);
>  static bool getUTCOffset(const QString &string, int &offset, bool colon, int &result);
> @@ -89,10 +81,8 @@ static int findString_internal(const QString &string, const char *ptr, int count
>  template<int disp> static inline
>  int findString(const QString &string, const char array[][disp], int count, int &offset)
>  { return findString_internal(string, array[0], count, offset, disp); }
> -static QDate checkDate(int year, int month, int day, Status&);
>
> -static const int MIN_YEAR = -4712;        // minimum year which QDate allows
> -static const int NO_NUMBER = 0x8000000;   // indicates that no number is present in string conversion functions
> +static const int NO_NUMBER = std::numeric_limits<int>::min();   // indicates that no number is present in string conversion functions
>
>  #ifdef COMPILING_TESTS
>  KDECORE_EXPORT int KDateTime_utcCacheHit  = 0;
> @@ -317,7 +307,6 @@ class KDateTimePrivate : public QSharedData
>      KDateTimePrivate()
>          : QSharedData(),
>            specType(KDateTime::Invalid),
> -          status(stValid),
>            utcCached(true),
>            convertedCached(false),
>            m2ndOccurrence(false),
> @@ -329,7 +318,6 @@ class KDateTimePrivate : public QSharedData
>          : QSharedData(),
>            mDt(d),
>            specType(s.type()),
> -          status(stValid),
>            utcCached(false),
>            convertedCached(false),
>            m2ndOccurrence(false),
> @@ -360,7 +348,6 @@ class KDateTimePrivate : public QSharedData
>            ut(rhs.ut),
>            converted(rhs.converted),
>            specType(rhs.specType),
> -          status(rhs.status),
>            utcCached(rhs.utcCached),
>            convertedCached(rhs.convertedCached),
>            m2ndOccurrence(rhs.m2ndOccurrence),
> @@ -456,7 +443,6 @@ private:
>      } converted;
>  public:
>      KDateTime::SpecType   specType          : 4; // time spec type (N.B. need 3 bits + sign bit, since enums are signed on some platforms)
> -    Status                status            : 2; // reason for invalid status
>      mutable bool          utcCached         : 1; // true if 'ut' is valid
>      mutable bool          convertedCached   : 1; // true if 'converted' is valid
>      mutable bool          m2ndOccurrence    : 1; // this is the second occurrence of a time zone time
> @@ -827,7 +813,6 @@ KDateTime &KDateTime::operator=(const KDateTime &other)
>  void      KDateTime::detach()                   { d.detach(); }
>  bool      KDateTime::isNull() const             { return d->dt().isNull(); }
>  bool      KDateTime::isValid() const            { return d->specType != Invalid  &&  d->dt().isValid(); }
> -bool      KDateTime::outOfRange() const         { return d->status == stTooEarly; }
>  bool      KDateTime::isDateOnly() const         { return d->dateOnly(); }
>  bool      KDateTime::isLocalZone() const        { return d->specType == TimeZone  &&  d->specZone == KSystemTimeZones::local(); }
>  bool      KDateTime::isClockTime() const        { return d->specType == ClockTime; }
> @@ -1928,8 +1913,7 @@ KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *
>                      }
>                  }
>              }
> -            Status invalid = stValid;
> -            QDate qdate = checkDate(year, month+1, day, invalid);   // convert date, and check for out-of-range
> +            QDate qdate(year, month+1, day);   // convert date, and check for out-of-range
>              if (!qdate.isValid())
>                  break;
>              KDateTime result(qdate, QTime(hour, minute, second), Spec(OffsetFromUTC, offset));
> @@ -1949,12 +1933,6 @@ KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *
>                  if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400)   // (max abs(offset) is 100 hours)
>                      break;    // the time isn't the last second of the day
>              }
> -            if (invalid)
> -            {
> -                KDateTime dt;            // date out of range - return invalid KDateTime ...
> -                dt.d->status = invalid;  // ... with reason for error
> -                return dt;
> -            }
>              return result;
>          }
>          case RFC3339Date:   // format is YYYY-MM-DDThh:mm:ss[.s]TZ
> @@ -2112,15 +2090,14 @@ KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *
>                  }
>              }
>              int month, day;
> -            Status invalid = stValid;
>              if (parts[3].length() == 3)
>              {
>                  // A day of the year is specified
>                  day = parts[3].toInt(&ok);
>                  if (!ok || day < 1 || day > 366)
>                      break;
> -                d = checkDate(year, 1, 1, invalid).addDays(day - 1);   // convert date, and check for out-of-range
> -                if (!d.isValid()  ||  (!invalid && d.year() != year))
> +                d = QDate(year, 1, 1).addDays(day - 1);   // convert date, and check for out-of-range
> +                if (!d.isValid()  ||  (d.year() != year))
>                      break;
>                  day   = d.day();
>                  month = d.month();
> @@ -2132,18 +2109,12 @@ KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *
>                  day   = parts[3].right(2).toInt(&ok1);
>                  if (!ok || !ok1)
>                      break;
> -                d = checkDate(year, month, day, invalid);   // convert date, and check for out-of-range
> +                d = QDate(year, month, day);   // convert date, and check for out-of-range
>                  if (!d.isValid())
>                      break;
>              }
>              if (dateOnly)
>              {
> -                if (invalid)
> -                {
> -                    KDateTime dt;            // date out of range - return invalid KDateTime ...
> -                    dt.d->status = invalid;    // ... with reason for error
> -                    return dt;
> -                }
>                  return KDateTime(d, Spec(ClockTime));
>              }
>              if (hour == 24  && !minute && !second && !msecs)
> @@ -2159,12 +2130,6 @@ KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *
>              if (parts[8].isEmpty())
>              {
>                  // No UTC offset is specified. Don't try to validate leap seconds.
> -                if (invalid)
> -                {
> -                    KDateTime dt;            // date out of range - return invalid KDateTime ...
> -                    dt.d->status = invalid;  // ... with reason for error
> -                    return dt;
> -                }
>                  return KDateTime(d, t, KDateTimePrivate::fromStringDefault());
>              }
>              int offset = 0;
> @@ -2194,12 +2159,6 @@ KDateTime KDateTime::fromString(const QString &string, TimeFormat format, bool *
>                  if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400)   // (max abs(offset) is 100 hours)
>                      break;    // the time isn't the last second of the day
>              }
> -            if (invalid)
> -            {
> -                KDateTime dt;            // date out of range - return invalid KDateTime ...
> -                dt.d->status = invalid;  // ... with reason for error
> -                return dt;
> -            }
>              return KDateTime(d, t, Spec(spec, offset));
>          }
>          case QtTextDate:    // format is Wdy Mth DD [hh:mm:ss] YYYY [±hhmm]
> @@ -2274,10 +2233,9 @@ KDateTime KDateTime::fromString(const QString &string, const QString &format,
>  {
>      int     utcOffset = 0;    // UTC offset in seconds
>      bool    dateOnly = false;
> -    Status invalid = stValid;
>      QString zoneName;
>      QByteArray zoneAbbrev;
> -    QDateTime qdt = fromStr(string, format, utcOffset, zoneName, zoneAbbrev, dateOnly, invalid);
> +    QDateTime qdt = fromStr(string, format, utcOffset, zoneName, zoneAbbrev, dateOnly);
>      if (!qdt.isValid())
>          return KDateTime();
>      if (zones)
> @@ -2292,97 +2250,88 @@ KDateTime KDateTime::fromString(const QString &string, const QString &format,
>              zone = zones->zone(zoneName);
>              zname = true;
>          }
> -        else if (!invalid)
> +        else if (!zoneAbbrev.isEmpty())
>          {
> -            if (!zoneAbbrev.isEmpty())
> +            // A time zone abbreviation has been found.
> +            // Use the time zone which contains it, if any, provided that the
> +            // abbreviation applies at the specified date/time.
> +            bool useUtcOffset = false;
> +            const KTimeZones::ZoneMap z = zones->zones();
> +            for (KTimeZones::ZoneMap::ConstIterator it = z.constBegin();  it != z.constEnd();  ++it)
>              {
> -                // A time zone abbreviation has been found.
> -                // Use the time zone which contains it, if any, provided that the
> -                // abbreviation applies at the specified date/time.
> -                bool useUtcOffset = false;
> -                const KTimeZones::ZoneMap z = zones->zones();
> -                for (KTimeZones::ZoneMap::ConstIterator it = z.constBegin();  it != z.constEnd();  ++it)
> +                if (it.value().abbreviations().contains(zoneAbbrev))
>                  {
> -                    if (it.value().abbreviations().contains(zoneAbbrev))
> +                    int offset2;
> +                    int offset = it.value().offsetAtZoneTime(qdt, &offset2);
> +                    QDateTime ut(qdt);
> +                    ut.setTimeSpec(Qt::UTC);
> +                    ut.addSecs(-offset);
> +                    if (it.value().abbreviation(ut) != zoneAbbrev)
>                      {
> -                        int offset2;
> -                        int offset = it.value().offsetAtZoneTime(qdt, &offset2);
> -                        QDateTime ut(qdt);
> -                        ut.setTimeSpec(Qt::UTC);
> -                        ut.addSecs(-offset);
> +                        if (offset == offset2)
> +                            continue;     // abbreviation doesn't apply at specified time
> +                        ut.addSecs(offset - offset2);
>                          if (it.value().abbreviation(ut) != zoneAbbrev)
> -                        {
> -                            if (offset == offset2)
> -                                continue;     // abbreviation doesn't apply at specified time
> -                            ut.addSecs(offset - offset2);
> -                            if (it.value().abbreviation(ut) != zoneAbbrev)
> -                                continue;     // abbreviation doesn't apply at specified time
> -                            offset = offset2;
> -                        }
> -                        // Found a time zone which uses this abbreviation at the specified date/time
> -                        if (zone.isValid())
> -                        {
> -                            // Abbreviation is used by more than one time zone
> -                            if (!offsetIfAmbiguous  ||  offset != utcOffset)
> -                                return KDateTime();
> -                            useUtcOffset = true;
> -                        }
> -                        else
> -                        {
> -                            zone = it.value();
> -                            utcOffset = offset;
> -                        }
> +                            continue;     // abbreviation doesn't apply at specified time
> +                        offset = offset2;
> +                    }
> +                    // Found a time zone which uses this abbreviation at the specified date/time
> +                    if (zone.isValid())
> +                    {
> +                        // Abbreviation is used by more than one time zone
> +                        if (!offsetIfAmbiguous  ||  offset != utcOffset)
> +                            return KDateTime();
> +                        useUtcOffset = true;
> +                    }
> +                    else
> +                    {
> +                        zone = it.value();
> +                        utcOffset = offset;
>                      }
>                  }
> -                if (useUtcOffset)
> -                {
> -                    zone = KTimeZone();
> -                    if (!utcOffset)
> -                        qdt.setTimeSpec(Qt::UTC);
> -                }
> -                else
> -                    zname = true;
>              }
> -            else if (utcOffset  ||  qdt.timeSpec() == Qt::UTC)
> +            if (useUtcOffset)
> +            {
> +                zone = KTimeZone();
> +                if (!utcOffset)
> +                    qdt.setTimeSpec(Qt::UTC);
> +            }
> +            else
> +                zname = true;
> +        }
> +        else if (utcOffset  ||  qdt.timeSpec() == Qt::UTC)
> +        {
> +            // A UTC offset has been found.
> +            // Use the time zone which contains it, if any.
> +            // For a date-only value, use the start of the day.
> +            QDateTime dtUTC = qdt;
> +            dtUTC.setTimeSpec(Qt::UTC);
> +            dtUTC.addSecs(-utcOffset);
> +            const KTimeZones::ZoneMap z = zones->zones();
> +            for (KTimeZones::ZoneMap::ConstIterator it = z.constBegin();  it != z.constEnd();  ++it)
>              {
> -                // A UTC offset has been found.
> -                // Use the time zone which contains it, if any.
> -                // For a date-only value, use the start of the day.
> -                QDateTime dtUTC = qdt;
> -                dtUTC.setTimeSpec(Qt::UTC);
> -                dtUTC.addSecs(-utcOffset);
> -                const KTimeZones::ZoneMap z = zones->zones();
> -                for (KTimeZones::ZoneMap::ConstIterator it = z.constBegin();  it != z.constEnd();  ++it)
> +                QList<int> offsets = it.value().utcOffsets();
> +                if ((offsets.isEmpty() || offsets.contains(utcOffset))
> +                &&  it.value().offsetAtUtc(dtUTC) == utcOffset)
>                  {
> -                    QList<int> offsets = it.value().utcOffsets();
> -                    if ((offsets.isEmpty() || offsets.contains(utcOffset))
> -                    &&  it.value().offsetAtUtc(dtUTC) == utcOffset)
> +                    // Found a time zone which uses this offset at the specified time
> +                    if (zone.isValid()  ||  !utcOffset)
>                      {
> -                        // Found a time zone which uses this offset at the specified time
> -                        if (zone.isValid()  ||  !utcOffset)
> -                        {
> -                            // UTC offset is used by more than one time zone
> -                            if (!offsetIfAmbiguous)
> -                                return KDateTime();
> -                            if (invalid)
> -                            {
> -                                KDateTime dt;            // date out of range - return invalid KDateTime ...
> -                                dt.d->status = invalid;    // ... with reason for error
> -                                return dt;
> -                            }
> -                            if (dateOnly)
> -                                return KDateTime(qdt.date(), Spec(OffsetFromUTC, utcOffset));
> -                            qdt.setTimeSpec(Qt::LocalTime);
> -                            return KDateTime(qdt, Spec(OffsetFromUTC, utcOffset));
> -                        }
> -                        zone = it.value();
> +                        // UTC offset is used by more than one time zone
> +                        if (!offsetIfAmbiguous)
> +                            return KDateTime();
> +                        if (dateOnly)
> +                            return KDateTime(qdt.date(), Spec(OffsetFromUTC, utcOffset));
> +                        qdt.setTimeSpec(Qt::LocalTime);
> +                        return KDateTime(qdt, Spec(OffsetFromUTC, utcOffset));
>                      }
> +                    zone = it.value();
>                  }
>              }
>          }
>          if (!zone.isValid() && zname)
>              return KDateTime();    // an unknown zone name or abbreviation was found
> -        if (zone.isValid() && !invalid)
> +        if (zone.isValid())
>          {
>              if (dateOnly)
>                  return KDateTime(qdt.date(), Spec(zone));
> @@ -2391,12 +2340,6 @@ KDateTime KDateTime::fromString(const QString &string, const QString &format,
>      }
>
>      // No time zone match was found
> -    if (invalid)
> -    {
> -        KDateTime dt;            // date out of range - return invalid KDateTime ...
> -        dt.d->status = invalid;    // ... with reason for error
> -        return dt;
> -    }
>      KDateTime result;
>      if (utcOffset)
>      {
> @@ -2474,9 +2417,8 @@ QDataStream & operator>>(QDataStream &s, KDateTime &kdt)
>   * utcOffset == 0, that indicates that no UTC offset was found.
>   */
>  QDateTime fromStr(const QString& string, const QString& format, int& utcOffset,
> -                  QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly, Status &status)
> +                  QString& zoneName, QByteArray& zoneAbbrev, bool& dateOnly)
>  {
> -    status = stValid;
>      QString str = string.simplified();
>      int year      = NO_NUMBER;
>      int month     = NO_NUMBER;
> @@ -2796,10 +2738,10 @@ QDateTime fromStr(const QString& string, const QString& format, int& utcOffset,
>          year = KDateTime::currentLocalDate().year();
>      if (month == NO_NUMBER)
>          month = 1;
> -    QDate d = checkDate(year, month, (day > 0 ? day : 1), status);   // convert date, and check for out-of-range
> +    QDate d = QDate(year, month, (day > 0 ? day : 1));   // convert date, and check for out-of-range
>      if (!d.isValid())
>          return QDateTime();
> -    if (dayOfWeek != NO_NUMBER  &&  !status)
> +    if (dayOfWeek != NO_NUMBER)
>      {
>          if (day == NO_NUMBER)
>          {
> @@ -3049,28 +2991,3 @@ int findString_internal(const QString &string, const char *array, int count, int
>      }
>      return -1;
>  }
> -
> -/*
> - * Return the QDate for a given year, month and day.
> - * If in error, check whether the reason is that the year is out of range.
> - * If so, return a valid (but wrong) date but with 'status' set to the
> - * appropriate error code. If no error, 'status' is set to stValid.
> - */
> -QDate checkDate(int year, int month, int day, Status &status)
> -{
> -    status = stValid;
> -    QDate qdate(year, month, day);
> -    if (qdate.isValid())
> -        return qdate;
> -
> -    // Invalid date - check whether it's simply out of range
> -    if (year < MIN_YEAR)
> -    {
> -        bool leap = (year % 4 == 0) && (year % 100 || year % 400 == 0);
> -        qdate.setDate((leap ? 2000 : 2001), month, day);
> -        if (qdate.isValid())
> -            status = stTooEarly;
> -    }
> -    return qdate;
> -}
> -
> diff --git a/kdecore/date/kdatetime.h b/kdecore/date/kdatetime.h
> index d0eb07d..03a757d 100644
> --- a/kdecore/date/kdatetime.h
> +++ b/kdecore/date/kdatetime.h
> @@ -1291,19 +1291,13 @@ class KDECORE_EXPORT KDateTime //krazy:exclude=dpointer (implicitly shared)
>       * to RFCDate. Only set it to RFCDateDay if you want to return an error
>       * when the day of the week is omitted.
>       *
> -     * For @p format = ISODate or RFCDate[Day], if an invalid KDateTime is
> -     * returned, you can check why @p format was considered invalid by use of
> -     * outOfRange(). If that method returns true, it indicates that @p format
> -     * was in fact valid, but the date lies outside the range which can be
> -     * represented by QDate.
> -     *
>       * @param string string to convert
>       * @param format format code. LocalDate cannot be used here.
>       * @param negZero if non-null, the value is set to true if a UTC offset of
>       *                '-0000' is found or, for RFC 2822 format, an unrecognised
>       *                or invalid time zone abbreviation is found, else false.
>       * @return KDateTime value, or an invalid KDateTime if either parameter is invalid
> -     * @see setFromStringDefault(), toString(), outOfRange(), QString::fromString()
> +     * @see setFromStringDefault(), toString(), QString::fromString()
>       */
>      static KDateTime fromString(const QString &string, TimeFormat format = ISODate, bool *negZero = 0);
>
> @@ -1427,11 +1421,6 @@ class KDECORE_EXPORT KDateTime //krazy:exclude=dpointer (implicitly shared)
>       * appears more than once but with different values, the weekday name does
>       * not tally with the date, an invalid KDateTime is returned.
>       *
> -     * If an invalid KDateTime is returned, you can check why @p format was
> -     * considered invalid by use of outOfRange(). If that method returns true,
> -     * it indicates that @p format was in fact valid, but the date lies outside
> -     * the range which can be represented by QDate.
> -     *
>       * @param string string to convert
>       * @param format format string
>       * @param zones time zone collection, or null for none
> @@ -1442,7 +1431,7 @@ class KDECORE_EXPORT KDateTime //krazy:exclude=dpointer (implicitly shared)
>       *         time zone information doesn't match any in @p zones, or if the
>       *         time zone information is ambiguous and @p offsetIfAmbiguous is
>       *         false
> -     * @see setFromStringDefault(), toString(), outOfRange()
> +     * @see setFromStringDefault(), toString()
>       */
>      static KDateTime fromString(const QString &string, const QString &format,
>                                  const KTimeZones *zones = 0, bool offsetIfAmbiguous = true);
> @@ -1463,17 +1452,17 @@ class KDECORE_EXPORT KDateTime //krazy:exclude=dpointer (implicitly shared)
>
>
>      /**
> -     * Checks whether the date/time returned by the last call to fromString()
> -     * was invalid because an otherwise valid date was outside the range which
> -     * can be represented by QDate. This status occurs when fromString() read
> -     * a valid string containing a year earlier than -4712 (4713 BC). On exit
> -     * from fromString(), if outOfRange() returns @c true, isValid() will
> -     * return @c false.
> +     * Always returns false, as dates earlier than -4712 are now supported by
> +     * @c KDateTime.
>       *
> -     * @return @c true if date was earlier than -4712, else @c false
> -     * @see isValid(), fromString()
> +     * @return @c false
> +     * @see isValid()
> +     * @deprecated since 5.0, we now supports all valid dates.
>       */
> -    bool outOfRange() const;
> +    inline bool outOfRange() const
> +    {
> +        return false;
> +    }
>
>      /**
>       * Compare this instance with another to determine whether they are
> diff --git a/kdecore/tests/kdatetimetest.cpp b/kdecore/tests/kdatetimetest.cpp
> index 812abc5..c38aea0 100644
> --- a/kdecore/tests/kdatetimetest.cpp
> +++ b/kdecore/tests/kdatetimetest.cpp
> @@ -3803,10 +3803,6 @@ void KDateTimeTest::strings_format()
>      s = dt.toString(QLatin1String("%Y"));
>      QCOMPARE(s, QString::fromLatin1("123456"));
>
> -    dt = KDateTime::fromString(QLatin1String("-471412311430:01.3+0500"), QLatin1String("%Y%m%d%H%M%:S%:s%z"));
> -    QVERIFY(!dt.isValid());    // too early
> -    QVERIFY(dt.outOfRange());
> -
>      dtutc = KDateTime::fromString(QLatin1String("2000-01-01T00:00:00.000+0000"), QLatin1String("%Y-%m-%dT%H:%M%:S%:s%z"));
>      QVERIFY(dtutc.isValid());
>      dt =    KDateTime::fromString(QLatin1String("2000-01-01T05:00:00.000+0500"), QLatin1String("%Y-%m-%dT%H:%M%:S%:s%z"));
> --
> 1.7.10.4
>
That is quite a large number as now limit! That's even way before the
universe was "born".


More information about the Kde-frameworks-devel mailing list