Permalink
Browse files

Better support for time zones

More tests, more robustness when running in different timezones, and
support for UTC dates with the "Z" date format.
  • Loading branch information...
1 parent 495ef13 commit 9e9bf3089306e0502c750c163101d31da495f4be Thomas Freeman committed Oct 2, 2012
View
@@ -228,22 +228,24 @@ let parse source =
let (|Date|String|) input =
let msDateRegex = new System.Text.RegularExpressions.Regex(@"^\\\/Date\((-?\d+)(?:-\d+)?\)\\\/$")
- let iso8601Regex = new System.Text.RegularExpressions.Regex(@"^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$")
+ let iso8601Regex = new System.Text.RegularExpressions.Regex(@"^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?((?<IsUTC>[zZ])|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$")
let matchesMS = msDateRegex.Match(input)
if matchesMS.Success then
matchesMS.Groups.[1].Value
|> Double.Parse
- |> (new DateTime(1970, 1, 1)).AddMilliseconds
+ |> (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds
|> (fun x -> Date(x))
- elif iso8601Regex.IsMatch(input) then
- input
- |> DateTime.TryParse
- |> (fun (parsed, d) -> if parsed then
- Date(d)
- else
- String(input))
else
- String(input)
+ let matches = iso8601Regex.Match(input)
+ if matches.Success then
+ input
+ |> fun s -> DateTime.TryParse(s, CultureInfo.InvariantCulture, (if matches.Groups.["IsUTC"].Success then DateTimeStyles.AssumeUniversal ||| DateTimeStyles.AdjustToUniversal else DateTimeStyles.AssumeLocal))
+ |> (fun (parsed, d) -> if parsed then
+ Date(d)
+ else
+ String(input))
+ else
+ String(input)
let map = function
| Token.Number number ->
@@ -31,23 +31,39 @@ let ``Can parse document with text and float``() =
[<Test>]
let ``Can parse document with date``() =
let j = parse "{\"anniversary\": \"\\/Date(869080830450)\\/\"}"
- j.GetDate "anniversary" |> should equal (new System.DateTime(1997, 07, 16, 19, 20, 30, 450))
+ j.GetDate "anniversary" |> should equal (new System.DateTime(1997, 07, 16, 19, 20, 30, 450, System.DateTimeKind.Utc))
+ (j.GetDate "anniversary").Kind |> should equal System.DateTimeKind.Utc
[<Test>]
let ``Can parse document with iso date``() =
let j = parse "{\"anniversary\": \"2009-05-19 14:39:22.500\"}"
- j.GetDate "anniversary" |> should equal (new System.DateTime(2009, 05, 19, 14, 39, 22, 500))
+ j.GetDate "anniversary" |> should equal (new System.DateTime(2009, 05, 19, 14, 39, 22, 500, System.DateTimeKind.Local))
+ (j.GetDate "anniversary").Kind |> should equal System.DateTimeKind.Local
+
+[<Test>]
+[<SetCulture("zh-CN")>]
+let ``Can parse document with iso date in local culture``() =
+ let j = parse "{\"anniversary\": \"2009-05-19 14:39:22.500\"}"
+ j.GetDate "anniversary" |> should equal (new System.DateTime(2009, 05, 19, 14, 39, 22, 500, System.DateTimeKind.Local))
+ (j.GetDate "anniversary").Kind |> should equal System.DateTimeKind.Local
[<Test>]
let ``Can parse document with partial iso date``() =
let j = parse "{\"anniversary\": \"2009-05-19\"}"
- j.GetDate "anniversary" |> should equal (new System.DateTime(2009, 05, 19))
+ j.GetDate "anniversary" |> should equal (new System.DateTime(2009, 05, 19, 0, 0, 0, System.DateTimeKind.Local))
+ (j.GetDate "anniversary").Kind |> should equal System.DateTimeKind.Local
[<Test>]
let ``Can parse document with timezone iso date``() =
let j = parse "{\"anniversary\": \"2009-05-19 14:39:22+0600\"}"
- (j.GetDate "anniversary").ToUniversalTime() |> should equal (new System.DateTime(2009, 05, 19, 8, 39, 22))
-
+ (j.GetDate "anniversary").ToUniversalTime() |> should equal (new System.DateTime(2009, 05, 19, 8, 39, 22, System.DateTimeKind.Utc))
+
+[<Test>]
+let ``Can parse document with UTC iso date``() =
+ let j = parse "{\"anniversary\": \"2009-05-19 14:39:22Z\"}"
+ (j.GetDate "anniversary").ToUniversalTime() |> should equal (new System.DateTime(2009, 05, 19, 14, 39, 22, System.DateTimeKind.Utc))
+ (j.GetDate "anniversary").Kind |> should equal System.DateTimeKind.Utc
+
// TODO: Due to limitations in the current ISO 8601 datetime parsing these fail, and should be made to pass
//[<Test>]
//let ``Cant Yet parse document with basic iso date``() =
@@ -1,4 +1,6 @@
{
"birthdate": "\/Date(869080830450)\/"
"anniversary": "1997-07-16T19:20:30.45+01:00"
+ "NoTimeZone": "1997-07-16T19:20:30"
+ "UtcTime": "1997-07-16T19:50:30Z"
}
@@ -15,4 +15,15 @@ let ``Can parse microsoft format dates``() =
[<Test>]
let ``Can parse ISO 8601 dates``() =
let dates = new DateJSON()
- dates.Root.Anniversary |> should equal (new DateTime(1997, 7, 16, 19, 20, 30, 450))
+ dates.Root.Anniversary.ToUniversalTime() |> should equal (new DateTime(1997, 7, 16, 18, 20, 30, 450))
+
+[<Test>]
+let ``Can parse UTC dates``() =
+ let dates = new DateJSON()
+ dates.Root.UtcTime.ToUniversalTime() |> should equal (new DateTime(1997, 7, 16, 19, 50, 30, 0))
+
+[<Test>]
+[<SetCulture("zh-CN")>]
+let ``Can parse ISO 8601 dates in the correct culture``() =
+ let dates = new DateJSON()
+ dates.Root.NoTimeZone |> should equal (new DateTime(1997, 7, 16, 19, 20, 30, 00, System.DateTimeKind.Local))

0 comments on commit 9e9bf30

Please sign in to comment.