16
16
using System . Text ;
17
17
using ServiceStack . Text . Json ;
18
18
using ServiceStack . Text . Support ;
19
+ using System . Text . RegularExpressions ;
19
20
20
21
namespace ServiceStack . Text . Common
21
22
{
@@ -24,11 +25,14 @@ public static class DateTimeSerializer
24
25
public const string CondensedDateTimeFormat = "yyyyMMdd" ; //8
25
26
public const string ShortDateTimeFormat = "yyyy-MM-dd" ; //11
26
27
public const string DefaultDateTimeFormat = "dd/MM/yyyy HH:mm:ss" ; //20
28
+ public const string DefaultDateTimeFormatWithFraction = "dd/MM/yyyy HH:mm:ss.fff" ; //24
27
29
public const string XsdDateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ" ; //29
28
30
public const string XsdDateTimeFormat3F = "yyyy-MM-ddTHH:mm:ss.fffZ" ; //25
29
31
public const string XsdDateTimeFormatSeconds = "yyyy-MM-ddTHH:mm:ssZ" ; //21
30
32
public const string DateTimeFormatSecondsUtcOffset = "yyyy-MM-ddTHH:mm:sszzz" ; //22
33
+ public const string DateTimeFormatSecondsNoOffset = "yyyy-MM-ddTHH:mm:ss" ;
31
34
public const string DateTimeFormatTicksUtcOffset = "yyyy-MM-ddTHH:mm:ss.fffffffzzz" ; //30
35
+ public const string DateTimeFormatTicksNoUtcOffset = "yyyy-MM-ddTHH:mm:ss.fffffff" ;
32
36
33
37
public const string EscapedWcfJsonPrefix = "\\ /Date(" ;
34
38
public const string EscapedWcfJsonSuffix = ")\\ /" ;
@@ -41,20 +45,25 @@ public static class DateTimeSerializer
41
45
private static readonly int XsdTimeSeparatorIndex = XsdDateTimeFormat . IndexOf ( XsdTimeSeparator ) ;
42
46
private const string XsdUtcSuffix = "Z" ;
43
47
private static readonly char [ ] DateTimeSeperators = new [ ] { '-' , '/' } ;
44
-
48
+ private static readonly Regex UtcOffsetInfoRegex = new Regex ( "([+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])" , RegexOptions . Compiled ) ;
45
49
public static Func < string , Exception , DateTime > OnParseErrorFn { get ; set ; }
46
50
47
51
/// <summary>
48
- /// If AlwaysUseUtc is set to true then convert all DateTime to UTC.
52
+ /// If AlwaysUseUtc is set to true then convert all DateTime to UTC. If PreserveUtc is set to true then UTC dates will not convert to local
49
53
/// </summary>
50
54
/// <param name="dateTime"></param>
51
55
/// <returns></returns>
52
56
public static DateTime Prepare ( this DateTime dateTime , bool parsedAsUtc = false )
53
57
{
58
+ if ( JsConfig . SkipDateTimeConversion )
59
+ {
60
+ return dateTime ;
61
+ }
54
62
if ( JsConfig . AlwaysUseUtc )
55
63
{
56
64
return dateTime . Kind != DateTimeKind . Utc ? dateTime . ToStableUniversalTime ( ) : dateTime ;
57
65
}
66
+
58
67
return parsedAsUtc ? dateTime . ToLocalTime ( ) : dateTime ;
59
68
}
60
69
@@ -91,6 +100,16 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
91
100
return unspecifiedDate . Prepare ( ) ;
92
101
}
93
102
103
+ if ( dateTimeStr . Length == DefaultDateTimeFormatWithFraction . Length )
104
+ {
105
+ var unspecifiedDate = JsConfig . AssumeUtc
106
+ ? DateTime . Parse ( dateTimeStr , CultureInfo . InvariantCulture , DateTimeStyles . AssumeUniversal )
107
+ : DateTime . Parse ( dateTimeStr , CultureInfo . InvariantCulture ) ;
108
+
109
+ return unspecifiedDate . Prepare ( ) ;
110
+ }
111
+ DateTimeKind kind = DateTimeKind . Unspecified ;
112
+
94
113
switch ( JsConfig . DateHandler )
95
114
{
96
115
case DateHandler . UnixTime :
@@ -103,6 +122,12 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
103
122
if ( long . TryParse ( dateTimeStr , out unixTimeMs ) )
104
123
return unixTimeMs . FromUnixTimeMs ( ) ;
105
124
break ;
125
+ case DateHandler . ISO8601 :
126
+ if ( JsConfig . SkipDateTimeConversion )
127
+ {
128
+ dateTimeStr = RemoveUtcOffsets ( dateTimeStr , out kind ) ;
129
+ }
130
+ break ;
106
131
}
107
132
108
133
dateTimeStr = RepairXsdTimeSeparator ( dateTimeStr ) ;
@@ -139,8 +164,21 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
139
164
140
165
try
141
166
{
142
- var assumeKind = JsConfig . AssumeUtc ? DateTimeStyles . AssumeUniversal : DateTimeStyles . AssumeLocal ;
143
- var dateTime = DateTime . Parse ( dateTimeStr , CultureInfo . InvariantCulture , assumeKind ) ;
167
+ DateTime dateTime ;
168
+ if ( JsConfig . SkipDateTimeConversion )
169
+ {
170
+ dateTime = DateTime . Parse ( dateTimeStr , null ,
171
+ kind == DateTimeKind . Unspecified ?
172
+ DateTimeStyles . None :
173
+ kind == DateTimeKind . Local ?
174
+ DateTimeStyles . AssumeLocal :
175
+ DateTimeStyles . AssumeUniversal ) ;
176
+ }
177
+ else
178
+ {
179
+ var assumeKind = JsConfig . AssumeUtc ? DateTimeStyles . AssumeUniversal : DateTimeStyles . AssumeLocal ;
180
+ dateTime = DateTime . Parse ( dateTimeStr , CultureInfo . InvariantCulture , assumeKind ) ;
181
+ }
144
182
return dateTime . Prepare ( ) ;
145
183
}
146
184
catch ( FormatException )
@@ -161,6 +199,18 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
161
199
}
162
200
}
163
201
202
+ private static string RemoveUtcOffsets ( string dateTimeStr , out DateTimeKind kind )
203
+ {
204
+ var startOfTz = UtcOffsetInfoRegex . Match ( dateTimeStr ) ;
205
+ if ( startOfTz . Index > 0 )
206
+ {
207
+ kind = DateTimeKind . Local ;
208
+ return dateTimeStr . Substring ( 0 , startOfTz . Index ) ;
209
+ }
210
+ kind = dateTimeStr . Contains ( "Z" ) ? DateTimeKind . Utc : DateTimeKind . Unspecified ;
211
+ return dateTimeStr ;
212
+ }
213
+
164
214
/// <summary>
165
215
/// Repairs an out-of-spec XML date/time string which incorrectly uses a space instead of a 'T' to separate the date from the time.
166
216
/// These string are occasionally generated by SQLite and can cause errors in OrmLite when reading these columns from the DB.
@@ -180,7 +230,7 @@ private static string RepairXsdTimeSeparator(string dateTimeStr)
180
230
181
231
public static DateTime ? ParseManual ( string dateTimeStr )
182
232
{
183
- var dateKind = JsConfig . AssumeUtc || JsConfig . AlwaysUseUtc
233
+ var dateKind = JsConfig . AssumeUtc || JsConfig . AlwaysUseUtc
184
234
? DateTimeKind . Utc
185
235
: DateTimeKind . Local ;
186
236
@@ -201,6 +251,7 @@ private static string RepairXsdTimeSeparator(string dateTimeStr)
201
251
if ( dateTimeStr . EndsWith ( XsdUtcSuffix ) )
202
252
{
203
253
dateTimeStr = dateTimeStr . Substring ( 0 , dateTimeStr . Length - 1 ) ;
254
+ dateKind = JsConfig . SkipDateTimeConversion ? DateTimeKind . Utc : dateKind ;
204
255
}
205
256
206
257
var parts = dateTimeStr . Split ( 'T' ) ;
@@ -320,7 +371,7 @@ public static DateTimeOffset ParseDateTimeOffset(string dateTimeOffsetStr)
320
371
if ( Env . IsMono )
321
372
{
322
373
// Without that Mono uses a Local timezone))
323
- dateTimeOffsetStr = dateTimeOffsetStr . Substring ( 0 , dateTimeOffsetStr . Length - 1 ) + "+00:00" ;
374
+ dateTimeOffsetStr = dateTimeOffsetStr . Substring ( 0 , dateTimeOffsetStr . Length - 1 ) + "+00:00" ;
324
375
}
325
376
}
326
377
@@ -364,7 +415,7 @@ public static TimeSpan ParseTimeSpan(string dateTimeStr)
364
415
{
365
416
return dateTimeStr . StartsWith ( "P" , StringComparison . Ordinal ) || dateTimeStr . StartsWith ( "-P" , StringComparison . Ordinal )
366
417
? ParseXsdTimeSpan ( dateTimeStr )
367
- : dateTimeStr . Contains ( ":" )
418
+ : dateTimeStr . Contains ( ":" )
368
419
? TimeSpan . Parse ( dateTimeStr )
369
420
: ParseNSTimeInterval ( dateTimeStr ) ;
370
421
}
@@ -399,11 +450,26 @@ public static string ToShortestXsdDateTimeString(DateTime dateTime)
399
450
var timeOfDay = dateTime . TimeOfDay ;
400
451
401
452
var isStartOfDay = timeOfDay . Ticks == 0 ;
402
- if ( isStartOfDay )
453
+ if ( isStartOfDay && ! ( JsConfig . SkipDateTimeConversion ) )
403
454
return dateTime . ToString ( ShortDateTimeFormat ) ;
404
455
405
- var hasFractionalSecs = ( timeOfDay . Milliseconds != 0 )
406
- || ( ( timeOfDay . Ticks % TimeSpan . TicksPerMillisecond ) != 0 ) ;
456
+ var hasFractionalSecs = ( timeOfDay . Milliseconds != 0 )
457
+ || ( ( timeOfDay . Ticks % TimeSpan . TicksPerMillisecond ) != 0 ) ;
458
+ if ( JsConfig . SkipDateTimeConversion )
459
+ {
460
+ if ( ! hasFractionalSecs )
461
+ return dateTime . Kind == DateTimeKind . Local
462
+ ? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
463
+ : dateTime . Kind == DateTimeKind . Unspecified
464
+ ? dateTime . ToString ( DateTimeFormatSecondsNoOffset )
465
+ : dateTime . ToStableUniversalTime ( ) . ToString ( XsdDateTimeFormatSeconds ) ;
466
+
467
+ return dateTime . Kind == DateTimeKind . Local
468
+ ? dateTime . ToString ( DateTimeFormatTicksUtcOffset )
469
+ : dateTime . Kind == DateTimeKind . Unspecified
470
+ ? dateTime . ToString ( DateTimeFormatTicksNoUtcOffset )
471
+ : PclExport . Instance . ToXsdDateTimeString ( dateTime ) ;
472
+ }
407
473
if ( ! hasFractionalSecs )
408
474
return dateTime . Kind != DateTimeKind . Utc
409
475
? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
@@ -528,7 +594,15 @@ public static void WriteWcfJsonDate(TextWriter writer, DateTime dateTime)
528
594
529
595
if ( JsConfig . DateHandler == DateHandler . ISO8601 )
530
596
{
597
+ if ( ! JsConfig . SkipDateTimeConversion )
598
+ {
531
599
writer . Write ( dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
600
+ }
601
+ else
602
+ {
603
+ var dt = dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ;
604
+ writer . Write ( dt ) ;
605
+ }
532
606
return ;
533
607
}
534
608
0 commit comments