[Bug]: Days in a Row doesn't count sessions started before midnight #3237

Closed
opened 2026-04-25 00:14:26 +02:00 by adam · 10 comments
Owner

Originally created by @pwinnski on GitHub (Feb 14, 2026).

What happened?

Yesterday, I started listening to an audiobook at 23:41 local time. I stopped at 00:19 this morning. Logs show the server was notified as it was happening.

{"timestamp":"2026-02-13 23:41:29.001","source":"PlaybackSessionManager.js:110","message":"[PlaybackSessionManager] Syncing local session \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501) (updatedAt: 1771047685926)","levelName":"INFO","level":2}
{"timestamp":"2026-02-13 23:41:29.024","source":"PlaybackSessionManager.js:205","message":"[PlaybackSessionManager] Inserting new session for \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501)","levelName":"DEBUG","level":1}
{"timestamp":"2026-02-13 23:41:29.035","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] Array.afterUpsert: Clearing cache","levelName":"DEBUG","level":1}
{"timestamp":"2026-02-13 23:41:29.036","source":"PlaybackSessionManager.js:235","message":"[PlaybackSessionManager] Updating progress for \"Dreams Bigger than Heartbreak\" with current time 10198.510925705 (previously 10228.510925705)","levelName":"INFO","level":2}
{"timestamp":"2026-02-13 23:41:29.048","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] mediaProgress.afterUpdate: Clearing cache","levelName":"DEBUG","level":1}
{"timestamp":"2026-02-13 23:41:29.048","source":"MediaProgress.js:254","message":"[MediaProgress] Manually setting updatedAt to '2026-02-14 05:41:25.926 +00:00' (media item 3d9e125e-7b74-45ee-85b7-e77a6a0b37cd)","levelName":"INFO","level":2}

There are a number of updates, and then the final update when I stopped:

{"timestamp":"2026-02-14 00:19:45.148","source":"PlaybackSessionManager.js:110","message":"[PlaybackSessionManager] Syncing local session \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501) (updatedAt: 1771049981985.0002)","levelName":"INFO","level":2}
{"timestamp":"2026-02-14 00:19:45.159","source":"PlaybackSessionManager.js:219","message":"[PlaybackSessionManager] Updated session for \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501)","levelName":"DEBUG","level":1}
{"timestamp":"2026-02-14 00:19:45.165","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] Object.afterBulkUpdate: Clearing cache","levelName":"DEBUG","level":1}
{"timestamp":"2026-02-14 00:19:45.165","source":"PlaybackSessionManager.js:235","message":"[PlaybackSessionManager] Updating progress for \"Dreams Bigger than Heartbreak\" with current time 13640.542524161 (previously 13630.500833037)","levelName":"INFO","level":2}
{"timestamp":"2026-02-14 00:19:45.172","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] mediaProgress.afterUpdate: Clearing cache","levelName":"DEBUG","level":1}
{"timestamp":"2026-02-14 00:19:45.172","source":"MediaProgress.js:254","message":"[MediaProgress] Manually setting updatedAt to '2026-02-14 06:19:41.985 +00:00' (media item 3d9e125e-7b74-45ee-85b7-e77a6a0b37cd)","levelName":"INFO","level":2}

Since I didn't stop listening until after midnight, and that was my only listening session for that day, my server stats show 0 minutes for yesterday, rather than 19, and my streak reset to 0. So now my client shows a 176-day listening streak, while the server shows a 1-day streak, and they will apparently by 175 days out of sync unless I do some manual database editing.

What did you expect to happen?

I would expect sessions that cross date boundaries to have listen time split between the days, and to count for both days when tracking the listening streak.

Steps to reproduce the issue

  1. Start listening for the first time before midnight, and don't pause or stop until after midnight.
  2. Check the stats.

Audiobookshelf version

v2.32.1

How are you running audiobookshelf?

Docker

What OS is your Audiobookshelf server hosted from?

macOS

If the issue is being seen in the UI, what browsers are you seeing the problem on?

None

Logs


Additional Notes

Poking around, I think maybe this could be fixed in ApiRouter#getUserListeningStatsHelpers(), so I'm going to dig into that.

