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.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.