Thanks! That solved it. I can see I’m going to need a JavaScript course. Most of my coding was 10+ years ago and in C, C++ or Perl 5. Rust, so much rust…
I modified your countdown method, adding a dateDiff method using Mordred’s answer from this stackoverflow question. Now the table shows that their birthday date is 1 month(s) 20 Day(s) away.
var start = moment().startOf('day');
var end = moment(start).add(dv.current().duration);
var dateformat = "YYYY-MM-DD";
if (dv.current().dateformat) { dateformat = dv.current().dateformat; }
// info text above table, {0}=duration, {1}=start date, {2}=end date
// parameters can be left out, or the string empty
var infotext = "Upcoming birthdays for {0} from now ({1} – {2})<br><br>";
//======================================================================
function nextBirthday(birthday) {
// Get person’s next birthday on or after "start"
// returns a moment
// need to "unparse" because DV has already converted YAML birthday to DateTime object
// shouldn’t harm if already a string
var bday = moment(birthday.toString());
var bdayNext = moment(bday).year(start.year());
if (bdayNext.isBefore(start, 'day')) {
bdayNext.add(1, "year");
}
return bdayNext;
}
function turns(birthday) {
// Get the age in years a person will turn to on their next birthday
// need to "unparse" because DV has already converted YAML birthday to DateTime object
// shouldn’t harm if already a string
var bday = moment(birthday.toString());
return nextBirthday(birthday).diff(bday, 'years');
}
function dateDiff(startingDate, endingDate) {
let startDate = new Date(new Date(startingDate).toISOString().substr(0, 10));
if (!endingDate) {
endingDate = new Date().toISOString().substr(0, 10); // need date in YYYY-MM-DD format
}
let endDate = new Date(endingDate);
if (startDate > endDate) {
const swap = startDate;
startDate = endDate;
endDate = swap;
}
const startYear = startDate.getFullYear();
const february = (startYear % 4 === 0 && startYear % 100 !== 0) || startYear % 400 === 0 ? 29 : 28;
const daysInMonth = [31, february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let yearDiff = endDate.getFullYear() - startYear;
let monthDiff = endDate.getMonth() - startDate.getMonth();
if (monthDiff < 0) {
yearDiff--;
monthDiff += 12;
}
let dayDiff = endDate.getDate() - startDate.getDate();
if (dayDiff < 0) {
if (monthDiff > 0) {
monthDiff--;
} else {
yearDiff--;
monthDiff = 11;
}
dayDiff += daysInMonth[startDate.getMonth()];
}
return monthDiff + ' Month(s) ' + dayDiff + ' Day(s) away';
}
function countdown(birthday){
var bday = moment(birthday.toString())
const setTime = new Date(bday);
const nowTime = new Date();
const dateDifference = dateDiff(nowTime, setTime);
return dateDifference;
}
function showBirthday(birthday) {
// Determine if this birthday is in the range to be shown
// including the start date, excluding the end date
// because that comes from a duration calculation
// for use with "where", returns true or false
if (birthday) {
// need to "unparse" because DV has already converted YAML birthday to DateTime object
// shouldn’t harm if already a string
var bday = moment(birthday.toString());
var bdayNext = nextBirthday(birthday);
if (bdayNext.isBetween(start, end, 'day', '[)')) {
return true;
} else {
return false;
}
} else {
return false;
}
}
function sortByNextBirthday(a, b) {
// comparator function for "sort"
if (nextBirthday(a).isBefore(nextBirthday(b))) {
return -1;
}
if (nextBirthday(a).isAfter(nextBirthday(b))) {
return 1;
}
// they’re equal
return 0;
}
//======================================================================
dv.paragraph(infotext.format(moment.duration(dv.current().duration.toString()).humanize(), start.format(dateformat), end.format(dateformat)));
dv.table(
["Name", "Birthday", "Turns", "Countdown"],
dv.pages(dv.current().searchterm)
// use a function to see if this birthday is in range to be shown
.where(p => showBirthday(p.birthday))
// use a comparator function to sort by next birthday
.sort(p => p.birthday, 'asc', sortByNextBirthday)
.map(p => [
p.file.link,
p.birthday ? nextBirthday(p.birthday).format(dateformat) : '–',
turns(p.birthday),
countdown(nextBirthday(p.birthday)),
])
);
Hey, thanks for a great JS code. I tried to make it work but i didnt succed.
I put these in the front matter:
birthday: 2000-01-01
#searchterm: "#family or #friends"
searchterm: '"People"'
duration: 1 year
And added the code in another note as a plain text, unfortunate i just see the code but it doesnt run, what did i do wrong?
Thanks.
Hello all, I’m sure this is probably very easy, but I’m a bit new to dataview.
I would like to use dataview to build a table (not a dataview TASK view) that includes a column with the note name and link (the first column) and a column with the text of the first uncomplete task that appears on that page. I’ve been trying all day to figure this out and nothing I do seems to come even close.
Many thanks in advance!
Last july, @Moonbase59 , created a script to list the current hotkeys, and I got news of this today. I discovered that if multiple keys was defined, it didn’t show the alternate keys provided, so I modified the scripts a little.
Modifications I made to script include:
- Allowing for showing multiple keys for a given command
- Simplified the join operations on the modifiers, and allowed for usage of symbols, if one are so inclined
- Changed the second listing to only show commands without a hotkey, and as a result I also removed the hotkey column for that listing
- Provided in-code examples of how to sort on either columns by changing which
.sort()
command is commented or not
With these listing I can now easier find conflicting hotkeys (even if more than two commands use the same key combination), and also easier see commands available for hotkeys.
New variant of Commands vs Hotkeys
### Commands with hotkeys sorted by name
```dataviewjs
function joinModifiers(modifiers, symbols=false) {
return modifiers.join(' ')
.replace('Mod', symbols ? '⌘' : 'CMD')
.replace('Alt', symbols ? '⌥' : 'OPT')
.replace('Ctrl', symbols ? '⌃' : 'CTRL')
.replace('Shift', symbols ? '⇧' : 'SHIFT')
}
function getHotkey(arr, highlight=true) {
let hi = highlight ? '**' : '';
let keys = []; // Store all key combinations defined for this command
let currKey = ""
// Find the default key setting for the command, if any
if (arr.hotkeys && arr.hotkeys.length > 0) {
console.log(arr.hotkeys)
for (let aKey of arr.hotkeys) {
keys.push(joinModifiers(aKey.modifiers) + ' ' + aKey.key)
}
}
// Handle custom hotkey setup, with possible multiple assignments
let ck = app.hotkeyManager.customKeys[arr.id];
if (ck) {
// Reset keys array, as custom keys override the default key setting
keys = []
for (let aKey of ck) {
keys.push(hi + joinModifiers(aKey.modifiers) + ' ' + aKey.key + hi)
}
}
return (keys.length != 0) ? keys.join('<br />') : 'none';
}
let cmds = dv.array(Object.entries(app.commands.commands))
.where(v => getHotkey(v[1]) != 'none')
//// Choose your sorting option by changing which line is commented out
.sort(v => v[1].id, 'asc'); // Sorted by the command name
// .sort(v => v[1].id, 'asc'); // Sorted by the descriptive name
//.sort(v => getHotkey(v[1], false), 'asc'); // Sorted by hotkey
dv.paragraph(cmds.length + " hotkeys. " +
"Both 'default' and <strong>'custom'</strong>.<br><br>");
dv.table(["Command", "Name", "Hotkeys"],
cmds.map(v => [
v[1].id,
v[1].name,
getHotkey(v[1]),
])
);
```
### Other available commands with no key assigned
```dataviewjs
function getHotkey(arr, highlight=true) {
let hi = highlight ? '**' : '';
let keys = []; // Store all key combinations defined for this command
let currKey = ""
// Find the default key setting for the command, if any
if (arr.hotkeys && arr.hotkeys.length > 0) {
console.log(arr.hotkeys)
for (let aKey of arr.hotkeys) {
keys.push(aKey.modifiers.join(' ')
.replace('Mod', 'CMD')
.replace('Alt', 'OPT')
.replace('Ctrl', 'CTRL')
.replace('Shift', 'SHIFT')
+ ' ' + aKey.key);
}
}
// Handle custom hotkey setup, with possible multiple assignments
let ck = app.hotkeyManager.customKeys[arr.id];
if (ck) {
// Reset keys array, as custom keys override the default key settings
keys = []
for (let aKey of ck) {
keys.push(hi + aKey.modifiers.join(' ')
.replace('Mod', 'CMD')
.replace('Alt', 'OPT')
.replace('Ctrl', 'CTRL')
.replace('Shift', 'SHIFT')
+ ' ' + aKey.key + hi)
}
}
return (keys.length != 0) ? keys.join('<br />') : 'none';
}
let cmds = dv.array(Object.entries(app.commands.commands))
.where(v => getHotkey(v[1]) == 'none')
//// Choose your sorting option by changing which line is commented out
.sort(v => v[1].id, 'asc'); // Sorted by the command name
// .sort(v => v[1].id, 'asc'); // Sorted by the descriptive name
//.sort(v => getHotkey(v[1], false), 'asc'); // Sorted by hotkey
dv.paragraph(cmds.length + " commands. " +
"Rest of commands which doesn't have any keys assigned to them.<br><br>");
dv.table(["Command", "Name"],
cmds.map(v => [
v[1].id,
v[1].name])
);
```
In the image below you can see the top part of the hotkeys currently defined for me, with some interesting (constructed!) conflicts.
See for example how Cmd P is connected to both opening the command palette, opening a daily note, and open the next daily note. Kind of stupid setup, so I need to fix that soon!
In addition one can see that for the first command, Navigate back, both Cmd Home and Opt ArrowLeft are valid combinations.
Whilst working with this we’ve got a FR re-opened, see A way to find which command is bound a specific hotkey, so please show some love to that, to get more attention to issues related to resolving hotkey conflicts, and to locate what a given command does.
I also found a plugin, see forum post in Show only assigned hotkeys - #3 by ichmoimeyo, which could display hotkeys on a visual image of the keyboard. Sadly, it’s in the early stages of development, and it doesn’t do the correct thing on Mac (as of today).
This question was also posted in the Help category, and solved there.
is it possible to create a bar chart from sum of different inlinefield ?
İ have a tag logbook and inlinefield games:: I ve been trying this one month now I am using obsidian charts
This is awesome but there is a small typo:
// .sort(v => v[1].id, 'asc'); // Sorted by the descriptive name
Should say
// .sort(v => v[1].name, 'asc'); // Sorted by the descriptive name
Cheers
I have gotten my Birthday list to work thanks to this post and have been using it quite happily for a while.
But I’ve come across something I can’t figure out. I want to ask and answer the question “How old will person be ON their birthday THIS year?”
So if I have the following file:
# Person Name
Start-Date:: 1980-12-05
Details:: turns `=date(date(today).year + "-" + this.Start-Date.month + "-" + this.Start-Date.day) - this.Start-Date` in `=date(today).year`
results in a Details line that reads:
Details:: turns in 2022
My date math is ignored.
I’ve tested without the Details:: callout - it’s definitely the two nested date() calls.
I can do this with DVJS, I’m just wondering if it’s something that can be done in straight inline DV. And if I’ve got an error or something in my code.
Topic
Summary
- How to calculate a person’s age?
Input
Summary
```md
# Person Name
Start-Date:: 1980-09-06
Details::
```
Notes
Summary
Q1: What is the data type of this.Start-Date.month
(or this.Start-Date.day
)?
Summary_Q1
A1:
Another Example: A1_11
```md
month_type = `=typeof(this.Start-Date.month)`
day_type = `=typeof(this.Start-Date.day)`
```
Screenshots: A1_11
month_type = number
day_type = number
Q2: How to transform this.Start-Date.month
into MM (or transform this.Start-Date.day
into dd)?
Summary_Q2
A2:
Another Example: A2_21
```md
month_MM = `=padleft(string(this.Start-Date.month),2,"0")`
day_dd = `=padleft(string(this.Start-Date.day),2,"0")`
```
Screenshots: A2_21
month_MM = 09
day_dd = 06
Q3: How to fix the bug in the Inline DQL?
Summary_Q3
Original Example: Q3 (To be modified)
```md
Details:: turns `=date(date(today).year + "-" + this.Start-Date.month + "-" + this.Start-Date.day) - this.Start-Date` in `=date(today).year`
```
A3:
Another Example: A3_31
```md
Details:: turns `=date(date("today").year + "-" + padleft(string(this.Start-Date.month),2,"0") + "-" + padleft(string(this.Start-Date.day),2,"0")) - this.Start-Date` in `=date("today").year`
```
Screenshots: A3_31
Details:: turns 42 years in 2022
Q4: How to simplify the Inline DQL(A3_31) when calculating date("2022-09-06")-date("1980-09-06")
with skipping the same month and day?
Summary_Q4
A4:
Another Example: A4_41
```md
Details:: turns `=date("today").year - this.Start-Date.year` years in `=date("today").year`
```
Screenshots: A4_41
Details:: turns 42 years in 2022
Q5: How to calculate a person’s age in months and years by using the dur
function?
THANKS!
I debated using the padleft to adjust it, but since my dates worked fine when I didn’t try specifying the year date(today) - this.Start-Date
I didn’t think that was an issue.
Of course, A4 is even better, and what I’ll be using.
Thanks again for the QA. My eyes were burning attempting it myself.
Hoping some of you DataviewJS veterans can help with this.
I currently have a DataviewJS + Buttons script that creates a button that changes the status variable in the yaml header. That all works great.
---
defer_date: 2022-11-22
status: on-going
---
```dataviewjs
const {update} = this.app.plugins.plugins["metaedit"].api
const {createButton} = app.plugins.plugins["buttons"]
dv.el("p", "Status: `$= dv.current().status`")
createButton({app, el: this.container, args: {name: "on going"}, clickOverride: {click: update, params: ['status', "on-going", dv.current().file.path]}})
createButton({app, el: this.container, args: {name: "completed"}, clickOverride: {click: update, params: ['status', "completed", dv.current().file.path]}})
Now, I’m trying to create a similar button for adding one day to the defer_date variable. (The idea is to let me push out the start date for certain projects)
However, when I click on the button, it turns the defer_date to just a number 1669122000000
and I don’t know how to change it back to a date.
Can someone help?
---
defer_date: 2022-11-22
---
```dataviewjs
let d = dv.current().defer_date;
const {update} = this.app.plugins.plugins["metaedit"].api
const {createButton} = app.plugins.plugins["buttons"]
dv.el("p", "Defer Date: `$= dv.current().defer_date`")
createButton({app, el: this.container, args: {name: "+1"}, clickOverride: {click: update, params: ['defer_date', d + 86400000, dv.current().file.path]}})
Topic
Summary
- How to get the day before the day “2022-11-18” in the format “yyyy-MM-dd”?
- How to get the day after the day “2022-11-18” in the format “yyyy-MM-dd”?
Test
Summary
- dataview: v0.5.46
- MetaEdit: v1.7.2 (For Q3 and A3_31)
- Buttons : v0.4.19 (For Q3 and A3_31)
Input
Summary
---
defer_date: 2022-11-18
status: on going
---
Notes
Summary
Q1: How to get the day before the day “2022-11-18” in the format “yyyy-MM-dd”?
Summary_Q1
A1:
Another Example: A1_11
```dataviewjs
let d2 = dv.date('2022-11-18').minus({ days: 1 }).toFormat("yyyy-MM-dd");
dv.span(d2);
```
Screenshots: A1_11
2022-11-17
Q2: How to get the day after the day “2022-11-18” in the format “yyyy-MM-dd”?
Summary_Q2
A2:
Another Example: A2_21
```dataviewjs
let d2 = dv.date('2022-11-18').plus({ days: 1 }).toFormat("yyyy-MM-dd");
dv.span(d2);
```
Screenshots: A2_21
2022-11-19
Q3: How to fix the bug in the DVJS?
Summary_Q3
Original Example: Q3 (To be modified)
```dataviewjs
// M11.define d:
// #####################################################################
let d = dv.current().defer_date;
// M21.define update: require the plugin MetaEdit
// #####################################################################
const { update } = this.app.plugins.plugins["metaedit"].api;
// M31.define createButton: require the plugin Buttons
// #####################################################################
const { createButton } = app.plugins.plugins["buttons"];
// M41.output defer_date:
// #####################################################################
dv.el("p", "Defer Date: `$= dv.current().defer_date`");
// M51.createButton "+1":
// #####################################################################
createButton({
app,
el: this.container,
args: { name: "+1" },
clickOverride: {
click: update,
params: ["defer_date", d + 86400000, dv.current().file.path],
},
});
```
Screenshots: Q3
Defer Date: 1668787200000
A3:
Another Example: A3_31
Bugs fixed:
- The Step M13 is added.
d + 86400000
is replaced withd2
in the Step M51.
```dataviewjs
// M11.define d:
// #####################################################################
let d = dv.current().defer_date;
// M13.define d2: the day after the day `d`
// #####################################################################
// let d2 = dv.date("2022-11-18").plus({ days: 1 }).toFormat("yyyy-MM-dd");
let d2 = d.plus({ days: 1 }).toFormat("yyyy-MM-dd");
// M21.define update: require the plugin MetaEdit
// #####################################################################
const { update } = this.app.plugins.plugins["metaedit"].api;
// M31.define createButton: require the plugin Buttons
// #####################################################################
const { createButton } = app.plugins.plugins["buttons"];
// M41.output defer_date:
// #####################################################################
dv.el("p", "Defer Date: `$= dv.current().defer_date`");
// M51.createButton "+1":
// #####################################################################
createButton({
app,
el: this.container,
args: { name: "+1" },
clickOverride: {
click: update,
params: ["defer_date", d2, dv.current().file.path],
},
});
```
Screenshots: A3_31
Defer Date: 11 19, 2022
Thank you! I was doing my brains in trying to get that to work.
Hello,
I’m looking to automatically generate the location: [lon, lat]
in my YAML frontmatter for Daily Notes. Ideally with a link to Google. How could I accomplish this?
Looks like:
.sort(v => v[1].id, ‘asc’); // Sorted by the command name
should be:
.sort(v => v[1].name, ‘asc’); // Sorted by the command name
“name” not “id”
Right?
Yeah, that seems correct. I’m thinking about redoing that party of script, to also enable easier sorting and detection of duplicates.
But good catch!
Great snip! Would it be possible to split the Plugin Name into its own column? I see the Plugin Name is in the Name column of your code, but I would like them separate for sorting and data-extraction purposes.