Obsidian includes the Moment.js library for parsing, validating, manipulating, and formatting dates and times.
Overview
The moment object in Obsidian is a direct reference to the Moment.js library. You can use it to:
- Parse dates in various formats
- Format dates for display
- Manipulate dates (add/subtract time)
- Compare dates
- Work with time zones and locales
Importing moment
import { moment } from 'obsidian';
Basic Usage
Creating Moments
import { moment } from 'obsidian';
// Current date and time
const now = moment();
// Parse a date string
const date1 = moment('2024-03-15');
const date2 = moment('March 15, 2024', 'MMMM DD, YYYY');
// From a JavaScript Date
const jsDate = new Date();
const date3 = moment(jsDate);
// From a timestamp
const date4 = moment(1710504000000);
// From an array [year, month, day, hour, minute, second, millisecond]
const date5 = moment([2024, 2, 15]); // March 15, 2024 (months are 0-indexed)
const date = moment('2024-03-15');
// Common formats
date.format('YYYY-MM-DD'); // "2024-03-15"
date.format('MMM DD, YYYY'); // "Mar 15, 2024"
date.format('MMMM Do YYYY'); // "March 15th 2024"
date.format('dddd, MMMM Do YYYY'); // "Friday, March 15th 2024"
date.format('h:mm A'); // "12:00 AM"
date.format('YYYY-MM-DD HH:mm:ss'); // "2024-03-15 00:00:00"
// Relative time
date.fromNow(); // "2 days ago" (relative to now)
date.from(moment('2024-03-20')); // "5 days ago" (relative to another date)
date.toNow(); // "in 2 days" (from perspective of date)
date.calendar(); // "Last Friday at 12:00 AM"
Common Use Cases in Obsidian
Daily Notes
class DailyNotesPlugin extends Plugin {
async createDailyNote() {
const today = moment().format('YYYY-MM-DD');
const fileName = `${today}.md`;
const file = this.app.vault.getAbstractFileByPath(`Daily/${fileName}`);
if (!file) {
await this.app.vault.create(`Daily/${fileName}`,
`# ${moment().format('MMMM Do, YYYY')}\n\n`
);
}
}
async openTodayNote() {
const today = moment().format('YYYY-MM-DD');
const file = this.app.vault.getAbstractFileByPath(`Daily/${today}.md`);
if (file instanceof TFile) {
await this.app.workspace.getLeaf().openFile(file);
}
}
}
class MyPlugin extends Plugin {
formatFileDate(file: TFile): string {
const mtime = moment(file.stat.mtime);
// Show relative time if recent
if (mtime.isAfter(moment().subtract(7, 'days'))) {
return mtime.fromNow();
}
// Otherwise show formatted date
return mtime.format('MMM DD, YYYY');
}
displayFileInfo(file: TFile) {
const created = moment(file.stat.ctime);
const modified = moment(file.stat.mtime);
console.log(`Created: ${created.format('YYYY-MM-DD HH:mm')}`);
console.log(`Modified: ${modified.fromNow()}`);
console.log(`Age: ${created.toNow(true)}`);
}
}
Date Manipulation
import { moment } from 'obsidian';
// Add time
const tomorrow = moment().add(1, 'day');
const nextWeek = moment().add(1, 'week');
const nextMonth = moment().add(1, 'month');
const in90Minutes = moment().add(90, 'minutes');
// Subtract time
const yesterday = moment().subtract(1, 'day');
const lastWeek = moment().subtract(1, 'week');
const lastYear = moment().subtract(1, 'year');
// Start/end of period
const startOfDay = moment().startOf('day');
const endOfMonth = moment().endOf('month');
const startOfWeek = moment().startOf('week');
// Chaining
const date = moment()
.add(1, 'month')
.subtract(3, 'days')
.startOf('day');
Date Comparisons
const date1 = moment('2024-03-15');
const date2 = moment('2024-03-20');
// Comparison methods
date1.isBefore(date2); // true
date1.isAfter(date2); // false
date1.isSame(date2); // false
date1.isSameOrBefore(date2); // true
date1.isSameOrAfter(date2); // false
date1.isBetween(date2, date3); // check if between two dates
// Comparison with granularity
date1.isSame(date2, 'month'); // true (same month)
date1.isSame(date2, 'day'); // false (different day)
// Difference between dates
const daysDiff = date2.diff(date1, 'days'); // 5
const hoursDiff = date2.diff(date1, 'hours'); // 120
const yearsDiff = moment().diff('1990-01-01', 'years'); // age calculation
class MyPlugin extends Plugin {
parseDateFromFilename(filename: string): moment.Moment | null {
// Try multiple date formats
const formats = [
'YYYY-MM-DD',
'YYYYMMDD',
'MM-DD-YYYY',
'DD.MM.YYYY'
];
for (const format of formats) {
const parsed = moment(filename, format, true); // strict parsing
if (parsed.isValid()) {
return parsed;
}
}
return null;
}
extractDatesFromContent(content: string): moment.Moment[] {
const dates: moment.Moment[] = [];
const dateRegex = /\d{4}-\d{2}-\d{2}/g;
const matches = content.match(dateRegex);
if (matches) {
for (const match of matches) {
const date = moment(match, 'YYYY-MM-DD');
if (date.isValid()) {
dates.push(date);
}
}
}
return dates;
}
}
Task Management
class TaskPlugin extends Plugin {
isTaskOverdue(task: { dueDate: string }): boolean {
const due = moment(task.dueDate);
return due.isValid() && due.isBefore(moment(), 'day');
}
getTaskStatus(task: { dueDate: string }): string {
const due = moment(task.dueDate);
if (!due.isValid()) {
return 'No due date';
}
const now = moment();
if (due.isBefore(now, 'day')) {
return `Overdue by ${now.diff(due, 'days')} days`;
}
if (due.isSame(now, 'day')) {
return 'Due today';
}
if (due.isSame(now.add(1, 'day'), 'day')) {
return 'Due tomorrow';
}
return `Due in ${due.diff(now, 'days')} days`;
}
sortTasksByDueDate(tasks: Array<{ dueDate: string }>) {
return tasks.sort((a, b) => {
const dateA = moment(a.dueDate);
const dateB = moment(b.dueDate);
if (!dateA.isValid()) return 1;
if (!dateB.isValid()) return -1;
return dateA.diff(dateB);
});
}
}
| Token | Example | Description |
|---|
YYYY | 2024 | 4-digit year |
YY | 24 | 2-digit year |
MMMM | March | Full month name |
MMM | Mar | Short month name |
MM | 03 | Month (01-12) |
M | 3 | Month (1-12) |
DD | 15 | Day with leading zero |
D | 15 | Day without leading zero |
dddd | Friday | Full day name |
ddd | Fri | Short day name |
HH | 14 | Hour (00-23) |
hh | 02 | Hour (01-12) |
mm | 05 | Minutes |
ss | 30 | Seconds |
A | PM | AM/PM |
Do | 15th | Day with ordinal |
Validation
const date = moment('2024-03-15');
// Check if valid
if (date.isValid()) {
console.log('Valid date');
} else {
console.log('Invalid date');
}
// Strict parsing
const strictDate = moment('03/15/2024', 'MM/DD/YYYY', true);
if (strictDate.isValid()) {
console.log('Matches format exactly');
}
// Check for specific date properties
date.isLeapYear(); // Check if leap year
date.isBefore(moment()); // Check if in the past
date.isAfter(moment()); // Check if in the future
Obsidian also provides a MomentFormatComponent for building date format pickers in settings:
class MySettingTab extends PluginSettingTab {
display() {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName('Date format')
.setDesc('Format for displaying dates')
.addMomentFormat((format) => {
format
.setDefaultFormat('YYYY-MM-DD')
.setValue(this.plugin.settings.dateFormat)
.onChange(async (value) => {
this.plugin.settings.dateFormat = value;
await this.plugin.saveSettings();
});
});
}
}
Best Practices
Always store dates in ISO 8601 format (YYYY-MM-DD) for consistency:
// Good
const dateString = moment().format('YYYY-MM-DD');
await this.app.vault.create('note.md', `date: ${dateString}`);
// Bad - ambiguous format
const dateString = moment().format('MM/DD/YY');
Validate Dates
Always check if a moment is valid before using it:
function processDate(dateString: string) {
const date = moment(dateString);
if (!date.isValid()) {
new Notice('Invalid date format');
return;
}
// Use the date
console.log(date.format('YYYY-MM-DD'));
}
Use Relative Times for Recent Events
function formatDate(timestamp: number): string {
const date = moment(timestamp);
const now = moment();
// Show relative time if within last week
if (date.isAfter(now.subtract(7, 'days'))) {
return date.fromNow();
}
// Otherwise show formatted date
return date.format('MMM DD, YYYY');
}
Further Reading
While Moment.js is included in Obsidian for convenience, note that Moment.js is in maintenance mode. For new projects outside of Obsidian, consider alternatives like Day.js or date-fns. However, since Obsidian includes Moment.js, it’s perfectly fine to use it in your plugins.