Exporting Chats

Why WhatsApp Export Parsers Break: 14 Formats Tested

Why a WhatsApp export parser that works on one phone fails on another, with real format examples, safer architecture, code and a 24-case benchmark.

WhatsApp export parser pipeline from localized timestamps through normalization, matching and validated messages
Visual guide to WhatsApp export parser

A WhatsApp export parser usually breaks because the timestamp at the start of each message is not one universal format. Brackets, separators, date order, seconds, AM/PM position, numeral script and invisible direction marks can all change with the phone and locale. A parser that recognizes only DD/MM/YY, HH:MM - Name: text may appear correct in development and then merge an entire German, US, Dutch or Arabic export into one message.

Why one WhatsApp timestamp regex fails

The first parser I wrote had the usual shape: split the file into lines, run one regex against each line, and treat anything that matched as a new message. It worked against the sample in front of me. That was the problem. The sample came from one phone with one regional setting.

This pattern is a reasonable first experiment for an Android export using a slash date and an English AM/PM marker:

const header = /^(\d{1,2}\/\d{1,2}\/\d{2,4}),\s+(\d{1,2}:\d{2})\s+(AM|PM)\s+-\s+([^:]+):\s+(.*)$/i;

It matches:

3/15/24, 2:30 PM - Researcher: Hello

It does not match any of these valid shapes from the public fixture set:

[2024/07/09, 08:01:49] Researcher: Morning
15.03.2024, 14:30 - Researcher: Guten Tag
[15-03-2024, 14:30:01] Researcher: Hoi
[15/3/24 下午 2:30:45] Researcher: 你好
2026-01-13, 12:22 PM - Researcher: Hello
WhatsApp export parser regex matching one US timestamp while rejecting European, ISO and CJK examples
The same message header changes shape with device and locale.

When a header fails to match, the common continuation-line rule appends it to the preceding message. That makes the bug quiet. Instead of throwing an error, the parser can report one enormous message with thousands of lines. A count check is therefore as important as the regex itself.

The exported file is plain text, but its structure depends on the exporting environment. The academic ChatDashboard parsing description reports the same underlying issue: operating system, language, date settings and time settings affect the exported log structure. WhatsApp's official export instructions explain how to create the file; they do not promise one developer-facing timestamp schema.

The timestamp families a parser meets

The 2026.07 benchmark groups 14 layouts by structural family. The point is not that 14 is a permanent total. It is that a production parser needs explicit coverage and fixtures rather than an assumption that one example represents the format.

WhatsApp export parser format matrix covering bracketed, dashed, dotted, slash, ISO and CJK timestamp families
Fourteen published timestamp layouts in the 2026.07 fixture set.
Family
iOS year-first slash
Example
[2024/07/09, 08:01:49] Name: text
Structural difference
Brackets, seconds, year first, no dash
Family
iOS slash + AM/PM
Example
[3/15/24, 2:30:45 PM] Name: text
Structural difference
Month/day ambiguity and optional seconds
Family
Android slash
Example
3/15/24, 2:30 PM - Name: text
Structural difference
No brackets and a dash before sender
Family
European dotted
Example
15.03.2024, 14:30 - Name: text
Structural difference
Dots, day first and 24-hour time
Family
Dutch dashed
Example
15-03-2024 14:30 - Name: text
Structural difference
Dash date and no comma
Family
ISO year-first
Example
2026-01-13, 12:22 PM - Name: text
Structural difference
Year first with dash separators
Family
CJK marker
Example
[15/3/24 下午 2:30:45] Name: text
Structural difference
Day-period marker appears before time

Some families differ only in punctuation. Others change the meaning of the numeric fields. Bracket detection alone is not enough: iOS can emit bracketed slash, dotted and dash-date forms, while non-bracketed Android-style forms can use slash, dash or year-first dates.

This is also why a permissive expression such as ^(.+?), (.+?) - (.+?): (.*)$ is not a dependable fix. It accepts more lines, but it pushes all the difficult work into later guesses and can mistake message text for a header. A parser should be tolerant about known equivalent syntax and strict about the boundary that declares a new message.

