Sleep Tracking

What I’m trying to do

I want to track my sleep in obsidian.
I save my going-to-bed time (assuming I sleep immidiately) and my wake up time (logged right after I wake up) as sleepTime and upTime properties in my daily notes (stored in Journal/Daily directory).
Both of these properties (YAML frontmatter) are of Date&Time Type.
I generate them by the following:

---
sleepTime: <% moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('YYYY-MM-DD HH:mm:ss') %>
upTime: <% moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').add(-1, 'd').format('YYYY-MM-DD HH:mm:ss') %>
---

P.S. I do the add(1) and add(-1) to make the property not give an error, even in the template.
Also, since I usually sleep post 12am, I have incremented the sleep date by default.

Things I have tried

The closest I have reached, which is not much is the following dataview query.

TABLE date(sleepTime).hour*60 + date(sleepTime).minute AS SleepTime, date(upTime).hour*60 + date(upTime).minute AS UpTime
FROM "Journal/Daily"
WHERE sleepTime
SORT file.day

OR
TABLE date(sleepTime).hour+":"+date(sleepTime).minute AS SleepTime, date(upTime).hour+":"+date(upTime).minute AS UpTime
to just get the times.

It would be great if I can calculate the sleepDuration also.

From googling/redditing, I think this will be easier to do with DataViewJS but I have no prior knowledge of JS to do it myself.

Please suggest/help.

Thank you

Have you figured this out by now? It’s taken me some time to get around to answering this, but dataview is picky about its date/time formats, see Date. In particular it needs for the T to be present between the date part and the time part for it to automagically consider it a date.

If you don’t do that, you’ll need to use a special form of date() to re-parse it into a date using something like: date(sleepTime, "yyyy-MM-dd HH:mm:ss") to transform it back into a proper date.

When it’s a proper date, you should be able to do stuff like subtraction of the dates and so on.

Without changing the date format, which I strongly suggest you do, the following query might return something useful:

```dataview
TABLE sTime, uTime, (uTime - sTime) as "Sleep duration" 
FROM "Journal/Daily"
FLATTEN date(sleepTime, "yyyy-MM-dd HH:mm:ss") as sTime
FLATTEN date(upTime, "yyyy-MM-dd HH:mm:ss") as uTime
WHERE sleepTime
SORT file.day
```

The duration in the last column could be formatted using durationformat() if you so want.

Very interesting. Here’s a wrench in the works. In my journals, As part of the events I keep track of during a day, I record my ‘awake’ time when I wake up, and then my ‘asleep’ time when I go to sleep. The problem in calculating duration of course is that they are now on different days. Have a clever way to grab the awake time from today’s journal and subtract the asleep time from yesterday journal? I imagine we’d need to switch to dataviewjs…

You should be able to use file.day subtract a day, put that into a link, and extract yesterdays asleep time… No need for dataviewjs for this operation. :slight_smile:

The only tricky part is related to picking the date format for the daily notes link… and possibly when you like me often go askerp on the “wrong” side of midnight.

What format are your asleep and awake times? Full datetime formats? Just the hour and minutes?

I hear you about the wrong side of midnight, but thankfully I’m at an age where that usually only happends on new years eve. Currently the times are just text in the body: eg. ### ⏰6:30 AM - Awake under my “Events” header, but I’ve been considering moving them to the frontmatter, so I could use a script to make whatever format works best when I batch move them all (PowerShell to the rescue!). The journal names are in the format YYYY-MM-DD_ddd which I’m sure is not ideal for dataview to use for date info, but I do have a ‘created’ property (which for journals matches the date of the journal) in each file in the format YYYY-MM-DDTHH:MM:SS-06:00 if that could help.

Hmm… I’m of that age, that I’m often back on the other side again… :stuck_out_tongue:

Oh well, here is a query where each daily note has two properties. awake in the form HH:mm denoting when you woke this particular day, and asleep in the same format when you went to sleep. So asleep - awake would give you how many hour you’ve not slept that particular day.

But with the following query, which assumes just the YYYY-MM-DD format, but could easily be adjusted to allow for other variants:

```dataview
TABLE asleep as "(asleep)", awake, yesAsleep, sleepDuration
WHERE file.folder = this.file.folder
  AND file != this.file
FLATTEN file.day - dur("1 day") as yesDay
FLATTEN link(string(yesDay)).asleep as yesAsleep
FLATTEN date(file.day + "T" + awake) - date(choice(yesAsleep[0] = "0", file.day, yesDay) + "T" + yesAsleep) as sleepDuration
SORT file.day
```

I got the following output from my test vault:

(And todays Norwegian lesson is that time(r) is hour(s) and minutt(er) is minute(s))

Note that if the asleep is set to something with a 0 as the first character, it’s assumed to be the current day, and not yesterday. This allows you to go to sleep from 10:00 today until 09:59 the following day… That should suffice for most people, even those with slightly off sleeping schedules! :smiley:

The logic makes sense. In mine, I can get a table that shows asleep and awake for each file, but of course because of my file name format (yyyy-MM-dd_ddd), I don’t get duration. I tried to get the date to the correct format a couple ways, but my dataview skill set must not have reached the ‘easily’ level yet:
FLATTEN dateformat(file.day, "yyyy-MM-dd") - dur("1 day") as yesDay fails
FLATTEN file.day.split(0,-4) - dur("1 day") as yesDay also fails

Try this:

```dataview
TABLE asleep as "(asleep)", awake, yesAsleep, sleepDuration
WHERE file.folder = this.file.folder
  AND file != this.file
FLATTEN file.day - dur("1 day") as yesDay
FLATTEN link(dateformat(yesDay,  "yyyy-MM-dd_ccc")).asleep as yesAsleep
FLATTEN date(file.day + "T" + awake) - date(choice(yesAsleep[0] = "0", file.day, yesDay) + "T" + yesAsleep) as sleepDuration
SORT file.day
```

Do remember that dataview’s date format tokens is slightly different than those of Templater and Daily Notes. I think this did be the equivalent though. You need to dateformat on the date within the link as shown above.

That did it! At first I still wasn’t getting duration, but I changed my Dataview default date format from MM/dd/yy to yyyy-MM-dd and then it worked! Thanks, this will give me something to work with as I figure out how to better include sleep info into my journals!

1 Like