{
  "slug": "csv-date-normalizer",
  "name": "gizza-ai/csv-date-normalizer",
  "version": "0.1.0",
  "title": "CSV Date Normalizer — Convert Mixed Date Formats to ISO 8601 — gizza.ai",
  "description": "Normalize a CSV date column to ISO 8601, Unix time, SQL, or a custom pattern. Infers day-first vs month-first per column. Free, runs in your browser.",
  "tags": [
    "csv date normalizer",
    "normalize csv dates",
    "convert csv dates to iso 8601",
    "fix mixed date formats",
    "dd/mm vs mm/dd csv",
    "excel serial date to iso",
    "unix timestamp to date csv",
    "standardize date column"
  ],
  "category": "data",
  "urls": {
    "page": "https://gizza.ai/tools/csv-date-normalizer/",
    "markdown": "https://gizza.ai/tools/csv-date-normalizer/index.md",
    "descriptor": "https://gizza.ai/tools/csv-date-normalizer/tool.json",
    "deep_link_example": "https://gizza.ai/tools/csv-date-normalizer/?data=id%2Cjoined%0A1%2C2021-06-01%0A2%2C06%2F15%2F2021%0A3%2C15%20Jan%202024%0A4%2Cnot%20a%20date&columns=auto&format=iso-auto&custom_format=%25d%20%25B%20%25Y&date_order=auto&year_pivot=68&excel_serial=true&on_error=keep&has_header=true&delimiter=auto&output=csv"
  },
  "cli": "gizza tool csv-date-normalizer \"id,joined\n1,2021-06-01\n2,06/15/2021\n3,15 Jan 2024\n4,not a date\"",
  "tool": {
    "description": "Normalize the dates in one or more CSV columns to a single format. A column holding 2021-06-01, 06/15/2021, 15 Jan 2024, Tue, 15 Jan 2024 10:30:00 +0000, 45000 and 1700000000 comes back as one consistent ISO 8601 column. The hard part is 03/04/2024, and this tool does not guess per cell: it reads the whole column first, uses the rows that can only be one thing (a day above 12) to settle day-first vs month-first for that column, and reports which it chose and why — including a 'conflict' verdict when the same column proves BOTH orders. Recognised inputs include ISO 8601 with T/Z/offsets, slash- dot- and dash-separated numerics, written month names with ordinals (Jan 15th, 2024), leading weekday names, 12-hour times with am/pm, two-digit years via a configurable century pivot, compact 20240115 / 20240115103000, Unix epoch seconds and milliseconds, and Excel 1900-system serials. columns takes header names and/or 0-based indexes, or 'auto' to detect date columns (auto never claims a column of bare numbers, so timestamps and Excel serials must be named). format is iso-auto (default, keeps each value's precision), iso-date, iso-datetime, iso-utc, unix-seconds, unix-millis, us-date, eu-date, sql, compact, rfc2822, or custom with a strftime custom_format. date_order forces day-first or month-first; year_pivot sets the two-digit-year cut-off (68 by default); excel_serial can be turned off so numeric columns are left alone; on_error keeps, blanks, or errors on an unreadable cell; has_header and delimiter describe the table; output is csv, a plain-text report, or json. Only the named columns are touched — other columns, the header, quoting and row order are unchanged, and impossible calendar dates like 2021-02-30 are refused rather than rolled over. Max 5,000,000 bytes. Runs entirely in the sandbox; nothing is uploaded.",
    "parameters": {
      "additionalProperties": false,
      "properties": {
        "columns": {
          "default": "auto",
          "description": "Which columns to normalize: a comma-separated list of header names and/or 0-based indexes ('joined', 'start,2'), or 'auto' (default) to detect them. Auto-detection claims a column when at least 60% of its non-blank cells parse as a WRITTEN date, and deliberately never claims a column of bare numbers — so Unix timestamps and Excel serials must be named explicitly.",
          "type": "string"
        },
        "custom_format": {
          "default": "",
          "description": "The strftime/chrono pattern used when format is 'custom' — for example '%d %B %Y' gives 15 January 2024, '%b %-d, %Y' gives Jan 15, 2024, '%Y/%m/%d %H:%M' gives 2024/01/15 10:30. Required (and only read) for format = 'custom'; an unknown specifier is rejected rather than emitted literally.",
          "type": "string"
        },
        "data": {
          "description": "The CSV/delimited table to normalize, as text. Only the chosen date columns are rewritten — every other column, the header row, the quoting and the row order are passed through unchanged. Max 5,000,000 bytes.",
          "type": "string"
        },
        "date_order": {
          "default": "auto",
          "description": "How to read an all-numeric date whose two leading parts are both 12 or less, like 03/04/2024. 'auto' (default) decides PER COLUMN from the rows that can only be one thing — a single 25/12/2021 in the column proves day-first for every other value in it — and falls back to month-first when the column holds no such proof, reporting that as the reason. 'day-first' reads 03/04/2024 as 4 March; 'month-first' reads it as 3 April. Values that settle themselves (ISO, written month names) ignore this setting.",
          "enum": [
            "auto",
            "day-first",
            "month-first"
          ],
          "type": "string"
        },
        "delimiter": {
          "default": "auto",
          "description": "Field separator: 'auto' (default) sniffs it from the first non-blank line, or give a name ('comma', 'tab', 'semicolon', 'pipe') or any single character. The output uses the same separator as the input.",
          "type": "string"
        },
        "excel_serial": {
          "default": true,
          "description": "When true (default), a bare number between 1 and 2958465 in a named column is read as an Excel 1900-system serial (45000 -> 2023-03-15), with a fractional part becoming the time of day. Turn it off when such a column holds real numbers — quantities, ids, amounts — that must not be reinterpreted as dates. Unix epoch seconds/milliseconds are recognised regardless of this flag, by magnitude.",
          "type": "boolean"
        },
        "format": {
          "default": "iso-auto",
          "description": "Target output format. 'iso-auto' (default) keeps each value's precision — 2024-01-15 for a date-only cell, 2024-01-15T10:30:00Z when the cell carried a time; 'iso-date' -> 2024-01-15; 'iso-datetime' -> 2024-01-15T10:30:00 (offset dropped); 'iso-utc' -> 2024-01-15T05:00:00Z (shifted to UTC); 'unix-seconds' / 'unix-millis' -> epoch integers; 'us-date' -> 01/15/2024; 'eu-date' -> 15/01/2024; 'sql' -> 2024-01-15 10:30:00; 'compact' -> 20240115; 'rfc2822' -> Mon, 15 Jan 2024 10:30:00 +0000; 'custom' uses custom_format.",
          "enum": [
            "iso-auto",
            "iso-date",
            "iso-datetime",
            "iso-utc",
            "unix-seconds",
            "unix-millis",
            "us-date",
            "eu-date",
            "sql",
            "compact",
            "rfc2822",
            "custom"
          ],
          "type": "string"
        },
        "has_header": {
          "default": true,
          "description": "When true (default), row 1 is a header: it is never rewritten and its names can be used in 'columns'. Turn it off for a headerless table — every row is then treated as data and columns must be given as 0-based indexes.",
          "type": "boolean"
        },
        "on_error": {
          "default": "keep",
          "description": "What to do with a cell that cannot be read as a date: 'keep' (default) leaves the original text in place and counts it in the report, 'blank' empties the cell, 'error' aborts the whole run naming the first offending row, line and column. Blank cells are never touched under any setting.",
          "enum": [
            "keep",
            "blank",
            "error"
          ],
          "type": "string"
        },
        "output": {
          "default": "csv",
          "description": "What to return: 'csv' (default) is the rewritten table; 'report' is a plain-text audit — per column the day/month order used and WHY, plus counts of converted / already-normalized / unreadable / blank cells and the first 20 unreadable values with their row and line numbers; 'json' is the same audit as a JSON object with the rewritten table under 'csv'.",
          "enum": [
            "csv",
            "report",
            "json"
          ],
          "type": "string"
        },
        "year_pivot": {
          "default": 68,
          "description": "Century cut-off for two-digit years: a value at or below the pivot becomes 20xx, above it becomes 19xx. The default 68 is the POSIX/Excel rule, so 68 -> 2068 and 69 -> 1969. Set it to 99 to force every two-digit year into the 2000s, or to 0 to force them all into the 1900s.",
          "maximum": 99,
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "data"
      ],
      "type": "object"
    }
  }
}