# Date Format Normalizer

Paste text with dates in mixed formats and get them all rewritten to ISO 8601 or any format you pick, with the day/month order settled from the text. Free and private.

## Run it

- **CLI:** `gizza tool date-format-normalizer "Order 4471 was placed 03/04/2024 and shipped 15/04/2024.
The invoice is dated 22 April 2024 and falls due 2024-05-06.
Support ticket opened Friday, 3 May 2024 at 2:30 PM."`
- **Web:** https://gizza.ai/tools/date-format-normalizer/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/date-format-normalizer/tool.json

## Inputs

- `text` — Text (dates can be in any mix of formats) _(field)_
- `output_format` — Rewrite every date as _(field)_
- `custom_format` — Custom pattern (only used by the custom format) _(field)_
- `separator` — Separator between the numeric fields _(field)_
- `month_style` — Month name written as _(field)_
- `year_style` — Year written as _(field)_
- `leading_zeros` — Pad single-digit days and months with a zero _(field)_
- `input_order` — Reading a numeric date like 03/04/2024 _(field)_
- `two_digit_year_pivot` — Two-digit years at or below this become 20xx _(field)_
- `keep_time` — Keep the clock time when a date has one _(field)_
- `time_style` — Clock time shown as _(field)_
- `output_timezone` — Move dates that carry an offset to (IANA name, UTC, or an offset) _(field)_
- `detect_timestamps` — Also read bare 10- and 13-digit numbers as unix timestamps _(field)_
- `output_mode` — Give me back _(field)_

## Output

- Normalized text (text)

## Query parameters

Open the tool pre-filled and auto-run via URL:

- `text` — Text (dates can be in any mix of formats)
- `output_format` — Rewrite every date as
- `custom_format` — Custom pattern (only used by the custom format)
- `separator` — Separator between the numeric fields
- `month_style` — Month name written as
- `year_style` — Year written as
- `leading_zeros` — Pad single-digit days and months with a zero
- `input_order` — Reading a numeric date like 03/04/2024
- `two_digit_year_pivot` — Two-digit years at or below this become 20xx
- `keep_time` — Keep the clock time when a date has one
- `time_style` — Clock time shown as
- `output_timezone` — Move dates that carry an offset to (IANA name, UTC, or an offset)
- `detect_timestamps` — Also read bare 10- and 13-digit numbers as unix timestamps
- `output_mode` — Give me back

Example: `https://gizza.ai/tools/date-format-normalizer/?text=Order%204471%20was%20placed%2003%2F04%2F2024%20and%20shipped%2015%2F04%2F2024.%0AThe%20invoice%20is%20dated%2022%20April%202024%20and%20falls%20due%202024-05-06.%0ASupport%20ticket%20opened%20Friday%2C%203%20May%202024%20at%202%3A30%20PM.&output_format=iso&custom_format=%25d.%25m.%25Y&separator=dash&month_style=full&year_style=four&leading_zeros=true&input_order=auto&two_digit_year_pivot=68&keep_time=true&time_style=24h&output_timezone=source&detect_timestamps=true&output_mode=text`

---

## About this tool

Real text almost never uses one date format. A document that passed through a spreadsheet, an
export, an email thread and someone's handwriting ends up with `03/04/2024`, `15 April 2024`,
`2024-04-22` and `5.1.24` sitting in the same paragraph. This tool finds every one of them and
rewrites them into the single format you pick, leaving the rest of the text byte-for-byte
untouched.

The interesting part is not the rendering — it is deciding what `03/04/2024` means. Instead of
guessing per date, the tool reads the whole text first and lets the dates that can only be one
thing settle it for the rest: a single `15/04/2024` in the paste proves the source writes
day-first, so `03/04/2024` is 3 April, not 4 March. When nothing in the text settles it, the
month-first reading is used and the **report** mode tells you exactly which dates were guessed.

### Worked example

Input:

```
Order 4471 was placed 03/04/2024 and shipped 15/04/2024.
The invoice is dated 22 April 2024 and falls due 2024-05-06.
Support ticket opened Friday, 3 May 2024 at 2:30 PM.
```

Output with the default ISO 8601 format:

```
Order 4471 was placed 2024-04-03 and shipped 2024-04-15.
The invoice is dated 2024-04-22 and falls due 2024-05-06.
Support ticket opened 2024-05-03T14:30.
```

Three source forms, one target form. `15/04/2024` settled the day-first reading for
`03/04/2024`; the weekday name was absorbed into the date it belonged to; the clock time came
along with its date; and the sentence's full stop survived.

Switch the format to **Day first** with a **dot** separator and the same text renders
`03.04.2024`, `15.04.2024`, `22.04.2024`, `06.05.2024`. Switch to **Year first** with the
separator set to **none** and `scan 5 Jan 2024.pdf` becomes `scan 20240105.pdf` — the stamp that
makes a folder of files sort itself.

### What it recognizes

| Form | Examples |
| --- | --- |
| ISO 8601 | `2024-01-05`, `2024-1-5`, `2024-01-05T14:30:00Z`, `2024-01-05 14:30` |
| Numeric | `01/05/2024`, `5.1.2024`, `5-1-24`, `2024/01/05` |
| Month name | `January 5, 2024`, `Jan. 5th, 2024`, `5 Jan 2024`, `5th of January 2024` |
| With a weekday | `Friday, 5 January 2024`, `Fri, 05 Jan 2024 14:30:00 +0100` |
| Unix epoch | `1704465000`, `1704465000123` — only when you switch timestamp detection on |

A clock time written next to a date is picked up with it, including the `at 2:30 PM` phrasing and
a trailing `Z`, `UTC` or `+01:00`.

### Output formats

