Email response contains invalid ISO-8601 date with month=00 in receivedAt

Issue Description

Summary

Stalwart’s JMAP server occasionally emits ISO-8601 / RFC 3339 timestamps with a
00 month component in the Email object (observed on receivedAt). This is
not a valid ISO-8601 date — month must be in the range 0112 per
ISO-8601 §5.2.1 — and standards-compliant JMAP clients reject the entire
response.

Reproducer

A normal Email/get round-trip against Stalwart returns a payload along the
lines of:

{
  "methodResponses": [[
    "Email/get",
    {
      "accountId": "...",
      "list": [{
        "id": "...",
        "receivedAt": "2026-00-07T19:14:51Z",
        ...
      }]
    },
    "0"
  ]]
}

Note the 2026-00-07T19:14:51Z — month component is 00.

Expected behavior

Per ISO-8601 / RFC 3339 the month component must be 01-12. Either the
internally stored timestamp should be valid, or the serializer must reject /
correct the out-of-range value.

Actual behavior

The malformed string is sent verbatim. Java clients using
java.time.OffsetDateTime.parse(...) (which all standards-compliant Java
implementations rely on, including the rs.ltt.jmap.gson reference adapter)
fail with:

java.time.format.DateTimeParseException: Text '2026-00-07T19:14:51Z' could not be parsed:
  Invalid value for MonthOfYear (valid values 1 - 12): 0

The exception aborts deserialization of the whole Email/get response, so
no messages from the affected mailbox can be listed in the client.

Suggested fix

Most likely an off-by-one in the date generation path (tm_mon is 0-based in
some C/Rust APIs, calendar months are 1-based). Auditing the
Email-serialization code for any bare month field that hasn't been
incremented from 0 to 1 should locate the source.

Impact

Any JMAP client that uses a strict ISO-8601 parser (i.e. all of them, per
spec) cannot list/show messages from a Stalwart mailbox once a single message
carries such a corrupt timestamp.

Workaround on the client side

We've shipped a tolerant OffsetDateTime adapter in our JMAP client that
substitutes null for unparseable values rather than crashing the whole
response, but the underlying data is still corrupt and should be fixed
server-side.

### Expected Behavior
Email response contains valid ISO-8601 

### Actual Behavior
Email response contains invalid ISO-8601 





### Stalwart Version
v0.16.x

### Installation Method
Docker

### Database Backend
RocksDB

### Blob Storage
RocksDB

### Search Engine
Internal

### Directory Backend
Internal



### I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on

### I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on

### I have set the logging level to trace and included relevant log output if applicable.
on

### I agree to follow the project's Code of Conduct.
on

This looks like a genuine bug. Based on the source code, UTCDateTime is used throughout the server for receivedAt serialization, and the report’s hypothesis about an off-by-one in the month field is well-reasoned. Here’s a summary for the community:


This is a valid bug report. The receivedAt field in Email/get responses is being serialized with a 00 month component (e.g. 2026-00-07T19:14:51Z), which is illegal per ISO-8601 / RFC 3339 — valid months are 0112. Any spec-compliant JMAP client (including all Java clients using java.time.OffsetDateTime) will reject the entire Email/get response when it encounters such a value.

Root cause (likely): The UTCDateTime type in crates/registry/src/types/datetime.rs stores a month: u8 field. The bug is almost certainly an off-by-one error in the from_timestamp conversion path — one code path produces month values in the range 0–11 (typical of C’s tm_mon) without adding 1 before storing or serializing.

Impact: All messages with a corrupt timestamp become inaccessible to standard JMAP clients, since the malformed date aborts deserialization of the whole response.

Recommended action: Please open a GitHub issue on the Stalwart server repository with this report if you haven’t already — this is a server-side data integrity bug that needs a fix in the UTCDateTime::from_timestamp implementation. Include the reproduction steps and the exact Stalwart version (v0.16.x). The workaround of a tolerant client-side adapter is sensible in the interim, but the stored timestamps themselves may also be corrupt (not just the serialization), so a data migration pass may be needed after the fix is shipped.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

Fixed.