How to Track Time Spent on Activities (Obsidian Dataview Functionality)
Track your time in Obsidian with Dataview! Log activities, run a query, and get insights — because what isn’t tracked isn’t managed.

Where do I spend my time?
I provide Personal Knowledge Management (PKM) consulting, and recently, a client came to me with an interesting request:
The Request: Seeing Time Spent on Activities
The client wanted an easy way to track how much time they spend on certain activities. At first glance, this seemed simple, but implementing it efficiently required a few layers of complexity.
To achieve this, we’ll use the Dataview community plugin in Obsidian. This plugin enables advanced queries to extract information from notes. Additionally, we need to activate DataviewJS, which allows us to write custom JavaScript queries for more flexible data retrieval. (See the screenshot below for setup instructions.)
1. Create a Page for the Query
Create a new note and name it “Where am I spending my time?”
Paste the following DataviewJS query into the note.
Optional inputs: startDateRange, endDateRange, and userSpecifiedTags, which you can specify at the top of the query.
This query scans your vault, extracts logs, calculates time spent on each activity, and displays the results in a table.
```dataviewjs
// Define your date range (inclusive)
const startDateRange = new Date("1900-01-01"); // Adjust the start date
const endDateRange = new Date("9999-01-31"); // Adjust the end date
// Define tags to filter (set to an empty array to retrieve all tags)
const userSpecifiedTags = []; // Set tags here (e.g., ["#work", "#hiring-meetings"]), or leave empty for all tags
// Collect tasks with valid metadata within the date range
const tasks = [];
for (const page of dv.pages()) {
if (page.file.tasks) {
for (const task of page.file.tasks) {
const startTimeMatch = task.text.match(/\[startTime::\s*(\d{2}:\d{2})\s*\]/);
const endTimeMatch = task.text.match(/\[endTime::\s*(\d{2}:\d{2})\s*\]/);
const tagMatches = task.text.match(/#[\w-]+/gi); // Match multiple hashtags, case-insensitive
if (startTimeMatch && endTimeMatch && tagMatches) {
const startTime = startTimeMatch[1];
const endTime = endTimeMatch[1];
try {
// Get the note's creation date using `file.ctime`
const noteDate = new Date(page.file.ctime);
// Check if the note's creation date falls within the specified range
if (noteDate >= startDateRange && noteDate <= endDateRange) {
// Parse start and end times using JavaScript Date
const today = noteDate.toISOString().split("T")[0]; // Format note date as YYYY-MM-DD
const startDate = new Date(`${today}T${startTime}:00`);
const endDate = new Date(`${today}T${endTime}:00`);
if (startDate && endDate && startDate < endDate) {
const duration = (endDate - startDate) / (1000 * 60); // Convert ms to minutes
// Add a record for each hashtag that matches the filter
for (const tag of tagMatches) {
if (userSpecifiedTags.length === 0 || userSpecifiedTags.includes(tag)) {
tasks.push({ tag, duration });
}
}
} else {
dv.span(`Invalid time range for task: ${task.text}\n`);
}
}
} catch (e) {
dv.span(`Error parsing dates for task: ${task.text}\n`);
}
}
}
}
}
// Group by tag and sum durations
const grouped = tasks.reduce((acc, task) => {
acc[task.tag] = (acc[task.tag] || 0) + task.duration;
return acc;
}, {});
// Render results
if (Object.keys(grouped).length > 0) {
dv.table(["Activity", "Total Time (minutes)"],
Object.entries(grouped).map(([tag, totalTime]) => [tag, totalTime]));
} else {
dv.span(`No valid tasks with durations to display for the date range ${startDateRange.toISOString().split("T")[0]} to ${endDateRange.toISOString().split("T")[0]}.\n`);
}
```
2. Start Logging Activities
To track time, use the following log format in any of your notes (e.g.):
On your daily note of 2025–01–20 you have the following entries
- [.] running a company admin #ceo-duties [startTime:: 16:00] [endTime:: 20:00]
- [.] Running from my problems #exercise [startTime:: 16:00] [endTime:: 16:30]
On you daily note of 2025–01–27 you have the following entries
- [.] Trained legs for the first time in 2025 #exercise [startTime:: 07:00] [endTime:: 08:00]
- [.] In meetings with my OnlyFans manager #meetings [startTime:: 12:00] [endTime:: 13:00]
Breakdown of Each Component
- [.]
→ Uses Obsidian’s task box functionality. This allows Dataview to read and process the line.#ceo-duties #exercise
→ Tags specify the activity grouping. The query will collect and sum all time entries for each tag across your entire vault.[startTime:: 16:00] [endTime:: 20:00]]
→ These define the time spent. The query calculates the difference and adds it to the total time per tag.
3. Output
The query generates a table summarizing time spent on each tag:
Tags are clickable, allowing you to quickly locate where each activity was logged.
4. Considerations
endTime
must be greater thanstartTime
(e.g.,endTime15:00
cannot be beforestartTime11:00
).
5. Potential Improvements
Future enhancements could include:
Allowing the query to pull start and end dates from user metadata
Letting users specify which tags to filter in a metadata field
Displaying time in different formats (hours, minutes, percentages, etc.)
6. Conclusion
This setup provides a simple but powerful way to track time spent on activities in Obsidian. By logging activities with start and end times, you can generate real-time insights into how you allocate your time across different tasks.
Let me know if you have any questions or suggestions for improvements!
Get in Touch
I share PKM tips and tutorials on my YouTube channel and offer consulting, demo vaults, and courses to kickstart your PKM journey.
YouTube: ConstructByDee
Website: ConstructByDee.com