X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FMsgTime.pm;h=58e11d726a5cc23ca81618e6efd51333e4daac85;hb=9bd675d33ad1e49bd2ebe12a1d216216e61380de;hp=87664f4b0909a80f7ad441cabacb5450f27a8533;hpb=35ac61764499c272d2760de2b2a432be412ecede;p=public-inbox.git diff --git a/lib/PublicInbox/MsgTime.pm b/lib/PublicInbox/MsgTime.pm index 87664f4b..58e11d72 100644 --- a/lib/PublicInbox/MsgTime.pm +++ b/lib/PublicInbox/MsgTime.pm @@ -1,51 +1,92 @@ -# Copyright (C) 2018 all contributors +# Copyright (C) 2018-2019 all contributors # License: AGPL-3.0+ + +# Various date/time-related functions package PublicInbox::MsgTime; use strict; use warnings; use base qw(Exporter); -our @EXPORT_OK = qw(msg_timestamp); -use Date::Parse qw(str2time); -use Time::Zone qw(tz_offset); +our @EXPORT_OK = qw(msg_timestamp msg_datestamp); +use Date::Parse qw(str2time strptime); -sub msg_timestamp ($) { +sub str2date_zone ($) { + my ($date) = @_; + + my $ts = str2time($date); + return undef unless(defined $ts); + + # off is the time zone offset in seconds from GMT + my ($ss,$mm,$hh,$day,$month,$year,$off) = strptime($date); + return undef unless(defined $off); + + # Compute the time zone from offset + my $sign = ($off < 0) ? '-' : '+'; + my $hour = abs(int($off / 3600)); + my $min = ($off / 60) % 60; + my $zone = sprintf('%s%02d%02d', $sign, $hour, $min); + + # "-1200" is the furthest westermost zone offset, + # but git fast-import is liberal so we use "-1400" + if ($zone >= 1400 || $zone <= -1400) { + warn "bogus TZ offset: $zone, ignoring and assuming +0000\n"; + $zone = '+0000'; + } + [$ts, $zone]; +} + +sub time_response ($) { + my ($ret) = @_; + wantarray ? @$ret : $ret->[0]; +} + +sub msg_received_at ($) { my ($hdr) = @_; # Email::MIME::Header - my ($ts, $zone); - my $mid; my @recvd = $hdr->header_raw('Received'); + my ($ts); foreach my $r (@recvd) { - $zone = undef; - $r =~ /\s*(\d+\s+[[:alpha:]]+\s+\d{2,4}\s+ - \d+\D\d+(?:\D\d+)\s+([\+\-]\d+))/sx or next; - $zone = $2; - $ts = eval { str2time($1) } and last; - $mid ||= $hdr->header_raw('Message-ID'); + $r =~ /\s*([0-9]+\s+[a-zA-Z]+\s+[0-9]{2,4}\s+ + [0-9]+[^0-9][0-9]+(?:[^0-9][0-9]+) + \s+([\+\-][0-9]+))/sx or next; + $ts = eval { str2date_zone($1) } and return $ts; + my $mid = $hdr->header_raw('Message-ID'); warn "no date in $mid Received: $r\n"; } - unless (defined $ts) { - my @date = $hdr->header_raw('Date'); - foreach my $d (@date) { - $zone = undef; - $ts = eval { str2time($d) }; - if ($@) { - $mid ||= $hdr->header_raw('Message-ID'); - warn "bad Date: $d in $mid: $@\n"; - } elsif ($d =~ /\s+([\+\-]\d+)\s*\z/) { - $zone = $1; - } + undef; +} + +sub msg_date_only ($) { + my ($hdr) = @_; # Email::MIME::Header + my @date = $hdr->header_raw('Date'); + my ($ts); + foreach my $d (@date) { + # Y2K problems: 3-digit years + $d =~ s!([A-Za-z]{3}) ([0-9]{3}) ([0-9]{2}:[0-9]{2}:[0-9]{2})! + my $yyyy = $2 + 1900; "$1 $yyyy $3"!e; + $ts = eval { str2date_zone($d) } and return $ts; + if ($@) { + my $mid = $hdr->header_raw('Message-ID'); + warn "bad Date: $d in $mid: $@\n"; } } - $ts = time unless defined $ts; - return $ts unless wantarray; + undef; +} - $zone ||= '+0000'; - # "-1200" is the furthest westermost zone offset, - # but git fast-import is liberal so we use "-1400" - if ($zone >= 1400 || $zone <= -1400) { - warn "bogus TZ offset: $zone, ignoring and assuming +0000\n"; - $zone = '+0000'; - } - ($ts, $zone); +# Favors Received header for sorting globally +sub msg_timestamp ($) { + my ($hdr) = @_; # Email::MIME::Header + my $ret; + $ret = msg_received_at($hdr) and return time_response($ret); + $ret = msg_date_only($hdr) and return time_response($ret); + wantarray ? (time, '+0000') : time; +} + +# Favors the Date: header for display and sorting within a thread +sub msg_datestamp ($) { + my ($hdr) = @_; # Email::MIME::Header + my $ret; + $ret = msg_date_only($hdr) and return time_response($ret); + $ret = msg_received_at($hdr) and return time_response($ret); + wantarray ? (time, '+0000') : time; } 1;