Originally created by @pwinnski on GitHub (Feb 14, 2026). ### What happened? Yesterday, I started listening to an audiobook at 23:41 local time. I stopped at 00:19 this morning. Logs show the server was notified as it was happening. ``` {"timestamp":"2026-02-13 23:41:29.001","source":"PlaybackSessionManager.js:110","message":"[PlaybackSessionManager] Syncing local session \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501) (updatedAt: 1771047685926)","levelName":"INFO","level":2} {"timestamp":"2026-02-13 23:41:29.024","source":"PlaybackSessionManager.js:205","message":"[PlaybackSessionManager] Inserting new session for \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501)","levelName":"DEBUG","level":1} {"timestamp":"2026-02-13 23:41:29.035","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] Array.afterUpsert: Clearing cache","levelName":"DEBUG","level":1} {"timestamp":"2026-02-13 23:41:29.036","source":"PlaybackSessionManager.js:235","message":"[PlaybackSessionManager] Updating progress for \"Dreams Bigger than Heartbreak\" with current time 10198.510925705 (previously 10228.510925705)","levelName":"INFO","level":2} {"timestamp":"2026-02-13 23:41:29.048","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] mediaProgress.afterUpdate: Clearing cache","levelName":"DEBUG","level":1} {"timestamp":"2026-02-13 23:41:29.048","source":"MediaProgress.js:254","message":"[MediaProgress] Manually setting updatedAt to '2026-02-14 05:41:25.926 +00:00' (media item 3d9e125e-7b74-45ee-85b7-e77a6a0b37cd)","levelName":"INFO","level":2} ``` There are a number of updates, and then the final update when I stopped: ``` {"timestamp":"2026-02-14 00:19:45.148","source":"PlaybackSessionManager.js:110","message":"[PlaybackSessionManager] Syncing local session \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501) (updatedAt: 1771049981985.0002)","levelName":"INFO","level":2} {"timestamp":"2026-02-14 00:19:45.159","source":"PlaybackSessionManager.js:219","message":"[PlaybackSessionManager] Updated session for \"Dreams Bigger than Heartbreak\" (18584EFA-725B-4720-8A00-1A561B70B501)","levelName":"DEBUG","level":1} {"timestamp":"2026-02-14 00:19:45.165","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] Object.afterBulkUpdate: Clearing cache","levelName":"DEBUG","level":1} {"timestamp":"2026-02-14 00:19:45.165","source":"PlaybackSessionManager.js:235","message":"[PlaybackSessionManager] Updating progress for \"Dreams Bigger than Heartbreak\" with current time 13640.542524161 (previously 13630.500833037)","levelName":"INFO","level":2} {"timestamp":"2026-02-14 00:19:45.172","source":"ApiCacheManager.js:20","message":"[ApiCacheManager] mediaProgress.afterUpdate: Clearing cache","levelName":"DEBUG","level":1} {"timestamp":"2026-02-14 00:19:45.172","source":"MediaProgress.js:254","message":"[MediaProgress] Manually setting updatedAt to '2026-02-14 06:19:41.985 +00:00' (media item 3d9e125e-7b74-45ee-85b7-e77a6a0b37cd)","levelName":"INFO","level":2} ``` Since I didn't stop listening until after midnight, and that was my only listening session for that day, my server stats show 0 minutes for yesterday, rather than 19, and my streak reset to 0. So now my client shows a 176-day listening streak, while the server shows a 1-day streak, and they will apparently by 175 days out of sync unless I do some manual database editing. ### What did you expect to happen? I would expect sessions that cross date boundaries to have listen time split between the days, and to count for both days when tracking the listening streak. ### Steps to reproduce the issue 1. Start listening for the first time before midnight, and don't pause or stop until after midnight. 2. Check the stats. ### Audiobookshelf version v2.32.1 ### How are you running audiobookshelf? Docker ### What OS is your Audiobookshelf server hosted from? macOS ### If the issue is being seen in the UI, what browsers are you seeing the problem on? None ### Logs ```shell ``` ### Additional Notes Poking around, I think maybe this could be fixed in `ApiRouter#getUserListeningStatsHelpers()`, so I'm going to dig into that.
adam added the bug label 2026-04-25 00:14:26 +02:00
adam closed this issue 2026-04-25 00:14:27 +02:00
Author
Owner

@Vito0912 commented on GitHub (Feb 14, 2026):

Duplicate of https://github.com/advplyr/audiobookshelf/issues/1445

TL;DR ABS has no way of tracking this atm and it is impossible to retroactively add this missing data without some heuristic

@Vito0912 commented on GitHub (Feb 14, 2026): Duplicate of https://github.com/advplyr/audiobookshelf/issues/1445 TL;DR ABS has no way of tracking this atm and it is impossible to retroactively add this missing data without some heuristic
Author
Owner

