# todo.export-csv
Writes a list of todos as CSV text: a header row and one row per todo, in the
order given. Use it for a "download my todos" button, a backup, or handing the
list to a spreadsheet or another app. todo.import-csv reads the same text
back.
## The file format
CSV as in [RFC 4180, Common Format and MIME Type for Comma-Separated Values
(CSV) Files](https://www.rfc-editor.org/rfc/rfc4180). The same format is
written by todo.export-csv and read by todo.import-csv.
- A header row, then one row per todo. todo.export-csv writes these 13 columns
in this order:
`id,title,notes,done,priority,due,tags,recurrenceFrequency,recurrenceInterval,recurrenceAnchor,createdAt,completedAt,order`
- `done` is `true` or `false`. `tags` are joined with single spaces. The
repeat takes three columns, all empty when the todo does not repeat.
`order` and `recurrenceInterval` are decimal integers. A null field
(`notes`, `due`, `completedAt`) is empty.
- A field is quoted with double quotes exactly when it contains a comma, a
double quote, CR or LF, and a double quote inside it is doubled. Nothing
else is quoted, and spaces are kept as they are.
- Every row, the last one included, ends with CRLF.
**Caveat for spreadsheets.** Nothing is escaped against formula injection: a
title such as `=1+1` or `=HYPERLINK(...)` is written as it is, because
prefixing it with `'` would change the title when the file is read back. If
your app offers the file for opening in Excel, LibreOffice or Google Sheets,
warn people, or write a separate spreadsheet export that escapes cells
starting with `=`, `+`, `-` or `@`.
## Why it is shaped this way
One column per field, with the repeat split into three plain columns rather
than packed into one, so the file is readable in a spreadsheet and easy for
other tools to produce. Columns are named after the `Todo` fields, so the
header documents itself.
CRLF line endings and minimal quoting are what RFC 4180 describes and what
spreadsheets expect; quoting only when needed keeps the file diff-friendly.
## Edge cases
- No todos: just the header line, still ending in CRLF.
- Notes that are `""` are written as an empty field, the same as null
notes, so they read back as null. Every other valid list reads back exactly:
`importCsv(exportCsv(todos))` equals `todos`.
- Line breaks inside notes (LF, CRLF or a lone CR) are kept inside quotes as
they are.
- It does not validate and never throws: a todo is written as it is, even an
invalid one (a negative order is written `-1`). Validate on the way in, or
let todo.import-csv refuse the file.
- The text is a string; encode it as UTF-8 when saving. No byte order mark is
written (some versions of Excel need one to read UTF-8; add `\uFEFF` in
front if yours does, todo.import-csv accepts it).