ISO 8601 · year-first, day-first and month-first numeric (with your choice of dash, slash, dot,
space or no separator) · full or abbreviated month names in either order · RFC 2822 email dates ·
Unix seconds or milliseconds · or any chrono/strftime pattern you want, such as `%B %-d, %Y` or
`%Y%m%d`.

Three ways to get the result back: the **text** with the dates rewritten in place, a **list** of
just the normalized values one per line for pasting into a spreadsheet column, or a **report**
giving each date's line and column, what was found, what it became, and whether the day/month
order had to be guessed.

### Limits and edge cases

- Up to **1,000,000 bytes** of text per run. Split anything larger and run the parts separately.
- Strings that look like dates but are not — `2024-02-30`, `13/13/2024`, a version number like
  `1.2.2024` where no valid reading exists — are left exactly as written, never guessed at.
- Bare 10- and 13-digit numbers are only read as timestamps when you ask for it, and then only
  when they land between 1973 and 2100. Order numbers and phone numbers stay untouched.
- Two-digit years need a century: the pivot decides it, defaulting to the POSIX rule where 68 and
  below mean 20xx.
- The timezone setting moves only the dates that carry an explicit offset. A date written with no
  zone has nothing to convert from, so it stays where it is.
- Everything runs in your browser. No upload, no account, no network round-trip, and the same
  input always produces the same output.

## FAQ

<details>
<summary>How does it decide whether 03/04/2024 is 3 April or 4 March?</summary>

It reads the whole text before rewriting anything. Any numeric date with a field above 12 can
only be read one way — `15/04/2024` must be day-first, `04/15/2024` must be month-first — and
those dates set the reading for every ambiguous date in the same text. If the text contains no
such date, or contradicts itself, the month-first reading is used and the **report** output mode
flags every date that was guessed, so you can check them instead of trusting them. You can also
skip the inference entirely by setting the reading to **Day first** or **Month first** yourself.

</details>

<details>
<summary>Does it change anything other than the dates?</summary>

No. In **text** mode the output is the input with each detected date span swapped for its
rewritten form; every other character, including line breaks and punctuation, is passed through
untouched. Two things are deliberately absorbed into the date they belong to: a weekday name
written in front of it (`Friday, 5 January 2024` becomes one date) and a clock time written after
it. A sentence-ending full stop after `2:30 pm.` is kept — only the `pm` is treated as part of
the time.

</details>

<details>
<summary>What happens to times and timezones?</summary>

A time found next to a date travels with it and is rendered on the 24-hour or 12-hour clock, with
seconds shown only when the source had them. Turn **Keep the clock time** off to reduce
everything to bare dates. The timezone field moves the dates that carry an explicit offset — an
ISO stamp ending in `Z` or `+01:00`, an RFC 2822 date, a detected epoch value — into UTC, an IANA
zone such as `Europe/Berlin` with daylight saving applied per date, or a fixed offset. That can
change the calendar day, which is the point. Dates written without any zone are left exactly
where they are.

</details>

<details>
<summary>Can I get a format that is not in the dropdown?</summary>

Yes — choose the **custom** format and write a strftime pattern. `%d.%m.%Y` gives `05.01.2024`,
`%B %-d, %Y` gives `January 5, 2024`, `%Y%m%d` gives `20240105`, `%A %d %b %y` gives
`Friday 05 Jan 24`. The common fields are `%Y`/`%y` for the year, `%m`/`%d` for month and day
(`%-m`/`%-d` without the leading zero), `%B`/`%b` for month names, `%A` for the weekday,
`%H:%M:%S` for a 24-hour clock, `%I:%M %p` for a 12-hour one and `%z` for the offset. An invalid
pattern is reported rather than half-rendered.

</details>

<details>
<summary>Why are my order numbers not being converted?</summary>

Because timestamp detection is off by default. Most long numbers in real text are order ids,
account references or phone numbers, and turning every 10-digit number into a date would ruin
more documents than it fixes. Switch **Also read bare 10- and 13-digit numbers as unix
timestamps** on when you are working with log exports or API payloads where epoch values really
are dates — and note that even then only values between 1973 and 2100 are accepted.

</details>

<details>
<summary>How do I check what it actually did?</summary>

Set the output to **report**. You get a `#` header with how many dates were found, the mix of
source forms detected, and which day/month order was chosen and why, followed by one
tab-separated line per date: its line and column in the source, the original string, the
rewritten value, and an `ambiguous` marker when the day/month order had to be inferred. It pastes
straight into a spreadsheet if you want to review a large document column by column.

</details>

## Related tools

- [Absolute value, sign, or negation for a whole column](https://gizza.ai/tools/absolute-value-transformer/): Paste a column of numbers and apply absolute value, sign extraction (-1/0/1), sign flipping, or force-negative to every value at once, with rounding and an audit table.
- [Adjacency Matrix Converter](https://gizza.ai/tools/adjacency-matrix-converter/): Convert a graph between edge list, adjacency matrix, and incidence matrix — directed or undirected, weighted or not. Free, private, runs in your browser.
- [Amazon Order Analyzer](https://gizza.ai/tools/amazon-order-analyzer/): Paste an Amazon order-history CSV export to summarize total spend by month, top items, and category breakdowns. Browser-only, private, with Markdown or JSON output.
- [ARFF Converter](https://gizza.ai/tools/arff-converter/): Convert Weka ARFF datasets to CSV and CSV tables back to ARFF locally — nominal attributes, numeric types, dates, sparse rows, missing values, and type rows.
- [Avro to JSON Converter](https://gizza.ai/tools/avro-to-json/): Decode Apache Avro Object Container Files (.avro / OCF) to JSON, NDJSON, or the embedded schema — no .avsc needed, free and private in your browser.
