in lib/src/intl/date_builder.dart [234:325]
DateTime _correctForErrors(DateTime result, int retries) {
// There are 3 kinds of errors that we know of
//
// 1 - Issue 15560, sometimes we get UTC even when we asked for local, or
// they get constructed as if in UTC and then have the offset subtracted.
// Retry, possibly several times, until we get something that looks valid,
// or we give up.
//
// 1a) - It appears that sometimes we get incorrect timezone offsets that
// are not directly related to UTC. Also check for those and retry or
// compensate.
//
// 2 - Timezone transitions. If we ask for the time during a timezone
// transition then it will offset it by that transition. This is
// particularly a problem if the timezone transition happens at midnight,
// and we're looking for a date with no time component. This happens in
// Brazil, and we can end up with 11:00pm the previous day. Add time to
// compensate.
//
// 3 - Invalid input which the constructor nevertheless accepts. Just return
// what it created, and verify will catch it if we're in strict mode.
// If we've exhausted our retries, just return the input - it's not just a
// flaky result.
if (retries <= 0) {
return result;
}
var leapYear = date_computation.isLeapYear(result);
var resultDayOfYear =
date_computation.dayOfYear(result.month, result.day, leapYear);
// Check for the UTC failure. Are we expecting to produce a local time, but
// the result is UTC. However, the local time might happen to be the same as
// UTC. To be thorough, check if either the hour/day don't agree with what
// we expect, or is a new DateTime in a non-UTC timezone.
if (!utc &&
result.isUtc &&
(result.hour != hour24 ||
result.day != resultDayOfYear ||
!DateTime.now().isUtc)) {
// This may be a UTC failure. Retry and if the result doesn't look
// like it's in the UTC time zone, use that instead.
_retried++;
return asDate(retries: retries - 1);
}
if (dateOnly && result.hour != 0) {
// This could be a flake, try again.
var tryAgain = asDate(retries: retries - 1);
if (tryAgain != result) {
// Trying again gave a different answer, so presumably it worked.
return tryAgain;
}
// Trying again didn't work, try to force the offset.
var expectedDayOfYear = dayOfYear == 0
? date_computation.dayOfYear(month, day, leapYear)
: dayOfYear;
// If we're _dateOnly, then hours should be zero, but might have been
// offset to e.g. 11:00pm the previous day. Add that time back in. This
// might be because of an erratic error, but it might also be because of a
// time zone (Brazil) where there is no midnight at a daylight savings
// time transition. In that case we will retry, but eventually give up and
// return 1:00am on the correct date.
var daysPrevious = expectedDayOfYear - resultDayOfYear;
// For example, if it's the day before at 11:00pm, we offset by (24 - 23),
// so +1. If it's the same day at 1:00am, we offset by (0 - 1), so -1.
var offset = (daysPrevious * 24) - result.hour;
var adjusted = result.add(Duration(hours: offset));
// Check if the adjustment worked. This can fail on a time zone transition
// where midnight doesn't exist.
if (adjusted.hour == 0) {
return adjusted;
}
// Adjusting did not work. Just check if the adjusted date is right. And
// if it's not, just give up and return [result]. The scenario where this
// might correctly happen is if we're in a Brazil time zone, jump forward
// to 1:00 am because of a DST transition, and trying to go backwards 1
// hour takes us back to 11:00pm the day before. In that case the 1:00am
// answer on the correct date is preferable.
var adjustedDayOfYear =
date_computation.dayOfYear(adjusted.month, adjusted.day, leapYear);
if (adjustedDayOfYear != expectedDayOfYear) {
return result;
}
return adjusted;
}
// None of our corrections applied, just return the uncorrected date.
return result;
}