diff --git a/src/MongoDB.Bson/IO/JsonReader.cs b/src/MongoDB.Bson/IO/JsonReader.cs index 0d5a483ff60..0b764c8ced2 100644 --- a/src/MongoDB.Bson/IO/JsonReader.cs +++ b/src/MongoDB.Bson/IO/JsonReader.cs @@ -1373,7 +1373,9 @@ private DateTime ParseJavaScriptDateTimeString(string dateTimeString) { // if DateTime.TryParse succeeds we're done, otherwise assume it's an RFC 822 formatted DateTime string DateTime dateTime; - if (DateTime.TryParse(dateTimeString, out dateTime)) + + // DateTime.Parse doesn't understand military time zones, so don't call it when a military time zone is present + if (!Regex.IsMatch(dateTimeString, " [A-Y]$") && DateTime.TryParse(dateTimeString, out dateTime)) { return dateTime; } @@ -1479,30 +1481,30 @@ private DateTime ParseJavaScriptDateTimeString(string dateTimeString) case "MDT": offset = TimeSpan.FromHours(-6); break; case "PST": offset = TimeSpan.FromHours(-8); break; case "PDT": offset = TimeSpan.FromHours(-7); break; - case "A": offset = TimeSpan.FromHours(-1); break; - case "B": offset = TimeSpan.FromHours(-2); break; - case "C": offset = TimeSpan.FromHours(-3); break; - case "D": offset = TimeSpan.FromHours(-4); break; - case "E": offset = TimeSpan.FromHours(-5); break; - case "F": offset = TimeSpan.FromHours(-6); break; - case "G": offset = TimeSpan.FromHours(-7); break; - case "H": offset = TimeSpan.FromHours(-8); break; - case "I": offset = TimeSpan.FromHours(-9); break; - case "K": offset = TimeSpan.FromHours(-10); break; - case "L": offset = TimeSpan.FromHours(-11); break; - case "M": offset = TimeSpan.FromHours(-12); break; - case "N": offset = TimeSpan.FromHours(1); break; - case "O": offset = TimeSpan.FromHours(2); break; - case "P": offset = TimeSpan.FromHours(3); break; - case "Q": offset = TimeSpan.FromHours(4); break; - case "R": offset = TimeSpan.FromHours(5); break; - case "S": offset = TimeSpan.FromHours(6); break; - case "T": offset = TimeSpan.FromHours(7); break; - case "U": offset = TimeSpan.FromHours(8); break; - case "V": offset = TimeSpan.FromHours(9); break; - case "W": offset = TimeSpan.FromHours(10); break; - case "X": offset = TimeSpan.FromHours(11); break; - case "Y": offset = TimeSpan.FromHours(12); break; + case "A": offset = TimeSpan.FromHours(1); break; + case "B": offset = TimeSpan.FromHours(2); break; + case "C": offset = TimeSpan.FromHours(3); break; + case "D": offset = TimeSpan.FromHours(4); break; + case "E": offset = TimeSpan.FromHours(5); break; + case "F": offset = TimeSpan.FromHours(6); break; + case "G": offset = TimeSpan.FromHours(7); break; + case "H": offset = TimeSpan.FromHours(8); break; + case "I": offset = TimeSpan.FromHours(9); break; + case "K": offset = TimeSpan.FromHours(10); break; + case "L": offset = TimeSpan.FromHours(11); break; + case "M": offset = TimeSpan.FromHours(12); break; + case "N": offset = TimeSpan.FromHours(-1); break; + case "O": offset = TimeSpan.FromHours(-2); break; + case "P": offset = TimeSpan.FromHours(-3); break; + case "Q": offset = TimeSpan.FromHours(-4); break; + case "R": offset = TimeSpan.FromHours(-5); break; + case "S": offset = TimeSpan.FromHours(-6); break; + case "T": offset = TimeSpan.FromHours(-7); break; + case "U": offset = TimeSpan.FromHours(-8); break; + case "V": offset = TimeSpan.FromHours(-9); break; + case "W": offset = TimeSpan.FromHours(-10); break; + case "X": offset = TimeSpan.FromHours(-11); break; + case "Y": offset = TimeSpan.FromHours(-12); break; default: var offsetSign = zone.Substring(0); var offsetHours = zone.Substring(1, 2); diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs index 64566845177..6061d948fa6 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp275Tests.cs @@ -22,125 +22,110 @@ namespace MongoDB.Bson.Tests.Jira { public class CSharp275Tests { - private class Test - { - public string Json; - public string Iso; - public Test(string json, string iso) - { - this.Json = json; - this.Iso = iso; - } - } - - private Test[] _tests = new Test[] - { + public static object[][] TestParseDatesData => [ // note: use EST/EDT in all Json values to ensure DateTime.Parse doesn't work // test with dayOfWeek - new Test("Mon, 10 Oct 2011 11:22:33 EDT", "2011-10-10T11:22:33-04:00"), - new Test("Tue, 11 Oct 2011 11:22:33 EDT", "2011-10-11T11:22:33-04:00"), - new Test("Wed, 12 Oct 2011 11:22:33 EDT", "2011-10-12T11:22:33-04:00"), - new Test("Thu, 13 Oct 2011 11:22:33 EDT", "2011-10-13T11:22:33-04:00"), - new Test("Fri, 14 Oct 2011 11:22:33 EDT", "2011-10-14T11:22:33-04:00"), - new Test("Sat, 15 Oct 2011 11:22:33 EDT", "2011-10-15T11:22:33-04:00"), - new Test("Sun, 16 Oct 2011 11:22:33 EDT", "2011-10-16T11:22:33-04:00"), - // test without dayOfWeek - new Test("10 Oct 2011 11:22:33 EDT", "2011-10-10T11:22:33-04:00"), - new Test("11 Oct 2011 11:22:33 EDT", "2011-10-11T11:22:33-04:00"), - new Test("12 Oct 2011 11:22:33 EDT", "2011-10-12T11:22:33-04:00"), - new Test("13 Oct 2011 11:22:33 EDT", "2011-10-13T11:22:33-04:00"), - new Test("14 Oct 2011 11:22:33 EDT", "2011-10-14T11:22:33-04:00"), - new Test("15 Oct 2011 11:22:33 EDT", "2011-10-15T11:22:33-04:00"), - new Test("16 Oct 2011 11:22:33 EDT", "2011-10-16T11:22:33-04:00"), - // test monthName - new Test("1 Jan 2011 11:22:33 EST", "2011-01-01T11:22:33-05:00"), - new Test("1 Feb 2011 11:22:33 EST", "2011-02-01T11:22:33-05:00"), - new Test("1 Mar 2011 11:22:33 EST", "2011-03-01T11:22:33-05:00"), - new Test("1 Apr 2011 11:22:33 EDT", "2011-04-01T11:22:33-04:00"), - new Test("1 May 2011 11:22:33 EDT", "2011-05-01T11:22:33-04:00"), - new Test("1 Jun 2011 11:22:33 EDT", "2011-06-01T11:22:33-04:00"), - new Test("1 Jul 2011 11:22:33 EDT", "2011-07-01T11:22:33-04:00"), - new Test("1 Aug 2011 11:22:33 EDT", "2011-08-01T11:22:33-04:00"), - new Test("1 Sep 2011 11:22:33 EDT", "2011-09-01T11:22:33-04:00"), - new Test("1 Oct 2011 11:22:33 EDT", "2011-10-01T11:22:33-04:00"), - new Test("1 Nov 2011 11:22:33 EDT", "2011-11-01T11:22:33-04:00"), - new Test("1 Dec 2011 11:22:33 EST", "2011-12-01T11:22:33-05:00"), - // test 2-digit year - new Test("Mon, 1 Jan 01 11:22:33 EST", "2001-01-01T11:22:33-5:00"), - new Test("Mon, 1 Jan 29 11:22:33 EST", "2029-01-01T11:22:33-5:00"), - new Test("Tue, 1 Jan 30 11:22:33 EST", "2030-01-01T11:22:33-5:00"), - new Test("Wed, 1 Jan 31 11:22:33 EST", "2031-01-01T11:22:33-5:00"), - new Test("Thu, 1 Jan 32 11:22:33 EST", "2032-01-01T11:22:33-5:00"), - new Test("Fri, 1 Jan 99 11:22:33 EST", "1999-01-01T11:22:33-5:00"), - // test time zones - new Test("Mon, 10 Oct 2011 11:22:33 UT", "2011-10-10T11:22:33-00:00"), - new Test("Mon, 10 Oct 2011 11:22:33 GMT", "2011-10-10T11:22:33-00:00"), - new Test("Mon, 10 Oct 2011 11:22:33 EST", "2011-10-10T11:22:33-05:00"), - new Test("Mon, 10 Oct 2011 11:22:33 EDT", "2011-10-10T11:22:33-04:00"), - new Test("Mon, 10 Oct 2011 11:22:33 CST", "2011-10-10T11:22:33-06:00"), - new Test("Mon, 10 Oct 2011 11:22:33 CDT", "2011-10-10T11:22:33-05:00"), - new Test("Mon, 10 Oct 2011 11:22:33 MST", "2011-10-10T11:22:33-07:00"), - new Test("Mon, 10 Oct 2011 11:22:33 MDT", "2011-10-10T11:22:33-06:00"), - new Test("Mon, 10 Oct 2011 11:22:33 PST", "2011-10-10T11:22:33-08:00"), - new Test("Mon, 10 Oct 2011 11:22:33 PDT", "2011-10-10T11:22:33-07:00"), - // TODO: Investigate Military Time Failures - // https://jira.mongodb.org/browse/CSHARP-3268 - //new Test("Mon, 10 Oct 2011 11:22:33 A", "2011-10-10T11:22:33+01:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 B", "2011-10-10T11:22:33+02:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 C", "2011-10-10T11:22:33+03:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 D", "2011-10-10T11:22:33+04:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 E", "2011-10-10T11:22:33+05:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 F", "2011-10-10T11:22:33+06:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 G", "2011-10-10T11:22:33+07:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 H", "2011-10-10T11:22:33+08:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 I", "2011-10-10T11:22:33+09:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 K", "2011-10-10T11:22:33+10:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 L", "2011-10-10T11:22:33+11:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 M", "2011-10-10T11:22:33+12:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 N", "2011-10-10T11:22:33-01:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 O", "2011-10-10T11:22:33-02:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 P", "2011-10-10T11:22:33-03:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 Q", "2011-10-10T11:22:33-04:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 R", "2011-10-10T11:22:33-05:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 S", "2011-10-10T11:22:33-06:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 T", "2011-10-10T11:22:33-07:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 U", "2011-10-10T11:22:33-08:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 V", "2011-10-10T11:22:33-09:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 W", "2011-10-10T11:22:33-10:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 X", "2011-10-10T11:22:33-11:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 Y", "2011-10-10T11:22:33-12:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 Z", "2011-10-10T11:22:33-00:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 +0000", "2011-10-10T11:22:33+00:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 -0000", "2011-10-10T11:22:33-00:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 +0100", "2011-10-10T11:22:33+01:00"), - //new Test("Mon, 10 Oct 2011 11:22:33 -0100", "2011-10-10T11:22:33-01:00") - }; + ["Mon, 10 Oct 2011 11:22:33 EDT", "2011-10-10T11:22:33-04:00"], + ["Tue, 11 Oct 2011 11:22:33 EDT", "2011-10-11T11:22:33-04:00"], + ["Wed, 12 Oct 2011 11:22:33 EDT", "2011-10-12T11:22:33-04:00"], + ["Thu, 13 Oct 2011 11:22:33 EDT", "2011-10-13T11:22:33-04:00"], + ["Fri, 14 Oct 2011 11:22:33 EDT", "2011-10-14T11:22:33-04:00"], + ["Sat, 15 Oct 2011 11:22:33 EDT", "2011-10-15T11:22:33-04:00"], + ["Sun, 16 Oct 2011 11:22:33 EDT", "2011-10-16T11:22:33-04:00"], + // // test without dayOfWeek + ["10 Oct 2011 11:22:33 EDT", "2011-10-10T11:22:33-04:00"], + ["11 Oct 2011 11:22:33 EDT", "2011-10-11T11:22:33-04:00"], + ["12 Oct 2011 11:22:33 EDT", "2011-10-12T11:22:33-04:00"], + ["13 Oct 2011 11:22:33 EDT", "2011-10-13T11:22:33-04:00"], + ["14 Oct 2011 11:22:33 EDT", "2011-10-14T11:22:33-04:00"], + ["15 Oct 2011 11:22:33 EDT", "2011-10-15T11:22:33-04:00"], + ["16 Oct 2011 11:22:33 EDT", "2011-10-16T11:22:33-04:00"], + // // test monthName + ["1 Jan 2011 11:22:33 EST", "2011-01-01T11:22:33-05:00"], + ["1 Feb 2011 11:22:33 EST", "2011-02-01T11:22:33-05:00"], + ["1 Mar 2011 11:22:33 EST", "2011-03-01T11:22:33-05:00"], + ["1 Apr 2011 11:22:33 EDT", "2011-04-01T11:22:33-04:00"], + ["1 May 2011 11:22:33 EDT", "2011-05-01T11:22:33-04:00"], + ["1 Jun 2011 11:22:33 EDT", "2011-06-01T11:22:33-04:00"], + ["1 Jul 2011 11:22:33 EDT", "2011-07-01T11:22:33-04:00"], + ["1 Aug 2011 11:22:33 EDT", "2011-08-01T11:22:33-04:00"], + ["1 Sep 2011 11:22:33 EDT", "2011-09-01T11:22:33-04:00"], + ["1 Oct 2011 11:22:33 EDT", "2011-10-01T11:22:33-04:00"], + ["1 Nov 2011 11:22:33 EDT", "2011-11-01T11:22:33-04:00"], + ["1 Dec 2011 11:22:33 EST", "2011-12-01T11:22:33-05:00"], + // // test 2-digit year + ["Mon, 1 Jan 01 11:22:33 EST", "2001-01-01T11:22:33-5:00"], + ["Mon, 1 Jan 29 11:22:33 EST", "2029-01-01T11:22:33-5:00"], + ["Tue, 1 Jan 30 11:22:33 EST", "2030-01-01T11:22:33-5:00"], + ["Wed, 1 Jan 31 11:22:33 EST", "2031-01-01T11:22:33-5:00"], + ["Thu, 1 Jan 32 11:22:33 EST", "2032-01-01T11:22:33-5:00"], + ["Fri, 1 Jan 99 11:22:33 EST", "1999-01-01T11:22:33-5:00"], + // // test time zones + ["Mon, 10 Oct 2011 11:22:33 UT", "2011-10-10T11:22:33-00:00"], + ["Mon, 10 Oct 2011 11:22:33 GMT", "2011-10-10T11:22:33-00:00"], + ["Mon, 10 Oct 2011 11:22:33 EST", "2011-10-10T11:22:33-05:00"], + ["Mon, 10 Oct 2011 11:22:33 EDT", "2011-10-10T11:22:33-04:00"], + ["Mon, 10 Oct 2011 11:22:33 CST", "2011-10-10T11:22:33-06:00"], + ["Mon, 10 Oct 2011 11:22:33 CDT", "2011-10-10T11:22:33-05:00"], + ["Mon, 10 Oct 2011 11:22:33 MST", "2011-10-10T11:22:33-07:00"], + ["Mon, 10 Oct 2011 11:22:33 MDT", "2011-10-10T11:22:33-06:00"], + ["Mon, 10 Oct 2011 11:22:33 PST", "2011-10-10T11:22:33-08:00"], + ["Mon, 10 Oct 2011 11:22:33 PDT", "2011-10-10T11:22:33-07:00"], + // military time zones + ["Mon, 10 Oct 2011 11:22:33 A", "2011-10-10T11:22:33+01:00"], + ["Mon, 10 Oct 2011 11:22:33 B", "2011-10-10T11:22:33+02:00"], + ["Mon, 10 Oct 2011 11:22:33 C", "2011-10-10T11:22:33+03:00"], + ["Mon, 10 Oct 2011 11:22:33 D", "2011-10-10T11:22:33+04:00"], + ["Mon, 10 Oct 2011 11:22:33 E", "2011-10-10T11:22:33+05:00"], + ["Mon, 10 Oct 2011 11:22:33 F", "2011-10-10T11:22:33+06:00"], + ["Mon, 10 Oct 2011 11:22:33 G", "2011-10-10T11:22:33+07:00"], + ["Mon, 10 Oct 2011 11:22:33 H", "2011-10-10T11:22:33+08:00"], + ["Mon, 10 Oct 2011 11:22:33 I", "2011-10-10T11:22:33+09:00"], + ["Mon, 10 Oct 2011 11:22:33 K", "2011-10-10T11:22:33+10:00"], + ["Mon, 10 Oct 2011 11:22:33 L", "2011-10-10T11:22:33+11:00"], + ["Mon, 10 Oct 2011 11:22:33 M", "2011-10-10T11:22:33+12:00"], + ["Mon, 10 Oct 2011 11:22:33 N", "2011-10-10T11:22:33-01:00"], + ["Mon, 10 Oct 2011 11:22:33 O", "2011-10-10T11:22:33-02:00"], + ["Mon, 10 Oct 2011 11:22:33 P", "2011-10-10T11:22:33-03:00"], + ["Mon, 10 Oct 2011 11:22:33 Q", "2011-10-10T11:22:33-04:00"], + ["Mon, 10 Oct 2011 11:22:33 R", "2011-10-10T11:22:33-05:00"], + ["Mon, 10 Oct 2011 11:22:33 S", "2011-10-10T11:22:33-06:00"], + ["Mon, 10 Oct 2011 11:22:33 T", "2011-10-10T11:22:33-07:00"], + ["Mon, 10 Oct 2011 11:22:33 U", "2011-10-10T11:22:33-08:00"], + ["Mon, 10 Oct 2011 11:22:33 V", "2011-10-10T11:22:33-09:00"], + ["Mon, 10 Oct 2011 11:22:33 W", "2011-10-10T11:22:33-10:00"], + ["Mon, 10 Oct 2011 11:22:33 X", "2011-10-10T11:22:33-11:00"], + ["Mon, 10 Oct 2011 11:22:33 Y", "2011-10-10T11:22:33-12:00"], + ["Mon, 10 Oct 2011 11:22:33 Z", "2011-10-10T11:22:33-00:00"], + ["Mon, 10 Oct 2011 11:22:33 +0000", "2011-10-10T11:22:33+00:00"], + ["Mon, 10 Oct 2011 11:22:33 -0000", "2011-10-10T11:22:33-00:00"], + ["Mon, 10 Oct 2011 11:22:33 +0100", "2011-10-10T11:22:33+01:00"], + ["Mon, 10 Oct 2011 11:22:33 -0100", "2011-10-10T11:22:33-01:00"] + ]; - [Fact] - public void TestParseDates() + [Theory] + [MemberData(nameof(TestParseDatesData))] + public void TestParseDates(string dateString, string isoString) { - foreach (var test in _tests) + var json = string.Format("{{ date : new Date('{0}') }}", dateString); + BsonDocument document = null; + try + { + document = BsonDocument.Parse(json); + } + catch (Exception ex) + { + var message = string.Format("Error parsing: new Date(\"{0}\"). Message: {1}.", dateString, ex.Message); + throw new AssertionException(message); // note: the test data for 2-digit years needs to be adjusted at the beginning of each year + } + var dateTime = document["date"].ToUniversalTime(); + var expected = DateTime.Parse(isoString).ToUniversalTime(); + Assert.Equal(DateTimeKind.Utc, dateTime.Kind); + Assert.Equal(DateTimeKind.Utc, expected.Kind); + if (dateTime != expected) { - var json = string.Format("{{ date : new Date('{0}') }}", test.Json); - BsonDocument document = null; - try - { - document = BsonDocument.Parse(json); - } - catch (Exception ex) - { - var message = string.Format("Error parsing: new Date(\"{0}\"). Message: {1}.", test.Json, ex.Message); - throw new AssertionException(message); // note: the test data for 2-digit years needs to be adjusted at the beginning of each year - } - var dateTime = document["date"].ToUniversalTime(); - var expected = DateTime.Parse(test.Iso).ToUniversalTime(); - Assert.Equal(DateTimeKind.Utc, dateTime.Kind); - Assert.Equal(DateTimeKind.Utc, expected.Kind); - if (dateTime != expected) - { - var message = string.Format("Parsing new Date(\"{0}\") did not yield expected result {1}.", test.Json, expected.ToString("o")); - throw new AssertionException(message); - } + var message = string.Format("Parsing new Date(\"{0}\") did not yield expected result {1}.", dateString, expected.ToString("o")); + throw new AssertionException(message); } } }