解析 RFC 822 日期(并使时区正常工作)



刚刚意识到DateFormat并不关心时区字段。下面的两个打印将同时输出。

import 'package:intl/intl.dart';
void main() {
var formatter = DateFormat('EEE, dd MMM yyyy HH:mm:ss zzz');
print(formatter.parse('Tue, 9 Jun 2020 19:46:10 +0000'));
print(formatter.parse('Tue, 9 Jun 2020 19:46:10 +0200'));
}

可悲的是,我不能改用DateTime.parse,因为它只接受 ISO-8601 字符串。

这就引出了一个问题,如何在 Dart 中正确解析 RFC 822 时间戳?

根据jamesdin的评论,除了手动执行此操作之外,我找不到任何其他方法。最后得到:

const MONTHS = {
'Jan': '01',
'Feb': '02',
'Mar': '03',
'Apr': '04',
'May': '05',
'Jun': '06',
'Jul': '07',
'Aug': '08',
'Sep': '09',
'Oct': '10',
'Nov': '11',
'Dec': '12',
};
DateTime parseRfc822(String input) {
var splits = input.split(' ');
var reformatted = splits[3] +
'-' +
MONTHS[splits[2]] +
'-' +
(splits[1].length == 1 ? '0' + splits[1] : splits[1]) +
' ' +
splits[4] +
' ' +
splits[5];
return DateTime.tryParse(reformatted);
}

虽然代码有更多的行,但我发现这更具可读性。我希望这有助于某人理解和使用。

const _MONTHS = {
'Jan': '01',
'Feb': '02',
'Mar': '03',
'Apr': '04',
'May': '05',
'Jun': '06',
'Jul': '07',
'Aug': '08',
'Sep': '09',
'Oct': '10',
'Nov': '11',
'Dec': '12',
};
DateTime? parseRfc822(String input) {
input = input.replaceFirst('GMT', '+0000');
final splits = input.split(' ');
final splitYear = splits[3];
final splitMonth = _MONTHS[splits[2]];
if (splitMonth == null) return null;
var splitDay = splits[1];
if (splitDay.length == 1) {
splitDay = '0$splitDay';
}
final splitTime = splits[4], splitZone = splits[5];
var reformatted = '$splitYear-$splitMonth-$splitDay $splitTime $splitZone';
return DateTime.tryParse(reformatted);
}

我想出了这个解决方案,它利用了转换器。

class Rfc822ToDateTimeConverter extends Converter<String, DateTime> {
@override
DateTime convert(String input) {
final segments = input.split(' ');
final day = int.parse(segments[1]);
final month = _parseMonth(segments[2]);
final year = int.parse(segments[3]);
final (hour, minute, second) = _parseHour(segments[4]);
final (isTzAddition, tzDuration) = _parseTzVal(segments[5]);
final dtAgnostic = DateTime(year, month, day, hour, minute, second);
final DateTime dtResolved;
if (isTzAddition) {
dtResolved = dtAgnostic.subtract(tzDuration);
} else {
dtResolved = dtAgnostic.add(tzDuration);
}
return dtResolved;
}
int _parseMonth(String input) {
const months = {
'Jan': 1,
'Feb': 2,
'Mar': 3,
'Apr': 4,
'May': 5,
'Jun': 6,
'Jul': 7,
'Aug': 8,
'Sep': 9,
'Oct': 10,
'Nov': 11,
'Dec': 12,
};
return months[input]!;
}
(int, int, int) _parseHour(String input) {
final segments = input.split(':');
return (
int.parse(segments[0]),
int.parse(segments[1]),
int.parse(segments[2]),
);
}
(bool, Duration) _parseTzVal(String input) {
if (input == 'GMT') {
return (true, Duration.zero);
}
final hoursStr = input.substring(1, 3);
final minutesStr = input.substring(3, 5);
final hours = int.parse(hoursStr);
final minutes = int.parse(minutesStr);
final isAddition = input.startsWith('+');
final duration = Duration(hours: hours, minutes: minutes);
return (isAddition, duration);
}
}

最新更新