[Development] Two-digit dates: what century should we use ?

Edward Welbourne edward.welbourne at qt.io
Thu Nov 7 12:21:07 CET 2019

On 6. Nov 2019, at 13:38, Edward Welbourne <edward.welbourne at qt.io> wrote:
>>> Simplest would be to have two optional arguments, startYear and
>>> span; in 5.15, startYear must be supplied (to distinguish this
>>> overload from the old method) but span can be optional, defaulting
>>> to 100 (also used if span < 0 or span > 100).  The old method will
>>> behave as if it were the new method with startYear = 1900, span =
>>> 100.  (We could specify endYear instead of span, but its default
>>> gets trickier.)  Two-digit years then get mapped to a year with
>>> startYear <= year < startYear + span.  If there is no such year with
>>> the given last digits, the field is treated as unparseable.  Using
>>> span=0 breaks short formats, but we can accept that as a deliberate
>>> choice some client code may make.

Eike Ziller (7 November 2019 09:16)
> Not sure what usefulness span != 100 would have.

Use-cases for two-digit years where the potential ambiguity *isn't* a
compelling argument to Not Do That typically have shorter ranges of
plausible dates than a full century.  A school system's admissions
software, at least when automatically handling data from some source,
should probably flag as an error - or, at least, "needs a real human to
check what's going on here" - any date outside some fairly narrow range
of a few decades.  Such a system can sensibly use two-digit years,
precisely because, for example, any birth-date with a two-digit year in
the range 20--60 is almost certainly an error, better handled by
rejecting the record being read (and requiring non-automated handling)
than by importing suspect data.

>>> There then remains the question of what the default should be in Qt
>>> 6 (where we'll naturally unify the overloads).  If that's not
>>> 1900--1999, then we should deprecate the old method at 5.15 and
>>> encourage its callers to explicitly pass 1900 as startYear, if
>>> that's really what they want.  Sample options:
>>> * keep 1900-1999, discourage use of ShortFormat;
>>> * rolling window based on currentDate(), as I described earlier;
>>> * we update startYear's default with each major release of Qt.

André Pönitz (6 November 2019 18:28)
>> First option seems to be the one least surprising.

For some classes of users, if only because every release is the same.
On the other hand, QTBUG-74323 was submitted by someone surprised by it.

Back to Eike:
> Why would you change the default from what it is now?

So that round-tripping works: clients whose code writes out dates and
reads them back in again will be unhappy if the date they get back is a
century different from the one they put in.  Most of the dates anyone
writes out and reads back in these days are vaguely near the present; in
particular, not in the first few decades of the 1900s.

However, I'm fine with any API that lets those who need that specify the
range that'll get them what they expect.  If that's a bit painful, I'm
fairly comfortable with it, since I consider the use of two-digit years
to be "unwise".  Forcing those who do so to think about what they mean
by it is probably a good thing.

> A default that is changing in behavior, either automatically via
> currentDate, or “manually” with major releases, is creating surprises.
> The least surprise is a clearly documented, fixed default behavior,
> with the obvious hint that the behavior should better be explicitly
> stated by the user of the function.  E.g. by changing the signature to
> fromString(const QString &string, Qt::DateFormat format = Qt::TextDate, int startForTwoDigitYears = 1900)

That sounds eminently reasonable.  It leaves client code with the option
of passing (QDate::currentDate.year() / 10) * 10 - 60 or whatever suits
their use-case better.

>>> For the last, for example, we could make startYear=1940 in Qt6; by
>>> the time we get to a major release in the late '20s, we'd be ready
>>> to move that to startYear=1950.  I guess we can be confident of at
>>> least one major release per not much more than a decade, which
>>> suffices to ensure that dates throughout the supported life-time of
>>> that release do round-trip, while reaching well into the past.
>>> That's assuming range=100: if we use a buffer zone, range=90 for
>>> example, we'd probably want Qt6 using startYear=1950 already (so
>>> '40s are invalid and '30s are the 2030s).
>>> Any objections to this revised plan ?
>>> Anyone want to make the case for keeping 1900--1999 as default ?

Back to André:
>> You mentioned other systems that used that approach.
>> I would expect interacting with such systems is easiest, if the rules
>> are the same.
>> A shifting window would probably be unique to Qt, i.e. cause friction
>> in all cases.

I do not feel eager to argue against any of that ;^>

OK, so no need to deprecate anything (yay), just add start and span
parameters (with the latter defaulting to 100) and take care about
documenting the result.  In 5.15 the new API is a new overload, for the
sake of binary-compatibility; in Qt6 the two overloads get merged, with
the start-year being 1900 for compatibility with the past and various
other things that do the same.

Any further or remaining objections ?


More information about the Development mailing list