When I view a message in the pager mutt displays the time in the Date header in UTC rather than my local time zone. The index view displays the local time correctly. I found this old mailing list post that describes how to get the local time to display in the status bar at the bottom of the screen, but this still doesn't "fix" the time in the Date header at the top of the screen. Is there any way to get the pager to convert the Date header time to local time?
8 Answers
The formatting in the index is controlled by the index_format
setting -- it's generated by mutt. The Date
header isn't controlled by mutt, it's a header included with the message that just gets displayed. If it shows UTC time it's because the sending server decided to use UTC when generating the header. The only way to change it is to actually change the message itself, either when you receive it or when you view it.
Changing it as it comes in means adding a filter to your mail delivery agent, but it needs to be sophisticated enough to parse the existing Date
header and rewrite it. It's almost certainly better to just have mutt reformat the message when you look at it. You can set the display_filter
property to an executable file, and it will pipe any message you open through the executable before displaying it.
You'll need to write a program or shell script that reads each line of the message and replaces the one with the Date header, or find an existing script (there's one here that might work, although it doesn't seem like it should really be necessary to involve a temporary file)
http://www.mail-archive.com/mutt-users@mutt.org/msg44341.html
This suggests using the 'pager_format', to make it show the letter date in the local timezone:
set pager_format="%4C %Z %[!%b %e at %I:%M %p] %.20n %s%* -- (%P)"
In your .muttrc
add the following line:
set display_filter="exec sed -r \"s/^Date:\\s*(([F-Wa-u]{3},\\s*)?[[:digit:]]{1,2}\\s+[A-Sa-y]{3}\\s+[[:digit:]]{4}\\s+[[:digit:]]{1,2}:[[:digit:]]{1,2}(:[[:digit:]]{1,2})?\\s+[+-][[:digit:]]{4})/date +'Date: %a, %d %b %Y %H:%M:%S %z' -d '\\1'/e\""
This will change the Date:
header in the message (for display only) to your local timezone if the header contained a valid RFC formatted date. If the provided date format was incorrect (we are dealing with untrusted user input after all) it will be preserved. To combat a possible attempt to inject the shell code through the header the sed
pattern implements a whitelist based on RFC 5322 (this RFC defines the format of the Date:
field).
Note that mutt
limits the command line to be no more than 255 character long, hence I optimised the original sed
command that had stricter whitelist to fit into 255 bytes. If you plan to do other things with the message, then the full sed
command you can put in a script is:
sed -r "s/^Date:\s*(((Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s*)?[[:digit:]]{1,2}\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+[[:digit:]]{4}\s+[[:digit:]]{1,2}:[[:digit:]]{1,2}(:[[:digit:]]{1,2})?\s+[+-][[:digit:]]{4})/date +'Date: %a, %d %b %Y %H:%M:%S %z' -d '\1'/e"
-
Is there a possibility to make that work on macOS? I would like to include that in my
filter.sed
file. Thanks! May 6, 2019 at 15:29 -
There is not, unless you are using GNU sed (e.g. via Homebrew) -- the above
sed
commands rely on thee
modifier to thes
command. That modifier is a GNU extension and macOS'essed
is the BSD one.– galaxyMay 8, 2019 at 6:40 -
Answer by @viric is good. No need to change message via filter. Jul 16, 2019 at 7:05
-
1@Jangid, please read the original request and it specifically asks for the proper display of a local zone in the top of the page and not in the status line.– galaxyJul 17, 2019 at 5:34
-
1
Obligatory Perl solution that preserves the "Date" header while adding an extra "Local-Date" header to contain the localtime version:
#!/usr/bin/perl -n
use Date::Parse;
use POSIX;
use strict;
if (/^Date: (.*)$/) {
my $datestr = $1;
my $date = strftime ("%a, %d %b %Y %H:%M:%S",
localtime (str2time ($datestr)));
print "Local-Date: $date\n";
}
print;
Taking Gilles' advice, here's a version that uses a temp file and formail
.
#!/bin/bash
TMPFILE=$(mktemp)
# save the message to a file
cat - >"$TMPFILE"
# extract the date header
DATE=$( formail -xDate: < "$TMPFILE" )
# convert to the current timezone (defined by TZ)
DATE=$( date -R -d "$DATE" )
# output the modified message
echo "Date: $DATE"
formail -fI Date < "$TMPFILE"
# clean up
rm -f "$TMPFILE"
-
This is susceptible to code injection attacks through a malformed
Date:
header -- if you go into a subshell you need to validate/sanitise the input.– galaxyApr 29, 2019 at 7:27
Here is a procmail solution:
# extract date from mail
:0
TMPDATE=| formail -x Date
# get local date
LOCALDATE=`date --rfc-2822 -d "$TMPDATE"`
# add it new header to the mail
:0 f
| formail -I "LocalDate: $LOCALDATE"
And display LocalDate: in muttrc:
unignore localdate
set display_filter="/PATH/TO/THIS/PYTHON/FILE"
in your .muttrc
.
Python 3.3:
#!/usr/bin/env python3
import sys
import re
# import subprocess
from email.utils import format_datetime, parsedate_to_datetime
in_headers = True
for line in sys.stdin.readlines():
if line == "\n": in_headers = False
match = re.match(r'^Date: (.+)', line)
if not in_headers or not match:
print(line, end="")
continue
date_string = match.group(1)
# use this if you do not have python 3.3+
# converted_date = subprocess.Popen(['date','-d',date_string], stdout=subprocess.PIPE).communicate()[0].strip()
converted_date = format_datetime(parsedate_to_datetime(date_string).astimezone(tz=None))
print('Date:', converted_date)
-
1The commented line is susceptible to code injection attacks through a malformed
Date:
header -- if you go into a subshell you need to validate/sanitise the input, otherwise the script is OK from the security standpoint. However, it is quite heavy for a script that runs on every message you browse.– galaxyApr 29, 2019 at 7:29
I made a shell script as described at the end of Michael's answer that does the job for me:
#!/bin/bash
while IFS= read -r LINE; do
if [[ "${LINE}" =~ ^Date:\ .* ]]; then
DATE=${LINE#Date: }
# convert to the current timezone (defined by TZ)
DATE=$(date -d "${DATE}")
printf '%s' "Date: ${DATE}"
elif [[ -n $LINE ]]; then
# We've reach the end of the headers, so stop parsing
echo
exec cat
else
printf '%s\n' "${LINE}"
fi
done
-
2Parsing emails is pretty tricky. I recommend using a dedicated tool like
formail
(part of the procmail package) orreformail
(part of maildrop) to extract the existingDate:
header and put a new one in place. Take care of edge cases such as a missing or malformattedDate:
header. Aug 15, 2011 at 16:29