Birthday tracking in Daily Notes

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.