Metadata-Version: 2.4
Name: dey
Version: 0.2.2
Summary: Terminal time tracker
Author-email: David Woodburn <david.woodburn@icloud.com>
License-Expression: MIT
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Introduction

`dey` is a terminal-based time tracker for logging work hours against named
jobs.

The name comes from the [Alexander Dey Time Register Clock][1] (1888), an early
punch-clock that recorded employee hours on a paper roll. Like its namesake,
`dey` uses a simple punch-in, punch-out model: start a timer against a job, stop
it later. You can also backfill entries for past times.

Using a spreadsheet to track work hours is cumbersome: Excel is slow to open,
times and dates must be entered manually, multiple jobs create an unwieldy
number of columns, and generating summaries is complicated. This program offers
a simple terminal-based alternative. It automatically records the current date
and time, resolves short job-name fragments by substring matching, and produces
clear daily and weekly summaries that can be filtered the same way. A single
command also lets you undo the last entry, adjust its time, or change its job.

The design deliberately stays simple. Only one job can be active at a time.
Features such as billing calculations, notes, hierarchical projects, tags, idle
detection, goals, configuration files, and shell completions are omitted so the
tool remains a focused Unix-style utility: easy to understand, quick to use with
short commands, and simple to combine with other programs through its clean,
well-formatted output.

A simple example shows creating a job for the first time, starting time tracking
against that job, and then stopping the work over an hour later:
```bash
> dey -j meetings
  New job "meetings" created.
> dey -t
  00:00 | 07:42 -        meetings
> dey -t
  01:05 | 07:42 - 08:47  meetings
```

# Data Storage

The data for the program is saved in text files at

| OS      | Location                                    |
| :------ | :------------------------------------------ |
| Windows | `%LOCALAPPDATA%\dey\`                       |
| Linux   | `$XDG_CONFIG_HOME/dey/` or `~/.config/dey/` |
| macOS   | `~/.config/dey/`                            |

The `jobs.txt` file stores definitions of jobs:
```
a  meetings
a  permissive vacation
a  permissive sickness
i  Project X
```
Each job is defined with an ID (the row number in the file), an active, `a`, or
inactive, `i`, flag, and a label. The inactive flag hides the job from searches
when starting work on a job; it does not hide the history of work done for those
jobs. The jobs are in the order they were created.

The `logs.txt` file stores the work done, called "logs":
```
20260919  07:31  0001  09:22
20260919  10:03  0003  13:45
20260920  08:10  0001  10:50
20260926  08:30  0002
```
Each log contains a date represented as an integer in `YYYYMMDD` format, a start
time, a job ID, and a stop time. Times are in 24-hour format. Job IDs are
four-digit integers, which means up to 9999 different jobs can exist. A missing
stop time indicates that the log is currently active. Times are recorded to the
nearest minute; finer precision is omitted because minute precision introduces
only a zero-bias error, and many organizations do not require accuracy beyond
one-tenth of an hour.

The time intervals of different logs cannot overlap, so at any given moment,
work can be performed on only one job. As a consequence, no more than one log
may have a missing stop time because it is current. A log cannot span more than
one day. At the latest, it must end by 23:59. Consequently, no date arithmetic
is performed. Logs are listed in the order they were created, which may differ
from their chronological order.

Both the `jobs` file and the `logs` file have been designed so that in most
cases, a full rewrite of the file is not necessary. Instead, a new job or log
can be appended to the corresponding file. Though negligible anyway, this does
reduce processing time and wear on solid state discs. It is not intended that
the user ever directly modify these files; if you do, be very careful not to
corrupt the format.

If full editing ability were supported, a log ID would need to accompany each
log in order to address the specific log. Start and stop times would need to be
directly specified. Even an interactive interface for editing would be
preferable. These are all directions that would take away from the simplicity of
this program. Therefore, what is not supported is the ability to edit any
arbitrary log, besides the last. Consequently, it is the responsibility of the
user to pay attention when entering logs and not create the occasion for editing
logs several entries prior. The last entry can be modified, but anything prior
must be modified by undoing entries. That said, the logs file is formatted such
that it is not too difficult to directly edit, and the line numbers beside the
jobs and visible with a code editor correspond to the job IDs.

# Interface

Just the name of the program prints the status:
```bash
> dey
  07:32 | 14:10 - 15:05  meetings
> dey
  07:32 | 14:10 -        meetings
