diff --git a/lib/mastodon/routes/statuses.js b/lib/mastodon/routes/statuses.js index 6fec78c..267bd29 100644 --- a/lib/mastodon/routes/statuses.js +++ b/lib/mastodon/routes/statuses.js @@ -749,16 +749,36 @@ async function findTimelineItemById(collection, id) { item = await collection.findOne({ published: new Date(publishedDate) }); if (item) return item; - // Try date-range lookup for timezone-offset stored strings (+01:00 etc.) - // Some AP servers emit non-UTC dates; decodeCursor normalizes to UTC but - // the stored string may differ. Search a ±1 s window using a regex on the - // ms value, or simply try the raw numeric id as a direct uid/url lookup. + // Try ±1 s range lookup for timezone-offset stored strings (+01:00 etc.) + // and BSON Date fields. The UTC-ISO string range query used above fails when + // the stored value has a non-UTC timezone — "2026-03-21T16:33:50+01:00" is + // lexicographically outside ["2026-03-21T15:33:50Z", "2026-03-21T15:33:51Z"]. + // $dateFromString parses any ISO 8601 format (including offsets) to a Date, + // $toLong converts it to ms-since-epoch, and the numeric range always matches. const ms = Number.parseInt(id, 10); if (ms > 0) { - const lo = new Date(ms - 999).toISOString().replace(/\.999Z$/, "Z"); - const hi = new Date(ms + 999).toISOString().replace(/\.999Z$/, "Z"); + const lo = new Date(ms - 999); + const hi = new Date(ms + 999); item = await collection.findOne({ - published: { $gte: lo, $lte: hi }, + $or: [ + // BSON Date stored (Micropub pipeline) — direct Date range comparison + { published: { $gte: lo, $lte: hi } }, + // String stored with any timezone format — parse via $dateFromString + { + $expr: { + $and: [ + { $gte: [ + { $toLong: { $dateFromString: { dateString: "$published", onError: 0, onNull: 0 } } }, + ms - 999, + ] }, + { $lte: [ + { $toLong: { $dateFromString: { dateString: "$published", onError: 0, onNull: 0 } } }, + ms + 999, + ] }, + ], + }, + }, + ], }); if (item) return item; }