Normalize before matching

Regular expressions are easier to reason about after equivalent characters have one representation. The input can contain characters that look familiar on screen but are not the ASCII characters in your pattern.

The published parser performs a normalization pass before trying timestamp families:

  1. Remove carriage returns and selected directional control marks around headers.
  2. Convert narrow no-break and non-breaking spaces to ordinary spaces.
  3. Map localized AM/PM markers to AM or PM.
  4. Translate Arabic-Indic, Persian, Devanagari and Thai numerals to ASCII digits.
  5. Normalize punctuation such as the Arabic comma where it appears in the header.
WhatsApp export parser normalization of directional marks, locale digits, spacing, commas and AM PM markers
Normalize equivalent symbols before matching structural patterns.

Here is a reduced JavaScript version of the digit step:

function translateDigits(text) {
  return [...text].map((char) => {
    const point = char.codePointAt(0);
    if (point >= 0x0660 && point <= 0x0669) return String(point - 0x0660); // Arabic-Indic
    if (point >= 0x06f0 && point <= 0x06f9) return String(point - 0x06f0); // Persian
    if (point >= 0x0966 && point <= 0x096f) return String(point - 0x0966); // Devanagari
    if (point >= 0x0e50 && point <= 0x0e59) return String(point - 0x0e50); // Thai
    return char;
  }).join('');
}

For example, the Arabic-Indic header ١٥/٠٣/٢٤, ١٤:٣٠ becomes 15/03/24, 14:30 before structural matching. The message body remains unchanged. Normalization should be scoped: rewriting the entire message body can damage the content you were asked to preserve.

Normalization reduces the number of patterns, but it does not remove date-order ambiguity. It makes symbols comparable; it does not tell you whether 03/04/24 means 3 April or March 4.

Use ordered format families

The parser uses an ordered list of sender and system-message patterns. More specific shapes are tested before more general shapes. A match returns the date string, time string, optional day-period marker, sender and body, plus a pattern type that controls date construction.

Conceptually, the loop looks like this:

for (const pattern of timestampPatterns) {
  const senderMatch = line.match(pattern.senderRegex);
  if (senderMatch) return parseSenderMessage(senderMatch, pattern.type);

  const systemMatch = line.match(pattern.systemRegex);
  if (systemMatch) return parseSystemMessage(systemMatch, pattern.type);
}
return null; // continuation text, preamble or unsupported header

Pattern order matters. A broad slash-date expression placed first can consume a more specific four-digit Brazilian form and apply the wrong month/day rule. A sender pattern should also run before a corresponding system pattern so the colon in Name: message is not swallowed as generic system text.

Keep timestamp recognition separate from message classification. Once the header boundary is known, the body can be classified as text, media, call, deleted or system using its own localized markers. Combining every possible body type into the timestamp regex makes the structural expression difficult to test and easy to break.

Treat ambiguous numeric dates honestly

15/03/24 is not ambiguous because 15 cannot be a month. 03/15/24 is equally clear. 03/04/24 is not. Without context, both 3 April and March 4 are valid interpretations.

WhatsApp export parser decision tree for ambiguous day month values such as 03 slash 04 slash 24
Some dates cannot be resolved from a single line alone.

The published parser uses structural context and a limited heuristic:

  • If the first number is above 12, treat it as the day.
  • If the second number is above 12, treat it as the day.
  • If both are 12 or below and an English AM/PM marker is present, prefer the US month-first interpretation.
  • Otherwise prefer day-first for the generic slash family.

This is useful, not infallible. The right production behavior is to retain the raw line, record the detected pattern and make ambiguous dates auditable. For evidence, research or compliance work, compare the result with the source device and surrounding messages. The parser should not turn a heuristic into a certainty claim.

A stronger application can detect locale from a sample of unambiguous dates across the file, then apply the inferred order to ambiguous dates. Even then, keep a warning when the sample provides no decisive values.

Parse messages as state, not isolated lines

