Last active
December 15, 2025 03:51
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| }` | |
| }); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Fuzzy Date Converter for TaskPaper
Converts natural language date tags to ISO 8601 format in TaskPaper 3.9.4.
Supported Patterns
@due(today),@due(tomorrow),@due(yesterday)@due(next Tuesday),@due(this Friday),@due(last Monday)@due(in 3 days),@due(in 2 weeks),@due(5 days ago)@due(next week),@due(last month),@due(next year)@due(end of week),@due(start of month)Usage
All dates are converted to
YYYY-MM-DDformat. 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