nmh-workers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Nmh-workers] Back to Dates


From: Bob Carragher
Subject: Re: [Nmh-workers] Back to Dates
Date: Sat, 3 Jan 2015 21:25:55 -0800

Thank you very much, Ken, both for this nice recipe and the
detailed explanation of the code!  It actually cleared up a
couple of misconceptions I had!

It has also helped make a monster of my Date: field.  For
example, I now generate the following when viewing this message:

     Date:       Thu, 01 Jan 2015 23:07:40 -0500 (2015-01-01 20:07:40
[2 days ago])

The recipe for it, for the insanely curious, is:

     Date:formatfield="%<(nodate{text})%{text}%|\
     %(pretty{text})%(void(szone{text}))%<(eq 1) \
     (%(date2local{text})%04(year{text})-%02(mon{text})-%02(mday{text}) \
     %02(hour{text}):%02(min{text}):%02(sec{text})\
     %<(rclock{text})\
     %<(gt 60) [\
     %<(gt 299592000)%(void (plus 157680000))%(divide 315360000) decade\
     %?(gt 31492800)%(void (plus 15768000))%(divide 31536000) year\
     %?(gt 2548800)%(void (plus 1296000))%(divide 2592000) month\
     %?(gt 84600)%(void (plus 43200))%(divide 86400) day\
     %?(gt 3300)%(void (plus 1800))%(divide 3600) hour\
     %|%(void (plus 30))%(divide 60) minute%>%<(gt 1)s%> ago]%>%>)%>%>"

I modified the recipe that Ken posted by extending it in the
obvious way (adding months, years, and even decades), but also to
only output when a message is at least 1 minute old.  I also
incorporated the recipe posted a little while back, for displaying
both the sender's date as-is, and my local time (and date).

Bob

