12
12
13
13
using System ;
14
14
using System . Globalization ;
15
+ using System . IO ;
16
+ using System . Text ;
15
17
using System . Xml ;
16
18
using ServiceStack . Text . Json ;
17
19
@@ -32,6 +34,7 @@ public static class DateTimeSerializer
32
34
public const string EscapedWcfJsonSuffix = ")\\ /" ;
33
35
public const string WcfJsonPrefix = "/Date(" ;
34
36
public const char WcfJsonSuffix = ')' ;
37
+ public const string UnspecifiedOffset = "-0000" ;
35
38
36
39
/// <summary>
37
40
/// If AlwaysUseUtc is set to true then convert all DateTime to UTC.
@@ -83,9 +86,9 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
83
86
return XmlConvert . ToDateTimeOffset ( dateTimeStr , dateTimeType ) . DateTime . Prepare ( ) ;
84
87
#else
85
88
var dateTime = Env . IsMono ? ParseManual ( dateTimeStr ) : null ;
86
- if ( dateTime != null )
89
+ if ( dateTime != null )
87
90
return dateTime . Value ;
88
-
91
+
89
92
return XmlConvert . ToDateTime ( dateTimeStr , XmlDateTimeSerializationMode . Utc ) . Prepare ( ) ;
90
93
#endif
91
94
}
@@ -94,10 +97,10 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
94
97
{
95
98
return DateTime . Parse ( dateTimeStr , null , DateTimeStyles . AssumeLocal ) . Prepare ( ) ;
96
99
}
97
- catch ( FormatException )
100
+ catch ( FormatException )
98
101
{
99
102
var manualDate = ParseManual ( dateTimeStr ) ;
100
- if ( manualDate != null )
103
+ if ( manualDate != null )
101
104
return manualDate . Value ;
102
105
103
106
throw ;
@@ -149,7 +152,7 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
149
152
int . TryParse ( timeParts [ 1 ] , out min ) ;
150
153
151
154
var secParts = timeParts [ 2 ] . Split ( '.' ) ;
152
- int . TryParse ( secParts [ 0 ] , out ss ) ;
155
+ int . TryParse ( secParts [ 0 ] , out ss ) ;
153
156
if ( secParts . Length == 2 )
154
157
{
155
158
var msStr = secParts [ 1 ] . PadRight ( 3 , '0' ) ;
@@ -179,7 +182,7 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
179
182
}
180
183
else
181
184
{
182
- hh = int . Parse ( timeOffset . Substring ( 0 , 2 ) ) ;
185
+ hh = int . Parse ( timeOffset . Substring ( 0 , 2 ) ) ;
183
186
min = int . Parse ( timeOffset . Substring ( 2 ) ) ;
184
187
}
185
188
@@ -299,7 +302,7 @@ public static string ToShortestXsdDateTimeString(DateTime dateTime)
299
302
return dateTime . Kind != DateTimeKind . Utc
300
303
? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
301
304
: dateTime . ToStableUniversalTime ( ) . ToString ( XsdDateTimeFormatSeconds ) ;
302
-
305
+
303
306
return dateTime . Kind != DateTimeKind . Utc
304
307
? dateTime . ToString ( DateTimeFormatTicksUtcOffset )
305
308
: ToXsdDateTimeString ( dateTime ) ;
@@ -340,10 +343,10 @@ public static DateTimeOffset ParseWcfJsonDateOffset(string wcfJsonDate)
340
343
return unixTime . FromUnixTimeMs ( ) ;
341
344
}
342
345
343
- if ( JsConfig . DateHandler == JsonDateHandler . DCJSCompatible )
346
+ // DCJS ignores the offset and considers it local time if any offset exists
347
+ // REVIEW: DCJS shoves offset in a separate field 'offsetMinutes', we have the offset in the format, so shouldn't we use it?
348
+ if ( JsConfig . DateHandler == JsonDateHandler . DCJSCompatible || timeZone == UnspecifiedOffset )
344
349
{
345
- // DCJS ignores the offset and considers it local time if any offset exists
346
- // REVIEW: DCJS shoves offset in a separate field 'offsetMinutes', we have the offset in the format, so shouldn't we use it?
347
350
return unixTime . FromUnixTimeMs ( ) . ToLocalTime ( ) ;
348
351
}
349
352
@@ -385,9 +388,9 @@ public static DateTime ParseWcfJsonDate(string wcfJsonDate)
385
388
return unixTime . FromUnixTimeMs ( ) ;
386
389
}
387
390
388
- if ( JsConfig . DateHandler == JsonDateHandler . DCJSCompatible )
391
+ // DCJS ignores the offset and considers it local time if any offset exists
392
+ if ( JsConfig . DateHandler == JsonDateHandler . DCJSCompatible || timeZone == UnspecifiedOffset )
389
393
{
390
- // DCJS ignores the offset and considers it local time if any offset exists
391
394
return unixTime . FromUnixTimeMs ( ) . ToLocalTime ( ) ;
392
395
}
393
396
@@ -396,34 +399,74 @@ public static DateTime ParseWcfJsonDate(string wcfJsonDate)
396
399
return new DateTimeOffset ( date , offset ) . DateTime ;
397
400
}
398
401
399
- public static string ToWcfJsonDate ( DateTime dateTime )
402
+ private static TimeZoneInfo LocalTimeZone = TimeZoneInfo . Local ;
403
+ public static void WriteWcfJsonDate ( TextWriter writer , DateTime dateTime )
400
404
{
401
405
if ( JsConfig . DateHandler == JsonDateHandler . ISO8601 )
402
406
{
403
- return dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ;
407
+ writer . Write ( dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
408
+ return ;
404
409
}
405
410
406
411
var timestamp = dateTime . ToUnixTimeMs ( ) ;
407
- var offset = dateTime . Kind == DateTimeKind . Utc
408
- ? string . Empty
409
- : TimeZoneInfo . Local . GetUtcOffset ( dateTime ) . ToTimeOffsetString ( ) ;
412
+ string offset = null ;
413
+ if ( dateTime . Kind != DateTimeKind . Utc )
414
+ {
415
+ if ( JsConfig . DateHandler == JsonDateHandler . TimestampOffset && dateTime . Kind == DateTimeKind . Unspecified )
416
+ offset = UnspecifiedOffset ;
417
+ else
418
+ offset = LocalTimeZone . GetUtcOffset ( dateTime ) . ToTimeOffsetString ( ) ;
419
+ }
410
420
411
- return EscapedWcfJsonPrefix + timestamp + offset + EscapedWcfJsonSuffix ;
421
+ writer . Write ( EscapedWcfJsonPrefix ) ;
422
+ writer . Write ( timestamp ) ;
423
+ if ( offset != null )
424
+ {
425
+ writer . Write ( offset ) ;
426
+ }
427
+ writer . Write ( EscapedWcfJsonSuffix ) ;
412
428
}
413
429
414
- public static string ToWcfJsonDateTimeOffset ( DateTimeOffset dateTimeOffset )
430
+ public static string ToWcfJsonDate ( DateTime dateTime )
431
+ {
432
+ var sb = new StringBuilder ( ) ;
433
+ using ( var writer = new StringWriter ( sb ) )
434
+ {
435
+ WriteWcfJsonDate ( writer , dateTime ) ;
436
+ return sb . ToString ( ) ;
437
+ }
438
+ }
439
+
440
+ public static void WriteWcfJsonDateTimeOffset ( TextWriter writer , DateTimeOffset dateTimeOffset )
415
441
{
416
442
if ( JsConfig . DateHandler == JsonDateHandler . ISO8601 )
417
443
{
418
- return dateTimeOffset . ToString ( "o" , CultureInfo . InvariantCulture ) ;
444
+ writer . Write ( dateTimeOffset . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
445
+ return ;
419
446
}
420
447
421
448
var timestamp = dateTimeOffset . Ticks . ToUnixTimeMs ( ) ;
422
449
var offset = dateTimeOffset . Offset == TimeSpan . Zero
423
- ? string . Empty
450
+ ? null
424
451
: dateTimeOffset . Offset . ToTimeOffsetString ( ) ;
425
452
426
- return EscapedWcfJsonPrefix + timestamp + offset + EscapedWcfJsonSuffix ;
453
+ writer . Write ( EscapedWcfJsonPrefix ) ;
454
+ writer . Write ( timestamp ) ;
455
+ if ( offset != null )
456
+ {
457
+ writer . Write ( offset ) ;
458
+ }
459
+ writer . Write ( EscapedWcfJsonSuffix ) ;
460
+ }
461
+
462
+ public static string ToWcfJsonDateTimeOffset ( DateTimeOffset dateTimeOffset )
463
+ {
464
+ var sb = new StringBuilder ( ) ;
465
+ using ( var writer = new StringWriter ( sb ) )
466
+ {
467
+ WriteWcfJsonDateTimeOffset ( writer , dateTimeOffset ) ;
468
+ return sb . ToString ( ) ;
469
+ }
427
470
}
428
471
}
429
472
}
0 commit comments