- Python 100%
| LICENSES | ||
| README.md | ||
| toggl2timeclock.py | ||
toggl2timeclock
Convert a Toggl CSV export to timeclock.el format compatible with hledger (and probably others).
Installation
This is a standalone Python script with no external dependencies.
Download toggl2timeclock.py and run it in your terminal.
Usage
Run the script and pass it an input filename, e.g.
python3 toggl2timeclock.py 2025.csv
Output will be written to stdout, warnings (for example, if some of your fields contained characters not allowed in hledger timeclock files) to stderr.
Redirect as required, for example:
python3 toggl2timeclock.py 2025.csv >toggl-2025.timeclock 2>toggl2timeclock.log
Instead of giving an input filename, you can also specify - to have it read from stdin.
Configuration
There are some command-line options you can give in order to customize your output.
Maximum allowed overlap
In Toggl, time entries can overlap freely.
In hledger up to version 1.43, they can't, since every o line will end the previous i line, ignoring any account names in the o line.
This was changed in hledger 1.43 to allow overlapping, but only on different accounts.
You cannot overlap multiple entries in the same account.
This, I think, is a sane decision, but in Toggl, same-account overlaps are trivial to create.
Also, the implementation in hledger 1.43 is buggy and, for example, doesn't allow one entry to start at the exact same second another entry ended (if it's the same account) or produces wrong results (if it's different accounts).
This will surely be addressed in an upcoming version.
For now, you can use hledger's --timeclock-old option to switch back to the previous implementation, but to be honest, I would seriously discourage you from using any overlapping at all.
These constraints mean a couple of things for our conversion:
- On some versions of
hledger, the end of one entry and the start of another need to be at least one second apart.toggl2timeclock.pyhas the--min-pause=Noption to allow you to delay the start of the later entry, should you need it. - While some versions of
hledgerallow overlaps, they're too limited to represent Toggl's flexibility. Therefore,toggl2timeclock.pydoesn't allow them at all and instead provides you with the--fix-overlap-max=Noption to, again, delay the start of later entries so that they don't overlap with previous ones.
--min-pause=N allows you to specify the minimum number of seconds that two entries should be apart.
By default, that number is 0, which allows entries to start at the same second as a previous one ended.
Alternatively, if you need to conform to hledger 1.43's behavior of requiring at least one second between entries, you can set this number to 1.
You can also set it to an even higher number, if that helps you somehow. But note that this will always move the start time of the later entry (shortening it in the process). End times will never be changed for any entry. If you set this number to a value that's too high, entries might want to start later than they end, or be reduced to zero seconds of length, both of which is an error and will cause the conversion to abort.
Entries that have been modified by --min-pause will receive the tag toggl2timeclock-extended-pause:N, where N is the number of seconds between this entry and the previous one before the modification.
--fix-overlap-max=N on the other hand allows you to specify the maximum overlap (in seconds) that toggl2timeclock.py will fix automatically.
It defaults to 60.
"Fixing" means that the start time of the later entry will be delayed so that it no longer overlaps with a previous entry.
This will shorten the later entry; its end time will not be adjusted, in order to prevent a cascade of shifting timestamps.
If there is more overlap between two entries than the value of --fix-overlap-max, conversion will abort with an error message.
As with --min-pause, it is also an error if the modification of an entry would cause an entry to start after its end or be shorter than one second.
Entries that have been modified by --fix-overlap-max will receive the tag toggl2timeclock-removed-overlap:N, where N is the number of seconds that this entry previously overlapped with the previous one before the modification.
Having entries that start the second another entry ends are pretty common in Toggl, for example when using the "continue" feature, which will end the current entry and start another one.
Overlapping entries of up to a minute or two are also common if you edit start or end times manually, since you can only edit the hours and minutes, not the seconds. Longer overlaps can happen when you screwed something up while editing, or Toggl got confused (e.g. during offline operation), or you did it on purpose. You should experiment and check how large the overlaps in your input file actually are, and how much inaccuracy (resulting from shortening entries) you can tolerate.
Renaming the billable tag
Toggl entries are either billable or not.
By default, toggl2timeclock.py represents this as a tag named billable which has yes or no as its value.
If you'd like to customize that, you can, using --billable=NAME:YES:NO.
For example, to instead use a tag named free that has no has its value when something is billable, and yes if it's not, use --billable=free:no:yes.
Default values for client, project, and task
Toggl allows you to create time tracking entries without a client, project, or task.
toggl2timeclock.py however uses all three in the account names it generates.
By default, if one of these fields is not set in the input CSV, the output will use the literal values no client, no project, and no task.
Using the --no-client=TEXT, --no-project=TEXT, and --no-task=TEXT options, you can customize these values.
For example, an entry that has no client and no task, but only the project name "Garage", when setting --no-client=personal and --no-task=-, the resulting account name will be personal:Garage:-.
There is currently no way to tell toggl2timeclock.py to use account names with more or less than 3 parts.
Replacing client, project, or task names
There is some rudimentary support in toggl2timeclock.py to replace some of the values in the input data.
For example, to rename the client ACME Corp. to just acme, you could do --replace-client='ACME Corp.:acme'.
Since colon characters are not allowed in hledger account name parts, the split between the old and the new value will be made at the last colon.
For example, if you have a task named Tron: Legacy in your input file, you could rename it to tron2 using --replace-task='Tron: Legacy:tron2'.
If you need anything more fancy than that, you should use other tools to prepare your CSV file.
License
This project is licensed under the MIT license.