From 290dc2da70655db9d4590600dfd37b825b81c1f8 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 7 Oct 2019 11:09:47 +0200 Subject: Be more fussy about the MakeDay() calculation The ES Date spec goes into minute detail about doing various algorithms in the dumbest possible way (like InLeapYear() calling DaysInYear(), rather than the other way round) but, in MakeDay(), leaves the implementation to solve the problem of finding the start of the first day of a specified month in a given year. So exercise the freedom we have in that to be a little more robust; and actually check we have met the conditions the spec requires, returning NaN (as specified) if not. Added tests for some denormal dates and date-times and for a date mentioned in QTBUG-78996. Task-number: QTBUG-78996 Change-Id: I8d9a841dd1f1d9995273a3de8f6f9130207c7c2b Reviewed-by: Ulf Hermann Reviewed-by: Qt CI Bot --- src/qml/jsruntime/qv4dateobject.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/qml/jsruntime') diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 21c6a5d06b..e313ebe300 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -296,13 +296,28 @@ static double MakeDay(double year, double month, double day) if (month < 0) month += 12.0; - double d = DayFromYear(year); - bool leap = InLeapYear(d*msPerDay); + /* Quoting the spec: - d += DayFromMonth(month, leap); - d += day - 1; + Find a value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn + and DateFromTime(t) is 1; but if this is not possible (because some + argument is out of range), return NaN. + */ + double first = DayFromYear(year); + /* Beware floating-point glitches: don't test the first millisecond of a + * year, month or day when we could test a moment firmly in the interior of + * the interval. A rounding glitch might give the first millisecond to the + * preceding interval. + */ + bool leap = InLeapYear((first + 60) * msPerDay); - return d; + first += DayFromMonth(month, leap); + const double t = first * msPerDay + msPerDay / 2; // Noon on the first of the month + Q_ASSERT(Day(t) == first); + if (YearFromTime(t) != year || MonthFromTime(t) != month || DateFromTime(t) != 1) { + qWarning("Apparently out-of-range date %.0f-%02.0f-%02.0f", year, month, day); + return qt_qnan(); + } + return first + day - 1; } static inline double MakeDate(double day, double time) -- cgit v1.2.3