Time since an event

Hi,

I’m wondering if there’s any prior art on displaying “the time since” a date such that depending on the difference, the format would change progressively from hours, to days+hours, to months+days+hours, etc.

Thanks!
Surge

Hi Surge,

I did a bit of date work recently, with lots of help from others.

The days operator is a good place to start for the code - not sure if your question is only about the actual display formatting of the result or the code to calculate the result.

$:/core/modules/filters/days.js

There you can see the code working in UTC representation which is the internal representation for Tiddlywiki.

There are JS libraries that do this. I’m sure there are more, but I can think of these two:

I imagine they could be imported and used in TW fairly easily.

The first one is more fully-featured. The second one is extremely lightweight.

The built-in <$view> widget has an option for displaying dates in relative format. For example, this displays “2 months ago” on tiddlywiki.com:

<$view format="relativedate" tiddler="HelloThere" field="modified"/>

The format operator also supports relativedate format.

2 Likes

Nice!

But damn, I was thinking this was a good small project to use to learn more about importing JS libraries! Obviously I still could, but it would feel so… redundant.

Hello, I use projectify (duedate field when creating a todo) and this code to show:
Current date and weeknummer and the list:

checkbox, tiddlertitle, date, duedate (hours, days, weeks) and weeknummer based on the duedate

<<hide titlebar>>
<<hide subtitle>>
! <<now 0DD-0MM-YYYY>> WEEK <$view field=modified format=date template="WW" />
<$vars today=<<now format:"YYYY0MM0DD">>>
<$vars currentWW=<<now format:"WW">>>
<$list filter="[tag[todo]has[due]!tag[done]] :filter[get[due]compare:date:gt<today>] +[sort[due]]">
    <$set name="tiddlerWW" value=<<currentTiddler>get[due]format[date]template[WW]>>
    <$list filter="[<tiddlerWW>compare:number:eq<currentWW>]">
        <tr><html>&nbsp;&nbsp;</html><html>&nbsp;&nbsp;</html>
          <td> <$checkbox tag="done"></$checkbox> <$link to=<<currentTiddler>>><$view field="caption">{{!!title}}</$view></$link></td><html>&nbsp;&nbsp;</html>
          <td> </td>
          <td> <$view field="due" format="date" template="ddd DD mmm"/></td>
          <td> <$view field="due" format="relativedate"/></td>
          <td> </td>
          <td><$view field="due" format="date" template="WW"/></td>
        </tr>

Sorry, misread. This is for future items… I use this code to show the time since (with todays week and the week of the duedate):

<$vars today=<<now format:"YYYY0MM0DD">>>
<$view field="due" format="relativedate"/>
Huidige Week <$view field=modified format=date template="WW" /> Eind Week <td><$view field="due" format="date" template="WW"/></td>

That’s very nice, but given this date 19980111, it returns 25 years ago. How do I include months and days?

Hi @Surge

The “relativedate” filter rounds the period to the largest unit covered by the period. In other words, if the period is over 1 year, then the output is just the number of years, with smaller units (month, day, hour, minute, second) being ignored. This matches the behaviour of relative dates on platforms like GitHub.

Hi @jeremyruston,

Thanks again, I can live with this quite well.

Some of these events that I want to track are of personal achievement nature where at least months and days are important (along with the number of years if appropriate). Is there a way, to include months and days always?

If not, it’s ok, but curious what would need to be done to support something like that.

Thanks!
Surge

Slightly unrelated: given the date 19781209, the below produces 1978-12-08:

<$text text={{{ [{!!event}format:date[0MM-0DD-YYYY]] }}} />

I seem to remember that in Javascript days start with 0. Is that what’s going on here?

One thing to Concider is the inclusion of unix time in recent versions you could subtract an old date from now in unix time and the result is the diference. You then need to convert this to d m y values.

We need only so this once as a community and we can share this.

The format:date[] filter operator assumes that all inputs are UTC datetime values and, if no time portion is specified, it assumes midnight (= 000000000). In your example, the filter operand (output format) is “0MM-0DD-YYYY”, which is a local date format. As a result, the output is being automatically adjusted to match your local timezone.

If you are west of GMT, there is a negative offset value for your local timezone (e.g., “UTC-05” for USA Eastern Time), and the date that results will be one day before the date that was specified in the input.

To prevent this automatic “time shift” from occurring, you need to specify “[UTC]” at the beginning of the output format. However, you can’t directly include the “[UTC]” within a literal operand value which uses square brackets as delimiters to indicate that the operand is a literal value, and the literal filter operand syntax does not handle nested square brackets.

To work around this, you can first define the desired format as a variable, and then refer to that variable in the filter syntax, like this:

<$let fmt="[UTC]0MM-0DD-YYYY">
<$text text={{{ [{!!event}format:date<fmt>] }}} />

enjoy,
-e

1 Like

One annoying complication for accurately converting the unix time difference (in milliseconds) to “days, months, years” is that a day isn’t exactly 24 hours… it’s actually 23 hours, 56 minutes, 4.091 seconds. In addition, the days-per-month varies: February is 28 or 29 days (for leap years), April/June/Sepember/November are 30 days, and the rest are 31 days.

1 Like

I’m working on a custom macro to get the functionality I have in mind. I think it would be difficult to do with built-in functionality.

1 Like

This seems to be working:

/*\
title: daysSince
type: application/javascript
module-type: macro

daysSince
\*/

  (function() {
  "use strict";
  exports.name = "daysSince";
  exports.params = [
    {name: "date"}
  ];

  exports.run = function(date) {
    var date1 = new Date(date+"T00:00:00");
    var date2 = new Date();

    var milliseconds = date2.getTime() - date1.getTime();

    if (milliseconds < 0) {
      return date + " is less than today";
    }

    // console.log("MILI="+milliseconds, milliseconds < 0);

    var seconds = Math.floor(milliseconds/1000);

    var y = Math.floor(seconds / (365*24*3600));
    var d = Math.floor(seconds % (365*24*3600) / (24*3600));

    var result = [];
    var yDisplay = y > 0 ? y + (y == 1 ? " year" : " years") : "";
    var dDisplay = d > 0 ? d + (d == 1 ? " day" : " days") : "";

    if (yDisplay) {
      result.push(yDisplay);
    }

    if (dDisplay) {
      result.push(dDisplay);
    }

    return result.join(", ");
  }
  })();

Yes @EricShulman calendars are quite inconvenient. In many ways days old or in the future only a number of days is a true interval but we do think in terms of months and years and the truth is the larger the interval the more approximate is ok.