Mutt: conditional date format in “index_format”

datemutt

I have following value set for index_format in mutt:

"%Z %{%Y %b %e  %H:%M} %?X?(%X)&   ? %-22.22F  %.100s %> %5c "

which displays date in the format as

2013 Dec 5

I was wondering whether it is possible to have different date formats depending on how old the email is. By that I mean:

for less than 7 days:  today, yesterday, tuesday, monday
this year:             Dec 5
older than this year:  2013 Dec 5

I think I have seen this functionality in Thunderbird. Would be nice to have it in mutt

Best Answer

If you are using the "development" version of mutt (v1.5+) - and you absolutely should - there is the possibility to use an external filter as described in the manual.

First you need a script that can output different things according to the age of a message. Here is an example in Python:

#!/usr/bin/env python
"""mutt format date

Prints different index_format strings for mutt according to a
messages age.

The single command line argument should be a unix timestamp
giving the message's date (%{}, etc. in Mutt).
"""

import sys
from datetime import datetime

INDEX_FORMAT = "%Z {} %?X?(%X)&   ? %-22.22F  %.100s %> %5c%"

def age_fmt(msg_date, now):
    # use iso date for messages of the previous year and before
    if msg_date.date().year < now.date().year:
        return '%[%Y-%m-%d]'

    # use "Month Day" for messages of this year
    if msg_date.date() < now.date():
        return '%10[%b %e]'

    # if a message appears to come from the future
    if msg_date > now:
        return '  b0rken'

    # use only the time for messages that arrived today
    return '%10[%H:%m]'

if __name__ == '__main__':
    msg_date = datetime.fromtimestamp(int(sys.argv[1]))
    now = datetime.now()
    print INDEX_FORMAT.format(age_fmt(msg_date, now))

Save this as mutt-fmt-date somewhere on your PATH.

Two things are important here:

  • The format string has to contain one occurance of {} which is replaced with the return value of age_fmt() by Python.
  • The format string has to end with a % so that Mutt will interpret it.

Then you can use it in your .muttrc as follows:

set index_format="mutt-fmt-date %[%s] |"

Mutt will then

  1. interpret %[%s] according to the rules for format strings.
  2. call mutt-fmt-date with the result of 1. as argument (because of the | at the end).
  3. interpret what it gets back from the script as format string again (because of the % at the end).

Caveat: the script will be executed for every message that is to be about be displayed. The resulting delay can be quite noticable when scrolling through a mailbox.

Here is a version in C that performs somewhat adequately:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define DAY (time_t)86400
#define YEAR (time_t)31556926

int main(int argc, const char *argv[]) {
    time_t current_time;
    time_t message_time;

    const char *old, *recent, *today;
    const char *format;

    current_time = time(NULL);

    if (argc!=6) {
        printf("Usage: %s old recent today format timestamp\n", argv[0]);
        return 2;
    }

    old = argv[1];
    recent = argv[2];
    today = argv[3];

    format = argv[4];

    message_time = atoi(argv[5]);

    if ((message_time/YEAR) < (current_time/YEAR)) {
        printf(format, old);
    } else if ((message_time/DAY) < (current_time/DAY)) {
        printf(format, recent);
    } else {
        printf(format, today);
    }

    return 0;
}

This goes together with the muttrc line:

set index_format='mfdate "%[%d.%m.%y]" "%8[%e. %b]" "%8[%H:%m]" "%Z %%s %-20.20L %?y?[%-5.5y]&       ? %?M?+& ?%s%%" "%[%s]" |'
Related Question