@@ -26,63 +26,85 @@ class TimestampTokenRenderer : OutputTemplateTokenRenderer
2626{
2727 readonly ConsoleTheme _theme ;
2828 readonly PropertyToken _token ;
29+ readonly string ? _format ;
2930 readonly IFormatProvider ? _formatProvider ;
31+ readonly bool _convertToUtc ;
3032
31- public TimestampTokenRenderer ( ConsoleTheme theme , PropertyToken token , IFormatProvider ? formatProvider )
33+ public TimestampTokenRenderer ( ConsoleTheme theme , PropertyToken token , IFormatProvider ? formatProvider , bool convertToUtc )
3234 {
33- _theme = theme ;
34- _token = token ;
35- _formatProvider = formatProvider ;
36- }
35+ _theme = theme ;
36+ _token = token ;
37+ _format = token . Format ;
38+ _formatProvider = formatProvider ;
39+ _convertToUtc = convertToUtc ;
40+ }
3741
3842 public override void Render ( LogEvent logEvent , TextWriter output )
3943 {
40- var sv = new DateTimeOffsetValue ( logEvent . Timestamp ) ;
41-
42- var _ = 0 ;
43- using ( _theme . Apply ( output , ConsoleThemeStyle . SecondaryText , ref _ ) )
44+ var _ = 0 ;
45+ using ( _theme . Apply ( output , ConsoleThemeStyle . SecondaryText , ref _ ) )
46+ {
47+ if ( _token . Alignment is null )
4448 {
45- if ( _token . Alignment is null )
46- {
47- sv . Render ( output , _token . Format , _formatProvider ) ;
48- }
49- else
50- {
51- var buffer = new StringWriter ( ) ;
52- sv . Render ( buffer , _token . Format , _formatProvider ) ;
53- var str = buffer . ToString ( ) ;
54- Padding . Apply ( output , str , _token . Alignment ) ;
55- }
49+ Render ( output , logEvent . Timestamp ) ;
50+ }
51+ else
52+ {
53+ var buffer = new StringWriter ( ) ;
54+ Render ( buffer , logEvent . Timestamp ) ;
55+ var str = buffer . ToString ( ) ;
56+ Padding . Apply ( output , str , _token . Alignment ) ;
5657 }
5758 }
59+ }
5860
59- readonly struct DateTimeOffsetValue
61+ private void Render ( TextWriter output , DateTimeOffset timestamp )
6062 {
61- public DateTimeOffsetValue ( DateTimeOffset value )
62- {
63- Value = value ;
64- }
63+ // When a DateTimeOffset is converted to a string, the default format automatically adds the "+00:00" explicit offset to the output string.
64+ // As the TimestampTokenRenderer is also used for rendering the UtcTimestamp which is always in UTC by definition, the +00:00 suffix should be avoided.
65+ // This is done using the same approach as Serilog's MessageTemplateTextFormatter. In case output should be converted to UTC, in order to avoid a zone specifier,
66+ // the DateTimeOffset is converted to a DateTime which then renders as expected.
6567
66- public DateTimeOffset Value { get ; }
68+ var custom = ( ICustomFormatter ? ) _formatProvider ? . GetFormat ( typeof ( ICustomFormatter ) ) ;
69+ if ( custom != null )
70+ {
71+ output . Write ( custom . Format ( _format , _convertToUtc ? timestamp . UtcDateTime : timestamp , _formatProvider ) ) ;
72+ return ;
73+ }
6774
68- public void Render ( TextWriter output , string ? format = null , IFormatProvider ? formatProvider = null )
75+ if ( _convertToUtc )
6976 {
70- var custom = ( ICustomFormatter ? ) formatProvider ? . GetFormat ( typeof ( ICustomFormatter ) ) ;
71- if ( custom != null )
72- {
73- output . Write ( custom . Format ( format , Value , formatProvider ) ) ;
74- return ;
75- }
77+ RenderDateTime ( output , timestamp . UtcDateTime ) ;
78+ }
79+ else
80+ {
81+ RenderDateTimeOffset ( output , timestamp ) ;
82+ }
83+ }
7684
85+ private void RenderDateTimeOffset ( TextWriter output , DateTimeOffset timestamp )
86+ {
7787#if FEATURE_SPAN
78- Span < char > buffer = stackalloc char [ 32 ] ;
79- if ( Value . TryFormat ( buffer , out int written , format , formatProvider ?? CultureInfo . InvariantCulture ) )
80- output . Write ( buffer . Slice ( 0 , written ) ) ;
81- else
82- output . Write ( Value . ToString ( format , formatProvider ?? CultureInfo . InvariantCulture ) ) ;
88+ Span < char > buffer = stackalloc char [ 32 ] ;
89+ if ( timestamp . TryFormat ( buffer , out int written , _format , _formatProvider ?? CultureInfo . InvariantCulture ) )
90+ output . Write ( buffer . Slice ( 0 , written ) ) ;
91+ else
92+ output . Write ( timestamp . ToString ( _format , _formatProvider ?? CultureInfo . InvariantCulture ) ) ;
8393#else
84- output . Write ( Value . ToString ( format , formatProvider ?? CultureInfo . InvariantCulture ) ) ;
94+ output . Write ( timestamp . ToString ( _format , _formatProvider ?? CultureInfo . InvariantCulture ) ) ;
95+ #endif
96+ }
97+
98+ private void RenderDateTime ( TextWriter output , DateTime utcTimestamp )
99+ {
100+ #if FEATURE_SPAN
101+ Span < char > buffer = stackalloc char [ 32 ] ;
102+ if ( utcTimestamp . TryFormat ( buffer , out int written , _format , _formatProvider ?? CultureInfo . InvariantCulture ) )
103+ output . Write ( buffer . Slice ( 0 , written ) ) ;
104+ else
105+ output . Write ( utcTimestamp . ToString ( _format , _formatProvider ?? CultureInfo . InvariantCulture ) ) ;
106+ #else
107+ output . Write ( utcTimestamp . ToString ( _format , _formatProvider ?? CultureInfo . InvariantCulture ) ) ;
85108#endif
86- }
87109 }
88110}
0 commit comments