A WhatsApp message can contain line breaks. Only its first line has a timestamp header. The following lines must be appended to the current message until another recognized header begins.

3/15/24, 2:30 PM - Alice: first line
second line
third line
3/15/24, 2:31 PM - Bob: next message
WhatsApp export parser state machine joining multiline text and separating sender messages from system events
Lines without a new header belong to the current message.

A minimal state machine is clearer than splitting on a regex:

const messages = [];
let current = null;

for (const line of normalizedText.split('\n')) {
  const header = recognizeHeader(line);
  if (header) {
    if (current) messages.push(current);
    current = header;
  } else if (current) {
    current.text += `\n${line}`;
  }
}
if (current) messages.push(current);

This state also gives you a place to handle preamble text, blank lines and unsupported headers deliberately. The first unmatched line should not create a phantom message. An unmatched line after a valid message is continuation text. A run of thousands of unmatched lines is a diagnostic signal that header recognition probably failed.

System messages need a separate path because they have a timestamp but no sender/body colon. Participant joins, group changes and encryption notices are not sender-attributed chat messages, yet dropping them changes message counts and chronology. The benchmark includes one system case, one multiline case and participant extraction across two senders for that reason.

How the 24-case benchmark works

The WhatsApp Export Parser Benchmark 2026 is intentionally small enough to inspect. It contains no customer messages and no private chats. Every fixture is synthetic and declares its input plus expected structured fields.

WhatsApp export parser benchmark method using 24 synthetic fixtures across Node.js and Python runtimes
The same expected fields are asserted in both runtimes.

The suite currently contains:

  • 14 timestamp cases covering bracketed and non-bracketed layouts, slash/dot/dash separators, year-first dates, seconds, and day-period placement.
  • 4 normalization cases using Arabic-Indic, Persian, Devanagari and Thai numerals.
  • 6 behavior cases for multiline messages, system messages, media markers, deleted messages, call markers and participant extraction.

Both runners read the same fixture JSON. They assert message count and, when applicable, participants, sender, text, type, year, month, day, hour and minute. A failure produces a non-zero exit status, which makes the benchmark usable in CI.

{
  "id": "timestamp-android-slash",
  "input": "3/15/24, 2:30 PM - Researcher: Hello",
  "expected": {
    "messageCount": 1,
    "sender": "Researcher",
    "year": 2024,
    "month": 3,
    "day": 15,
    "hour": 14,
    "type": "text"
  }
}

Node.js 0.1.1 and Python 0.1.1 each passed 24 of 24 published cases. Read that result narrowly. It does not prove support for every device, app version, locale, malformed ZIP or future WhatsApp change. It proves that the two published implementations satisfy the explicit expectations in this public fixture set. You can download the fixture JSON, case results CSV and full summary.

A safer implementation shape

If you need a parser rather than a finished PDF, the open-source package exposes the same approach in Node and Python. The source, runners and release artifacts are available in the whatsapp-chat-to-pdf GitHub repository.

Node.js:

npm install whatsapp-chat-to-pdf
import fs from 'node:fs';
import { parseText } from 'whatsapp-chat-to-pdf';

const input = fs.readFileSync('_chat.txt', 'utf8');
const chat = parseText(input);

console.log(chat.messageCount);
console.log(chat.participants);
console.log(chat.messages[0]);

Python:

pip install whatsapp-chat-to-pdf
from whatsapp_chat_to_pdf import parse_file

chat = parse_file("WhatsApp Chat with Alice.zip")
print(chat.message_count)
print(chat.participants)
print(chat.messages[0])

The implementation sequence is the transferable part:

  1. Find the real chat text inside a TXT or ZIP, ignoring macOS __MACOSX and ._ resource-fork entries.
  2. Normalize structural Unicode variants without rewriting the message body unnecessarily.
  3. Recognize an ordered timestamp family and retain the raw input line.
  4. Build the timestamp with an explicit rule for that family.
  5. Stitch continuation lines into the current message.
  6. Classify the message body separately.
  7. Extract participants from non-system senders.
  8. Assert fields against synthetic fixtures in every supported runtime.

