Templater plugin (1.5 and newer): New Syntax and Examples

I just played around a little with the new version 1.5 of the Templater plugin. Well done, I must say!

Thought I share some nifty examples:

Next Monday:

EDIT: This is not “next Monday” in some countries but “next Sunday” instead. Some countries start their week on sundays, so this should actually be labelled “Next start-of-the-week-day” … Note that it depends on your current (Obsidian) language setting.

<% tp.date.weekday("dddd YYYY-MM-DD", 7) %>

First day next month:

<% tp.date.now("dddd YYYY-MM-DD", "P1M", tp.date.now("YYYY-MM") + "-01") %>

You can even use moments.js directly, this allows for some nice tricks—and in the current locale, too!

My birthday this year (in forum-like format)

<% moment("07-19", "MM-DD").fromNow() %>

will show something like: in 3 months or in 3 Monaten.

Days until Christmas (without checking if it’s already past Christmas)

<% moment.duration(moment("12-24", "MM-DD").diff(moment(), "days")) %>

Have fun inventing more (and let us know about them)!

14 Likes

tp.date.weekday() that’s a new one :grin: !

And I also had a lot of fun with Templater, Periodic Notes, Calendar, some JS but mostly Moment.js (once someone told me on discord this could be done) yesterday while setting up my Daily/Weekly/Monthly and Yearly Notes :blush: .

I use mostly the tp.file.title variable so most of what I use Templater for (for now) relies on how I format my daily (weekly/monthly/yearly) notes :blush: .

The tp.file.title variable is very useful when using Calendar/Periodic Notes as it still allows me to create any daily notes (either in the past or future using Calendar) without having to fear potential mistakes/errors.

As the non-dev’ person that I am :innocent: , who has no JS knowledge (last time I did a little bit of javascript was 20 years ago :laughing: ) avoiding mistakes/errors is quite important :blush: .

But here is what I’ve got so far :blush: :


For the total of days (as a number) in a month :

In a Daily Note with the title formatted as YYYY-MM-DD and using Moment.js .daysInMonth() :

<% moment(tp.file.title,'YYYY-MM-DD').daysInMonth() %>

Example : for the daily note 2021-04-14, the result will be 30


For the Start/End of a month :

In a Monthly Note with the title formatted as YYYY-MM using Moment.js .startOf() or .endOf() and the result formatted as ddd. DD/MM

<% moment(tp.file.title,'YYYY-MM').startOf('month').format('ddd. DD/MM') %>

Example : for the monthly note 2021-04, the result will be Thu. 01/04

<% moment(tp.file.title,'YYYY-MM').endOf('month').format('ddd. DD/MM') %>

Example : for the monthly note 2021-04, the result will be Fri. 30/04


For the number of days left in a month (using a daily notes as a starting point):

Daily Notes title formatted as YYYY-MM-DD using .endOf() and .diff()

<% moment(moment(tp.file.title,'YYYY-MM-DD').endOf('month')).diff(moment(tp.file.title,'YYYY-MM-DD'),'days') %>

This :point_up_2: is literally just a subtraction :blush: .

