diff --git a/hledger-lib/Hledger/Data/Timeclock.hs b/hledger-lib/Hledger/Data/Timeclock.hs index 3b3ddae5a..f85bd3388 100644 --- a/hledger-lib/Hledger/Data/Timeclock.hs +++ b/hledger-lib/Hledger/Data/Timeclock.hs @@ -57,12 +57,12 @@ instance Read TimeclockCode where data Session = Session { in' :: TimeclockEntry, out :: TimeclockEntry - } + } deriving Show data Sessions = Sessions { completed :: [Session], active :: [TimeclockEntry] - } + } deriving Show -- | Find the relevant clockin in the actives list that should be paired with this clockout. -- If there is a session that has the same account name, then use that. @@ -124,23 +124,28 @@ pairClockEntries (entry : rest) actives sessions else entry : actives -- | Convert time log entries to journal transactions, allowing multiple clocked-in sessions at once. +-- This is the new, default behaviour. +-- Entries are processed in time order, then (for entries with the same time) in parse order. -- When there is no clockout, one is added with the provided current time. -- Sessions crossing midnight are split into days to give accurate per-day totals. -- If any entries cannot be paired as expected, an error is raised. --- This is the new, default behaviour. timeclockEntriesToTransactions :: LocalTime -> [TimeclockEntry] -> [Transaction] timeclockEntriesToTransactions now entries = transactions where - -- XXX should they be date sorted ? or processed in the order written ? - sessions = pairClockEntries (sortBy (\e1 e2 -> compare (tldatetime e1) (tldatetime e2)) entries) [] [] + sessions = dbg6 "sessions" $ pairClockEntries (sortTimeClockEntries entries) [] [] transactionsFromSession s = entryFromTimeclockInOut (in' s) (out s) -- If any "in" sessions are in the future, then set their out time to the initial time outtime te = max now (tldatetime te) createout te = TimeclockEntry (tlsourcepos te) Out (outtime te) (tlaccount te) "" "" [] outs = map createout (active sessions) - stillopen = pairClockEntries ((active sessions) <> outs) [] [] + stillopen = dbg6 "stillopen" $ pairClockEntries ((active sessions) <> outs) [] [] transactions = map transactionsFromSession $ sortBy (\s1 s2 -> compare (in' s1) (in' s2)) (completed sessions ++ completed stillopen) +-- | Sort timeclock entries first by date and time (with time zone ignored as usual), then by file position. +-- Ie, sort by time, but preserve the parse order of entries with the same time. +sortTimeClockEntries :: [TimeclockEntry] -> [TimeclockEntry] +sortTimeClockEntries = sortBy (\e1 e2 -> compare (tldatetime e1, tlsourcepos e1) (tldatetime e2, tlsourcepos e2)) + -- | Convert time log entries to journal transactions, allowing only one clocked-in session at a time. -- Entries must be a strict alternation of in and out, beginning with in. -- When there is no clockout, one is added with the provided current time. diff --git a/hledger/test/timeclock.test b/hledger/test/timeclock.test index 343b16d70..a65fbfc41 100644 --- a/hledger/test/timeclock.test +++ b/hledger/test/timeclock.test @@ -160,7 +160,37 @@ $ hledger --old-timeclock -f timeclock:- print >= +# ** 12. A session can begin at another session's end time (#2417). +< +i 2025-01-01 12:00:00 a +o 2025-01-01 13:00:00 +i 2025-01-01 13:00:00 b +o 2025-01-01 14:00:00 +$ hledger -f timeclock:- print +2025-01-01 * 12:00-13:00 + (a) 1.00h + +2025-01-01 * 13:00-14:00 + (b) 1.00h + +>= + +# ** 13. Even if that session has the same account name (#2417). +< +i 2024-01-01 13:00:00 a +o 2024-01-01 14:00:00 +i 2024-01-01 14:00:00 a +o 2024-01-01 15:00:00 + +$ hledger -f timeclock:- print +2024-01-01 * 13:00-14:00 + (a) 1.00h + +2024-01-01 * 14:00-15:00 + (a) 1.00h + +>= # ** OLD: