Sorting only by date from field value in yyyy-mm-dd format?

Hello Tiddlywiki users,

I have tiddlers of employees, with their birthday as field and value in yyyy-mm-dd format. I manage to get a list of employees with birthday in current month with this:

<$list filter="[tag[Employees]sort[Birthday]search:Birthday{$:/temp/Calendar!!Month}]" emptyMessage="<li>No one has birthday in this month</li>">
  <li>{{!!Birthday}}: <$link to={{!!title}}>{{!!title}}</$link></li>
</$list>

$:/temp/Calendar!!Month has a field value of “-mm-” so anyone with that in birthday field get into the list.

The result is something like this;

1990-04-30: Adam
1995-04-10: Bob
2000-04-20: Charles

Sorting ISO8601 results in sorting by older to younger age. I would like it to only sorting by date, to have earlier date first like this;

1995-04-10: Bob
2000-04-20: Charles
1990-04-30: Adam

Is there a way to use “-dd” from birthday field to sort list filter?

Thank you.

I used simplified data (just April), but you can adapt. You can use the sort filter run prefix (FRP).

<$list filter="[tag[Employees]]:sort:string[get[birthday]split[-]last[]]">
<<currentTiddler>> {{!!birthday}} <br/>
</$list>

The sort FRP takes the birthday, splits it by the hyphen, and selects the last member of the resulting string (the date) which is then used to sort the results.

1 Like

Thank you!

I adapt your code to mine and now I can sort date while keeping that month filter running.

<$list filter="[tag[Employees]search:Birthday{$:/temp/Calendar!!Month}]:sort:string[get[Birthday]split[-]last[]]" emptyMessage="<li>No one has birthday in this month</li>">
  <li>{{!!Birthday}}: <$link to={{!!title}}>{{!!title}}</$link></li>
</$list>

This feels very topical to me as just in the last few days I’d started adding notes for a few people in my personal kb TW, and added ‘birthday’ fields where appropriate. I’d gotten as far as populating a dozen or so folks with that, with the data in TW core date format, and then constructing a table which calculated age since date of birth, added age in days because-I-could, and if there was a deathday field, gave that information too (and a similar memorial table for all people with deathday fields too)

2025-04-12T02:29:53_33acd943

Sorting, however, was simply oldest to youngest, which I wasn’t too happy with, and your post got me rethinking from first principles - especially that birthdays are a special form of date where the time/timezone is 100% irrelevant for future calculations! ie, even if you were born at 1am in the +1400 timezone (and so 99.9+% of the rest of the planet’s surface and population were on the previous day) it doesn’t matter where you later travel, the date celebrated for your birthday is the one recorded at that time of birth. relative offset is irrelevant. This had been a small headache to my calculations since I initially filled the birthday field with just YYYYMMDD - which works out for me in my timezone, but it felt awkward knowing the calculations could be made inaccurate via an external timezone setting (and adding a “12” to the field to make it think midday UTC, just felt clumsy in a different way and only minimises the problem, not properly solve it), plus my setup wouldn’t suit your filter need at all.

So in the last day I went down the rabbithole of adding ParseDate from tiddlytools, so now I can fill the field with a human readable date (as you’ve done), but still retain all my calculations due to being able to treat it as a parseable date (this version also fixed an off-by-one error on the age-in-days from the previous!)

2025-04-12T02:42:47_ddabcad3

