1616using System . Text ;
1717using ServiceStack . Text . Json ;
1818using ServiceStack . Text . Support ;
19+ using System . Text . RegularExpressions ;
1920
2021namespace ServiceStack . Text . Common
2122{
@@ -24,11 +25,14 @@ public static class DateTimeSerializer
2425 public const string CondensedDateTimeFormat = "yyyyMMdd" ; //8
2526 public const string ShortDateTimeFormat = "yyyy-MM-dd" ; //11
2627 public const string DefaultDateTimeFormat = "dd/MM/yyyy HH:mm:ss" ; //20
28+ public const string DefaultDateTimeFormatWithFraction = "dd/MM/yyyy HH:mm:ss.fff" ; //24
2729 public const string XsdDateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ" ; //29
2830 public const string XsdDateTimeFormat3F = "yyyy-MM-ddTHH:mm:ss.fffZ" ; //25
2931 public const string XsdDateTimeFormatSeconds = "yyyy-MM-ddTHH:mm:ssZ" ; //21
3032 public const string DateTimeFormatSecondsUtcOffset = "yyyy-MM-ddTHH:mm:sszzz" ; //22
33+ public const string DateTimeFormatSecondsNoOffset = "yyyy-MM-ddTHH:mm:ss" ;
3134 public const string DateTimeFormatTicksUtcOffset = "yyyy-MM-ddTHH:mm:ss.fffffffzzz" ; //30
35+ public const string DateTimeFormatTicksNoUtcOffset = "yyyy-MM-ddTHH:mm:ss.fffffff" ;
3236
3337 public const string EscapedWcfJsonPrefix = "\\ /Date(" ;
3438 public const string EscapedWcfJsonSuffix = ")\\ /" ;
@@ -41,20 +45,25 @@ public static class DateTimeSerializer
4145 private static readonly int XsdTimeSeparatorIndex = XsdDateTimeFormat . IndexOf ( XsdTimeSeparator ) ;
4246 private const string XsdUtcSuffix = "Z" ;
4347 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 ) ;
4549 public static Func < string , Exception , DateTime > OnParseErrorFn { get ; set ; }
4650
4751 /// <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
4953 /// </summary>
5054 /// <param name="dateTime"></param>
5155 /// <returns></returns>
5256 public static DateTime Prepare ( this DateTime dateTime , bool parsedAsUtc = false )
5357 {
58+ if ( JsConfig . SkipDateTimeConversion )
59+ {
60+ return dateTime ;
61+ }
5462 if ( JsConfig . AlwaysUseUtc )
5563 {
5664 return dateTime . Kind != DateTimeKind . Utc ? dateTime . ToStableUniversalTime ( ) : dateTime ;
5765 }
66+
5867 return parsedAsUtc ? dateTime . ToLocalTime ( ) : dateTime ;
5968 }
6069
@@ -91,6 +100,16 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
91100 return unspecifiedDate . Prepare ( ) ;
92101 }
93102
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+
94113 switch ( JsConfig . DateHandler )
95114 {
96115 case DateHandler . UnixTime :
@@ -103,6 +122,12 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
103122 if ( long . TryParse ( dateTimeStr , out unixTimeMs ) )
104123 return unixTimeMs . FromUnixTimeMs ( ) ;
105124 break ;
125+ case DateHandler . ISO8601 :
126+ if ( JsConfig . SkipDateTimeConversion )
127+ {
128+ dateTimeStr = RemoveUtcOffsets ( dateTimeStr , out kind ) ;
129+ }
130+ break ;
106131 }
107132
108133 dateTimeStr = RepairXsdTimeSeparator ( dateTimeStr ) ;
@@ -139,8 +164,21 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
139164
140165 try
141166 {
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+ }
144182 return dateTime . Prepare ( ) ;
145183 }
146184 catch ( FormatException )
@@ -161,6 +199,18 @@ public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
161199 }
162200 }
163201
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+
164214 /// <summary>
165215 /// 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.
166216 /// 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)
180230
181231 public static DateTime ? ParseManual ( string dateTimeStr )
182232 {
183- var dateKind = JsConfig . AssumeUtc || JsConfig . AlwaysUseUtc
233+ var dateKind = JsConfig . AssumeUtc || JsConfig . AlwaysUseUtc
184234 ? DateTimeKind . Utc
185235 : DateTimeKind . Local ;
186236
@@ -201,6 +251,7 @@ private static string RepairXsdTimeSeparator(string dateTimeStr)
201251 if ( dateTimeStr . EndsWith ( XsdUtcSuffix ) )
202252 {
203253 dateTimeStr = dateTimeStr . Substring ( 0 , dateTimeStr . Length - 1 ) ;
254+ dateKind = JsConfig . SkipDateTimeConversion ? DateTimeKind . Utc : dateKind ;
204255 }
205256
206257 var parts = dateTimeStr . Split ( 'T' ) ;
@@ -320,7 +371,7 @@ public static DateTimeOffset ParseDateTimeOffset(string dateTimeOffsetStr)
320371 if ( Env . IsMono )
321372 {
322373 // 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" ;
324375 }
325376 }
326377
@@ -364,7 +415,7 @@ public static TimeSpan ParseTimeSpan(string dateTimeStr)
364415 {
365416 return dateTimeStr . StartsWith ( "P" , StringComparison . Ordinal ) || dateTimeStr . StartsWith ( "-P" , StringComparison . Ordinal )
366417 ? ParseXsdTimeSpan ( dateTimeStr )
367- : dateTimeStr . Contains ( ":" )
418+ : dateTimeStr . Contains ( ":" )
368419 ? TimeSpan . Parse ( dateTimeStr )
369420 : ParseNSTimeInterval ( dateTimeStr ) ;
370421 }
@@ -399,11 +450,26 @@ public static string ToShortestXsdDateTimeString(DateTime dateTime)
399450 var timeOfDay = dateTime . TimeOfDay ;
400451
401452 var isStartOfDay = timeOfDay . Ticks == 0 ;
402- if ( isStartOfDay )
453+ if ( isStartOfDay && ! ( JsConfig . SkipDateTimeConversion ) )
403454 return dateTime . ToString ( ShortDateTimeFormat ) ;
404455
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+ }
407473 if ( ! hasFractionalSecs )
408474 return dateTime . Kind != DateTimeKind . Utc
409475 ? dateTime . ToString ( DateTimeFormatSecondsUtcOffset )
@@ -528,7 +594,15 @@ public static void WriteWcfJsonDate(TextWriter writer, DateTime dateTime)
528594
529595 if ( JsConfig . DateHandler == DateHandler . ISO8601 )
530596 {
597+ if ( ! JsConfig . SkipDateTimeConversion )
598+ {
531599 writer . Write ( dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ) ;
600+ }
601+ else
602+ {
603+ var dt = dateTime . ToString ( "o" , CultureInfo . InvariantCulture ) ;
604+ writer . Write ( dt ) ;
605+ }
532606 return ;
533607 }
534608
0 commit comments