1616using System . Text ;
1717using ServiceStack . Text . Json ;
1818using ServiceStack . Text . Support ;
19+ using System . Text . RegularExpressions ;
1920
2021namespace ServiceStack . Text . Common
2122{
@@ -29,7 +30,9 @@ public static class DateTimeSerializer
2930 public const string XsdDateTimeFormat3F = "yyyy-MM-ddTHH:mm:ss.fffZ" ; //25
3031 public const string XsdDateTimeFormatSeconds = "yyyy-MM-ddTHH:mm:ssZ" ; //21
3132 public const string DateTimeFormatSecondsUtcOffset = "yyyy-MM-ddTHH:mm:sszzz" ; //22
33+ public const string DateTimeFormatSecondsNoOffset = "yyyy-MM-ddTHH:mm:ss" ;
3234 public const string DateTimeFormatTicksUtcOffset = "yyyy-MM-ddTHH:mm:ss.fffffffzzz" ; //30
35+ public const string DateTimeFormatTicksNoUtcOffset = "yyyy-MM-ddTHH:mm:ss.fffffff" ;
3336
3437 public const string EscapedWcfJsonPrefix = "\\ /Date(" ;
3538 public const string EscapedWcfJsonSuffix = ")\\ /" ;
@@ -42,7 +45,7 @@ public static class DateTimeSerializer
4245 private static readonly int XsdTimeSeparatorIndex = XsdDateTimeFormat . IndexOf ( XsdTimeSeparator ) ;
4346 private const string XsdUtcSuffix = "Z" ;
4447 private static readonly char [ ] DateTimeSeperators = new [ ] { '-' , '/' } ;
45-
48+ private static readonly Regex UtcOffsetInfoRegex = new Regex ( "([+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])" , RegexOptions . Compiled ) ;
4649 public static Func < string , Exception , DateTime > OnParseErrorFn { get ; set ; }
4750
4851 /// <summary>
@@ -52,14 +55,15 @@ public static class DateTimeSerializer
5255 /// <returns></returns>
5356 public static DateTime Prepare ( this DateTime dateTime , bool parsedAsUtc = false )
5457 {
55- if ( JsConfig . AlwaysUseUtc )
58+ if ( JsConfig . SkipDateTimeConversion )
5659 {
57- return dateTime . Kind != DateTimeKind . Utc ? dateTime . ToStableUniversalTime ( ) : dateTime ;
60+ return dateTime ;
5861 }
59- if ( JsConfig . PreserveUtc && dateTime . Kind == DateTimeKind . Utc )
62+ if ( JsConfig . AlwaysUseUtc )
6063 {
61- return dateTime ;
64+ return dateTime . Kind != DateTimeKind . Utc ? dateTime . ToStableUniversalTime ( ) : dateTime ;
6265 }
66+
6367 return parsedAsUtc ? dateTime . ToLocalTime ( ) : dateTime ;
6468 }
6569
@@ -104,6 +108,7 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
104108
105109 return unspecifiedDate . Prepare ( ) ;
106110 }
111+ DateTimeKind kind = DateTimeKind . Unspecified ;
107112
108113 switch ( JsConfig . DateHandler )
109114 {
@@ -117,6 +122,12 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
117122 if ( long . TryParse ( dateTimeStr , out unixTimeMs ) )
118123 return unixTimeMs . FromUnixTimeMs ( ) ;
119124 break ;
125+ case DateHandler . ISO8601 :
126+ if ( JsConfig . SkipDateTimeConversion )
127+ {
128+ dateTimeStr = RemoveUtcOffsets ( dateTimeStr , out kind ) ;
129+ }
130+ break ;
120131 }
121132
122133 dateTimeStr = RepairXsdTimeSeparator ( dateTimeStr ) ;
@@ -153,7 +164,20 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
153164
154165 try
155166 {
156- var dateTime = DateTime . Parse ( dateTimeStr , null , DateTimeStyles . AssumeLocal ) ;
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+ dateTime = DateTime . Parse ( dateTimeStr , null , DateTimeStyles . AssumeLocal ) ;
180+ }
157181 return dateTime . Prepare ( ) ;
158182 }
159183 catch ( FormatException )
@@ -174,6 +198,18 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
174198 }
175199 }
176200
201+ private static string RemoveUtcOffsets ( string dateTimeStr , out DateTimeKind kind )
202+ {
203+ var startOfTz = UtcOffsetInfoRegex . Match ( dateTimeStr ) ;
204+ if ( startOfTz . Index > 0 )
205+ {
206+ kind = DateTimeKind . Local ;
207+ return dateTimeStr . Substring ( 0 , startOfTz . Index ) ;
208+ }
209+ kind = dateTimeStr . Contains ( "Z" ) ? DateTimeKind . Utc : DateTimeKind . Unspecified ;
210+ return dateTimeStr ;
211+ }
212+
177213 /// <summary>
178214 /// 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.
179215 /// These string are occasionally generated by SQLite and can cause errors in OrmLite when reading these columns from the DB.
@@ -213,7 +249,7 @@ private static string RepairXsdTimeSeparator(string dateTimeStr)
213249 if ( dateTimeStr . EndsWith ( XsdUtcSuffix ) )
214250 {
215251 dateTimeStr = dateTimeStr . Substring ( 0 , dateTimeStr . Length - 1 ) ;
216- dateKind = JsConfig . PreserveUtc ? DateTimeKind . Utc : dateKind ;
252+ dateKind = JsConfig . SkipDateTimeConversion ? DateTimeKind . Utc : dateKind ;
217253 }
218254
219255 var parts = dateTimeStr . Split ( 'T' ) ;
@@ -412,19 +448,37 @@ public static string ToShortestXsdDateTimeString(DateTime dateTime)
412448 var timeOfDay = dateTime . TimeOfDay ;
413449
414450 var isStartOfDay = timeOfDay . Ticks == 0 ;
415- if ( isStartOfDay && ! ( JsConfig . PreserveUtc && dateTime . Kind == DateTimeKind . Utc ) )
451+ if ( isStartOfDay && ! ( JsConfig . SkipDateTimeConversion ) )
416452 return dateTime . ToString ( ShortDateTimeFormat ) ;
417453
418454 var hasFractionalSecs = ( timeOfDay . Milliseconds != 0 )
419455 || ( ( timeOfDay . Ticks % TimeSpan . TicksPerMillisecond ) != 0 ) ;
420- if ( ! hasFractionalSecs )
421- return dateTime . Kind != DateTimeKind . Utc
422- ? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
423- : dateTime . ToStableUniversalTime ( ) . ToString ( XsdDateTimeFormatSeconds ) ;
456+ if ( JsConfig . SkipDateTimeConversion )
457+ {
458+ if ( ! hasFractionalSecs )
459+ return dateTime . Kind == DateTimeKind . Local
460+ ? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
461+ : dateTime . Kind == DateTimeKind . Unspecified
462+ ? dateTime . ToString ( DateTimeFormatSecondsNoOffset )
463+ : dateTime . ToStableUniversalTime ( ) . ToString ( XsdDateTimeFormatSeconds ) ;
464+
465+ return dateTime . Kind == DateTimeKind . Local
466+ ? dateTime . ToString ( DateTimeFormatTicksUtcOffset )
467+ : dateTime . Kind == DateTimeKind . Unspecified
468+ ? dateTime . ToString ( DateTimeFormatTicksNoUtcOffset )
469+ : PclExport . Instance . ToXsdDateTimeString ( dateTime ) ;
470+ }
471+ else
472+ {
473+ if ( ! hasFractionalSecs )
474+ return dateTime . Kind != DateTimeKind . Utc
475+ ? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
476+ : dateTime . ToStableUniversalTime ( ) . ToString ( XsdDateTimeFormatSeconds ) ;
424477
425- return dateTime . Kind != DateTimeKind . Utc
426- ? dateTime . ToString ( DateTimeFormatTicksUtcOffset )
427- : PclExport . Instance . ToXsdDateTimeString ( dateTime ) ;
478+ return dateTime . Kind != DateTimeKind . Utc
479+ ? dateTime . ToString ( DateTimeFormatTicksUtcOffset )
480+ : PclExport . Instance . ToXsdDateTimeString ( dateTime ) ;
481+ }
428482 }
429483
430484 static readonly char [ ] TimeZoneChars = new [ ] { '+' , '-' } ;
@@ -541,7 +595,15 @@ public static void WriteWcfJsonDate(TextWriter writer, DateTime dateTime)
541595
542596 if ( JsConfig . DateHandler == DateHandler . ISO8601 )
543597 {
544- writer . Write ( dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
598+ if ( ! JsConfig . SkipDateTimeConversion )
599+ {
600+ writer . Write ( dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
601+ }
602+ else
603+ {
604+ var dt = dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ;
605+ writer . Write ( dt ) ;
606+ }
545607 return ;
546608 }
547609
0 commit comments