If you are inspecting the export before writing code, the extract WhatsApp chat data guide explains the fields inside _chat.txt. The export chat meaning guide explains what the ZIP is and is not. For a formatted media-rich result rather than a parser library, the WhatsApp to PDF guide covers the hosted workflow.

Production checklist and limitations

WhatsApp export parser production checklist for normalization, patterns, classification, fixtures and limitations
A practical review list before trusting parsed output.

Before trusting a WhatsApp export parser, check these properties:

  • Format coverage is named. “Supports WhatsApp” is too broad. Publish the layouts or fixtures you actually test.
  • Normalization is tested independently. A format test written only with ASCII digits will not catch localized-numeral failures.
  • Raw input is retained. When a timestamp is ambiguous or classification looks wrong, the original line is needed for review.
  • Counts are plausible. Zero, one or an unexpectedly small number of messages should trigger a diagnostic rather than a confident empty output.
  • Multiline content survives. A continuation line should stay with its message and preserve its newline.
  • System messages are explicit. Do not attribute group events to the previous participant.
  • ZIP selection is defensive. Ignore resource forks and prefer the recognized chat filename over the first .txt entry.
  • Failures are visible. Record unsupported header samples using synthetic or safely redacted examples; never upload a private conversation to a public issue.
  • Claims stay bounded. Passing a fixture suite is not proof of export completeness, authorship, authenticity or legal admissibility.

The current benchmark does not measure speed, memory consumption, PDF fidelity, attachment matching or voice transcription. It does not statistically sample all WhatsApp versions. Those are separate tests. A useful next contribution is a new synthetic fixture representing a genuinely different header shape, accompanied by the expected parsed fields and a source description that does not expose private chat content.

Reproduce before you trust

Download the fixtures, run both public benchmark commands, then add a synthetic case shaped like the export you expect to receive. A parser is reliable to the extent that its assumptions are visible and repeatedly tested.

FAQ

What format is a WhatsApp exported chat?

WhatsApp exports one conversation as a plain-text chat file, optionally inside a ZIP with media. Message headers commonly contain a localized timestamp followed by a sender and message body, but punctuation, ordering and day-period markers differ across device and locale. Use WhatsApp's official Export Chat flow to create the file; treat the parser schema as something your own fixtures must verify.

Can one regex parse every WhatsApp export?

Not safely. One broad regex can accept many header shapes, but it cannot by itself resolve date order, localized digits, day-period markers, system events and multiline continuation. Use normalization plus ordered structural families, then construct dates according to the matched family and validate the result with field-level fixtures.

How do I parse multiline WhatsApp messages?

Read the export as a stateful stream. A line with a recognized timestamp header starts a new message. A non-header line after a message is appended to that current message with its newline preserved. Flush the current message when the next header appears and again at end of file.

Why does a parser swap the day and month?

Purely numeric slash dates are ambiguous when both values are 12 or below. Use unambiguous dates elsewhere in the file, structural markers and device context to infer the order, but retain the raw line and surface the assumption. For sensitive work, verify the interpreted date against the source device and surrounding conversation.

Is the 24 out of 24 benchmark result universal?

No. It means Node.js 0.1.1 and Python 0.1.1 passed the 24 synthetic fixtures published in benchmark version 2026.07. It does not cover every WhatsApp release, locale or damaged export and it makes no claim about authorship, authenticity, completeness or admissibility.

Key takeaways

  • A WhatsApp export parser needs multiple explicit timestamp families because brackets, separators, date order, seconds and day-period position vary.
  • Normalize structural Unicode variants and four tested numeral scripts before matching; do not rewrite the preserved message body unnecessarily.
  • Treat numeric day/month ambiguity as a visible assumption, not a fact inferred from one line.
  • Parse with state so multiline messages and system events remain separate and message counts stay meaningful.
  • The public benchmark reports 24 of 24 for two runtimes only against its 24 synthetic cases; fixtures and limitations are downloadable.
  • Production reliability comes from named assumptions, raw-line retention, diagnostics and regression fixtures rather than one permissive regex.