1212
1313using System ;
1414using System . Globalization ;
15+ using System . IO ;
16+ using System . Text ;
1517using System . Xml ;
1618using ServiceStack . Text . Json ;
1719
@@ -32,6 +34,7 @@ public static class DateTimeSerializer
3234 public const string EscapedWcfJsonSuffix = ")\\ /" ;
3335 public const string WcfJsonPrefix = "/Date(" ;
3436 public const char WcfJsonSuffix = ')' ;
37+ public const string UnspecifiedOffset = "-0000" ;
3538
3639 /// <summary>
3740 /// If AlwaysUseUtc is set to true then convert all DateTime to UTC.
@@ -83,9 +86,9 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
8386 return XmlConvert . ToDateTimeOffset ( dateTimeStr , dateTimeType ) . DateTime . Prepare ( ) ;
8487#else
8588 var dateTime = Env . IsMono ? ParseManual ( dateTimeStr ) : null ;
86- if ( dateTime != null )
89+ if ( dateTime != null )
8790 return dateTime . Value ;
88-
91+
8992 return XmlConvert . ToDateTime ( dateTimeStr , XmlDateTimeSerializationMode . Utc ) . Prepare ( ) ;
9093#endif
9194 }
@@ -94,10 +97,10 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
9497 {
9598 return DateTime . Parse ( dateTimeStr , null , DateTimeStyles . AssumeLocal ) . Prepare ( ) ;
9699 }
97- catch ( FormatException )
100+ catch ( FormatException )
98101 {
99102 var manualDate = ParseManual ( dateTimeStr ) ;
100- if ( manualDate != null )
103+ if ( manualDate != null )
101104 return manualDate . Value ;
102105
103106 throw ;
@@ -149,7 +152,7 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
149152 int . TryParse ( timeParts [ 1 ] , out min ) ;
150153
151154 var secParts = timeParts [ 2 ] . Split ( '.' ) ;
152- int . TryParse ( secParts [ 0 ] , out ss ) ;
155+ int . TryParse ( secParts [ 0 ] , out ss ) ;
153156 if ( secParts . Length == 2 )
154157 {
155158 var msStr = secParts [ 1 ] . PadRight ( 3 , '0' ) ;
@@ -179,7 +182,7 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
179182 }
180183 else
181184 {
182- hh = int . Parse ( timeOffset . Substring ( 0 , 2 ) ) ;
185+ hh = int . Parse ( timeOffset . Substring ( 0 , 2 ) ) ;
183186 min = int . Parse ( timeOffset . Substring ( 2 ) ) ;
184187 }
185188
@@ -299,7 +302,7 @@ public static string ToShortestXsdDateTimeString(DateTime dateTime)
299302 return dateTime . Kind != DateTimeKind . Utc
300303 ? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
301304 : dateTime . ToStableUniversalTime ( ) . ToString ( XsdDateTimeFormatSeconds ) ;
302-
305+
303306 return dateTime . Kind != DateTimeKind . Utc
304307 ? dateTime . ToString ( DateTimeFormatTicksUtcOffset )
305308 : ToXsdDateTimeString ( dateTime ) ;
@@ -340,10 +343,10 @@ public static DateTimeOffset ParseWcfJsonDateOffset(string wcfJsonDate)
340343 return unixTime . FromUnixTimeMs ( ) ;
341344 }
342345
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 )
344349 {
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?
347350 return unixTime . FromUnixTimeMs ( ) . ToLocalTime ( ) ;
348351 }
349352
@@ -385,9 +388,9 @@ public static DateTime ParseWcfJsonDate(string wcfJsonDate)
385388 return unixTime . FromUnixTimeMs ( ) ;
386389 }
387390
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 )
389393 {
390- // DCJS ignores the offset and considers it local time if any offset exists
391394 return unixTime . FromUnixTimeMs ( ) . ToLocalTime ( ) ;
392395 }
393396
@@ -396,34 +399,74 @@ public static DateTime ParseWcfJsonDate(string wcfJsonDate)
396399 return new DateTimeOffset ( date , offset ) . DateTime ;
397400 }
398401
399- public static string ToWcfJsonDate ( DateTime dateTime )
402+ private static TimeZoneInfo LocalTimeZone = TimeZoneInfo . Local ;
403+ public static void WriteWcfJsonDate ( TextWriter writer , DateTime dateTime )
400404 {
401405 if ( JsConfig . DateHandler == JsonDateHandler . ISO8601 )
402406 {
403- return dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ;
407+ writer . Write ( dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
408+ return ;
404409 }
405410
406411 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+ }
410420
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 ) ;
412428 }
413429
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 )
415441 {
416442 if ( JsConfig . DateHandler == JsonDateHandler . ISO8601 )
417443 {
418- return dateTimeOffset . ToString ( "o" , CultureInfo . InvariantCulture ) ;
444+ writer . Write ( dateTimeOffset . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
445+ return ;
419446 }
420447
421448 var timestamp = dateTimeOffset . Ticks . ToUnixTimeMs ( ) ;
422449 var offset = dateTimeOffset . Offset == TimeSpan . Zero
423- ? string . Empty
450+ ? null
424451 : dateTimeOffset . Offset . ToTimeOffsetString ( ) ;
425452
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+ }
427470 }
428471 }
429472}
0 commit comments