liamcain / obsidian-calendar-plugin

Simple calendar widget for Obsidian.
MIT License
1.56k stars 129 forks source link

BUG: the calenar panel showing and click action is wrong when has earlier diary notes #302

Open hotoo opened 1 year ago

hotoo commented 1 year ago

Before Submitting: Double-check that you are running the latest version of the plugin. The bug might have already been fixed 😄

Describe the bug

The calendar panel showing and click action is wrong when has earlier diary notes.

Steps to reproduce

  1. My Daily notes config see the bellow: image

  2. Calenar plugin config see the bellow: image

  3. When I startup Obsidian.app in macOS 13.1, the calendar panel showing was wrong: the day without diary note, but show little points. when I click this date, and open the wrong diary note. for example: click the date is 2023/02/27, and open diary note is 2010/10/27, the year and month was wrong. image

  4. And the prev month panel was missing every diary note, even those has diary notes. image image

  5. diary note files in Finder.app image

Expected behavior

  1. The calendar panel show the right points under the date by is it exists diary notes.
  2. Click calendar date, please open the right daily note file.
  3. Switch to other month, show and can click the right diary notes.

Screenshots

See the obove.

Environment (please specify)

OS

macOS 13.1

Obsidian Version (e.g. v0.10.6)

v1.1.9 (Settings → About → Current Version)

Theme (if applicable):

Default

If the bug is visual, please provide the name of the Community Theme you're using.

honor2016tw commented 1 year ago

Hello, my situation is similar to yours. My structure of notes be like: 2022/02/10.md

By refering to @Mariana-Yui 's comment in https://github.com/liamcain/obsidian-calendar-plugin/issues/224#issuecomment-1210689344 . There is a function named getDateFromFile defined in .obsidian\plugins\calendar\main.js line 125, which will handle your file and parse it into a date. In orginal version(1.5.10), only filename is considered. Therefore, for our scene, the program cannot obtain full date as expected.

So, I modified my main.js

diff main.js.ori main.js.new

132c132
<     const noteDate = window.moment(file.basename, format, true);
---
>     const noteDate = window.moment(file.path.match(/\d{4}\/\d{2}\/\d{2}/g), 'YYYY/MM/DD', true);

This works for me now.

hotoo commented 1 year ago

Add function:

function parseDateFormatToRegExp(format) {
  // https://momentjs.com/docs/#/displaying/
  const source = format.replace(/\[([^\]]+)\]|(\.)|(YYYYYY|YYYY|GGGG|gggg|DDDD|DDD|MM|DD|WW|ww|M|D|W|w|Q|d|E|e)/g, function($0, plaintext, dot, token) {
    if (plaintext) return plaintext;
    if (dot) return '\\.';
    switch(token) {
      case 'YYYYYY':
        return '[+-]\\d{6}'
      case 'YYYY':
      case 'GGGG':
      case 'gggg':
        return '\\d{4}';
      case 'DDDD':
        return '\\d{3}';
      case 'DDD':
        return '\\d{1,3}';
      case 'MM':
      case 'DD':
      case 'WW':
      case 'ww':
        return '\\d{2}';
      case 'M':
      case 'D':
      case 'W':
      case 'w':
        return '\\d{1,2}';
      case 'Q':
      case 'd':
      case 'E':
      case 'e':
        return '\\d';
    }
    return $0;
  });
  return new RegExp(source, '');
}

and change line 167,168:

-   const format = getSettings[granularity]().format.split("/").pop();
-   const noteDate = window.moment(file.basename, format, true);
+   const format = getSettings[granularity]().format;
+   const noteDate = window.moment(file.path.match(parseDateFormatToRegExp(format)), format, true);
twormtwo commented 1 year ago

@hotoo's code almost worked for me, I use the format YYYY/M/Do - dddd (eg 2022/11/29th - Tuesday) which uses dddd and Do format, so I added cases for those. Since the cases now include groups to match on the text of the days, I added the g flag to the RegExp.

function parseDateFormatToRegExp(format) {
  // https://momentjs.com/docs/#/displaying/
  const source = format.replace(/\[([^\]]+)\]|(\.)|(YYYYYY|YYYY|GGGG|gggg|DDDD|dddd|DDD|MM|DD|Do|WW|ww|M|D|W|w|Q|d|E|e)/g, function($0, plaintext, dot, token) {
    if (plaintext) return plaintext;
    if (dot) return '\\.';
    switch(token) {
      case 'YYYYYY':
        return '[+-]\\d{6}'
      case 'YYYY':
      case 'GGGG':
      case 'gggg':
        return '\\d{4}';
      case 'DDDD':
        return '\\d{3}';
      case 'dddd':
        return '(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day';
      case 'DDD':
        return '\\d{1,3}';
      case 'MM':
      case 'DD':
      case 'WW':
      case 'ww':
        return '\\d{2}';
      case 'Do':
        return '\\d{1,2}(st|nd|rd|th)';
      case 'M':
      case 'D':
      case 'W':
      case 'w':
        return '\\d{1,2}';
      case 'Q':
      case 'd':
      case 'E':
      case 'e':
        return '\\d';
    }
    return $0;
  });
  return new RegExp(source, 'g');
}
hotoo commented 1 year ago