> From:     Ken Hornstein <address@hidden>
> To:       address@hidden
> Date:     Thu, 01 Jan 2015 23:07:40 -0500
> Subject:  Re: [Nmh-workers] Back to Dates
>
> It occurs to me that it might be useful to break this down a bit, to provide
> some explanation as to what's going on here.  You can use fmttest on it to
> get the instructions this decodes into, but that's not always that useful
> unless you've been inside of the format code a lot.
>
> - 
> Date:formatfield="%<(nodate{text})%{text}%|%(pretty{text})%(date2local{text})
>   [\
>   %02(hour{text}):%02(min{text})\
>   %<(rclock{text})%<(gt 8596800)%| - \
>   %<(gt 84600)%(void (plus 43200))%(divide 86400) day\
>   %?(gt 3300)%(void (plus 1800))%(divide 3600) hour\
>   %|%(void (plus 30))%(divide 60) minute%>%<(gt 1)s%> ago%>%>]%>"
>
> This is the beginning of the line for the mhl file; it refers to any
> "date" header.  formatfield is the flag that says, "Run this header
> through mh-format, and here's the mh-format string".  The wrinkle here
> is that the value of the header is made available via the special {text}
> component; in scan(1), the date header is in {date}.  Other utilitities
> that make use of mh-format(5) do the same thing.
>
> The decomposed mh-format string is:
>
> 1) %<
>       If statement, will execute the next statements if the return value
>       is non-zero.  Well, if it's a function that returns a string, "true"
>       is if the string has a non-zero length.
>
> 2) (nodate{text})
>       Returns "true" if the {text} component (see above) is NOT a valid
>       date.  Note that it does not have a leading '%'; this is because
>         the "if" statement above knows that the following entry has to
>         be a function or component, so the '%' is assumed.  Also note
>       that the return value of this function is NOT output.
>
> 3) %{text}
>       Outputs the value of the {text}component.
>
> 3) %|
>       The "else" statement to 1) above.
>
> 4) %(pretty{text})
>       A "user-friendly" rendering of the date header.
>
> 5) %(date2local{text})
>       Converts the internally-stored date for {text} into the local
>       timezone.  This requires some additional explanation.
>
>       The first time address and date headers are accessed they are
>       run through the respective parsers and internally stored with
>       the component information.  So things like %(hour) don't have
>       to parse the date header text again; they get it directly from
>       the internal date structure.  What %(date2local) does is convert
>       the internal date structure to the local timezone, but it does
>       NOT change the text.  So in this case, %{text} still has the
>       original date header, but things like %(hour{text}) refer to
>       the hour in the local timezone.
>
> 6) "\n[ "
>       Text, output literally
>
> 7) %02(hour{text})
>       The hour part of the time (local timezone, see 5)), width of
>       2 digits, left padded with zeros.
>
> 8) ":"
>       Text, output literally
>
> 9) %02(min{text})
>       The minutes part of the time, in the local timezone, width of
>       2 digits, left padded with zeros.
>
> 10) %<
>       If statement, will execute this branch if 11) returns nonzero.
>
> 11) (rclock{text})
>       Returns the number of seconds "date" is off of the current time.
>       Stores the value in the "num" register.
>
> 12) %<
>       If statement, will execute this branch of 13) returns nonzero.
>
> 13) (gt 8596800)
>       If "num" is greater than 8596800 (99.5 hours), execute the next
>       branch ... which is null.  This is the cutoff that doesn't print
>       a date offset if the message is greater than 100 days old.
>
>       A digression here: normally functions like "%(gt) would clobber the
>       "num" register with a 0 or 1.  But this and other functions that
>       return boolean values in mh-format(5) are treated a bit special;
>       if they are combined with an if (%<) statement and do not store
>       their result in the "num" register if they are the test for
>       an if statement.  You can see this in the fmttest(1) if you look
>       carefully.
>
> 14) %|
>       Else statement for 13) above; there is no "less than" operator
>       in mh-format(5), that's why the there is nothing under the "true"
>       branch of the if statement.
>
> 15) " - "
>       Text, output literally
>
> 16) %<(gt 86400)
>       If statement, will execute the this branch if "num" is greater
>       than 86400 (one day).
>
> 15) %(void (plus 43200))
>       Add 43200 (1/2 day) to the value of "num"  The use of %(void)
>       prevents the result from being output (see mh-format(5) for more
>       details).  This it to round up for the calculation in 16).
>
> 16) %(divide 86400)
>       Divide "num" by 86400, which results in the number of days from
>       "now" and the original date of this message, and output the
>       value of the num register.
>
> 17) " day"
>       Text, output literally.
>
> 18) %?(gt 3300)
>       Else if statement corresponding to 16) above, will execute this
>       branch if "num" is greater than 3300) (almost 1 hour).
>
> 19) %(void (plus 1800))
>       Add 1800 (1/2 hour) the value of "num" for rounding purposes.
>
> 20) %(divide 3600)
>       Divide "num" by 3600 to get the number of hours the message time
>       is offset from current time.
>
> 21) " hour"
>       Text, output literally
>
> 22) %|
>       The else statement for the if-else if statement in 16) and 18).
>       This branch gets executed if no other test was true.
>
> 23) %(void (plus 30))%(divide 60)
>       Add 30 to "num" for rounding, and divide it by 60 to get the number
>       of minutes.
>
> 24) " minute"
>       Text, output literally
>
> 25) %>
>       Endif for branch statements in 16), 18), 22).
>
> 26) %<(gt 1)s%>
>       At this point "num" contains the value of the days, hours, or
>       minutes (depending on previous branches taken).  This statement
>       will output the character "s" if "num" is greater than one,
>       so that days, hours, or minutes is pluralized properly.
>
> 27) " ago"
>       Text, output literally.
>
> 28) %>
>       Endif, for the if statement in 12)
>
> 29) %>
>       Endif, for the if statement in 10
>
> 30) "]"
>       Text, output literally
>
> 31) %>
>       Endif, for the if statement in 1).
>
> So that's the whole format string broken down; it actually does a few
> clever things, like only requiring one test to cover all time units
> for proper pluralization.  It looks like a mess all compacted, but it's
> relatively straightforward once you break it down.  The big problem
> when writing mh-format(5) programs is you only have two variables: "num"
> that can hold an integer, and "str" which can hold a string.  A lot of
> functions clobber one or the other, so it's tough to keep a value around
> for a large number of statements.
>
> --Ken
>
> _______________________________________________
> Nmh-workers mailing list
> address@hidden
> https://lists.nongnu.org/mailman/listinfo/nmh-workers



reply via email to

[Prev in Thread] Current Thread [Next in Thread]