4
4
5
5
using System ;
6
6
using System . Collections . Generic ;
7
+ using System . Diagnostics . CodeAnalysis ;
7
8
using System . Globalization ;
8
9
using System . Linq ;
9
10
using SmartFormat . Core . Extensions ;
@@ -24,13 +25,14 @@ public class TimeFormatter : IFormatter
24
25
/// Obsolete. <see cref="IFormatter"/>s only have one unique name.
25
26
/// </summary>
26
27
[ Obsolete ( "Use property \" Name\" instead" , true ) ]
28
+ [ ExcludeFromCodeCoverage ]
27
29
public string [ ] Names { get ; set ; } = { "timespan" , "time" , string . Empty } ;
28
30
29
31
///<inheritdoc/>
30
32
public string Name { get ; set ; } = "time" ;
31
33
32
34
///<inheritdoc/>
33
- public bool CanAutoDetect { get ; set ; } = true ;
35
+ public bool CanAutoDetect { get ; set ; } = false ;
34
36
35
37
#region Constructors
36
38
@@ -60,6 +62,7 @@ public TimeFormatter()
60
62
/// <see cref="TimeFormatter"/> makes use of <see cref="PluralRules"/> and <see cref="PluralLocalizationFormatter"/>.
61
63
/// </remarks>
62
64
[ Obsolete ( "This constructor is not required. Changed process to determine the default culture." , true ) ]
65
+ [ ExcludeFromCodeCoverage ]
63
66
public TimeFormatter ( string defaultTwoLetterLanguageName )
64
67
{
65
68
if ( CommonLanguagesTimeTextInfo . GetTimeTextInfo ( defaultTwoLetterLanguageName ) == null )
@@ -111,6 +114,7 @@ public string FallbackLanguage
111
114
/// 3. The <see cref="CultureInfo.CurrentUICulture"/>.<br/>
112
115
/// </remarks>
113
116
[ Obsolete ( "This property is not supported any more. Changed process to get or set the default culture." , true ) ]
117
+ [ ExcludeFromCodeCoverage ]
114
118
public string DefaultTwoLetterISOLanguageName { get ; set ; } = "en" ;
115
119
116
120
#endregion
@@ -121,19 +125,37 @@ public string FallbackLanguage
121
125
public bool TryEvaluateFormat ( IFormattingInfo formattingInfo )
122
126
{
123
127
var format = formattingInfo . Format ;
124
- var current = formattingInfo . CurrentValue ;
128
+
129
+ // Auto-detection calls just return a failure to evaluate
130
+ if ( string . IsNullOrEmpty ( formattingInfo . Placeholder ? . FormatterName ) )
131
+ return false ;
132
+
133
+ #if NET6_0_OR_GREATER
134
+ if ( formattingInfo . CurrentValue is not ( TimeSpan or DateTime or DateTimeOffset or TimeOnly ) )
135
+ throw new FormattingException ( formattingInfo . Format ? . Items . FirstOrDefault ( ) ,
136
+ $ "'{ nameof ( TimeFormatter ) } ' can only process types of " +
137
+ $ "{ nameof ( TimeSpan ) } , { nameof ( DateTime ) } , { nameof ( DateTimeOffset ) } , { nameof ( TimeOnly ) } , " +
138
+ $ "but not '{ formattingInfo . CurrentValue ? . GetType ( ) } '", 0 ) ;
139
+ #else
140
+ if ( formattingInfo . CurrentValue is not ( TimeSpan or DateTime or DateTimeOffset ) )
141
+ throw new FormattingException ( formattingInfo . Format ? . Items . FirstOrDefault ( ) ,
142
+ $ "'{ nameof ( TimeFormatter ) } ' can only process types of " +
143
+ $ "{ nameof ( TimeSpan ) } , { nameof ( DateTime ) } , { nameof ( DateTimeOffset ) } , " +
144
+ $ "but not '{ formattingInfo . CurrentValue ? . GetType ( ) } '", 0 ) ;
145
+ #endif
125
146
126
147
// Now we have to check for a nested format.
127
148
// That is the one needed for the ListFormatter
128
149
var timeParts = GetTimeParts ( formattingInfo ) ;
129
150
if ( timeParts is null ) return false ;
130
151
131
- if ( format is { Length : > 0 , HasNested : true } )
152
+ if ( format is { Length : > 1 , HasNested : true } )
132
153
{
133
- current = timeParts ; // must be an IList to work with ListFormatter
134
-
135
- format . Items . RemoveAt ( 0 ) ; // That's the format for the TimeFormatter
136
- formattingInfo . FormatAsChild ( format , current ) ;
154
+ // Remove the format for the TimeFormatter
155
+ format . Items . RemoveAt ( 0 ) ;
156
+ // Try to invoke the child format - usually the ListFormatter
157
+ // to format the list of time parts
158
+ formattingInfo . FormatAsChild ( format , timeParts ) ;
137
159
return true ;
138
160
}
139
161
@@ -145,39 +167,28 @@ public bool TryEvaluateFormat(IFormattingInfo formattingInfo)
145
167
{
146
168
var format = formattingInfo . Format ;
147
169
var formatterName = formattingInfo . Placeholder ? . FormatterName ?? string . Empty ;
170
+
148
171
var current = formattingInfo . CurrentValue ;
149
172
150
173
var options = formattingInfo . FormatterOptions . Trim ( ) ;
151
174
var formatText = format ? . RawText . Trim ( ) ?? string . Empty ;
152
175
153
- // Not clear, whether we can process this format
154
- if ( formatterName == string . Empty && options == string . Empty && formatText == string . Empty ) return null ;
155
-
156
176
// In SmartFormat 2.x, the format could be included in options, with empty format.
157
177
// Using compatibility with v2, there is no reliable way to set a language as an option
158
178
var v2Compatibility = options != string . Empty && formatText == string . Empty ;
159
179
var formattingOptions = v2Compatibility ? options : formatText ;
160
180
161
- var fromTime = GetFromTime ( current , formattingOptions ) ;
181
+ var fromTime = GetFromTime ( current ) ;
162
182
163
- if ( fromTime is null )
164
- {
165
- // Auto detection calls just return a failure to evaluate
166
- if ( formatterName == string . Empty )
167
- return null ;
168
-
169
- // throw, if the formatter has been called explicitly
170
- throw new FormatException (
171
- $ "Formatter named '{ formatterName } ' can only process types of { nameof ( TimeSpan ) } , { nameof ( DateTime ) } , { nameof ( DateTimeOffset ) } ") ;
172
- }
183
+ if ( fromTime == null ) return null ;
173
184
174
185
var timeTextInfo = GetTimeTextInfo ( formattingInfo , v2Compatibility ) ;
175
186
176
- var timeSpanFormatOptions = TimeSpanFormatOptionsConverter . Parse ( v2Compatibility ? options : formatText ) ;
187
+ var timeSpanFormatOptions = TimeSpanFormatOptionsConverter . Parse ( formattingOptions ) ;
177
188
return fromTime . Value . ToTimeParts ( timeSpanFormatOptions , timeTextInfo ) ;
178
189
}
179
190
180
- private static TimeSpan ? GetFromTime ( object ? current , string ? formattingOptions )
191
+ private static TimeSpan ? GetFromTime ( object ? current )
181
192
{
182
193
TimeSpan ? fromTime = null ;
183
194
@@ -186,17 +197,16 @@ public bool TryEvaluateFormat(IFormattingInfo formattingInfo)
186
197
case TimeSpan timeSpan :
187
198
fromTime = timeSpan ;
188
199
break ;
200
+ #if NET6_0_OR_GREATER
201
+ case TimeOnly timeOnly :
202
+ fromTime = timeOnly . ToTimeSpan ( ) ;
203
+ break ;
204
+ #endif
189
205
case DateTime dateTime :
190
- if ( formattingOptions != string . Empty )
191
- {
192
- fromTime = SystemTime . Now ( ) . ToUniversalTime ( ) . Subtract ( dateTime . ToUniversalTime ( ) ) ;
193
- }
206
+ fromTime = SystemTime . Now ( ) . ToUniversalTime ( ) . Subtract ( dateTime . ToUniversalTime ( ) ) ;
194
207
break ;
195
208
case DateTimeOffset dateTimeOffset :
196
- if ( formattingOptions != string . Empty )
197
- {
198
- fromTime = SystemTime . OffsetNow ( ) . UtcDateTime . Subtract ( dateTimeOffset . UtcDateTime ) ;
199
- }
209
+ fromTime = SystemTime . OffsetNow ( ) . UtcDateTime . Subtract ( dateTimeOffset . UtcDateTime ) ;
200
210
break ;
201
211
}
202
212
@@ -224,7 +234,7 @@ private TimeTextInfo GetTimeTextInfo(IFormattingInfo formattingInfo, bool v2Comp
224
234
throw new ArgumentException ( $ "{ nameof ( TimeTextInfo ) } could not be found for the given { nameof ( IFormatProvider ) } .", nameof ( formattingInfo ) ) ;
225
235
}
226
236
227
- #endregion
237
+ #endregion
228
238
229
239
private static CultureInfo GetCultureInfo ( IFormattingInfo formattingInfo , bool v2Compatibility )
230
240
{
0 commit comments