Browse Source

Fixing Date.ToISOString pre 1970

Fixes #285
Brent McSharry 9 years ago
parent
commit
921bb6708a
2 changed files with 64 additions and 15 deletions
  1. 45 3
      Jint.Tests/Runtime/EngineTests.cs
  2. 19 12
      Jint/Native/Date/DatePrototype.cs

+ 45 - 3
Jint.Tests/Runtime/EngineTests.cs

@@ -940,7 +940,7 @@ namespace Jint.Tests.Runtime
                 Assert.Equal("jQuery.js", e.Source);
             }
         }
-
+#region DateParsingAndStrings
         [Fact]
         public void ParseShouldReturnNumber()
         {
@@ -1048,14 +1048,56 @@ namespace Jint.Tests.Runtime
         public void ShouldParseAsLocalTime(string date)
         {
             const string customName = "Custom Time";
-            var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName, customName, customName, null, false);
+            const int timespanMinutes = 11;
+            const int msPriorMidnight = -timespanMinutes * 60 * 1000;
+            var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, timespanMinutes, 0), customName, customName, customName, null, false);
             var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)).SetValue("d", date);
 
             var result = engine.Execute("Date.parse(d);").GetCompletionValue().AsNumber();
 
-            Assert.Equal(-11 * 60 * 1000, result);
+            Assert.Equal(msPriorMidnight, result);
         }
 
+        public static System.Collections.Generic.IEnumerable<object[]> TestDates
+        {
+            get
+            {
+                yield return new object[] { new DateTime(2000, 1, 1) };
+                yield return new object[] { new DateTime(2000, 1, 1, 0, 15, 15, 15) };
+                yield return new object[] { new DateTime(2000, 6, 1, 0, 15, 15, 15) };
+                yield return new object[] { new DateTime(1900, 1, 1) };
+                yield return new object[] { new DateTime(1900, 1, 1, 0, 15, 15, 15) };
+                yield return new object[] { new DateTime(1900, 6, 1, 0, 15, 15, 15) };
+            }
+        }
+
+        [Theory, MemberData("TestDates")]
+        public void TestDateToISOStringFormat(DateTime testDate)
+        {
+            const string customName = "Custom Time";
+            var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(+13, 0, 0), customName, customName, customName, null, false);
+
+            var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone));
+            var testDateTimeOffset = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate));
+            engine.Execute(
+                string.Format("var d = new Date({0},{1},{2},{3},{4},{5},{6});", testDateTimeOffset.Year, testDateTimeOffset.Month - 1, testDateTimeOffset.Day, testDateTimeOffset.Hour, testDateTimeOffset.Minute, testDateTimeOffset.Second, testDateTimeOffset.Millisecond));
+            Assert.Equal(testDateTimeOffset.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'"), engine.Execute("d.toISOString();").GetCompletionValue().ToString());
+        }
+
+        [Theory, MemberData("TestDates")]
+        public void TestDateToStringFormat(DateTime testDate)
+        {
+            const string customName = "Custom Time";
+            var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(+13, 0, 0), customName, customName, customName, null, false);
+
+            var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone));
+            var testDateTimeOffset = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate));
+            engine.Execute(
+                string.Format("var d = new Date({0},{1},{2},{3},{4},{5},{6});", testDateTimeOffset.Year, testDateTimeOffset.Month - 1, testDateTimeOffset.Day, testDateTimeOffset.Hour, testDateTimeOffset.Minute, testDateTimeOffset.Second, testDateTimeOffset.Millisecond));
+            Assert.Equal(testDateTimeOffset.ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'zzz"), engine.Execute("d.toString();").GetCompletionValue().ToString());
+        }
+        
+#endregion //DateParsingAndStrings
         [Fact]
         public void EmptyStringShouldMatchRegex()
         {

+ 19 - 12
Jint/Native/Date/DatePrototype.cs

@@ -539,15 +539,22 @@ namespace Jint.Native.Date
             {
                 throw new JavaScriptException(Engine.RangeError);
             }
-
+            double h = HourFromTime(t);
+            double m = MinFromTime(t);
+            double s = SecFromTime(t);
+            double ms = MsFromTime(t);
+            if (h < 0) { h += HoursPerDay; }
+            if (m < 0) { m += MinutesPerHour; }
+            if (s < 0) { s += SecondsPerMinute; }
+            if (ms < 0) { ms += MsPerSecond; }
             return string.Format("{0:0000}-{1:00}-{2:00}T{3:00}:{4:00}:{5:00}.{6:000}Z",
                 YearFromTime(t),
                 MonthFromTime(t)+1,
                 DateFromTime(t),
-                HourFromTime(t),
-                MinFromTime(t),
-                SecFromTime(t),
-                MsFromTime(t));
+                h,
+                m,
+                s,
+                ms);
         }
 
         private JsValue ToJSON(JsValue thisObj, JsValue[] arguments)
@@ -568,13 +575,13 @@ namespace Jint.Native.Date
             return toIso.TryCast<ICallable>().Call(o, Arguments.Empty);
         }
 
-        public static double HoursPerDay = 24;
-        public static double MinutesPerHour = 60;
-        public static double SecondsPerMinute = 60;
-        public static double MsPerSecond = 1000;
-        public static double MsPerMinute = 60000;
-        public static double MsPerHour = 3600000;
-        public static double MsPerDay = 86400000;
+        public const double HoursPerDay = 24;
+        public const double MinutesPerHour = 60;
+        public const double SecondsPerMinute = 60;
+        public const double MsPerSecond = 1000;
+        public const double MsPerMinute = 60000;
+        public const double MsPerHour = 3600000;
+        public const double MsPerDay = 86400000;
 
         /// <summary>
         /// 15.9.1.2