@pwinnski commented on GitHub (Feb 14, 2026):

Oops, I searched two different terms, but missed that one, sorry.

I don't think it's a question of retroactively modifying stored data, nor of tracking anything new. Since all of the complaints revolve around the stats displayed by DailyListeningChart.vue, it seems like modifying getUserListeningStatsHelpers() to return different results when s.createdAt and s.updatedAt are on different days might solve the problem, no?

The session itself has started and ended on different dates:

Image

I think that's all fine and good. The only issue is this display:

Image

Which seems to be based entirely on the return value of getUserListeningStatsHelpers(). I'm poking at that code now, so perhaps I'll find out I'm wrong.

@pwinnski commented on GitHub (Feb 14, 2026): Oops, I searched two different terms, but missed that one, sorry. I don't think it's a question of retroactively modifying stored data, nor of tracking anything new. Since all of the complaints revolve around the stats displayed by `DailyListeningChart.vue`, it seems like modifying `getUserListeningStatsHelpers()` to return different results when `s.createdAt` and `s.updatedAt` are on different days might solve the problem, no? The session itself has started and ended on different dates: <img width="304" height="120" alt="Image" src="https://github.com/user-attachments/assets/ef9b7e31-62cf-4b1f-af78-fe05bbcb4123" /> I think that's all fine and good. The only issue is this display: <img width="431" height="493" alt="Image" src="https://github.com/user-attachments/assets/6ed191b2-2696-4e71-a0df-2a40654797bb" /> Which seems to be based entirely on the return value of `getUserListeningStatsHelpers()`. I'm poking at that code now, so perhaps I'll find out I'm wrong.
Author
Owner

@Vito0912 commented on GitHub (Feb 14, 2026):

it seems like modifying getUserListeningStatsHelpers() to return different results when s.createdAt and s.updatedAt are on different days might solve the problem, no?

Then you have the other problem of users saying they started yesterday, but it was only counted today. There is no no that is flawless solution for this.

You can of course change the logic, but it will only be as good as what three timestamps can give you. You could do some calculation and split the percentage listened between yesterday and today, and so on. But that is why I said it is a heuristic. You will not be able to fix this, only move the problem to another side or make it less extreme, for example by splitting it by percentage.

Also not to say, that updatedAt can be way in the future (e.g. syncing it later, updating it later)

@Vito0912 commented on GitHub (Feb 14, 2026): > it seems like modifying getUserListeningStatsHelpers() to return different results when s.createdAt and s.updatedAt are on different days might solve the problem, no? Then you have the other problem of users saying they started yesterday, but it was only counted today. There is ~~no~~ *no that is flawless* solution for this. You can of course change the logic, but it will only be as good as what three timestamps can give you. You could do some calculation and split the percentage listened between yesterday and today, and so on. But that is why I said it is a heuristic. You will not be able to fix this, only move the problem to another side or make it less extreme, for example by splitting it by percentage. Also not to say, that updatedAt can be way in the future (e.g. syncing it later, *updating* it later)
Author
Owner

@pwinnski commented on GitHub (Feb 15, 2026):

One of us is not understanding the other, or perhaps both. I'm definitely not suggesting shifting everything to another day! Very few sessions cross midnight, so I'm only suggesting doing something slightly different in those cases. And yes, perhaps only "making it less extreme" by handling the simplest cases which are currently mishandled. A session that results in 38 minutes of progress on a chart 19 minutes after midnight is... odd.

IF we have a start time and an end time (which I realize is a big IF), then sessions themselves are fine, but listeningStats could easily be split so that instead instead of adding sessionTimeListening to the days[s.date] bucket, it adds sessionTimeListeningAfterMidnight to that bucket, and sessionTimeListeningBeforeMidnight to the days[s.date - 1] bucket, so to speak. Not those names, of course, and not that syntax, but conceptually. Then activity reflects reality, at least for listeningStats. I already have that coded, and have been testing it with web-based and client playback sessions, and poking around and learning. As you say, any solution is only as good as the timestamps, so pausing for extended periods and such is interesting.

If clients aren't providing accurate session start and end times, then there's no way around that, of course. But let's not let unattainable perfection be the enemy of good!

In the meantime, I'm fascinated by the interaction between getUserListeningStatsHelpers() and DailyListeningChart.vue, and learning some interesting things.

@pwinnski commented on GitHub (Feb 15, 2026): One of us is not understanding the other, or perhaps both. I'm definitely not suggesting shifting everything to another day! Very few sessions cross midnight, so I'm only suggesting doing something slightly different in those cases. And yes, perhaps only "making it less extreme" by handling the simplest cases which are currently mishandled. A session that results in 38 minutes of progress on a chart 19 minutes after midnight is... odd. *IF* we have a start time and an end time (which I realize is a big IF), then sessions themselves are fine, but `listeningStats` could easily be split so that instead instead of adding `sessionTimeListening` to the `days[s.date]` bucket, it adds `sessionTimeListeningAfterMidnight` to that bucket, and `sessionTimeListeningBeforeMidnight` to the `days[s.date - 1]` bucket, so to speak. Not those names, of course, and not that syntax, but conceptually. Then activity reflects reality, at least for `listeningStats`. I already have that coded, and have been testing it with web-based and client playback sessions, and poking around and learning. As you say, any solution is only as good as the timestamps, so pausing for extended periods and such is interesting. If clients aren't providing accurate session start and end times, then there's no way around that, of course. But let's not let unattainable perfection be the enemy of good! In the meantime, I'm fascinated by the interaction between `getUserListeningStatsHelpers()` and `DailyListeningChart.vue`, and learning some interesting things.
Author
Owner

@Vito0912 commented on GitHub (Feb 15, 2026):

No, I understand that. But the PR ignores that updatedAt has absolutely nothing to do with the timeListened. The relation can only be connected in a Pperfect ABS usage.
A session can be updated months or years later. Pausing, some clients and changing sessions, all will alter the updatedAt.
But anyway maybe the PR will get merged still idk (just a contributer)

@Vito0912 commented on GitHub (Feb 15, 2026): No, I understand that. But the PR ignores that updatedAt has absolutely nothing to do with the timeListened. The relation can only be connected in a Pperfect ABS usage. A session can be updated months or years later. Pausing, some clients and changing sessions, all will alter the updatedAt. But anyway maybe the PR will get merged still idk (just a contributer)
Author
Owner

@pwinnski commented on GitHub (Feb 15, 2026):

Sure, I understand. I did test with my mobile client and deliberately disabled wifi to cause a gap, but only by some minutes, not days. But of course, that's true now, and has the same effect on stats now. My PR doesn't make that situation worse, and defaults to using s.date rather than s.updatedAt everywhere possible to ensure behavior doesn't change at all if a session is anything close to normal. I would have ignored s.updatedAt completely, but s.date is a VarChar of just the date rather than a DateTime, so I reasoned that in the absence of any real time data, I'd take what I could get, since it could be no worse, and would usually be better.

One note: pausing, some clients, changing sessions, and so on, all of the things that can change updatedAt, none of them matter at all unless the date of s.startedAt (aka createdAt) and s.date differ. So the impact of this is as minimized as possible, and relies on s.date as much as possible. So just now I tested with an "accurate" createdAt but an updatedAt four days later, and several different times. In all cases, the results are better than the status quo, and only the time of updateAt matters, not the date.

P.S. Such modesty; you're a very active contributor. Also, I hope I haven't come across as negative; I've very much appreciated your feedback, and iterated through quite a few approaches to address as much as possible of your comments, most especially in preserving the exact current functionality when createdAt and date are the same.

@pwinnski commented on GitHub (Feb 15, 2026): Sure, I understand. I did test with my mobile client and deliberately disabled wifi to cause a gap, but only by some minutes, not days. But of course, that's true now, and has the same effect on stats now. My PR doesn't make that situation worse, and defaults to using `s.date` rather than `s.updatedAt` everywhere possible to ensure behavior doesn't change *at all* if a session is anything close to normal. I would have ignored `s.updatedAt` completely, but `s.date` is a VarChar of just the date rather than a DateTime, so I reasoned that in the absence of any real time data, I'd take what I could get, since it could be no worse, and would usually be better. One note: pausing, some clients, changing sessions, and so on, all of the things that can change `updatedAt`, none of them matter at all unless the date of `s.startedAt` (aka `createdAt`) and `s.date` differ. So the impact of this is as minimized as possible, and relies on `s.date` as much as possible. So just now I tested with an "accurate" `createdAt` but an `updatedAt` four days later, and several different times. In all cases, the results are better than the status quo, and only the *time* of `updateAt` matters, not the date. P.S. Such modesty; you're a *very* active contributor. Also, I hope I haven't come across as negative; I've very much appreciated your feedback, and iterated through quite a few approaches to address as much as possible of your comments, most especially in preserving the exact current functionality when `createdAt` and `date` are the same.
Author
Owner

