DataviewJS Snippet Showcase

Upcoming Birthdays (robust & flexible)

Here’s a rather robust and flexible list of upcoming birthdays that

  • shows link, next birthday, and age someone will turn to
  • has a configurabe date range (from today, for a “natural language” duration)
  • sorts by next birthday
  • uses locale settings from the language Obsidian is set to
  • doesn’t choke when crossing the year boundary
  • has YAML searchterm configuration
  • has YAML “natural language” duration configuration; “odd” data like duration: 0.5 years will be auto-translated to 6 months (or 6 Monate in German)
  • has YAML date format configuration (yay!)
  • has a configurable info line above the table (can be disabled)


Upcoming birthdays with English settings, custom date format and info line

The technically-minded may also find interesting:

  • utility functions that can be used for table data
  • custom function for “where”
  • comparator function for “sort”
  • easily translatable “info line” with parameters
  • how I need to undo Dataview’s YAML pre-parsing of dates and durations


Upcoming birthdays with German settings, custom date format and info line

Requirements

  • Dataview 0.3.3+
  • moment.js (comes with Obsidian)
  • Notes to be included must have a birthday as YYYY-MM-DD in the frontmatter:
    birthday: 1959-07-19
    
  • The note using this script must have the parameters searchterm and a (more or less) “natural language” duration in the frontmatter:
    #searchterm: "#family or #friends"
    searchterm: '"People"'
    duration: 1 year
    
  • The note using this script can have an additional dateformat in the frontmatter:
    dateformat: "ddd, D MMMM YYYY"
    
    If left out or empty, it will default to YYYY-MM-DD.

Code

YAML frontmatter example

#searchterm: "#family or #friends"
searchterm: '"People"'
duration: 1 year
dateformat: "ddd, D MMMM YYYY"

dataviewjs code

This contains some utility functions that can be used in your table, like nextBirthday(), turns().


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 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"],
    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)
        ])
);

Notes

24 Likes