Timestat is a command-line program for creating and displaying time statistics about activities.
Timestat's features include:
- Record actions like starting/stopping doing a task.
- Print different statistics and summaries.
- Set targets and print statistics about them.
- Select an activity with a pattern and/or interactively.
- All information is kept in plain-text files that are easy to edit manually too.
Let's have a look at a quick demo of Timestat:
$ export ACTIONFILES=$HOME/myactionfile
# If you use Windows paths such as "C:\dir", you should replace the default
# ":" separator with ";".
#$ export ACTIONFILE_SEPARATOR=';'
$ timestat add mywork # Started to work on 'mywork'
$ timestat add myotherwork # 20 minutes later, started to work on
# 'myotherwork'
$ timestat add mywork # Another 20 minutes later, back to 'mywork'
$ timestat add stop # 5 minutes later, stopped working
$ timestat add mywork # Some mywork again
$ timestat add stop # 10 minutes later, mywork stopped again
$ timestat add 10 myotherwork # Adding 10 minutes to 'myotherwork'
# that we did while not at the computer
$ cat $HOME/myactionfile
[2009-07-25 20:00:00] mywork
[2009-07-25 20:20:00] myotherwork
[2009-07-25 20:40:00] mywork
[2009-07-25 20:45:00] stop
[2009-07-25 20:55:00] mywork
[2009-07-25 21:05:00] stop
[2009-07-25 22:05:00] 10 myotherwork
$ ./timestat show
myotherwork: 00:30
mywork: 00:35
The same steps, assuming you have the bashrc configuration
described below (the tq
alias switches to the previous state):
$ tq mywork # Started to work on 'mywork'
"mywork" activity started
$ tq myotherwork # 20 minutes later, started to work on
# 'myotherwork'
"myotherwork" activity started, "mywork" activity stopped
$ tq # Another 20 minutes later, back to 'mywork'
"mywork" activity resumed, "myotherwork" activity stopped
$ tq stop # 5 minutes later, stopped working
"mywork" activity stopped
$ tq # Some mywork again
"mywork" activity resumed
$ tq # 10 minutes later, mywork stopped again
"mywork" activity stopped
$ ts add 10 myotherwork # Adding 10 minutes to 'myotherwork'
# that we did while not at the computer
$ cat $HOME/myactionfile
[2009-07-25 20:00:00] mywork
[2009-07-25 20:20:00] myotherwork
[2009-07-25 20:40:00] mywork
[2009-07-25 20:45:00] stop
[2009-07-25 20:55:00] mywork
[2009-07-25 21:05:00] stop
[2009-07-25 22:05:00] 10 myotherwork
$ ./timestat show
myotherwork: 00:30
mywork: 00:35
Timestat is a command line tool, and its general syntax is the following:
timestat [options] [COMMAND [PARAMETERS]]
-
-v, --verbose
: Display verbose printouts. -
-f ACTIONFILES, --actionfiles ACTIONFILES
: Action files to be used, separated with a colon. If not specified, theACTIONFILES
environmental variable is used. -
-i, --ignore-activities
: Ignore the given activities. The activities should be separated with a colon. -
-o, --only-activities
: Consider only the given activities. The activities should be separated with a colon. -
-I, --ignore-pattern
: Ignore the activities that match the pattern. -
-O, --only-pattern
: Consider only the activities that match the pattern.-i
and-I
are stronger than-o
and-O
, i.e. if something is both ignored and included, it will be ignore. This way one can write queries like "all work activity except for programming". The following command prints all activities that start with "work" except for "work/meeting" (e.g. "work/programming" is included but "work/meeting" and "mywork" are not):timestat show -O "^work" -i work/meeting
-
--ignore-case
: Be case insensitive when evaluating whether an activity matches a regular expression pattern. Note that the action file is always considered to be case sensitive, so if it contains a "Work" and a "work" entry, those will be different activities even if--ignore-case
is used. -
--only-expr ONLY_EXPR
:ONLY_EXPR
should be a Python 3 expression where theh
variable contains theHappening
object. The expression should return a boolean value. The effect of this option is those happenings will be ignored for which the expression's value isFalse
. For example the following command considers only activities that started between 8am and 4pm:timestat show --only-expr \ 'h.is_event() and 8 <= int(h.starttime.strftime("%H")) <= 16'
The
is_event
check is needed because targets are also filtered, but they are not events and hence they don't have astarttime
data member.As another example, let's list all occasions when I worked on the weekend:
timestat print events -O work --only-expr \ 'h.is_event() and int(h.starttime.strftime("%w")) in (6, 7)'
-
--multiply-timelen NUMBER
: Multiple the "time length" values with this number.
--from DATE
: Ignore happenings before this date. (Format: see below.)--to DATE
: Ignore happenings after this date. (Format: see below.) The default value istoday
, which has the advantage that future targets are not yet taken into account.-d DATE, --day DATE
: Work with actions that happened/started during this day. (Format: see below.)--day DATE
is equivalent to--from DATE --to DATE
.--week ISO_WEEK_NUMBER
: Work with actions that happened/started during this week.--week ISO_WEEK_NUMBER
is equivalent to--from wISO_WEEK_NUMBER-1 --to wISO_WEEK_NUMBER-7
.-t, --today
is equivalent to--day today
.-y, --yesterday
is equivalent to--day yesterday
.--this-week
: Work with days in this week.
Date formats:
t
,today
y
,yesterday
yy
: the day before yesterday.- n times
y
: n days before yesterday. YYYY-MM-DD
MM-DD
: Given day in the current year. (Be careful with it in January, since12-xx
means the end of the current year, which is 12 months in the future).YYYY-wWW-D
: ISO week day (e.g.2000-w02-3
is the Wednesday of week 2 in 2000).wWW-D
: ISO week day in the current year.D
: Day of the week. E.g.1
means last Monday (or today if today is Monday),2
is last Tuesday (or today), etc.
Usage:
timestat full-help
Usage:
timestat [options] a ACTION
timestat [options] add ACTION
If more than one parameter is given, they will be joined and handled as one action.
Options:
-f ACTIONFILES, --actionfiles ACTIONFILES
: see "Common Options".-a ACTIONFILE_SUBSTR, --actionfile ACTIONFILE_SUBSTR
: If--actionfile
is specified, thenACTION
will be added to the action file whose name containsACTIONFILE_SUBSTR
. If--actionfile
is not specified, the first action file will be used.
Examples:
$ timestat add programming # Started programming now
$ timestat add stop # Stopped programming
$ timestat add 20 programming # Add 20 minutes of programming
# The "second" action file is modified:
$ timestat \
--actionfiles=firstfile.txt:secondfile.txt \
--actionfile=second \
add 20 programming
Usage:
timestat [options] quickadd
timestat [options] quickadd ACTION
timestat [options] quickadd %ACTICITY_PATTERN
Switch to the previous state. The step is calculated in the following way:
- If
ACTION
is specified, start work on that action. - If an
ACTICITY_PATTERN
is specified after a percentage sign, find the activities with that pattern. If there is only one, start working on it; if there is more, ask the user which one they meant.
Otherwise (i.e. if no action or activity pattern is specified):
- If an activity is ongoing (according to the action file), stop that activity.
- If there is no ongoing activity, find out the previous state (either 'stopped' or a previous activity), and go into that state (i.e. either stop, or start the previous activity).
Options described in "Common options":
-f ACTIONFILES, --actionfiles ACTIONFILES
-i, --ignore-activities
-o, --only-activities
-I, --ignore-pattern
-O, --only-pattern
--ignore-case
: applies to ignore pattern, only pattern and activity pattern.--only-expr ONLY_EXPR
Other options:
-a ACTIONFILE_SUBSTR, --actionfile ACTIONFILE_SUBSTR
: See theadd
command.
Notes:
- If more than one parameter is given, they will be joined and handled as one action.
- Although the previous state is found out based on all action files,
the new line will be added to the one specified by
--actionfile
(or the default one).
Examples: see the Quick demo above.
Example about the activity pattern:
$ tq %hobby/cp # I want to start work on a task that matches the
# "hobby/cp" regular expression
Select the desired match (empty line = cancel):
1: hobby/cp/erl-utils
2: hobby/cp/ew
3: hobby/cp/exponwords
4: hobby/cp/offline-issues
5: hobby/cp/permet
6: hobby/cp/vim-erlang-tags
7: hobby/cp/vim-erlang-tags/python-rewrite
> 2 # The number "2" is typed by the user
"hobby/cp/ew" activity started
$
Usage:
timestat [options] e
timestat [options] edit
The EDITOR environment variable is used to open the files.
Options:
-f ACTIONFILES, --actionfiles ACTIONFILES
: see "Common Options".-a ACTIONFILE_SUBSTR, --actionfile ACTIONFILE
: Specify which action file to open in the editor. If not specified, the first action file will be opened. If--actionfile
is"ALL"
, then all files will be opened.
Usage:
timestat [options] list
Option:
-f ACTIONFILES, --actionfiles ACTIONFILES
: see "Common Options".
Usage (the two lines are equivalent):
timestat [options]
timestat [options] show
Different statistics are available with different options. The default format is to print all activities with the minutes that have been spent on them.
Options described in "Common options":
-f ACTIONFILES, --actionfiles ACTIONFILES
-i, --ignore-activities
-o, --only-activities
-I, --ignore-pattern
-O, --only-pattern
--ignore-case
--only-expr ONLY_EXPR
--multiply-timelen NUMBER
Options described in "Options for specifying dates":
--from DATE
--to DATE
-d DATE, --day DATE
-t, --today
-y, --yesterday
Other options:
-w, --weekly-sum
: Print a weekly summary (same as theshow-sum weekly
command).-s, --sum
: Print only the sum of the activity time.--nosum
: Don't print the sum of the activity time.--sort-time
: Sort the result by activity time.-c, --current
: Display the name of ongoing task, if any, and the time since the last action.--seconds
: Display the seconds in the printed intervals.
Examples:
$ timestat show
mywork: 20
myotherwork: 65
$ timestat -H show
mywork: 00:20
myotherwork: 01:05
$ timestat -c
myotherwork:20
Usage:
timestat [options] show-sum daily
timestat [options] show-sum weekly
timestat [options] show-sum monthly
Show a summary about how much time was spent on activities per day/per week/per month.
Options described in "Common options":
-f ACTIONFILES, --actionfiles ACTIONFILES
-i, --ignore-activities
-o, --only-activities
-I, --ignore-pattern
-O, --only-pattern
--ignore-case
--only-expr ONLY_EXPR
--multiply-timelen NUMBER
Options described in "Options for specifying dates":
--from DATE
--to DATE
-d DATE, --day DATE
-t, --today
-y, --yesterday
Other options:
--fill
: Print all dates (not only those with time spent).--show-time
: Print time too.--avg
: Print the average time spent.--block-size SECONDS
: Defines how many seconds should one "x" represent.
Example (one x
means one hour spent):
$ timestat show-sum daily -O ^work
2014-01-20 xxxxxx
2014-01-21 xxxxxxxx
2014-01-22 xxxxxxxxx
2014-01-23 xxxxxxx
2014-01-24 xxxxxxxx
$ timestat show-sum weekly --show-time -O ^hobby
2014-w01 (21:20) xxxxxxxxxxxxxxxxxxxxx
2014-w02 (07:15) xxxxxxx
2014-w03 (13:06) xxxxxxxxxxxxx
2014-w04 (16:52) xxxxxxxxxxxxxxxx
2014-w05 (09:24) xxxxxxxxx
2014-w06 (18:04) xxxxxxxxxxxxxxxxxx
Usage:
timestat [options] st
timestat [options] show-targets
Show a summary about the targets. See the "Targets" section for more information about targets.
Options described in "Common options":
-f ACTIONFILES, --actionfiles ACTIONFILES
-i, --ignore-activities
-o, --only-activities
-I, --ignore-pattern
-O, --only-pattern
--ignore-case
--only-expr ONLY_EXPR
--multiply-timelen NUMBER
Options described in "Options for specifying dates":
--from DATE
--to DATE
-d DATE, --day DATE
-t, --today
-y, --yesterday
Example: see the "Targets" section.
Usage:
timestat [options] ss
timestat [options] show-status
Show the current or last activity.
Options described in "Common options":
-f ACTIONFILES, --actionfiles ACTIONFILES
Example:
$ timestat show-status
State: on
Current activity: hobby/timestat
Start time: 15:37
Current time: 15:44
Time since started: 00:06
Usage:
timestat [options] print actions
timestat [options] print events
timestat [options] print happenings
timestat [options] print activities
Print all objects of the given type.
Options described in "Common options":
-f ACTIONFILES, --actionfiles ACTIONFILES
-i, --ignore-activities
-o, --only-activities
-I, --ignore-pattern
-O, --only-pattern
--ignore-case
--only-expr ONLY_EXPR
--multiply-timelen NUMBER
Options described in "Options for specifying dates":
--from DATE
--to DATE
-d DATE, --day DATE
-t, --today
-y, --yesterday
Usage:
timestat test
Usage:
timestat tc
timestat test-coverage
An example action file looks like this:
[2009-07-25 20:33:07] mywork
[2009-07-25 20:53:11] stop
[2009-07-25 20:54:11] 10 myotherwork
First let's get some terminology out of the way:
-
An activity is something you can do for periods of time. In the example above,
mywork
andmyotherwork
are activities. -
An action is something that happens at a certain moment. The example above describes three actions:
- Starting to work on
mywork
; - Stopping to work on
mywork
; - Working 10 minutes on
myotherwork
.
Not all actions relate to concrete time logged: for example if the target time for an activity is increased, that is also an action.
- Starting to work on
-
An event is something that is happening during a period of time. It has a beginning and an end. The example above describes two events:
- Working 30:04 minutes on
mywork
. - Working 10 minutes on
myotherwork
.
- Working 30:04 minutes on
-
A happening is either an event or an action that cannot be converted into an event. Currently this means that a happening is either an event or a target.
As you see, all lines in the action file describe an action, and they must follow the same format: a date (in the format above) and an action text:
[YYYY-mm-dd HH:MM:SS] ACTION_TEXT
The only exceptions to this rule are empty lines and comments, see later.
The action text may have the following formats:
-
Start action, which means that an activity is started (and if there is any other ongoing activity, that is finished):
ACTIVITY
where
ACTIVITY
may not contain whitespace. Examples:mywork work/task1 hobby/english/Catch22
-
Stop action, which means that the ongoing activity is stopped:
stop
-
Interval action, which means that some additional time should be logged on an activity (without interfering with an ongoing activity):
TIME_LENGTH ACTIVITY
where
TIME_LENGTH
specifies the length of the activity (either inMM
,HH:MM
orHH:MM:SS
format), andACTIVITY
may not contain whitespace. For example:10 myotherwork 1:30 myotherwork 1:30:12 myotherwork
-
Increase target action, which means that the target time of an activity is increased:
increase-target ACTIVITY TIME_LENGTH
where
ACTIVITY
may not contain whitespace. Examples:increase-target mywork 8:00 increase-target work/task1 90
Action files do not have to be sorted.
Currently if an activity is started in an action file, it has to be finished in that file. This is planned to be changed in the future.
Beside the descriptions of actions, an action file may contain empty lines and comment. Comment are marked with a hash mark. A comment is either a whole line, or it is after the description of an action. (In the latter case, only hash marks preceded by a space character are considered comments.)
So for example the following is a valid action file:
# My work
[2009-07-25 20:33:07] mywork # wow, this was hard
[2009-07-25 20:53:11] stop
# My jobs
[2009-07-25 20:54:11] job#01 # this was hard too
[2009-07-25 21:23:11] job#02 # this was easy
[2009-07-25 21:30:11] stop
In this example, the activities are called "mywork", "job#01" and "job#02".
You can define targets about how much you want to spend with a certain task group.
For example your action file might contain the following:
[2013-02-01 00:00:00] increase-target work 8:00
[2013-02-02 00:00:00] increase-target work 8:00
[2013-02-03 00:00:00] increase-target work 8:00
[2013-02-04 00:00:00] increase-target work 8:00
[2013-02-01 09:00:00] work
[2013-02-01 17:00:00] stop # 8 hours of work
[2013-02-02 09:00:00] work
[2013-02-02 17:00:00] stop # 8 hours of work
[2013-02-03 09:00:00] work
[2013-02-03 18:00:00] stop # 9 hours of work
[2013-02-04 09:00:00] work
[2013-02-04 15:00:00] stop # 6 ours of work
Timestat can produce the following reports:
$ timestat
work: 31:00
Sum: 31:00
$ timestat show-targets
Target name: work
Target time: 32:00
Actual time: 31:00
Difference: -01:00
Only targets between from
and to
are considered:
$ timestat show-targets --day 2013-02-03
Target name: work
Target time: 08:00
Actual time: 09:00
Difference: 01:00
Since to
is "today" by default, the future targets are ignored by default, so
you can add all workdays in the month is advance, you can still track your
balance every day.
The following lines are handy in .bashrc
or .bash_profile
:
export ACTIONFILES=.../myactionfile # List of action files
alias ts=.../timestat # timestat alias
alias tq=".../timestat quickadd" # timestat alias
If you use multiple machines, it is convenient to use a separate action file for each one, since this way you will not have merge problems with synchronizing them.
Since the modifier commands (add, quickadd, edit) will use the first action file by default, the most convenient setup is on each machine to have the action file corresponding to that machine as the first action file:
# Bashrc of machine 1:
export ACTIONFILES=.../machine_1.txt:.../machine_2.txt
# Bashrc of machine 2:
export ACTIONFILES=.../machine_2.txt:.../machine_1.txt
Timestat's unit tests provide 100% code coverage and I want to keep it that way. I also want to keep everything documented.
So I will accept pull requests only if:
- The new code has proper unit tests and keeps this rule. This is fortunately
easy because Timestat has built-in commands for executing all unit tests
measuring their coverage (
test
andtest-coverage
). - The new features are documented in the README.
- The new code does not contain training white space or lines longer than 80 characters.
It is recommended to put the following code into '.git/hooks/pre-commit'
(chmod +x
is also necessary) before creating commits:
#!/bin/bash
if ./timestat test; then
true # skip
else
echo "pre-commit hook: error: unit test failed, no commit."
exit 1
fi
if grep ' \+$' timestat README.markdown -q; then
echo "pre-commit hook: error: trailing white space"
exit 1
fi
if grep '.\{81,\}' timestat README.markdown -q; then
echo "pre-commit hook: error: line longer than 80 character"
exit 1
fi