Add fetchInternalDate; fix RFC 3501 zone rendering in date-time output - #124
Open
jappeace-sloth wants to merge 2 commits into
Open
Add fetchInternalDate; fix RFC 3501 zone rendering in date-time output#124jappeace-sloth wants to merge 2 commits into
jappeace-sloth wants to merge 2 commits into
Conversation
fetchInternalDate reads a message's INTERNALDATE as a CalendarTime, via the new stringToDatetimeIMAP (the inverse of datetimeToStringIMAP; accepts the quoted form and space-padded days). Motivation: appendFull can already write a message with an explicit date, but nothing in the library could read one, so copying mail from one account to another (a provider migration) lost every message's date. While adding the inverse, the round-trip exposed a bug in datetimeToStringIMAP's zone rendering: the offset-in-seconds was divided by 3600 and zero-padded to four digits, so +0100 was emitted as +0001 and negative offsets produced garbage like -000-1. The zone is now rendered as (+/-)HHMM per RFC 3501; show4 had no other users and is removed. Tests: HUnit cases for rendering (including a negative half-hour zone), parsing, the round-trip, space-padded days and rejection of malformed input, plus a scripted-connection test for fetchInternalDate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stringToDatetimeIMAP now returns Either DatetimeParseError CalendarTime, with a constructor per failure mode, each carrying the offending fragment; fetchInternalDate returns IO (Either FetchInternalDateError CalendarTime) instead of calling fail, so the caller decides both the failure behaviour and the wording. Tests assert exact error values for malformed dates, a signless zone and an unknown month, plus the absent-INTERNALDATE path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
We migrate small webshops between hosting providers, and when a shop leaves its old platform the bundled mailbox is terminated with the subscription. To preserve the owner's mail we copy every message from the old account into the new provider's mailbox over IMAP — admittedly a slightly unusual use of an IMAP client library, but one we actually ran into.
appendFullalready lets us write a message with its original flags and an explicit date, but nothing in the library could read a message's INTERNALDATE, so the copied mail lost all its dates. We ended up hand-rolling a minimal client for this one gap; this PR upstreams the missing piece so we can use HaskellNet instead.What's added
fetchInternalDate :: IMAPConnection -> UID -> IO (Either FetchInternalDateError CalendarTime)— reads a message's INTERNALDATE, built onfetchByByteStringlike the other fetchers. Failures (InternalDateAbsent,InternalDateInvalid) are returned, not thrown, so the caller decides what a missing or malformed date means.stringToDatetimeIMAP :: String -> Either DatetimeParseError CalendarTime— the inverse ofdatetimeToStringIMAP; accepts the quoted wire form and space-padded single-digit days per the RFC 3501 grammar.DatetimeParseErrorhas a constructor per failure mode, each carrying the offending fragment. Both functions and both error types are exported, sofetchInternalDate's result can be fed straight back intoappendFull.What's fixed
Adding the inverse exposed a bug in
datetimeToStringIMAP's zone rendering: the offset-in-seconds was divided by 3600 and zero-padded to four digits, so UTC+1 was emitted as+0001(i.e. one minute) and negative offsets produced garbage like-000-1. AnyappendFullwith a non-UTCCalendarTimesent a malformed or wrong date-time. The zone now renders as(+/-)HHMMper RFC 3501;show4had no remaining users and is removed.Tests
HUnit cases for rendering (including a negative half-hour zone), parsing, the render/parse round-trip, space-padded days, and rejection of malformed input, plus a scripted-connection test for
fetchInternalDatein the style of the existing fetch API tests. All 64 cases pass (cabal test, GHC 9.10.3).