```
This shows the total time worked for the present day, the start and stop times
and the job label of the latest log. If the latest log is current, nothing is
printed in place of the stop time. If no logs exist for the present day, a
statement to this effect is output.

Additional functionality is available with the following command arguments (flag
letters preceded by a dash) and their optional tails (text following the
arguments):

| Arguments                  | Description                          |
| :------------------------- | :----------------------------------- |
| `-l {label}`               | List all active jobs matching label  |
| `-L {label}`               | List all jobs matching label         |
| `-j {label} {label}`       | Create job, rename job               |
| `-i {label}`               | Make job active job inactive         |
| `-a {label}`               | Make job inactive job active         |
|                            |                                      |
| `-t {time} {date} {label}` | Toggle time or switch active job     |
| `-e {time} {label}`        | Edit time or job of last entry       |
| `-u {integer}`             | Undo last log entries (default to 1) |
|                            |                                      |
| `-d {date} {label}`        | Day report (default to recent date)  |
| `-w {weeks} {label}`       | Week report (default to recent week) |
|                            |                                      |
| `-h`                       | Print command help                   |

A `date` is entered in the format `YYYY-MM-DD` or `YY-MM-DD`. Time is entered in
the format `HH:MM`. Each argument is a standalone instruction that does not
depend on any other argument. Multiple command arguments can be given in a
single terminal command and they will be executed in the order they are written.

## Jobs

### List Jobs

The list of existing active jobs can be printed by entering the `-l` flag
without any job labels. This will print all the jobs in alphabetical order:
```bash
> dey -l
  meetings
  permissive sickness
  permissive vacation
```

The list can be narrowed down by entering part of a job label after the flag:
```bash
> dey -l perm
  permissive sickness
  permissive vacation
```
All string matching to job labels is case-insensitive.

All jobs including inactive ones can be listed with the `-L` flag:
```bash
> dey -L
  meetings
  permissive sickness
  permissive vacation
  Project X
```
The inactive jobs are marked in gray.

### Create a Job

A new job can be defined with the `-j` argument followed by the job label:
```bash
> dey -j Clean-up
  New job "Clean-up" created.
```
If a job by that exact name (using case-insensitive matching) already exists,
the command will be rejected.

### Rename a Job

An active job can be renamed by adding a second string:
```bash
> dey -j Clean-up Cleanup
  Job "Clean-up" renamed to "Cleanup".
```
When searching for matches on the first string, a partial match will be
accepted. For example, `vaca` would match `permissive vacation`. If no match or
more than one match can be found, the command will be rejected. If the second
string fully matches an existing job label, the command will be rejected.

Renaming an inactive job requires first making it active.

### Active and Inactive Jobs

An active job can be made inactive so that it no longer is chosen as a valid
match to new work by using the `-i` flag:
```bash
> dey -i Project\ X
  "Project X" is now inactive.
```
An inactive job can be made active by using the `-a` flag:
```bash
> dey -a Project\ X
  "Project X" is now active.
```

### Job Aliases (*not implemented*)

A job can have an alias (or description) by appending the extra text to the job
name separated by a colon:
```bash
> dey -j "Project X: Hyper-engine research"
```
This is useful when printing reports. Reports will list the names of projects.
Because many projects can lead to very similar names which can make entering
totals into official time-reporting software error-prone, the print feature will
emphasize those parts of the names which are unique. However, the alias of the
name will never be included in the emphasis because that part will never be used
in reporting. That said, job searches can still match to the alias.

## Toggle Clock

### Without Time

Time tracking is toggled using the `-t` argument:
```bash
> dey -t
  00:18 | 07:42 -        Project X
```
This will start a log if none is current or end a log if one is current. The
status will be automatically printed to give confirmation. The simplicity of not
specifying whether the time is a start or stop means there can be only one
active log at a time.

### With Time

By default, the current time is used, but any time can be explicitly specified
after the `-t` argument:
```bash
> dey -t 07:45
  00:15 | 07:45 -        Project X
```
This will mark the start or stop of a log with that time. If a new start or stop
time overlaps with any other log of the same day, the command will be rejected
and an explanation will be printed. So, giving a stop time that comes before the
start time of the same log will be rejected and an explanation will be printed.

Only one current log is allowed at a time. This is why the interface does not
provide separate start and stop commands. So, if a log is current (has no stop
time) another log cannot be started. Instead, if the time is after the start
time of the current log, the new time will be used as the stop time of the
current log.

It is possible to forget to stop a log at the end of the day. So, the next day
when a time is given, it will be used as the stop time of yesterday's log. If
the stop time is before the log's start time, the command will be rejected.
Clocking out will only apply to the same day as the start of the current log.
This means you can clock out by entering something like `dey -t 17:00` and this
will apply to the day the current log was started. The program will not mark the
log as continuing into the next day.

Because logs can be defined with specific times (other than the present), they
can be created out of order. The log file is not sorted in chronological order.
This is a deliberate choice to support undoing actions in the order they were
commanded.

If an active log was started on a prior day, the date must be specified. A log
cannot span across dates.

### With Date

By default, the current day is used, but any date can be specified explicitly
after the `-t` argument:
```bash
> dey -t 07:45 26-09-17
```
The format for dates is either `26-09-07` or `2026-09-07`.

### With Job Label

By default, the job of the last log is the assumed job for a new log. However,
any job can be specified:
```bash
> dey -t meetings
  00:18 | 07:42 -        meetings
