Skip to content

Instantly share code, notes, and snippets.

@brokosz
Last active December 15, 2025 03:51
Show Gist options
  • Select an option

  • Save brokosz/774dd1980bcb07b67d03283b20133420 to your computer and use it in GitHub Desktop.

Select an option

Save brokosz/774dd1980bcb07b67d03283b20133420 to your computer and use it in GitHub Desktop.
Converts natural language dates to ISO 8601 in TaskPaper 3.9.4. Supports "next Tuesday", "in 3 days", "end of month", etc.
Application('TaskPaper').documents[0].evaluate({
script: `function(editor, options) {
editor.outline.groupUndoAndChanges(function() {
let items = editor.outline.root.descendants;
items.forEach(item => {
let attrs = item.attributes;
for (let attr in attrs) {
let value = attrs[attr];
if (value && typeof value === 'string' && !value.match(/^\\d{4}-\\d{2}-\\d{2}$/)) {
let date = parseFuzzyDate(value);
if (date) {
item.setAttribute(attr, formatISO(date));
}
}
}
});
});
function parseFuzzyDate(str) {
let lower = str.toLowerCase().trim();
let now = new Date();
now.setHours(0, 0, 0, 0);
if (lower === 'today') return now;
if (lower === 'tomorrow') {
let d = new Date(now);
d.setDate(d.getDate() + 1);
return d;
}
if (lower === 'yesterday') {
let d = new Date(now);
d.setDate(d.getDate() - 1);
return d;
}
let dayMatch = lower.match(/(next|this|last)\\s+(sun|mon|tue|wed|thu|fri|sat|sunday|monday|tuesday|wednesday|thursday|friday|saturday)/);
if (dayMatch) {
let days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
let shortDays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
let dayName = dayMatch[2];
let targetDay = days.indexOf(dayName);
if (targetDay === -1) targetDay = shortDays.indexOf(dayName);
let currentDay = now.getDay();
let daysToAdd;
if (dayMatch[1] === 'next') {
daysToAdd = (targetDay - currentDay + 7) % 7;
if (daysToAdd === 0) daysToAdd = 7;
} else if (dayMatch[1] === 'this') {
daysToAdd = (targetDay - currentDay + 7) % 7;
} else {
daysToAdd = (targetDay - currentDay - 7) % 7;
if (daysToAdd === 0) daysToAdd = -7;
}
let d = new Date(now);
d.setDate(d.getDate() + daysToAdd);
return d;
}
let inMatch = lower.match(/in\\s+(\\d+)\\s+(day|week|month|year)s?/);
if (inMatch) {
let amount = parseInt(inMatch[1]);
let unit = inMatch[2];
let d = new Date(now);
if (unit === 'day') d.setDate(d.getDate() + amount);
else if (unit === 'week') d.setDate(d.getDate() + (amount * 7));
else if (unit === 'month') d.setMonth(d.getMonth() + amount);
else if (unit === 'year') d.setFullYear(d.getFullYear() + amount);
return d;
}
let agoMatch = lower.match(/(\\d+)\\s+(day|week|month|year)s?\\s+ago/);
if (agoMatch) {
let amount = parseInt(agoMatch[1]);
let unit = agoMatch[2];
let d = new Date(now);
if (unit === 'day') d.setDate(d.getDate() - amount);
else if (unit === 'week') d.setDate(d.getDate() - (amount * 7));
else if (unit === 'month') d.setMonth(d.getMonth() - amount);
else if (unit === 'year') d.setFullYear(d.getFullYear() - amount);
return d;
}
let periodMatch = lower.match(/(next|last)\\s+(week|month|year)/);
if (periodMatch) {
let d = new Date(now);
let amount = periodMatch[1] === 'next' ? 1 : -1;
if (periodMatch[2] === 'week') d.setDate(d.getDate() + (amount * 7));
else if (periodMatch[2] === 'month') d.setMonth(d.getMonth() + amount);
else if (periodMatch[2] === 'year') d.setFullYear(d.getFullYear() + amount);
return d;
}
let endMatch = lower.match(/end\\s+of\\s+(week|month|year)/);
if (endMatch) {
let d = new Date(now);
if (endMatch[1] === 'week') {
let daysUntilSunday = (7 - d.getDay()) % 7;
d.setDate(d.getDate() + daysUntilSunday);
} else if (endMatch[1] === 'month') {
d.setMonth(d.getMonth() + 1, 0);
} else if (endMatch[1] === 'year') {
d.setMonth(11, 31);
}
return d;
}
let startMatch = lower.match(/start\\s+of\\s+(week|month|year)/);
if (startMatch) {
let d = new Date(now);
if (startMatch[1] === 'week') {
d.setDate(d.getDate() - d.getDay());
} else if (startMatch[1] === 'month') {
d.setDate(1);
} else if (startMatch[1] === 'year') {
d.setMonth(0, 1);
}
return d;
}
return null;
}
function formatISO(date) {
let year = date.getFullYear();
let month = String(date.getMonth() + 1).padStart(2, '0');
let day = String(date.getDate()).padStart(2, '0');
return year + '-' + month + '-' + day;
}
}`
});
@brokosz
Copy link
Author

brokosz commented Dec 14, 2025

Fuzzy Date Converter for TaskPaper

Converts natural language date tags to ISO 8601 format in TaskPaper 3.9.4.

Supported Patterns

  • Relative: @due(today), @due(tomorrow), @due(yesterday)
  • Weekdays: @due(next Tuesday), @due(this Friday), @due(last Monday)
  • Intervals: @due(in 3 days), @due(in 2 weeks), @due(5 days ago)
  • Periods: @due(next week), @due(last month), @due(next year)
  • Boundaries: @due(end of week), @due(start of month)

Usage

All dates are converted to YYYY-MM-DD format. Works with any tag name (@due, @start, @deadline, etc.).

Run this script in TaskPaper to instantly convert all fuzzy dates in your document.

Compatibility

Tested with TaskPaper 3.9.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment