decodeDate static method
Decodes the given MIME dateText
to a DateTime
Implementation
static DateTime? decodeDate(String? dateText) {
/*
Date and time values occur in several header fields. This section
specifies the syntax for a full date and time specification. Though
folding white space is permitted throughout the date-time
specification, it is RECOMMENDED that a single space be used in each
place that FWS appears (whether it is required or optional); some
older implementations will not interpret longer sequences of folding
white space correctly.
date-time = [ day-of-week "," ] date time [CFWS]
day-of-week = ([FWS] day-name) / obs-day-of-week
day-name = "Mon" / "Tue" / "Wed" / "Thu" /
"Fri" / "Sat" / "Sun"
date = day month year
day = ([FWS] 1*2DIGIT FWS) / obs-day
month = "Jan" / "Feb" / "Mar" / "Apr" /
"May" / "Jun" / "Jul" / "Aug" /
"Sep" / "Oct" / "Nov" / "Dec"
year = (FWS 4*DIGIT FWS) / obs-year
time = time-of-day zone
time-of-day = hour ":" minute [ ":" second ]
hour = 2DIGIT / obs-hour
minute = 2DIGIT / obs-minute
second = 2DIGIT / obs-second
zone = (FWS ( "+" / "-" ) 4DIGIT) / obs-zone
The day is the numeric day of the month. The year is any numeric
year 1900 or later.
The time-of-day specifies the number of hours, minutes, and
optionally seconds since midnight of the date indicated.
The date and time-of-day SHOULD express local time.
The zone specifies the offset from Coordinated Universal Time (UTC,
formerly referred to as "Greenwich Mean Time") that the date and
time-of-day represent. The "+" or "-" indicates whether the time-of-
day is ahead of (i.e., east of) or behind (i.e., west of) Universal
Time. The first two digits indicate the number of hours difference
from Universal Time, and the last two digits indicate the number of
additional minutes difference from Universal Time. (Hence, +hhmm
means +(hh * 60 + mm) minutes, and -hhmm means -(hh * 60 + mm)
minutes). The form "+0000" SHOULD be used to indicate a time zone at
Universal Time. Though "-0000" also indicates Universal Time, it is
used to indicate that the time was generated on a system that may be
in a local time zone other than Universal Time and that the date-time
contains no information about the local time zone.
A date-time specification MUST be semantically valid. That is, the
day-of-week (if included) MUST be the day implied by the date, the
numeric day-of-month MUST be between 1 and the number of days allowed
for the specified month (in the specified year), the time-of-day MUST
be in the range 00:00:00 through 23:59:60 (the number of seconds
allowing for a leap second; see [RFC1305]), and the last two digits
of the zone MUST be within the range 00 through 59.
*/
if (dateText == null || dateText.isEmpty) {
return null;
}
final original = dateText;
var splitIndex = dateText.indexOf(',');
if (splitIndex != -1) {
// remove weekday
dateText = dateText.substring(splitIndex + 1).trim();
}
var spaceIndex = dateText.indexOf(' ');
if (spaceIndex == -1) {
return null;
}
final dayText = dateText.substring(0, spaceIndex);
dateText = dateText.substring(spaceIndex + 1).trimLeft();
spaceIndex = dateText.indexOf(' ');
if (spaceIndex == -1) {
return null;
}
final monthText = dateText.substring(0, spaceIndex);
dateText = dateText.substring(spaceIndex + 1).trimLeft();
spaceIndex = dateText.indexOf(' ');
if (spaceIndex == -1) {
return null;
}
final yearText = dateText.substring(0, spaceIndex);
dateText = dateText.substring(spaceIndex + 1).trimLeft();
spaceIndex = dateText.indexOf(' ');
var timeText = dateText;
var zoneText = '+0000';
if (spaceIndex != -1) {
timeText = dateText.substring(0, spaceIndex);
if (dateText.length > spaceIndex) {
dateText = dateText.substring(spaceIndex + 1).trim();
spaceIndex = dateText.indexOf(' ');
if (spaceIndex == -1) {
zoneText = dateText;
} else {
zoneText = dateText.substring(0, spaceIndex);
}
}
}
final dayOfMonth = int.tryParse(dayText);
if (dayOfMonth == null || dayOfMonth < 1 || dayOfMonth > 31) {
print('Invalid day $dayText in date $original');
return null;
}
final month = _monthsByName[monthText.toLowerCase()];
if (month == null) {
print('Invalid month $monthText in date $original');
return null;
}
final year = int.tryParse(yearText);
if (year == null) {
print('Invalid year $yearText in date $original');
return null;
}
var timeParts = timeText.split(':');
if (timeParts.length < 2) {
print('Invalid time $timeText in date $original');
return null;
}
int? second = 0;
final hour = int.tryParse(timeParts[0]);
final minute = int.tryParse(timeParts[1]);
if (timeParts.length > 2) {
second = int.tryParse(timeParts[2]);
}
if (hour == null || minute == null || second == null) {
print('Invalid time $timeText in date $original');
return null;
}
if (zoneText.length != 5) {
if (zoneText.length == 4 &&
!(zoneText.startsWith('+') || zoneText.startsWith('-'))) {
zoneText = '+' + zoneText;
} else {
// source: https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
var zoneOffset = _timeZonesByName[zoneText];
if (zoneOffset == null) {
print('invalid time zone [$zoneText] in $original');
return null;
}
zoneText = zoneOffset;
}
}
final timeZoneHours = int.tryParse(zoneText.substring(1, 3));
final timeZoneMinutes = int.tryParse(zoneText.substring(3));
if (timeZoneHours == null || timeZoneMinutes == null) {
print('invalid time zone $zoneText in $original');
return null;
}
var dateTime = DateTime.utc(year, month, dayOfMonth, hour, minute, second);
final isWesternTimeZone = zoneText.startsWith('+');
final timeZoneDuration =
Duration(hours: timeZoneHours, minutes: timeZoneMinutes);
if (isWesternTimeZone) {
dateTime = dateTime.subtract(timeZoneDuration);
} else {
dateTime = dateTime.add(timeZoneDuration);
}
return dateTime.toLocal();
}