```
If no log is current, the job specified is used as the job of the new log. If a
log is current, the current time or the time provided is used to stop the
current log and to create a new log and the specified job is used for the new
log. This makes it easy to switch jobs without having to explicitly stop the
current log and start another. If the specified job does not match any
previously-defined jobs, the command will be rejected.

### Undo Last Entry (*not implemented*)

The last entry (start or stop or a log) can be undone with the `-u` argument:
```bash
> dey -u
  No logs for today.
```
Multiple entries can be undone at once by providing an integer.

### Edit Last Entry (*not implemented*)

The last log entry (start time, job label, or stop time) can be edited with
the `-e` argument. So, if a log is current, the `-e` command can be used to
change the start time:
```bash
> dey -e 07:45
  00:15 | 07:45 -        Project X
```
Providing a job label will change the job of the last entry.
```bash
> dey -e meetings
  00:18 | 07:42 -        meetings
```
Both a time and a job label can be specified together.

## Reports

### Day Reports (*partially implemented*)

The day report shows the date of the last log and a color-coded graph of that
day's work. This is followed by that day's logs, each preceded by the
corresponding color-coding. Finally, the total hours for the day are printed:
```bash
> dey -d
  2026-11-09 ==================      ===================                 
  = 01:15 (1.25) permissive sick
  = 04:38 (4.63) Project X
    05:53 (5.88)
```

Colors are chosen from a subset of the 8-bit ANSI-escape-code colors based on
the order the jobs first appeared in the day. (Only 8-bit colors are used
because most terminal emulators support those but not all terminal emulators
support millions of colors.) Depending on the start and stop times for the day
and the column width of the present output stream, each character in the graph
corresponds to a 5, 10, 15, 20, or 30 minute increment. The start of the bar
corresponds to the beginning of the first log of the day and the end corresponds
to the end of the last or present if a log is current.

Because the day summary can accept a date, any past day can be reported. If a
date is provided (in the format `26-09-07` or `2026-09-07`) the summary of that
day will be printed. If a job label is provided as in `dey -d meetings`, the
summary of just that job (or any job to which the text substring matches) for
the current date will be printed. These two options can be combined as in `dey
-d 26-09-07 meetings` to print a report of just that job for that date. The
colors chosen will be the same as if all the jobs for the day were printed.

### Week Reports (*not implemented*)

The week report (`dey -w`) aligns with the beginning of a week, not the current
day. For example, if the current day is Wednesday, the current week would be
from Sunday of that week to Wednesday, not the past seven days. The purpose of
the week report is to show the total hours worked per day, per job, and for the
week. The report is printed in a markdown-compatible format:
```bash
> dey -w
  Total | 09-06 | 09-07 | 09-09 | 09-10 | 09-11 | Job
  :---: | :---: | :---: | :---: | :---: | :---: | :---
  13:15 | 02:45 | 08:00 | 02:00 | 00:00 | 00:30 | Meetings
  16:30 | 03:30 | 00:00 | 05:00 | 02:00 | 06:00 | Documentation
  10:15 | 02:00 | 00:00 | 01:30 | 05:30 | 01:15 | Education
   ---  |  ---  |  ---  |  ---  |  ---  |  ---  |
  40:00 | 08:15 | 08:00 | 08:30 | 07:30 | 07:45 |
```
More weeks can be specified with a number after the `-w` argument flag. The
additional weeks will be printed as separate tables in chronological order.

If a job label is provided, just like the day report, only logs matching that
search will be printed. Because of substring search, multiple job labels can be
matches.

# Alternatives

There are several alternatives, but I believe none approach this problem with
the same level of simplicity and succinctness or brevity as `dey`.

- [bartib][2]
- [timetrace][3]
- [timetrap][4]
- [timewarrior][5]
- [tmpo][6]
- [watson][7]
- [zeit][8]


[1]:https://americanhistory.si.edu/collections/object/nmah_856741
[2]:https://github.com/nikolassv/bartib
[3]:https://github.com/dominikbraun/timetrace
[4]:https://github.com/samg/timetrap
[5]:https://github.com/gothenburgbitfactory/timewarrior
[6]:https://github.com/DylanDevelops/tmpo
[7]:https://github.com/jazzband/Watson
[8]:https://github.com/mrusme/zeit
