-
Notifications
You must be signed in to change notification settings - Fork 0
/
TimeIntervalConverter.cs
142 lines (118 loc) · 4.48 KB
/
TimeIntervalConverter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace System.Text.Json.Converter
{
/// <summary>
/// Converts time intervals from convenient string format to system TimeSpan
/// Be aware, it is implemented for deserialization or read only!
/// Possible delimeters: - _:/'
/// Examples: "1h34m26s134ms"; "1h 12m127s"; "1d/12h 15m 7s"; "3d/12h_15m:12s---347ms"
/// </summary>
public class TimeIntervalConverter : JsonConverter<TimeSpan>
{
public override TimeSpan Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options )
{
var token = reader.GetString()
?? throw new ArgumentNullException( nameof( reader ) );
TimeSpan interval = TimeInterval.Parse( token );
return interval;
}
public override void Write(
Utf8JsonWriter writer,
TimeSpan value,
JsonSerializerOptions options )
{
throw new NotImplementedException();
}
}
[TypeConverter( typeof( TimeIntervalTypeConverter ) )]
public class TimeInterval
{
public readonly TimeSpan Value;
public TimeInterval( TimeSpan tspan )
{
Value = tspan;
}
public static TimeInterval By( TimeSpan tspan )
=> new( tspan );
public static implicit operator TimeSpan( TimeInterval interval )
=> interval.Value;
public static implicit operator TimeInterval( TimeSpan timeSpan )
=> By( timeSpan );
public static readonly Regex Filter =
new( @"(\d+)([dhms]+)[- _:\/']*",
RegexOptions.IgnoreCase
| RegexOptions.Singleline,
TimeSpan.FromMilliseconds( 100 ) );
public enum Unit { None, Day, Hour, Minute, Second, Millisecond }
public static string UngroupInterval( GroupCollection groups )
=> groups[1].Value;
public static string UngroupUnitName( GroupCollection groups )
=> groups[2].Value.Trim();
public static Unit GetUnits( string name )
=> name switch {
"d" => Unit.Day,
"h" => Unit.Hour,
"m" => Unit.Minute,
"s" => Unit.Second,
"ms" => Unit.Millisecond,
_ => Unit.None
};
public static TimeSpan ToTimeSpan( double interval, Unit unit )
=> unit switch {
Unit.Day => TimeSpan.FromDays( interval ),
Unit.Hour => TimeSpan.FromHours( interval ),
Unit.Minute => TimeSpan.FromMinutes( interval ),
Unit.Second => TimeSpan.FromSeconds( interval ),
Unit.Millisecond => TimeSpan.FromMilliseconds( interval ),
_ => throw new ArgumentOutOfRangeException( nameof( unit ) ),
};
public static TimeSpan Parse( string token )
{
TimeSpan result = TimeSpan.Zero;
var classicTimespan =
token.Contains( ':' )
&& TimeSpan.TryParse( token, out result );
if ( classicTimespan ) return result;
var matches = Filter.Matches( token );
foreach ( Match match in matches ) {
double interval =
double.Parse(
UngroupInterval( match.Groups ) );
var units =
GetUnits(
UngroupUnitName( match.Groups ) );
result += ToTimeSpan( interval, units );
}
return result;
}
}
public class TimeIntervalTypeConverter : TypeConverter
{
public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType )
=> sourceType == typeof( string )
|| base.CanConvertFrom( context, sourceType );
public override object ConvertFrom(
ITypeDescriptorContext context,
CultureInfo culture,
object value )
{
if ( value is string token ) {
try {
var timeSpan = TimeInterval.Parse( token );
return TimeInterval.By( timeSpan );
}
catch { }
}
return
base.ConvertFrom( context, culture, value );
}
}
}