Handle invalid dates more gracefully

Closes #766
This commit is contained in:
Tyrrrz 2022-01-05 18:54:23 +02:00
parent 81525a1076
commit e31ff55e33
4 changed files with 23 additions and 12 deletions

View file

@ -159,12 +159,18 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp)
{ {
var dateString = timestamp.Date is not null
? _context.FormatDate(timestamp.Date.Value)
: "Invalid date";
// Timestamp tooltips always use full date regardless of the configured format // Timestamp tooltips always use full date regardless of the configured format
var longDateString = timestamp.Value.ToLocalString("dddd, MMMM d, yyyy h:mm tt"); var longDateString = timestamp.Date is not null
? timestamp.Date.Value.ToLocalString("dddd, MMMM d, yyyy h:mm tt")
: "Invalid date";
_buffer _buffer
.Append($"<span class=\"timestamp\" title=\"{HtmlEncode(longDateString)}\">") .Append($"<span class=\"timestamp\" title=\"{HtmlEncode(longDateString)}\">")
.Append(HtmlEncode(_context.FormatDate(timestamp.Value))) .Append(HtmlEncode(dateString))
.Append("</span>"); .Append("</span>");
return base.VisitUnixTimestamp(timestamp); return base.VisitUnixTimestamp(timestamp);

View file

@ -73,7 +73,9 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor
protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp)
{ {
_buffer.Append( _buffer.Append(
_context.FormatDate(timestamp.Value) timestamp.Date is not null
? _context.FormatDate(timestamp.Date.Value)
: "Invalid date"
); );
return base.VisitUnixTimestamp(timestamp); return base.VisitUnixTimestamp(timestamp);

View file

@ -235,7 +235,7 @@ internal static partial class MarkdownParser
// Capture <t:12345678> or <t:12345678:R> // Capture <t:12345678> or <t:12345678:R>
private static readonly IMatcher<MarkdownNode> UnixTimestampNodeMatcher = new RegexMatcher<MarkdownNode>( private static readonly IMatcher<MarkdownNode> UnixTimestampNodeMatcher = new RegexMatcher<MarkdownNode>(
new Regex("<t:(\\d+)(?::\\w)?>", DefaultRegexOptions), new Regex("<t:(-?\\d+)(?::\\w)?>", DefaultRegexOptions),
(_, m) => (_, m) =>
{ {
// TODO: support formatting parameters // TODO: support formatting parameters
@ -244,17 +244,19 @@ internal static partial class MarkdownParser
if (!long.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, if (!long.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
out var offset)) out var offset))
{ {
return null; return new UnixTimestampNode(null);
} }
// Bound check try
// https://github.com/Tyrrrz/DiscordChatExporter/issues/681
if (offset < TimeSpan.MinValue.TotalSeconds || offset > TimeSpan.MaxValue.TotalSeconds)
{ {
return null; return new UnixTimestampNode(DateTimeOffset.UnixEpoch + TimeSpan.FromSeconds(offset));
}
// https://github.com/Tyrrrz/DiscordChatExporter/issues/681
// https://github.com/Tyrrrz/DiscordChatExporter/issues/766
catch (Exception ex) when (ex is ArgumentOutOfRangeException or OverflowException)
{
return new UnixTimestampNode(null);
} }
return new UnixTimestampNode(DateTimeOffset.UnixEpoch + TimeSpan.FromSeconds(offset));
} }
); );

View file

@ -2,4 +2,5 @@
namespace DiscordChatExporter.Core.Markdown; namespace DiscordChatExporter.Core.Markdown;
internal record UnixTimestampNode(DateTimeOffset Value) : MarkdownNode; // Null means invalid date
internal record UnixTimestampNode(DateTimeOffset? Date) : MarkdownNode;