Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions lib/util/datetime.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import 'package:intl/intl.dart';
import 'timezone.dart';

const rfc822DatePattern = 'EEE, dd MMM yyyy HH:mm:ss Z';
/// The `Z` part is not yet implemented according to https://pub.dev/documentation/intl/latest/intl/DateFormat-class.html
/// We will remove it for now and parse the timezone separately.
const rfc822DatePattern = 'EEE, dd MMM yyyy HH:mm:ss';
final rfc822DateFormat = DateFormat(rfc822DatePattern, 'en_US');

DateTime? parseDateTime(dateString) {
if (dateString == null) return null;
return _parseRfc822DateTime(dateString) ?? _parseIso8601DateTime(dateString);
}

/// Try to parse `dateString` as an RFC 822 date.
/// We will parse the date string as UTC and then
/// subtract the actual time offset from the parsed string.
DateTime? _parseRfc822DateTime(String dateString) {
try {
final num length = dateString.length.clamp(0, rfc822DatePattern.length);
final trimmedPattern = rfc822DatePattern.substring(
0,
length
as int?); //Some feeds use a shortened RFC 822 date, e.g. 'Tue, 04 Aug 2020'
final format = DateFormat(trimmedPattern, 'en_US');
return format.parse(dateString);
final localTime = rfc822DateFormat.parse(dateString, true);
final timezone = dateString.trim().split(' ').last;

final timeOffset = Duration(minutes: getTimeZoneOffset(timezone) ?? 0);
return localTime.subtract(timeOffset).toUtc();
} on FormatException {
return null;
}
Expand Down
37 changes: 37 additions & 0 deletions lib/util/timezone.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const timeZoneAbbreviations = {
'EET': 2 * 60,
'CET': 1 * 60,
'GMT': 0,
'AST': -4 * 60,
'EST': -5 * 60,
'EDT': -4 * 60,
'CST': -6 * 60,
'CDT': -5 * 60,
'MST': -7 * 60,
'MDT': -6 * 60,
'PST': -8 * 60,
'PDT': -7 * 60,
};

/// Test this regexp online at https://regex101.com/r/mem3xt/1
final offsetRegExp =
RegExp(r'^(?<sign>[+\-]?)(?<hours>\d{2}):?(?<minutes>\d{2})$');

/// Parse a potential timezone string and return the
/// time offset in minutes
int? getTimeZoneOffset(String timezone) {
// check if timezone is one of the known abbreviations
var offset = timeZoneAbbreviations[timezone.toUpperCase()];
if (offset != null) return offset;
// check if the timezone is of type offset
final match = offsetRegExp.firstMatch(timezone);
if (match != null) {
final sign = match.namedGroup('sign') == '-' ? -1 : 1;
// we know <hours> and <minutes> are not null because the RexExp matched
final hours = int.parse(match.namedGroup('hours')!);
final minutes = int.parse(match.namedGroup('minutes')!);
return sign * (60 * hours + minutes);
}

return null;
}
Loading