@Vito0912 commented on GitHub (Feb 15, 2026):

Sorry, to answer again. I think we are talking past each other, and I know I explain things very badly. That is why I do not answer issues much lately. Also, please do not take my messages as mean or anything. Some people do, but I do not want that.

So this is my last comment on this in the end it is not my decision. But I think it would break a lot of stats for users (or more do nothing, but see below) - depending on the app.

To keep it short, there are 3 or 4 important variables for us (I never named the date, but see below):

date - The actual date when the session started
startedAt - As you said, created at. I need to correct myself. Every time I wrote createdAt in my old comments, I actually meant date (but not startedAt). This does not have to be equal to date, but mostly is.
updatedAt - Already discussed above
timeListening - The actual listening time

A user can pause for an hour and still be in the same session. If a user started at 1PM, paused an hour from 2 PM to 3PM and closes the session at 4PM, the updatedAt will be 3 hours apart, while only listening two. This is not uncommon at all.

The date is the first time a person started the session. So if I start listening on 02/15/2026 at 23:59, it will show this date, no matter how long I listen.
Ref: https://github.com/advplyr/audiobookshelf/blob/master/server/objects/PlaybackSession.js#L237

startedAt and updatedAt are independent, because they do not have to be set. But even if we assume startedAt is set, startedAt will be about the same as date. If not startedAt either will be an arbirtary timestamp by an app or the time the session is created the first time (this is important if it's a local session, then date and startedAt could be different depending on the app. But one thing they defintily will not be that one is the started and one is the ended date)

So unless I miss something obvious and to my knwoeldge, the statement if (startDate === s.date) { in your PR will always be true. But even if it is not, and we enter the else statement, you again depend fully on updatedAt, which brings back all the initial issues I mentioned above.

@Vito0912 commented on GitHub (Feb 15, 2026): Sorry, to answer again. I think we are talking past each other, and I know I explain things very badly. That is why I do not answer issues much lately. Also, please do not take my messages as mean or anything. Some people do, but I do not want that. So this is my last comment on this in the end it is not my decision. But I think it would break a lot of stats for users (or more do nothing, but see below) - depending on the app. To keep it short, there are 3 or 4 important variables for us (I never named the `date`, but see below): `date` - The actual date when the session _**started**_ `startedAt` - As you said, created at. I need to correct myself. Every time I wrote createdAt in my old comments, I actually meant `date` (but not `startedAt`). This does not have to be equal to `date`, but mostly is. `updatedAt` - Already discussed above `timeListening` - The actual listening time A user can pause for an hour and still be in the same session. If a user started at 1PM, paused an hour from 2 PM to 3PM and closes the session at 4PM, the updatedAt will be 3 hours apart, while only listening two. This is not uncommon at all. The `date` is the first time a person **started** the session. So if I start listening on 02/15/2026 at 23:59, it will show this date, no matter how long I listen. Ref: https://github.com/advplyr/audiobookshelf/blob/master/server/objects/PlaybackSession.js#L237 `startedAt` and `updatedAt` are independent, because they do not have to be set. But even if we assume `startedAt` is set, `startedAt` will be about the same as `date`. If not `startedAt` either will be an arbirtary timestamp by an app or the time the session is created the first time (this is important if it's a local session, then `date` and `startedAt` could be different depending on the app. But one thing they defintily will not be that one is the started and one is the ended date) So unless I miss something obvious and to my knwoeldge, the statement `if (startDate === s.date) {` in your PR will always be true. But even if it is not, and we enter the else statement, you again depend fully on `updatedAt`, which brings back all the initial issues I mentioned above.
Author
Owner

@pwinnski commented on GitHub (Feb 15, 2026):

NOW I understand! What you are missing is that date is not the actual date the session started, but the date the session ended. This is easily demonstrably true with actual testing, and is the entire reason I created the issue and started looking into this in the first place!

Absolutely guaranteed, the session I started at 11:41pm local time on Friday, 2026-02-13, and ended at 12:19am local time on Saturday, 2026-02-14, has a date of 2026-02-14 and a dayOfWeek of Saturday. See the logs in the initial report above, and this record from the db:

sqlite> SELECT id, date, dayOfWeek, startTime, createdAt, updatedAt, timeListening FROM playbackSessions WHERE id='18584EFA-725B-4720-8A00-1A561B70B501';
18584EFA-725B-4720-8A00-1A561B70B501|2026-02-14|Saturday|10228.510925705|2026-02-14 05:41:25.916 +00:00|2026-02-14 06:19:41.985 +00:00|2286.72075200081

If what you said were accurate, I would have had no issue! The report would have shown activity on Friday and my streak would have continued. I mean, I told you, and also put in the PR, that I tested this many, many, many times. Clearly I saw that the else clause is taken whenever a session crosses midnight. Repeatedly!

You've linked to code that is called in a single place: PlaybackSessionManager.syncSession(), a method in which the next line is:

    Logger.debug(`[PlaybackSessionManager] syncSession "${session.id}" (Device: ${session.deviceDescription}) | Total Time Listened: ${session.timeListening}`)

Scroll back up to my logs and note that isn't what my logs show. Rather than syncSession(), my logs show that my server was using syncLocalSession(), which doesn't set date only on the first update, but on every update.
Ref: https://github.com/advplyr/audiobookshelf/blob/master/server/managers/PlaybackSessionManager.js#L216

So syncLocalSession() means that every session will have date and dayOfWeek set to the end of the session, rather than the beginning. Which it does. Hence the broken chart and broken streak and the issue.

I'm going to close the PR and just manually modify the database rather than keep going around about this. This whole flood of comments from you as someone with a high reputation reduces the already-low chances of the PR getting positive attention to below zero. Perhaps someday someone will look into why syncSession() and syncLocalSession() behave so differently, but not me, not today. I've put in too many hours staying up past midnight to test using real actual activity, so possibly it's sleep-deprivation talking, but this just doesn't seem worth it, despite all my efforts to ensure things got better rather than worse. They can stay worse.

@pwinnski commented on GitHub (Feb 15, 2026): NOW I understand! What you are missing is that `date` is *not* the actual date the session *started*, but the date the session *ended*. This is easily demonstrably true with actual testing, and is the entire reason I created the issue and started looking into this in the first place! Absolutely guaranteed, the session I started at 11:41pm local time on Friday, 2026-02-13, and ended at 12:19am local time on Saturday, 2026-02-14, has a `date` of `2026-02-14` and a `dayOfWeek` of `Saturday`. See the logs in the initial report above, and this record from the db: ```sql sqlite> SELECT id, date, dayOfWeek, startTime, createdAt, updatedAt, timeListening FROM playbackSessions WHERE id='18584EFA-725B-4720-8A00-1A561B70B501'; 18584EFA-725B-4720-8A00-1A561B70B501|2026-02-14|Saturday|10228.510925705|2026-02-14 05:41:25.916 +00:00|2026-02-14 06:19:41.985 +00:00|2286.72075200081 ``` If what you said were accurate, I would have had no issue! The report would have shown activity on Friday and my streak would have continued. I mean, I told you, and also put in the PR, that I tested this many, many, many times. Clearly I saw that the else clause is taken whenever a session crosses midnight. Repeatedly! You've linked to code that is called in a single place: PlaybackSessionManager.syncSession(), a method in which the next line is: ```javascript Logger.debug(`[PlaybackSessionManager] syncSession "${session.id}" (Device: ${session.deviceDescription}) | Total Time Listened: ${session.timeListening}`) ``` Scroll back up to my logs and note that isn't what my logs show. Rather than `syncSession()`, my logs show that my server was using `syncLocalSession()`, which doesn't set `date` only on the first update, but on every update. Ref: https://github.com/advplyr/audiobookshelf/blob/master/server/managers/PlaybackSessionManager.js#L216 So `syncLocalSession()` means that every session will have `date` and `dayOfWeek` set to the *end* of the session, rather than the beginning. Which it does. Hence the broken chart and broken streak and the issue. I'm going to close the PR and just manually modify the database rather than keep going around about this. This whole flood of comments from you as someone with a high reputation reduces the already-low chances of the PR getting positive attention to below zero. Perhaps someday someone will look into why `syncSession()` and `syncLocalSession()` behave so differently, but not me, not today. I've put in too many hours staying up past midnight to test using real actual activity, so possibly it's sleep-deprivation talking, but this just doesn't seem worth it, despite all my efforts to ensure things got better rather than worse. They can stay worse.
Author
Owner

@Vito0912 commented on GitHub (Feb 15, 2026):

I don't really understand why you closed the PR. Again, I have nothing to say about it getting merged or not.

The link you shared is for syncing local sessions if you are offline and are not connected to the internet, which makes a lot of sense, why it did not really affect that many people. Thus it's also NOT a duplicate of what I posted initally.

Some corrections

which doesn't set date only on the first update, but on every update.

From the code you linked this seems also incorrect. The date indeed gets set to the first time the session gets created the first time (after then the if !session will return true and no date updates occur [most third party clients use it that way])). But indeed it takes the last time, which really is a bit inconsistent with it. So this condition is only true for complete offline sessions that gets synced the first time. If that is after midnight then you have this issue.
I think it would make sense to change the title and reopen the issue to show the inconsistency here

And thanks for showing the oversight!

TL;DR: This only happens if you use local sessions that have an updatedAt after midnight and a created/startedAt before midnight, because the date of the local sessions get calculated on the first creation from the updatedAt, while non local sessions get the date set basically by startedAt

With that information in mind it might even be worth tbh. Still not a perfect solution, but it will not be destructive for syncs and it explains why it did happen to you and not to me while testing. Again sorry for the oversight in the whole conv. Normally it's the other way around, that people want syncing in the other direction
And for why I did say But one thing they defintily will not be that one is the started and one is the ended date, is because I looked at the update code for local sessions not for the creation code, my bad (because as in the TL;DR, the date is calculated the first time the session is synced, but is ignored after that completely). I think we all can agree that not updating the date is bad in general, especially with such inconsistent behaviour.

@Vito0912 commented on GitHub (Feb 15, 2026): I don't really understand why you closed the PR. Again, I have nothing to say about it getting merged or not. The link you shared is for syncing local sessions if you are offline and are not connected to the internet, which makes a lot of sense, why it did not really affect that many people. Thus it's also NOT a duplicate of what I posted initally. <details><summary>Some corrections</summary> </p> > which doesn't set date only on the first update, but on every update. From the code you linked this seems also incorrect. The date indeed gets set to the first time the session gets created the first time (after then the if !session will return true and no date updates occur [most third party clients use it that way])). But indeed it takes the last time, which really is a bit inconsistent with it. So this condition is only true for complete offline sessions that gets synced the first time. If that is after midnight then you have this issue. I think it would make sense to change the title and reopen the issue to show the inconsistency here <p> </details> And thanks for showing the oversight! TL;DR: This only happens if you use local sessions that have an updatedAt after midnight and a `created`/`startedAt` before midnight, because the date of the local sessions get calculated on the first creation from the `updatedAt`, while non local sessions get the date set basically by `startedAt` With that information in mind it might even be worth tbh. Still not a perfect solution, but it will not be destructive for syncs and it explains why it did happen to you and not to me while testing. Again sorry for the oversight in the whole conv. Normally it's the other way around, that people want syncing in the other direction And for why I did say `But one thing they defintily will not be that one is the started and one is the ended date`, is because I looked at the update code for local sessions not for the creation code, my bad (because as in the TL;DR, the date is calculated the first time the session is synced, but is ignored after that completely). I think we all can agree that not updating the date is bad in general, especially with such inconsistent behaviour.
Author
Owner

@Vito0912 commented on GitHub (Feb 15, 2026):

I updated my comment in that regard. With this in mind it actually makes sense to some degree. I understood it the way that it counted the day before, like the linked issue. I could have seen it by the image tbh, but I didn't and as you didn't mention that it was a wrong linked issue I just made wrong assumptions. I stand by my word that it is still no perfect solution, but it will fix your problem and it should do in a non destructive way for others. (because this way can't really be caused logically by any other means)
It's just important to note that this will only fix the local session sync, which why it didn't work when I tested it.
(Just a message ping, so you see it)

@Vito0912 commented on GitHub (Feb 15, 2026): I updated my comment in that regard. With this in mind it actually makes sense to some degree. I understood it the way that it counted the day before, like the linked issue. I could have seen it by the image tbh, but I didn't and as you didn't mention that it was a wrong linked issue I just made wrong assumptions. I stand by my word that it is still no perfect solution, but it will fix your problem and it should do in a non destructive way for others. (because this way can't really be caused logically by any other means) It's just important to note that this will only fix the local session sync, which why it didn't work when I tested it. (Just a message ping, so you see it)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/audiobookshelf#3237