In other words what it does is find the last day in the month from the date in the title of the daily notes (moment(tp.file.title,'YYYY-MM-DD').endOf('month')) as its own Moment() and then subtracts the days from the daily notes title “date” (.diff(moment(tp.file.title,'YYYY-MM-DD'),'days' )

So if you have a daily note titled 2021-04-14 the result will be 16 (as there are 30 days in April)


For getting a list of all days in a month (“pre-linked” if the daily note already exists):

In a Monthly Note with the title formatted as YYYY-MM

It took me a while to get this one, lots of trials and errors :sweat_smile: (first shared on Discord and its based on this Stack Overflow reply :blush: )

<%* const currentMonthDates = new Array(
moment(tp.file.title,'YYYY-MM').daysInMonth()).fill(null).map(
(x, i) => moment(tp.file.title,'YYYY-MM').startOf('month').add(i,'days').format("- [[[]YYYY-MM-DD[#]YYYY-MM-DD[|]DD/MM [⦗]ddd[.⦘]]]")).join('\r\n') %>
<%* tR +=  currentMonthDates %>

What this kind of does is create a “list” (array) from the start of the month and as long as there are days in the month (based on the note’s title). Then it gives each item in this list a specific format (.format("- [[[]YYYY-MM-DD[#]YYYY-MM-DD[|]DD/MM [⦗]ddd[.⦘]]]")).
In my case, once Templater does what it has to do : - [[YYYY-MM-DD#YYYY-MM-DD|DD/MM ⦗ddd.⦘]].
There are lots of brackets [] because this is how Moment.js escapes characters so it can correctly interpret the format I’m asking for :blush: .

Once the list is created and formatted, it gets rid of the comma , which originally separates each item in the list and replace it with a new line to get an actual unordered list :blush: .

The last part <%* tR += currentMonthDates %> is to display the constant created in the first part (following Templater Documentation) :blush: .


For the list of all months in a year (“pre-linked” if the monthly notes already exists):

In a Yearly Note with the title formatted as YYYY.
Source : this Stack Overflow reply :blush: .

<% Array.apply(0, Array(12)).map(function(_,i){return moment(tp.file.title,'YYYY').month(i).format("- [[[]YYYY-MM[#]YYYY-MM[|]MM - MMMM[]]]")}).join('\r\n') %>

It “kind of” works in the same way as the previous one : creates a list of the months in the year referenced by the title, apply a specific format and then get rid of the comma , in the list :blush: .

Note :

  • This part Array(12) is the length of the list (12 items)
  • This part moment(tp.file.title,'YYYY') is the starting point of the list (based on the note’s title)

To get the DateTime (Now()) in whatever language you want :

<% moment(tp.date.now("YYYY-MM-DD HH:MM")).locale('fr').format("LLLL") %>

:point_up: Updated and simplified in the post below :grin: :


… I might find others :blush: .

I’m absolutely no JS expert (far from that :blush: ) so there might be better ways to get to the same results too :innocent: !

Edit : Added some context, explanations (as best as I could :innocent: , trying to make this post more understandable for non-dev’ people like myself :blush: ) and sources :blush: .

5 Likes

Some nice ones, @Pch, thank you for these!

You can actually abbreviate the last one:

<% moment().locale('fr').format("LLLL") %>

And I could rewrite my “First day next month” like this:

<% moment().add(1, 'months').startOf('month').format('dddd YYYY-MM-DD') %>

We’re happily “misusing” the new Templater, hee hee :grin: !

1 Like

Ah! Thank you for the correction :blush: !
I was multitasking and got distracted :sweat_smile:!

Definitely :grin: !

This is great! I cant figure out how to get this expressed in: ‘x-Months, & y-Days’
Can you help?

Hi @Jeffurry :blush: !

Ok, I’ve spent lot of time on your enigma (because it was bugging me :sweat_smile: ) and I may have something for you :blush: .

Getting a difference between 2 dates in specific units of time (years, months days, …) is never easy because each unit relies on one another .
As for example, you wanted the result expressed in X Month(s) Y Days and this can’t really be done without calculating the year first (AFAIK) :blush: .

Apparently, after searching for a while, Moment.js is able to calculate all this pretty easily as long as you define a Start Date and a End Date and have the appropriate math to go with it (I’ve found my inspiration somewhere on the web) but the best I could do was to get the result in X Year(s) Y Month(s) Z Day(s) which was not exactly what you wanted… So I searched further :blush: .


:warning: Caution : I’m still no Javascript expert :innocent: (but after lots of tests, what I’m going to suggest seems to work :blush: )


But first, here is the complete code to put in the template (Editor) :blush: .
(The explanations are below :blush: )

<%*
var EndDate = moment('2023-12-31',"YYYY-MM-DD");
var StartDate = moment(tp.date.now("YYYY-MM-DD"));
var DiffYears = EndDate.diff(StartDate, 'year');
StartDate.add(DiffYears, 'years');
var DiffMonths = EndDate.diff(StartDate, 'months');
StartDate.add(DiffMonths, 'months');
var DiffDays = EndDate.diff(StartDate, 'days');
var NoDiff;
if(EndDate.isSame(StartDate)) {
	NoDiff= "Today";
	} else { 
	NoDiff="";
	};
var ResultYears;
if(DiffYears > 1) {
	ResultYears= DiffYears+ ' Years ';
	} else if (DiffYears == "1") { 
	ResultYears=DiffYears+' Year ';
	} else { 
	ResultYears="";
};
var ResultMonths;
if(DiffMonths > 1) {
	ResultMonths= DiffMonths+ ' Months ';
	} else if (DiffMonths == "1") { 
	ResultMonths=DiffMonths+ ' Month ';
	} else { 
	ResultMonths="";
	};
var ResultDays;
if(DiffDays > 1) {
	ResultDays= DiffDays+ ' Days ';
	} else if (DiffDays == "1") { 
	ResultDays=DiffDays+ ' Day ';
	} else { 
	ResultDays="";
	}
%>
<%* tR +=  NoDiff + ResultYears +  ResultMonths + ResultDays %>

And now, the explanations :

The only thing you should modify are the 2 variables EndDate and StartDate in

var EndDate = moment('2023-12-31',"YYYY-MM-DD");
var StartDate = moment(tp.date.now("YYYY-MM-DD"));

The EndDate must be in the future compared to the StartDate, otherwise the results will be negative (I think I didn’t really test that, sorry :innocent:)

For the StartDate you can use solely moment() which is Today but it will exclude Today from the results .
If you use moment(tp.date.now("YYYY-MM-DD")) as StartDate it’s still Today but Today will be included in the results.

You can also use (I think) for the StartDate something like moment(tp.file.title,"YYYY-MM-DD"), if it’s used in a Daily Note, but I didn’t test this.

Now, this part :

var DiffYears = EndDate.diff(StartDate, 'year');
StartDate.add(DiffYears, 'years');
var DiffMonths = EndDate.diff(StartDate, 'months');
StartDate.add(DiffMonths, 'months');
var DiffDays = EndDate.diff(StartDate, 'days');

Is the one that will calculate the difference between the EndDate and StartDate in years, months, days.

And this one :

var NoDiff;
if(EndDate.isSame(StartDate)) {
	NoDiff= "Today";
	} else { 
	NoDiff="";
	};

is there to compare the EndDate to the StartDate and if they are the same (.isSame) will be displayed in the result (in this case I just chose Today ).

But as you wanted the results displayed in X Month(s) Y Day(s) I added some other conditions :

The first one is for the year(s) :

var ResultYears;
if(DiffYears > 1) {
	ResultYears= DiffYears+ ' Years ';
	} else if (DiffYears == "1") { 
	ResultYears=DiffYears+' Year ';
	} else { 
	ResultYears="";
};

If the difference between the 2 dates is > 1 year , the result will say X years. If it’s at least = 1 year, the result will say 1 year and in any other cases (if the difference = 0 year), it won’t display the result for the year.

The same goes for the months (var ResultMonths;) and the days (var ResultDays;) :blush: .

Meaning that :
If the difference between the 2 dates is :

  • 1 year 0 month 2 days : the actual results will display as : 1 Year 2 Days
  • 0 year 0 month 5 days : the actual results will display as : 5 Days
  • 0 year 7 months 9 days : the actual results will display as : 7 Months 9 Days (which is the specific format you wanted :blush: )
  • 1 year 0 month 0 day : the actual results will display as : 1 Year
  • Etc …

And last but not least :

<%* tR +=  NoDiff + ResultYears +  ResultMonths + ResultDays %>

This will actually display the different variables from the first section :blush: .

Might not be perfect, but I did what I could :blush:

There are many ways to go there … let’s assume you want to use next year’s birthday if it’s already past your birthday, you could try something like this:

<%*
var a = moment("07-19", "MM-DD")
var b = moment()

if (a.isBefore(b)) { a.add(1, 'years') }

var m = a.diff(b, 'months')
b.add(m, 'months')

var d = a.diff(b,'days')

tR += m + ' month(s) and ' + d + ' day(s)'
%>

You won’t get moment.js’s nice locale and singular/plural features with this, though. Using .humanize() is difficult because moment.js uses some odd thresholds (see Moment.js | Docs).

Oh my! I turn my back for a day and look what happens. This is such a richness of things to digest, as well as what @Moonbase59 has provided!
I always start my note-taking with a daily note, so moment(tp.file.title,"YYYY-MM-DD") works quite nicely.
Thank you so much for this - mostly what I see here looks like
@(**& !"!" %$% ^^&&?! #[email protected]! **%
but it is much appreciated nonetheless :wink: I will give it some well focussed attention, to bring myself to understand it. Thank you again.

2 Likes

Oh my - thank you so much for spending the time to do this. It works very nicely for my purposes. You’re a star!

2 Likes

Hi all, I’ve read this thread with noob awe and it made me wonder if you could share in what cases you would use these kind of variables in a template. How do you make them to good use? And how do you keep the info (like days until day x) updated? Thanks!

1 Like

Hey @janpeeters, thanks for your interest!

These are not “self-updating” (though at least the moment.js stuff could in theory be used on a web page), but intended to be used as templates, i.e. called up and used only once (on invocation of the template).

Most people would probably use some of these with their “Daily Notes” template, to create links or nice-looking Date/Time strings. Others might keep some in small “snippet” template files (often in a separate “templates” folder) and call them up when needed, for instance with the Alt/Opt+E key combination.

Here are some possible usage ideas:

  • Within the “Daily Notes” template, to create links like “Yesterday’s Note”, “Tomorrow’s Note”, “Weekly Note”, and so on.
  • For multilingual people, to easily create standardized date/time strings in the other languages.
  • Within to-dos and calendar-type notes like “only xx days till our anniversary—get a present”.
  • “Just because we can.” Because it’s great to play with possibilities (and get distracted from the real work, hee hee …)

Have fun finding your use case!

4 Likes

Hi @Moonbase59 thanks for all your info, this clarifies a lot. I hope you don’t mind if I have some follow-up questions. If this gets too much off topic I hope I can contact you via pm.

It’s a pity that the info cannot be updated after e.g. changes to a note. I thought that the Templater: Replace templates in the active file function would be able to do this. Do you know what this function does?

Do you know of a way how to have an updating ‘last modified’ header in the note?

It replaces templater code within the currently open file, so you don’t have to load an external template. Like, you type <% tp.file.last_modified_date() %>, then press Alt/Opt+R and you’ll get the file’s last modified date and time.

I never had much use for this (except for testing), since the templater code is replaced with its result and thus gone afterwards.

Nope. If you find one, let me know :wink: Then again, Obsidian’s File Explorer pane shows the last modified date if you hover the mouse over a file, and it could possibly be retrieved “live” using the Dataview plugin.

We must remember, after all, that Obsidian is a system to create/handle plain Markdown text notes and not a “live”, variable- or event-driven system like a website could be.

Thanks a lot! Crystal clear.

1 Like

FYI, here’s a link to the Templater Docs.

Since this thread is not only about durations anymore, I’ll change its title to “New Syntax and moment.js Examples” :slight_smile:

1 Like

Here’s another example that came up today: Make a H1 heading that has the first letter of the note’s title uppercased.

# <% tp.file.title.charAt(0).toUpperCase() + tp.file.title.slice(1) %>

Shows nicely how to unleash the powers of Javascript using the Templater plugin.

2 Likes

Another one: Cursor placement in Templates plugin (preferably also for Daily Notes template) - #7 by Moonbase59

1 Like

I asked the author of Dataview on GitHub and it is possible:
Put an inline codeblock like this in your file with this code.

`= this.file.mtime`
2 Likes

Now this is a great find—thank you!

Now we’ll only have to wait for …

  • date/time string formatting
  • date/time strings being localized into the currently set Obsidian language, not the system language
2 Likes

Templater 1.6+: Combining dynamic commands and Javascript commands (undocumented)

Say you have a front matter field deceased: but only want to calculate/show the date if it’s defined (or non-empty), and a simple - if not.

You can actually combine Templater’s <%+ (dynamic) and <%* (Javascript) “introducers” as follows:

Use <%+*—this should produce dynamic results (if I’m not being caught by caching again):

<%+* if (tp.frontmatter["deceased"]) {tR+=moment(tp.frontmatter.deceased).format("ddd, YYYY-MM-DD")} else {tR+="-"}; %>

Now isn’t Templater great?

3 Likes