From f6fe338011c6ce3351db9a45b3d81daf4012cfc7 Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Tue, 22 Jul 2025 16:40:20 -0300 Subject: [PATCH 1/3] chore: Make timestamp accept ISO time --- packages/message-parser/src/grammar.pegjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/message-parser/src/grammar.pegjs b/packages/message-parser/src/grammar.pegjs index e69fcd86f4efe..3e665f3ebeb05 100644 --- a/packages/message-parser/src/grammar.pegjs +++ b/packages/message-parser/src/grammar.pegjs @@ -79,8 +79,9 @@ TimestampType = "t" / "T" / "d" / "D" / "f" / "F" / "R" Unixtime = d:Digit |10| { return d.join(''); } -Timestamp = "" { return timestamp(date, format); } / "" { return timestamp(date); } +ISO8601Date = year:Digit |4| "-" month:Digit |2| "-" day:Digit |2| "T" hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "." milliseconds:Digit |3| "Z" { return new Date(year.join('') + '-' + month.join('') + '-' + day.join('') + 'T' + hours.join('') + ':' + minutes.join('') + ':' + seconds.join('') + '.' + milliseconds.join('') + 'Z').getTime(); } +Timestamp = "" { return timestamp(date, format); } / "" { return timestamp(date); } /** * * Code Chunk From cc8f0db8cad4a4571751b12ce6173a9538396461 Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Tue, 22 Jul 2025 18:35:26 -0300 Subject: [PATCH 2/3] test: unit test & adjust --- packages/message-parser/src/grammar.pegjs | 2 +- packages/message-parser/tests/timestamp.test.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/message-parser/src/grammar.pegjs b/packages/message-parser/src/grammar.pegjs index 3e665f3ebeb05..6fc32f3042edc 100644 --- a/packages/message-parser/src/grammar.pegjs +++ b/packages/message-parser/src/grammar.pegjs @@ -79,7 +79,7 @@ TimestampType = "t" / "T" / "d" / "D" / "f" / "F" / "R" Unixtime = d:Digit |10| { return d.join(''); } -ISO8601Date = year:Digit |4| "-" month:Digit |2| "-" day:Digit |2| "T" hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "." milliseconds:Digit |3| "Z" { return new Date(year.join('') + '-' + month.join('') + '-' + day.join('') + 'T' + hours.join('') + ':' + minutes.join('') + ':' + seconds.join('') + '.' + milliseconds.join('') + 'Z').getTime(); } +ISO8601Date = year:Digit |4| "-" month:Digit |2| "-" day:Digit |2| "T" hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "." milliseconds:Digit |3| "Z" { return new Date(year.join('') + '-' + month.join('') + '-' + day.join('') + 'T' + hours.join('') + ':' + minutes.join('') + ':' + seconds.join('') + '.' + milliseconds.join('') + 'Z').getTime().toString(); } Timestamp = "" { return timestamp(date, format); } / "" { return timestamp(date); } /** diff --git a/packages/message-parser/tests/timestamp.test.ts b/packages/message-parser/tests/timestamp.test.ts index bb9b72655896e..9b1bcef3966a9 100644 --- a/packages/message-parser/tests/timestamp.test.ts +++ b/packages/message-parser/tests/timestamp.test.ts @@ -25,3 +25,16 @@ test.each([ ])('parses %p', (input, output) => { expect(parse(input)).toMatchObject(output); }); + +test.each([ + [ + '', + [paragraph([timestamp('1753178400000', 'R')])], + ], + [ + '', + [paragraph([timestamp('1753178400000', 'R')])], + ], +])('parses %p', (input, output) => { + expect(parse(input)).toMatchObject(output); +}); From 67eb39935efc6da181e035305f5735762adb56b8 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Thu, 24 Jul 2025 10:55:35 -0300 Subject: [PATCH 3/3] feat: Add support for parsing timestamps from hours and minutes --- packages/message-parser/src/grammar.pegjs | 24 ++++++++++++--- packages/message-parser/src/utils.ts | 14 +++++++++ .../message-parser/tests/timestamp.test.ts | 30 ++++++++++++++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/packages/message-parser/src/grammar.pegjs b/packages/message-parser/src/grammar.pegjs index 6fc32f3042edc..0173a2e089eea 100644 --- a/packages/message-parser/src/grammar.pegjs +++ b/packages/message-parser/src/grammar.pegjs @@ -33,6 +33,7 @@ tasks, unorderedList, timestamp, + timestampFromHours, } = require('./utils'); let skipBold = false; @@ -74,14 +75,29 @@ Blockquote = b:BlockquoteLine+ { return quote(b); } BlockquoteLine = ">" [ \t]* @Paragraph // +// +// +// TimestampType = "t" / "T" / "d" / "D" / "f" / "F" / "R" Unixtime = d:Digit |10| { return d.join(''); } -ISO8601Date = year:Digit |4| "-" month:Digit |2| "-" day:Digit |2| "T" hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "." milliseconds:Digit |3| "Z" { return new Date(year.join('') + '-' + month.join('') + '-' + day.join('') + 'T' + hours.join('') + ':' + minutes.join('') + ':' + seconds.join('') + '.' + milliseconds.join('') + 'Z').getTime().toString(); } +TimestampHoursMinutesSeconds = hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "Z"? { return timestampFromHours(hours.join(''), minutes.join(''), seconds.join('')); } + +TimestampHoursMinutes = hours:Digit |2| ":" minutes:Digit|2| "Z"? { return timestampFromHours(hours.join(''), minutes.join('')); } + + +Timestamp = TimestampHoursMinutesSeconds / TimestampHoursMinutes + + +ISO8601Date = year:Digit |4| "-" month:Digit |2| "-" day:Digit |2| "T" hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "." milliseconds:Digit |3| "Z"? { return new Date(year.join('') + '-' + month.join('') + '-' + day.join('') + 'T' + hours.join('') + ':' + minutes.join('') + ':' + seconds.join('') + '.' + milliseconds.join('') + 'Z').getTime().toString(); } + +ISO8601DateWithoutMilliseconds = year:Digit |4| "-" month:Digit |2| "-" day:Digit |2| "T" hours:Digit |2| ":" minutes:Digit|2| ":" seconds:Digit |2| "Z"? { return new Date(year.join('') + '-' + month.join('') + '-' + day.join('') + 'T' + hours.join('') + ':' + minutes.join('') + ':' + seconds.join('') + 'Z').getTime().toString(); } + + +TimestampRules = "" { return timestamp(date, format); } / "" { return timestamp(date); } -Timestamp = "" { return timestamp(date, format); } / "" { return timestamp(date); } /** * * Code Chunk @@ -231,7 +247,7 @@ InlineEmoji = & { return !skipInlineEmoji; } emo:Emoji { return emo; } InlineEmoticon = & { return !skipInlineEmoji; } emo:Emoticon & (EmoticonNeighbor / InlineItemPattern) { skipInlineEmoji = false; return emo; } InlineItemPattern = Whitespace - / Timestamp + / TimestampRules / MaybeReferences / AutolinkedPhone / AutolinkedEmail @@ -495,7 +511,7 @@ BoldEmoticon = & { return !skipBoldEmoji; } emo:Emoticon & (EmoticonNeighbor / B /* Strike */ Strikethrough = [\x7E] [\x7E] @StrikethroughContent [\x7E] [\x7E] / [\x7E] @StrikethroughContent [\x7E] -StrikethroughContent = text:(Timestamp / Whitespace / InlineCode / MaybeReferences / UserMention / ChannelMention / MaybeItalic / MaybeBold / Emoji / Emoticon / AnyStrike / Line)+ { +StrikethroughContent = text:(TimestampRules / Whitespace / InlineCode / MaybeReferences / UserMention / ChannelMention / MaybeItalic / MaybeBold / Emoji / Emoticon / AnyStrike / Line)+ { return strike(reducePlainTexts(text)); } diff --git a/packages/message-parser/src/utils.ts b/packages/message-parser/src/utils.ts index 5a52fad9b887c..3bf9b8f689c80 100644 --- a/packages/message-parser/src/utils.ts +++ b/packages/message-parser/src/utils.ts @@ -251,6 +251,20 @@ export const timestamp = ( }; }; +export const timestampFromHours = ( + hours: string, + minutes = '00', + seconds = '00', +) => { + const date = new Date(); + + const yearMonthDay = date.toISOString().split('T')[0]; + + return new Date(`${yearMonthDay}T${hours}:${minutes}:${seconds}Z`) + .getTime() + .toString(); +}; + export const extractFirstResult = ( value: Types[keyof Types]['value'], ): Types[keyof Types]['value'] => { diff --git a/packages/message-parser/tests/timestamp.test.ts b/packages/message-parser/tests/timestamp.test.ts index 9b1bcef3966a9..1073125aacc2e 100644 --- a/packages/message-parser/tests/timestamp.test.ts +++ b/packages/message-parser/tests/timestamp.test.ts @@ -1,5 +1,12 @@ import { parse } from '../src'; -import { bold, paragraph, plain, strike, timestamp } from '../src/utils'; +import { + bold, + paragraph, + plain, + strike, + timestamp, + timestampFromHours, +} from '../src/utils'; test.each([ [``, [paragraph([timestamp('1708551317')])]], @@ -35,6 +42,27 @@ test.each([ '', [paragraph([timestamp('1753178400000', 'R')])], ], + [ + '', + [paragraph([timestamp('1753178400000', 'R')])], + ], + ['', [paragraph([timestamp('1753178400000', 'R')])]], + [ + '', + [paragraph([timestamp(timestampFromHours('10', '00', '00'), 'R')])], + ], + [ + '', + [paragraph([timestamp(timestampFromHours('10', '00', '00'), 'R')])], + ], + [ + '', + [paragraph([timestamp(timestampFromHours('10', '00', '00'), 't')])], + ], + [ + '', + [paragraph([timestamp(timestampFromHours('10', '00', '00'), 't')])], + ], ])('parses %p', (input, output) => { expect(parse(input)).toMatchObject(output); });