Anyway, this is the revised code for the birthday table, where the ‘birthdate’ field is human readable (I’ve been filling it in as DDth MMM YYYY and then that gets included literally to the table for the places I want that format.

  <$list filter="[has:field[birthdate]] :sort:string:reverse[get[birthdate]split[ ]last[]]">
  <$let 
    now-ts=<<now TIMESTAMP>>
    bday-ts={{{ [{!!birthdate}unixtime[]] }}}
	age-days={{{ [<now-ts>subtract<bday-ts>divide[86400000]floor[]] }}}
	age-yrs={{{ [<now-ts>subtract<bday-ts>divide[315569280]floor[]divide[100]] }}}
    dday-ts={{{ [{!!deathday}format:date[TIMESTAMP]] }}}
  >
  <tr>
    <td><$link>{{!!title}}</$link></td>
    <td><strong><<age-yrs>> </strong>years since <strong>{{!!birthdate}}</strong></td>
    <td>(<<age-days>> days)</td>
    <td><$list filter="[<currentTiddler>has[deathdate]]"> 
(Passed {{!!deathdate}})
</$list></td>
  </tr>
  </$let>
  </$list>
</table>

I’ve not yet implemented a month filter, but I now feel like sorting the entire list into birthday order is within reach for me - iterate through a list of months, for each one filter as you’ve done, and within those filtered results, sort by day. (Sorting by “next upcoming birthday” feels the ultimate end-goal to me, though that’s definitely beyond my current skills)

2 Likes

This is what I’m using right now

<$list filter="[tag[Employees]search:Birthday{$:/temp/Calendar!!Month}]:sort:string[get[Birthday]split[-]last[]]" emptyMessage="<li>No one has birthday in this month</li>">
  <li>{{!!Birthday}}: <$link to={{!!title}}>{{!!title}}</$link> <$let YearCurrent={{{[<now YYYY>]}}} EventYear={{!!Birthday}} YearsSince={{{ [<YearCurrent>subtract<EventYear>] }}}>(//<<YearsSince>> years old//)</$let></li>
</$list>

And I get

  • 1995-04-10: Bob (30 years old)
  • 2000-04-20: Charles (25 years old)
  • 1990-04-30: Adam (35 years old)

I focus on their age by their birthday rather than their current age, because I need the numbers that I will use to wish them happy birthday. Each employee’s page also feature countdown to birthday (in XX days), and employment duration, which I convert to XX years and YY days because decimal in years confused me!

yeah, I agree sorting by upcoming birthdays is the ideal, and my sorting by age is only at the granularity of the year, and decimal in years is far from ideal too. I’m still revising mine

I’m wondering how {$:/temp/Calendar!!Month} is populated? …and if it could be replaced with <<now -0MM->>?

I’ve got a version of my table which is now filtered to the current month, and sorted by date within the month, by simplifying my initial list filter to just this: <$list filter="[search:birthdate<now mmm>] :sort:">

How do you calculate the countdown and account for leapyears? (I’ve got a draft of a calculation of years+days, but for my two example people, L is accurate (today is the 12th in my timezone, so 7 days ago was indeed the 5th), and the other is off by one (364 days ago was the 13th, not the 14th). I think this is a result of my using a year length of 31556928000 milliseconds, which correct for the tropical year (and thus for the calender year as a long term average), but incorrect for any given calender year).

L 	41.02 years since 5th April 1984 (41years, 7days) 	(14982 days) 	(Passed 12th July 2016)
N 	49.99 years since 14th April 1975 (49years, 364days) 	(18261 days)
1 Like

{$:/temp/Calendar!!Month} was defined with StartupActions plugin that set the value when I start the wiki . It is actually now macro like you suggest <$action-setfield $tiddler="$:/temp/Calendar" $field="Month" $value=<<now -0MM->>/>. When I wrote it I didn’t know how to use macro as input for search operator, so I did it this way since I already have this method generating some random number for use in “random featured articles.” Now I did it your way, thank you!

And leap year…oh leap year. I pulled my hair thinking about it for a long time… My current code will show 1984-04-05 as 41 years 6 days and 1975-04-12 as 50 Years 0 Days.

At first I used 1 year = 365.242125 days and all the anniversary numbers were all mess. Then I changed it to 365.25 because…

  • The earliest date (company founding) I used was 1939, long after the non-leap year that was 1900.
  • The next non-leap year is 2100, probably long after I’m done with my wiki.
  • Thus all the years I’m dealing with are within 1904-2096 that consistently get leap year every four years, so the average days per year is simple 365.25.

Then I found that remainder method I used the decimal did not matter much and will output wrong days anyway, just change where it going to be wrong…

<div class="CardGreen" align="center">
  <p align="center">
    <$let
    JoinDate={{!!JoinDate}}
    Years={{{ [<JoinDate>parsedate[YYYY0MM0DD]] }}}
    Today=<<now YYYY0MM0DD>>
    YearStart={{{ [<Years>parsedate[TIMESTAMP]] }}}
    YearCurrent={{{ [<Today>parsedate[TIMESTAMP]] }}}
    DaysSince={{{ [<YearCurrent>subtract<YearStart>divide[86400000]floor[]] }}}
    Years={{{ [<DaysSince>divide[365.25]floor[]] }}}
    Days={{{ [<DaysSince>remainder[365.25]floor[]] }}}>

    [img width=48 [Calendar_3.webp]]

    !! <<Years>> Years <<Days>> Days
    since joining the company
    </$let>
  </p>
</div>

It will misses a day in leap year. It misses one day in 1984 because if I input 1984-04-12 I will get 40 Years 365 Days. But 75% of input date will work fine as X years 0 days so long that it’s not leap years.

Back then I try to find if there’s some dedicate date-time plugin that will save me from tinkering with unix timestamp but found none. I don’t think it’s possible to get accurate years and days from timestamp simply with simple equation.

1 Like

yeah, I’m not sure the leap year issue is easily resolvable. I thought about the idea of doing calculations to capture someone’s age in integer years, add that number to the year of their birth, feed that number plus their month and day into the view widget (using it’s 'relativedate" format option), but it was feeling [a] beyond my TW coding skills and [b] unsure if it would solve for accurate countdown anyway!

I did end up taking @EricShulman’s “One Year Later” calculator from here: Relative date between two given dates, not now - native tiddlywiki and updating for the newer parsedate version of the code, to confirm my suspicion that it has the same leap year off-by-one problem.

From reading that thread though, I suspect the underlying javascript built in date stuff might handle things, but I cant tell what’s exposed to wikitext.

I think the script behind the scenes only deal with converting unix timestamp to readable date. In the end calculating difference between two dates still use arithmetic to subtract two timestamps. It is much easier to convert that into day but that’s the end of it. So long this operation does not get through dedicate date-time function we will never get a correct value with unit larger than day or week.

I use Windows Calculator to check my result during tinkering. The difference between 2025-03-13 and today is 1 month (31 days), and difference between 2025-02-13 and today is 2 month (59 days). No way we can get such things from simple subtracting without function that take month length into account. Let alone more time units…

yeah, I’ve written a few simple scripts on the linux commandline to deal with dates/times, and they work on unix epoch time wherever possible to do stuff, and I dont even try and calculate it back to years/months unless the underlying tool can do that.

Hence my equivalent to your screenshot:

$ datediff '14 apr 1975' '12 apr 2025'
18261d

(it can handle hours/minutes/seconds too and show those as part of the difference, but in the other direction I just leave it as days - dont even bother turning the days into a count of weeks.

My overall taste is I’d rather have accurate-but-slightly-confusing than easy-to-read but not confident about having an off-by-one race condition on the day - hence my first implementation stopped with fractional years rather than try to turn that into days. But not that it exactly helped: my local table is saying the 14th Apr 2025 is 50.00 years since 1975… though the 14th of April is still over 10 hours away in my timezone!

I’ll probably keep tinkering till my interest gets distracted… lol

I kept tinkering and I think I have a “days to birthday” method which is always accurate, including across leap years and new year boundaries. I’ve implemented it as two lists - those in the current month, and those in the next month. The method takes the persons birth day and month and joins it with the current year, and using that as the date of comparison. A similar trick lets me get the next month (and the year associated with next month), and thus get the birthdays coming up for them too. A bit of conditional if/then/else detects birthdays in the list that are history or future, and adjusts wording to suit.

2025-04-15T10:27:04_bbf07ae8

The code that makes this (this has a little more is follows (code example includes a little bit of extra debugging verbosity at the top)

Generate lists of birthdays for this and next month
<!-- note: 2.5million seconds = just shy of 29 days -->
<$let 
    now-monthyr=<<now MMM-YYYY>>
    midnow-monthyr={{{ "15th" [<now MMM-YYYY>] +[join[-]] }}}
    next-month={{{ [<midnow-monthyr>parsedate[TIMESTAMP]add[2500000000]unixtime[MMM]]  }}} 
    next-monthsyr={{{ [<midnow-monthyr>parsedate[TIMESTAMP]add[2500000000]unixtime[YYYY]]  }}} 
>

* this month: <<now-monthyr>>
* middle of this month: <<midnow-monthyr>>
* next month:  <<next-month>>-<<next-monthsyr>>

!! Current Birthdays
<ul>
  <li><<now MMM-YYYY>></li>
  <ul>
<$list filter="[search:birthdate<now MMM>] :sort[get[birthdate]split[ ]first[]]">
  <$let 
    now-ts=<<now TIMESTAMP>>
    bdate={{{ [{!!birthdate}parsedate[DDth MMM YYYY]] }}}
    byear={{{ [{!!birthdate}parsedate[YYYY]] }}}
    now-yr=<<now YYYY>>
    age-thisyr={{{ [<now-yr>subtract<byear>] }}}
    bday-thisyr={{{ [{!!birthdate}parsedate[DDth]]  [{!!birthdate}parsedate[MMM]] [<now YYYY>] +[join[-]] }}}
    days-to-bday={{{ [<bday-thisyr>parsedate[TIMESTAMP]subtract<now-ts>divide[86400000]ceil[]] }}}
    days-to-bday-abs={{{ [<days-to-bday>abs[]] }}}
  >
    <li><$link>{{!!title}}</$link>: <%if [<days-to-bday>compare:number:lt[0]] %> turned <<age-thisyr>> some <<days-to-bday-abs>> days ago<%else%> turning <<age-thisyr>> in <<days-to-bday>> days time<%endif%>  (born <<bdate>>)</li>
  </$let>
</$list>
  </ul>
  <li><<next-month>>-<<next-monthsyr>></li>
  <ul>
<$list filter="[search:birthdate<next-month>] :sort[get[birthdate]split[ ]first[]]">
  <$let 
    now-ts=<<now TIMESTAMP>>
    bdate={{{ [{!!birthdate}parsedate[DDth MMM YYYY]] }}}
    byear={{{ [{!!birthdate}parsedate[YYYY]] }}}
    now-yr=<<now YYYY>>
    age-thisyr={{{ [<now-yr>subtract<byear>] }}}
    next-bday={{{ [{!!birthdate}parsedate[DDth]]  [{!!birthdate}parsedate[MMM]] [<next-monthsyr>] +[join[-]] }}}
    days-to-bday={{{ [<next-bday>parsedate[TIMESTAMP]subtract<now-ts>divide[86400000]ceil[]] }}}
>
    <li><$link>{{!!title}}</$link>: turning <<age-thisyr>> in <<days-to-bday>> days time (born <<bdate>>)</li>
</$let>
</$list>
  </ul>
</ul>
</$let>

There is some mostly-duplication in the current-month and next-month list calculations, which I’ve not looked to improve yet but I’m sure there is potential there - possibly it could/should be made a procedure or something?

caveats

  • requires parsedate from Eric’s timer tools
  • dates are taken from the “birthdate” field in tidbits, and thanks to parsedate flexibility can be in a range of formats. However, sorting is based on the literal birthdate data, so keeping those consistent is important. I’m pretty sure (without testing) that your YYYY-MM-DD method will sort fine. I’m using 0DDth MMMM YYYY which works fine too.

I think that current month is only in code to determine next month so there’s no need to use two variables for that.

<$let 
  now-monthyr={{{ "1th" [<now MMM-YYYY>] +[join[-]] }}}
  next-month={{{ [<now-monthyr>parsedate[TIMESTAMP]add[2700000000]unixtime[MMM]]  }}}
  next-monthyr={{{ [<now-monthyr>parsedate[TIMESTAMP]add[2700000000]unixtime[YYYY]]  }}}
>

...

I have current month lock at first day, and next 31 days is always next month.

Other than that, so long as we are consistence in formatting our date format there should be no problem. As much as I want native solution in Tiddly parsedate is indeed powerful and a bit deviation in format should get thorugh the code without much problem too. Still, date-time function that will understand simple month+1 would solve a lot of problem here! Perhaps extra parameter that allow us to manupulate now macro a bit more to get next month or previous year and such.

yeah, that bit could be made more efficient, but I stopped cleaning it at that point in part for readability (mainly my own - I’m about to be away from all this for over a week and want to be able to pick this back up relatively easily when I circle back to it), and in part because I should be packing for the week away and not coding here! lol

I probably wont need a lot of date/time handling in my future TW use though, and since my experience of manipulating and understanding time strings is based heavily on either basic maths on unix epoch time, or utilising linux’ date command to handle many arbitrary date formats as input, parsedate slots in very nicely for the way I already think about things.

Agreed that a native solution (esp one that can handle relative offsets as part of the input) would be nicer. The 2022 thread where parsedate was updated (renamed from the previous convertdate) appears to have been done with the possibility of it being included in the core later on - which seems like a good idea to me.

I just want to add that dates in the yyyy-mm-dd format, or more specifically yyyy-0mm-0dd and along with the tiddlywiki timestamp YYYY0MM0DD0hh0mm0ss0XXX and other forms that contain the most significant values first eg YYYY … then followed by the next less signigicant value;

These date formats can be sorted as simple text or numbers as they are organised correctly to allow this.

If then you obtain a list of tiddlers in the order you require, perhaps filtering the list further, then for each tiddler you can use the dates found in each tiddler for subsequent display formating.

Sortby
However sometimes the date or value is computed on the fly and we want this to be used as the sort value. It can be done but if you can avoid this by storing the computed value or other tricks it does keep it simple.