@liamcain see this pr please :)

Deremat commented 3 months ago

Build on previous answers, I adapted main.js in the plugins directory directly. I had no luck building from source with npm after modifiying obsidian-daily-notes-interface (rollup export problem). The daily format I use is "YYYY/MM MMMM/DD ddd". Example: '2024/04 avril/15 lun..md' (local=french). No it works. Replace the getDateFromFile function. Add getDateFromFilePath and parseDateFormatToRegExp. Fallback to getDateFromFilename if getDateFromFilePath failed.

function getDateFromFile(file, granularity) {
    var d = getDateFromFilePath(file, granularity);
    if (d) {
        return d
    } else {
        return getDateFromFilename(file.basename, granularity);
    }
}

function parseDateFormatToRegExp(format) {
    // Adapted from https://github.com/liamcain/obsidian-calendar-plugin/issues/302#issuecomment-1435723898
    // ref: https://momentjs.com/docs/#/displaying/

    // TODO:
    // - adapt to international:
    //   - accented char not matched by [a-z]
    //   - international gotchas: for example case 'ddd' match to '[a-zA-Z]{3}' in english but '[a-zA-Zéû]{3}\\.?' in french (mind the . and éû) !!!
    // - missing cases ...?
    // - testing ?
    // - Change in .ts source files

    const source = format.replace(/\[([^\]]+)\]|(\.)|(?:\b(M|Mo|MM|MMM|MMMM|Q|Qo|D|Do|DD|DDD|DDDo|DDDD|d|do|dd|ddd|dddd|e|E|w|wo|ww|W|Wo|WW|YY|YYYY|YYYYYY|Y|y|N|NNNN|NNNNN|gg|gggg|GG|GGGG|A|a|H|HH|h|hh|k|kk|m|mm|s|ss|S|SS|SSS|SSSS|z|Z|ZZ|X|x)\b)/g, function ($0, plaintext, dot, token) {
        // console.debug('plaintext, dot, token', plaintext, dot, token)
        if (plaintext)
            return plaintext;
        if (dot)
            return '\\.';
        switch (token) {
            // TODO ???
            // case 'N': //  NN, NNN|BC AD
            // case 'NNNN': // Before Christ, Anno Domini
            // case 'NNNNN': // BC AD
            // case 'SSSS': // ... SSSSSSSSS|000[0..] 001[0..] ... 998[0..] 999[0..]
            // case 'Z': // -07:00 -06:00 ... +06:00 +07:00
            // case 'z': // or zz|EST CST ... MST PST
            // case 'ZZ': // -0700 -0600 ... +0600 +0700

            case 'A': // AM PM
            case 'a': // am pm
            case 'dd': // Su Mo ... Fr Sa         // TODO internationalize
                return '[a-zA-Z]{2}';
            case 'do': // 0th 1st ... 5th 6th     // TODO internationalize
            case 'Do': // 1st 2nd ... 30th 31st   // TODO internationalize
            case 'Mo': // 1st 2nd ... 11th 12th   // TODO internationalize
            case 'Qo': // 1st 2nd 3rd 4th         // TODO internationalize
            case 'wo': // 1st 2nd ... 52nd 53rd   // TODO internationalize
            case 'Wo': // 1st 2nd ... 52nd 53rd   // TODO internationalize
                return '\\d{1,2}[a-z]{2}';
            case 'DDDo': // 1st 2nd ... 364th 365th // TODO internationalize
                return '\\d{1,3}[a-z]{2}';
            case 'dddd': // Sunday Monday ... Friday Saturday      // TODO internationalize
            case 'MMMM': // January February ... November December // TODO internationalize
                return '[a-zA-Zéû]{3,10}';  // fr-FR               // TODO alpha char should include accented char (éàû ...)
            case 'ddd': // Sun Mon ... Fri Sat
                return '[a-zA-Zéû]{3}\\.?'; // fr-FR               // TODO alpha char should include accented char (éàû ...)
            case 'MMM': // Jan Feb ... Nov Dec
                return '[a-zA-Zéû]{3}';     // fr-FR               // TODO alpha char should include accented char (éàû ...)
            case 'd': // 0 1 ... 5 6
            case 'e': // 0 1 ... 5 6
            case 'E': // 1 2 ... 6 7
            case 'Q': // 1 2 3 4
            case 'S': // 0 1 ... 8 9
                return '\\d';
            case 'D': // 1 2 ... 30 31
            case 'H': // 0 1 ... 22 23
            case 'h': // 1 2 ... 11 12
            case 'k': // 1 2 ... 23 24
            case 'm': // 0 1 ... 58 59
            case 'M': // 1 2 ... 11 12
            case 's': // 0 1 ... 58 59
            case 'W': // 1 2 ... 52 53
            case 'w': // 1 2 ... 52 53
                return '\\d{1,2}';
            case 'DD': // 01 02 ... 30 31
            case 'gg': // 70 71 ... 29 30
            case 'GG': // 70 71 ... 29 30
            case 'HH': // 00 01 ... 22 23
            case 'hh': // 01 02 ... 11 12
            case 'kk': // 01 02 ... 23 24
            case 'mm': // 00 01 ... 58 59
            case 'MM': // 01 02 ... 11 12
            case 'ss': // 00 01 ... 58 59
            case 'SS': // 00 01 ... 98 99
            case 'WW': // 01 02 ... 52 53
            case 'ww': // 01 02 ... 52 53
            case 'YY': // 70 71 ... 29 30
                return '\\d{2}';
            case 'DDD': // 1 2 ... 364 365
                return '\\d{1,3}';
            case 'DDDD': // 001 002 ... 364 365
            case 'SSS': // 000 001 ... 998 999
                return '\\d{3}';
            case 'y': // 1 2 ... 2020 ...
                return '\\d{1,4}';
            case 'gggg': // 1970 1971 ... 2029 2030
            case 'GGGG': // 1970 1971 ... 2029 2030
            case 'YYYY': // 1970 1971 ... 2029 2030
                return '\\d{4}';
            case 'Y': // 1970 1971 ... 9999 +10000 +10001
                return '\\+?\\d{4,5}';
            case 'YYYYYY': // -001970 -001971 ... +001907 +001971
                return '[+-]\\d{6}';
            case 'X': // 1360013296
            case 'x': // 1360013296123
                return '\\d{10,13}';
        }
        return $0;
    });
    // console.debug('source reg=', source)
    return new RegExp(source, '');
}
function getDateFromFilePath(file, granularity) {
    // Example:
    //  local: French
    //  daily note format: "YYYY/MM MMMM/DD ddd"
    //  parseDateFormatToRegExp give regex pattern from format: /\d{4}\/\d{2} [a-zA-Zéû]{3,10}\/\d{2} [a-zA-Zéû]{3}\.?/
    //  '2024/04 avril/15 lun..md'.match(/\d{4}\/\d{2} [a-zA-Zéû]{3,10}\/\d{2} [a-zA-Zéû]{3}\.?/)  -> OK note is found when clicking on date

    const filename = file.basename;
    const getSettings = {
        day: getDailyNoteSettings,
        week: getWeeklyNoteSettings,
        month: getMonthlyNoteSettings,
        quarter: getQuarterlyNoteSettings,
        year: getYearlyNoteSettings,
    };
    // console.debug('getDateFromFilePath')

    const format = getSettings[granularity]().format;
    // console.debug('   format ', format)
    var regexFromFormat = parseDateFormatToRegExp(format)
    // console.debug('   regex pattern from format ', regexFromFormat)
    var parsedFormat = file.path.match(regexFromFormat)
    // console.debug(`   '${file.path}'.match(${regexFromFormat}) = ${parsedFormat}`)
    if (!parsedFormat) {
        console.warn('No parsed format from parseDateFormatToRegExp with format:', format)
        return null;
    }

    const noteDate = window.moment(parsedFormat, format, true);
    // console.debug('   -> ',noteDate)
    if (!noteDate.isValid()) {
        return null;
    }
    if (isFormatAmbiguous(format, granularity)) {
        if (granularity === "week") {
            const cleanFormat = removeEscapedCharacters(format);
            if (/w{1,2}/i.test(cleanFormat)) {
                return window.moment(filename, 
                // If format contains week, remove day & month formatting
                format.replace(/M{1,4}/g, "").replace(/D{1,4}/g, ""), false);
            }
        }
    }
    // console.debug('   -> ',noteDate)
    return noteDate;
}
function getDateFromFilename(filename, granularity) {
    const getSettings = {
        day: getDailyNoteSettings,
        week: getWeeklyNoteSettings,
        month: getMonthlyNoteSettings,
    };
    const format = getSettings[granularity]().format.split("/").pop();
    const noteDate = window.moment(filename, format, true);
    if (!noteDate.isValid()) {
        return null;
    }
    if (isFormatAmbiguous(format, granularity)) {
        if (granularity === "week") {
            const cleanFormat = removeEscapedCharacters(format);
            if (/w{1,2}/i.test(cleanFormat)) {
                return window.moment(filename, 
                // If format contains week, remove day & month formatting
                format.replace(/M{1,4}/g, "").replace(/D{1,4}/g, ""), false);
            }
        }
    }
    return noteDate;
}