What I’m trying to do
I have a note for people in a folder called “People” (one note for each person).
In the YAML of each note I have their name, relationship, birthday, etc.
The birthday is set as a date.
In my Daily Note, I am trying to create a table that will list up birthdays that occur today and also upcoming within the next 2 weeks in chronological order). If it matters, the title of all my daily notes is YYYY-MM-DD-dddd. I create daily notes by clicking on today’s date in the Calendar (Calendar plugin) which creates the note from a template.
Things I have tried
I can’t figure out what I’m missing. It catches birthdays that occur today but not upcoming birthdays.
SAMPLE YAML:
name: Jim McJim
birthday: 1980-12-30
relationship: friend
DATAVIEW QUERY:
TABLE name, birthday
FROM "People"
WHERE date(birthday).day = this.file.day.day
OR date(birthday).day <= this.file.day + dur(14 days)
SORT date(birthday) ASC
DAILY NOTE TEMPLATE (if relevant)
type: Daily note
Created: "{{date: YYYY-MM-DD-dddd}}"
On this day: "[[{{date-1Y: YYYY-MM-DD-dddd}}]]"
---
# <% moment(tp.file.title,'YYYY-MM-DD').format("dddd, MMMM DD, YYYY") %>
___
Any help, or advice on a better way to go about this would be much appreciated. I know there or some great plugins that track birthdays, but I would prefer a table for Birthdays in my Daily Note.
Thank you
I don’t know if my way is ‘better’, but here’s a different way of doing it. I’ve been tweaking mine to be more flexible with the types of dates I associate with people, and I’m storing dates differently, like this:
dates:
- "1974-07-02 | Birthday"
- "2007-04-21 | Wedding"
Then I have a ‘Birthdays’ note that shows a list of people, and includes their birth date, the days until their next birthday, and their current age, sorted by days until next birthday. Perhaps a variation of this could be used in a daily note template. To try this, use the following dataviewjs code (update the ‘Sets/People’ to the path to your ‘People’ folder):
```dataviewjs
const pages = dv.pages('"Sets/People"')
.where(p => p.fileClass == "People" && p.dates && p.dates.length > 0)
.filter(p => p.dates.some(date => date.includes("Birthday")));
const today = new Date();
const result = pages.map(p => {
const birthdays = p.dates
.filter(date => date.includes("Birthday"))
.map(date => new Date(date.split("|")[0].trim()));
const calculateDaysUntilNextBirthday = (birthday) => {
const nextBirthday = new Date(today.getFullYear(), birthday.getMonth(), birthday.getDate());
if (nextBirthday < today) {
nextBirthday.setFullYear(today.getFullYear() + 1);
}
return Math.ceil((nextBirthday - today) / (1000 * 60 * 60 * 24));
};
const nextBirthday = birthdays.reduce((closest, date) => {
const daysUntil = calculateDaysUntilNextBirthday(date);
return daysUntil < calculateDaysUntilNextBirthday(closest) ? date : closest;
}, birthdays[0]);
const daysUntilNextBirthday = calculateDaysUntilNextBirthday(nextBirthday);
const age = today.getFullYear() - nextBirthday.getFullYear() - (today < new Date(today.getFullYear(), nextBirthday.getMonth(), nextBirthday.getDate()) ? 1 : 0);
return {
name: "[[" + p.file.name + "]]",
birthday: nextBirthday.toLocaleDateString(),
daysUntilNextBirthday: daysUntilNextBirthday,
age: age
};
}).filter(p => p.birthday);
console.log(result);
const sortedResult = result.sort( a => a.daysUntilNextBirthday, 'asc')
const formattedResult = sortedResult.map(p => [
p.name,
p.birthday,
p.daysUntilNextBirthday,
p.age
]);
dv.table(["Name", "Birthday", "Days Until Next Birthday", "Age"], formattedResult);
```
1 Like
Thank you very much. I like how you can add other dates aside from birthday. I7ll try this one. It looks exactly what I’m trying to do.
Thanks again
1 Like
Thank you again. This was super helpful. I made a few little tweaks going back and forth with chatgpt.
I wanted to show the age the person will turn rather than current age.
Also I had to change line 2 from p.fileClass to p.type for it to work for me.
This is what I ended up with
const pages = dv.pages('"People"')
.where(p => p.type == "people" && p.dates && p.dates.length > 0)
.filter(p => p.dates.some(date => date.includes("Birthday")));
const today = new Date();
const result = pages.map(p => {
const birthdays = p.dates
.filter(date => date.includes("Birthday"))
.map(date => new Date(date.split("|")[0].trim()));
const calculateDaysUntilNextBirthday = (birthday) => {
const nextBirthday = new Date(today.getFullYear(), birthday.getMonth(), birthday.getDate());
if (nextBirthday < today) {
nextBirthday.setFullYear(today.getFullYear() + 1);
}
return Math.ceil((nextBirthday - today) / (1000 * 60 * 60 * 24));
};
// Get valid birthdays and calculate days until next birthday
const validBirthdays = birthdays.map(birthday => {
const daysUntil = calculateDaysUntilNextBirthday(birthday);
return { birthday, daysUntil };
}).filter(b => b.daysUntil >= 0 && b.daysUntil <= 30); // Filter for today and upcoming birthdays
// Include birthdays that are today
const todayBirthdays = birthdays.filter(birthday => {
const birthdayDate = new Date(today.getFullYear(), birthday.getMonth(), birthday.getDate());
return birthdayDate.toDateString() === today.toDateString(); // Check if the birthday is today
});
// Combine today's birthdays with upcoming birthdays
const allValidBirthdays = validBirthdays.concat(todayBirthdays.map(b => ({
birthday: b,
daysUntil: 0 // Set days until to 0 for today
})));
if (allValidBirthdays.length === 0) return null; // Skip if no upcoming birthdays
// Find the closest upcoming birthday (including today)
const nextBirthday = allValidBirthdays.reduce((closest, b) => {
return b.daysUntil < closest.daysUntil ? b : closest;
});
const daysUntilNextBirthday = nextBirthday.daysUntil;
// Create a new Date object for the next birthday to get the correct weekday
const birthdayDate = new Date(today.getFullYear(), nextBirthday.birthday.getMonth(), nextBirthday.birthday.getDate());
if (birthdayDate < today) {
birthdayDate.setFullYear(today.getFullYear() + 1);
}
// Calculate the age the person will be on their next birthday
const originalBirthday = nextBirthday.birthday; // Use the original birthday for age calculation
let turns = today.getFullYear() - originalBirthday.getFullYear(); // Calculate base age
// Check if today is the birthday
if (today.getDate() === originalBirthday.getDate() && today.getMonth() === originalBirthday.getMonth()) {
// If today is the birthday, no need to add 1 to turns
turns = today.getFullYear() - originalBirthday.getFullYear(); // Correctly set to the current age
} else if (today >= new Date(today.getFullYear(), originalBirthday.getMonth(), originalBirthday.getDate())) {
// Increment the turns if the birthday has occurred this year
turns++;
}
// Format the birthday date to "ddd, D MMMM"
const formattedBirthday = birthdayDate.toLocaleDateString("en-US", {
weekday: 'short', // Short day name (e.g., "Mon")
day: 'numeric', // Numeric day (e.g., "1")
month: 'long', // Full month name (e.g., "January")
});
// Final output format
return {
name: "[[" + p.file.name + "]]",
birthday: formattedBirthday,
daysUntilNextBirthday: daysUntilNextBirthday,
turns: turns // Change to "Turns"
};
}).filter(p => p !== null); // Filter out null results
// Sort the results by daysUntilNextBirthday
const sortedResult = result.sort( a => a.daysUntilNextBirthday, 'asc')
const formattedResult = sortedResult.map(p => [
p.name,
p.birthday,
p.daysUntilNextBirthday,
p.turns // Change to "Turns"
]);
dv.table(["Name", "Birthday", "Days Until Next Birthday", "Turns"], formattedResult);
1 Like
A few more details and an image of the end result for anyone else looking for something similar.