# How to list completed todos sorted by completion time?

**URL:** https://community.silverbullet.md/t/how-to-list-completed-todos-sorted-by-completion-time/3669
**Category:** General
**Created:** [December 25, 2025, 7:05pm UTC](https://community.silverbullet.md/t/how-to-list-completed-todos-sorted-by-completion-time/3669 "2025-12-25T19:05:39Z")
**Posts on this page:** 1
**Showing post:** 2

<div class="post-metadata">

### Author: ![Mr.Red](https://community.silverbullet.md/user_avatar/community.silverbullet.md/mr.red/32/821_2.png) [@Mr.Red](https://community.silverbullet.md/u/Mr.Red)
#### Post date: [December 25, 2025, 7:51pm UTC](https://community.silverbullet.md/t/how-to-list-completed-todos-sorted-by-completion-time/3669/2 "2025-12-25T19:51:03Z")

</div>

Silverbullet doesn’t assign a timestamp to the completed tasks by default.  
But we can tell it to do this, with this workaround. use this `space-lua` script:

```auto
event.listen {  
  name = "task:stateChange",  
  run = function(e)  

    if e.data.newState == "x" and not e.data.text:find("%[completed:") then  
      
      local text = e.data.text  
      local completedAt = os.date("%Y-%m-%dT%H:%M:%S")  
      local newText = text:gsub("^%[%s*%]", "[x]") .. " [completed: " .. completedAt .. "]"  
      editor.dispatch({ changes = { from = e.data.from, to = e.data.to, insert = newText } })  
    end  
  end  
}

```

What this does:

- Only trigger if the you just checked the box (‘x’) AND if there isn’t already a timestamp (prevents loops)
- 
  1. Replace the empty checkbox with 

- 
  1. Append the timestamp to the end of the line

 ![image](https://community.silverbullet.md/uploads/default/original/2X/0/02a2d209a38ae42f6b1b0012c57af3572b3eca73.png)

and with this in place you can sort a query based on the `completed` attribute.

This simple script can be even extended to also remove the `completed` attribute, if you uncheck the task.

that would look something like this:

```lua
event.listen {
  name = "task:stateChange",
  run = function(e)
    local data = e.data
    local text = data.text
    
    -- Safety check: ensure text exists to avoid 'nil value' errors
    if not text then return end

    -- CASE 1: Task marked as complete ('x')
    if data.newState == "x" then
      -- Check if timestamp already exists to prevent infinite loops/double-clicks
      if not string.find(text, "%[completed:") then
        -- Some sandboxes restrict os.date; we use a simple format
        local completedAt = os.date("%Y-%m-%dT%H:%M:%S")
        -- Append the timestamp at the end
        local newText = string.gsub(text, "%[%s*%]", "[x]") .. " [completed: " .. completedAt .. "]"
        
        editor.dispatch({
          changes = {
            from = data.from,
            to = data.to,
            insert = newText
          }
        })
      end

    -- CASE 2: Task marked as incomplete (' ')
    elseif data.newState == " " then
      -- Check if there is actually a timestamp to remove
      if string.find(text, "%[completed:") then        
        -- Remove the [completed: ...] tag and any leading space
        local cleanText = string.gsub(text, "%[[xX]%]", "[]")
        local newText = string.gsub(cleanText, "%s*%[completed: [^%]]+]", "")
        
        editor.dispatch({
          changes = {
            from = data.from,
            to = data.to,
            insert = newText
          }
        })
      end
    end
  end
}

```

---

_[View the full topic](https://community.silverbullet.md/t/how-to-list-completed-todos-sorted-by-completion-time/3669)._
