瀏覽代碼

Improve date handling and enable 262 tests (#690)

Marko Lahma 5 年之前
父節點
當前提交
177a5c7e72

+ 90 - 90
Jint.Tests.Ecma/TestCases/alltests.json

@@ -55995,8 +55995,8 @@
     source: "ch15/15.9/15.9.4/15.9.4.2/S15.9.4.2_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.4/15.9.4.2/S15.9.4.2_A3_T2.js"
   },
   {
@@ -56030,8 +56030,8 @@
     source: "ch15/15.9/15.9.4/15.9.4.3/S15.9.4.3_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.4/15.9.4.3/S15.9.4.3_A3_T2.js"
   },
   {
@@ -56295,8 +56295,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.1/S15.9.5.1_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.1/S15.9.5.1_A3_T2.js"
   },
   {
@@ -56330,8 +56330,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.10/S15.9.5.10_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.10/S15.9.5.10_A3_T2.js"
   },
   {
@@ -56365,8 +56365,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.11/S15.9.5.11_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.11/S15.9.5.11_A3_T2.js"
   },
   {
@@ -56400,8 +56400,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.12/S15.9.5.12_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.12/S15.9.5.12_A3_T2.js"
   },
   {
@@ -56435,8 +56435,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.13/S15.9.5.13_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.13/S15.9.5.13_A3_T2.js"
   },
   {
@@ -56470,8 +56470,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.14/S15.9.5.14_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.14/S15.9.5.14_A3_T2.js"
   },
   {
@@ -56505,8 +56505,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.15/S15.9.5.15_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.15/S15.9.5.15_A3_T2.js"
   },
   {
@@ -56540,8 +56540,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.16/S15.9.5.16_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.16/S15.9.5.16_A3_T2.js"
   },
   {
@@ -56575,8 +56575,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.17/S15.9.5.17_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.17/S15.9.5.17_A3_T2.js"
   },
   {
@@ -56610,8 +56610,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.18/S15.9.5.18_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.18/S15.9.5.18_A3_T2.js"
   },
   {
@@ -56645,8 +56645,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.19/S15.9.5.19_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.19/S15.9.5.19_A3_T2.js"
   },
   {
@@ -56680,8 +56680,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.2/S15.9.5.2_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.2/S15.9.5.2_A3_T2.js"
   },
   {
@@ -56715,8 +56715,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.20/S15.9.5.20_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.20/S15.9.5.20_A3_T2.js"
   },
   {
@@ -56750,8 +56750,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.21/S15.9.5.21_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.21/S15.9.5.21_A3_T2.js"
   },
   {
@@ -56785,8 +56785,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.22/S15.9.5.22_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.22/S15.9.5.22_A3_T2.js"
   },
   {
@@ -56820,8 +56820,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.23/S15.9.5.23_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.23/S15.9.5.23_A3_T2.js"
   },
   {
@@ -56855,8 +56855,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.24/S15.9.5.24_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.24/S15.9.5.24_A3_T2.js"
   },
   {
@@ -56890,8 +56890,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.25/S15.9.5.25_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.25/S15.9.5.25_A3_T2.js"
   },
   {
@@ -56925,8 +56925,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.26/S15.9.5.26_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.26/S15.9.5.26_A3_T2.js"
   },
   {
@@ -56960,8 +56960,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.27/S15.9.5.27_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.27/S15.9.5.27_A3_T2.js"
   },
   {
@@ -56995,8 +56995,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.28/S15.9.5.28_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.28/S15.9.5.28_A3_T2.js"
   },
   {
@@ -57030,8 +57030,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.29/S15.9.5.29_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.29/S15.9.5.29_A3_T2.js"
   },
   {
@@ -57065,8 +57065,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.3/S15.9.5.3_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.3/S15.9.5.3_A3_T2.js"
   },
   {
@@ -57100,8 +57100,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.30/S15.9.5.30_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.30/S15.9.5.30_A3_T2.js"
   },
   {
@@ -57135,8 +57135,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.31/S15.9.5.31_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.31/S15.9.5.31_A3_T2.js"
   },
   {
@@ -57170,8 +57170,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.32/S15.9.5.32_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.32/S15.9.5.32_A3_T2.js"
   },
   {
@@ -57205,8 +57205,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.33/S15.9.5.33_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.33/S15.9.5.33_A3_T2.js"
   },
   {
@@ -57240,8 +57240,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.34/S15.9.5.34_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.34/S15.9.5.34_A3_T2.js"
   },
   {
@@ -57275,8 +57275,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.35/S15.9.5.35_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.35/S15.9.5.35_A3_T2.js"
   },
   {
@@ -57310,8 +57310,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.36/S15.9.5.36_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.36/S15.9.5.36_A3_T2.js"
   },
   {
@@ -57345,8 +57345,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.37/S15.9.5.37_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.37/S15.9.5.37_A3_T2.js"
   },
   {
@@ -57380,8 +57380,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.38/S15.9.5.38_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.38/S15.9.5.38_A3_T2.js"
   },
   {
@@ -57415,8 +57415,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.39/S15.9.5.39_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.39/S15.9.5.39_A3_T2.js"
   },
   {
@@ -57450,8 +57450,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.4/S15.9.5.4_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.4/S15.9.5.4_A3_T2.js"
   },
   {
@@ -57460,8 +57460,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.4/S15.9.5.4_A3_T3.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.40/15.9.5.40_1.js"
   },
   {
@@ -57490,8 +57490,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.40/S15.9.5.40_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.40/S15.9.5.40_A3_T2.js"
   },
   {
@@ -57525,8 +57525,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.41/S15.9.5.41_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.41/S15.9.5.41_A3_T2.js"
   },
   {
@@ -57560,8 +57560,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.42/S15.9.5.42_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.42/S15.9.5.42_A3_T2.js"
   },
   {
@@ -57680,8 +57680,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.5/S15.9.5.5_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.5/S15.9.5.5_A3_T2.js"
   },
   {
@@ -57715,8 +57715,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.6/S15.9.5.6_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.6/S15.9.5.6_A3_T2.js"
   },
   {
@@ -57750,8 +57750,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.7/S15.9.5.7_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.7/S15.9.5.7_A3_T2.js"
   },
   {
@@ -57785,8 +57785,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.8/S15.9.5.8_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.8/S15.9.5.8_A3_T2.js"
   },
   {
@@ -57820,8 +57820,8 @@
     source: "ch15/15.9/15.9.5/15.9.5.9/S15.9.5.9_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "obsolete",
     source: "ch15/15.9/15.9.5/15.9.5.9/S15.9.5.9_A3_T2.js"
   },
   {

+ 15 - 0
Jint.Tests.Test262/DateTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class DateTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Date")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Date", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Date", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 2 - 1
Jint.Tests.Test262/Jint.Tests.Test262.csproj

@@ -1,12 +1,13 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netcoreapp3.0</TargetFramework>
+    <TargetFrameworks>net452;netcoreapp3.0</TargetFrameworks>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
+    <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 4 - 7
Jint.Tests.Test262/Test262Test.cs

@@ -27,11 +27,6 @@ namespace Jint.Tests.Test262
         private static readonly HashSet<string> _strictSkips =
             new HashSet<string>(StringComparer.OrdinalIgnoreCase);
         
-        private static readonly Dictionary<string, string> extraSourceFiles = new Dictionary<string, string>()
-        {
-            { "built-ins/Array/isArray/descriptor.js", "propertyHelper.js"}
-        };
-
         static Test262Test()
         {
             //NOTE: The Date tests in test262 assume the local timezone is Pacific Standard Time
@@ -50,6 +45,8 @@ namespace Jint.Tests.Test262
                 "compareArray.js",
                 "decimalToHexString.js",
                 "proxyTrapsHelper.js",
+                "dateConstants.js",
+                "assertRelativeDateMs.js"
             };
 
             Sources = new Dictionary<string, string>(files.Length);
@@ -158,7 +155,7 @@ namespace Jint.Tests.Test262
                 var flags = Regex.Match(code, "flags: \\[(.+?)\\]");
                 if (flags.Success)
                 {
-                    var items = flags.Groups[1].Captures[0].Value.Split(",");
+                    var items = flags.Groups[1].Captures[0].Value.Split(',');
                     foreach (var item in items.Select(x => x.Trim()))
                     {
                         switch (item)
@@ -175,7 +172,7 @@ namespace Jint.Tests.Test262
                 var features = Regex.Match(code, "features: \\[(.+?)\\]");
                 if (features.Success)
                 {
-                    var items = features.Groups[1].Captures[0].Value.Split(",");
+                    var items = features.Groups[1].Captures[0].Value.Split(',');
                     foreach (var item in items.Select(x => x.Trim()))
                     {
                         switch (item)

+ 28 - 0
Jint.Tests.Test262/test/skipped.json

@@ -1,4 +1,12 @@
 [
+  {
+    "source": "built-ins/Date/subclassing.js",
+    "reason": "subclassing not implemented"
+  },
+  {
+    "source": "built-ins/Date/parse/time-value-maximum-range.js",
+    "reason": "parsing of large/small years not implemented in .NET (-271821, +271821)"
+  },
   {
     "source": "built-ins/Proxy/enumerate/removed-does-not-trigger.js",
     "reason": "for-of not implemented"
@@ -281,6 +289,26 @@
     "source": "built-ins/StringIteratorPrototype/next/next-iteration-surrogate-pairs.js",
     "reason": "code point iteration not implemented"
   },
+  {
+    "source": "built-ins/Date/prototype/toTimeString/format.js",
+    "reason": "let/const not implemented"
+  },
+  {
+    "source": "built-ins/Date/prototype/toString/format.js",
+    "reason": "let/const not implemented"
+  },
+  {
+    "source": "built-ins/Date/parse/zero.js",
+    "reason": "let/const not implemented"
+  },
+  {
+    "source": "built-ins/Date/prototype/toUTCString/format.js",
+    "reason": "let/const not implemented"
+  },
+  {
+    "source": "built-ins/Date/prototype/toDateString/format.js",
+    "reason": "let/const not implemented"
+  },
   {
     "source": "language/statements/for-in/head-const-bound-names-fordecl-tdz.js",
     "reason": "let/const not implemented"

+ 11 - 15
Jint.Tests/Runtime/EngineTests.cs

@@ -437,23 +437,18 @@ namespace Jint.Tests.Runtime
 
         [Fact]
         public void DateConstructorWithInvalidParameters()
-        {
-            Assert.Throws<JavaScriptException>(() => RunTest("new Date (1,  Infinity);"));
-        }
-
-        [Fact]
-        public void ShouldConvertDateToNumber()
         {
             RunTest(@"
-                assert(Number(new Date(0)) === 0);
+                var dt = new Date (1,  Infinity);
+                assert(isNaN(dt.getTime()));
             ");
         }
 
         [Fact]
-        public void DatePrimitiveValueShouldBeNaN()
+        public void ShouldConvertDateToNumber()
         {
             RunTest(@"
-                assert(isNaN(Date.prototype.valueOf()));
+                assert(Number(new Date(0)) === 0);
             ");
         }
 
@@ -1236,10 +1231,10 @@ namespace Jint.Tests.Runtime
             Assert.Equal(-11 * 60 * 1000, parseLocalEpoch);
 
             var epochToLocalString = engine.Execute("var d = new Date(0); d.toString();").GetCompletionValue().AsString();
-            Assert.Equal("Thu Jan 01 1970 00:11:00 GMT+00:11", epochToLocalString);
+            Assert.Equal("Thu Jan 01 1970 00:11:00 GMT+0011", epochToLocalString);
 
             var epochToUTCString = engine.Execute("var d = new Date(0); d.toUTCString();").GetCompletionValue().AsString();
-            Assert.Equal("Thu Jan 01 1970 00:00:00 GMT", epochToUTCString);
+            Assert.Equal("Thu, 01 Jan 1970 00:00:00 GMT", epochToUTCString);
         }
 
         [Theory]
@@ -1343,7 +1338,8 @@ namespace Jint.Tests.Runtime
             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));
 
-            var expected = testDateTimeOffset.ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'zzz", CultureInfo.InvariantCulture);
+            var expected = testDateTimeOffset.ToString("ddd MMM dd yyyy HH:mm:ss", CultureInfo.InvariantCulture);
+            expected += testDateTimeOffset.ToString(" 'GMT'zzz", CultureInfo.InvariantCulture).Replace(":", "");
             var actual = engine.Execute("d.toString();").GetCompletionValue().ToString();
 
             Assert.Equal(expected, actual);
@@ -1900,9 +1896,9 @@ var prep = function (fn) { fn(); };
             engine.Execute(@"
                     var d = new Date(1433160000000);
 
-                    equal('Mon Jun 01 2015 05:00:00 GMT-07:00', d.toString());
+                    equal('Mon Jun 01 2015 05:00:00 GMT-0700', d.toString());
                     equal('Mon Jun 01 2015', d.toDateString());
-                    equal('05:00:00 GMT-07:00', d.toTimeString());
+                    equal('05:00:00 GMT-0700', d.toTimeString());
                     equal('lundi 1 juin 2015 05:00:00', d.toLocaleString());
                     equal('lundi 1 juin 2015', d.toLocaleDateString());
                     equal('05:00:00', d.toLocaleTimeString());
@@ -1922,7 +1918,7 @@ var prep = function (fn) { fn(); };
             engine.Execute(@"
                     var d = new Date(2016, 8, 1);
 
-                    equal('Thu Sep 01 2016 00:00:00 GMT-04:00', d.toString());
+                    equal('Thu Sep 01 2016 00:00:00 GMT-0400', d.toString());
                     equal('Thu Sep 01 2016', d.toDateString());
             ");
         }

+ 19 - 1
Jint/EsprimaExtensions.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Globalization;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Runtime;
@@ -14,7 +15,7 @@ namespace Jint
         {
             if (expression is Literal literal)
             {
-                return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
+                return LiteralKeyToString(literal);
             }
 
             if (!computed && expression is Identifier identifier)
@@ -53,5 +54,22 @@ namespace Jint
             var type = node.Type;
             return type == Nodes.FunctionExpression || type == Nodes.ArrowFunctionExpression || type == Nodes.ArrowParameterPlaceHolder;
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static Key LiteralKeyToString(Literal literal)
+        {
+            // prevent conversion to scientific notation
+            if (literal.Value is double d)
+            {
+                return DoubleToString(d);
+            }
+            return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static string DoubleToString(double d)
+        {
+            return (d - (long) d) == 0 ? ((long) d).ToString() : d.ToString(CultureInfo.InvariantCulture);
+        }
     }
 }

+ 48 - 50
Jint/Native/Date/DateConstructor.cs

@@ -68,7 +68,7 @@ namespace Jint.Native.Date
             // The value of the [[Prototype]] internal property of the Date constructor is the Function prototype object
             obj.PrototypeObject = DatePrototype.CreatePrototypeObject(engine, obj);
 
-            obj._length = new PropertyDescriptor(7, PropertyFlag.AllForbidden);
+            obj._length = new PropertyDescriptor(7, PropertyFlag.Configurable);
 
             // The initial value of Date.prototype is the Date prototype object
             obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
@@ -78,11 +78,14 @@ namespace Jint.Native.Date
 
         protected override void Initialize()
         {
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
+            
             _properties = new StringDictionarySlim<PropertyDescriptor>(3)
             {
-                ["parse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parse", Parse, 1), true, false, true),
-                ["UTC"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "utc", Utc, 7), true, false, true),
-                ["now"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "now", Now, 0), true, false, true)
+                ["parse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "parse", Parse, 1, lengthFlags), propertyFlags),
+                ["UTC"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "UTC", Utc, 7, lengthFlags), propertyFlags),
+                ["now"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "now", Now, 0, lengthFlags), propertyFlags)
             };
         }
 
@@ -110,7 +113,25 @@ namespace Jint.Native.Date
 
         private JsValue Utc(JsValue thisObj, JsValue[] arguments)
         {
-            return TimeClip(ConstructTimeValue(arguments, useUtc: true));
+            var y = TypeConverter.ToNumber(arguments.At(0));
+            var m = TypeConverter.ToNumber(arguments.At(1, JsNumber.PositiveZero));
+            var dt = TypeConverter.ToNumber(arguments.At(2, JsNumber.One));
+            var h = TypeConverter.ToNumber(arguments.At(3, JsNumber.PositiveZero));
+            var min = TypeConverter.ToNumber(arguments.At(4, JsNumber.PositiveZero));
+            var s = TypeConverter.ToNumber(arguments.At(5, JsNumber.PositiveZero));
+            var milli = TypeConverter.ToNumber(arguments.At(6, JsNumber.PositiveZero));
+
+            var yInteger = TypeConverter.ToInteger(y);
+            if (!double.IsNaN(y) && 0 <= yInteger && yInteger <= 99)
+            {
+                y  = yInteger + 1900;
+            }
+
+            var finalDate = DatePrototype.MakeDate(
+                DatePrototype.MakeDay(y, m, dt),
+                DatePrototype.MakeTime(h, min, s, milli));
+
+            return TimeClip(finalDate);
         }
 
         private static JsValue Now(JsValue thisObj, JsValue[] arguments)
@@ -134,64 +155,41 @@ namespace Jint.Native.Date
             }
             else if (arguments.Length == 1)
             {
+                if (arguments[0] is DateInstance date)
+                {
+                    return Construct(date.PrimitiveValue);
+                }
+                
                 var v = TypeConverter.ToPrimitive(arguments[0]);
                 if (v.IsString())
                 {
                     return Construct(((JsNumber) Parse(Undefined, Arguments.From(v)))._value);
                 }
 
-                return Construct(TypeConverter.ToNumber(v));
+                return Construct(TimeClip(TypeConverter.ToNumber(v)));
             }
             else
             {
-                return Construct(ConstructTimeValue(arguments, useUtc: false));
-            }
-        }
-
-        private double ConstructTimeValue(JsValue[] arguments, bool useUtc)
-        {
-            if (arguments.Length < 2)
-            {
-                ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(arguments), "There must be at least two arguments.");
-            }
-
-            int SafeInteger(JsValue value)
-            {
-                var integer = TypeConverter.ToInteger(value);
-                if (integer > int.MaxValue || integer < int.MinValue)
+                var y = TypeConverter.ToNumber(arguments.At(0));
+                var m = TypeConverter.ToNumber(arguments.At(1));
+                var dt = TypeConverter.ToNumber(arguments.At(2, JsNumber.One));
+                var h = TypeConverter.ToNumber(arguments.At(3, JsNumber.PositiveZero));
+                var min = TypeConverter.ToNumber(arguments.At(4, JsNumber.PositiveZero));
+                var s = TypeConverter.ToNumber(arguments.At(5, JsNumber.PositiveZero));
+                var milli = TypeConverter.ToNumber(arguments.At(6, JsNumber.PositiveZero));
+
+                var yInteger = TypeConverter.ToInteger(y);
+                if (!double.IsNaN(y) && 0 <= yInteger && yInteger <= 99)
                 {
-                    ExceptionHelper.ThrowTypeError(_engine, "Invalid Date.");
+                    y += 1900;
                 }
 
-                return (int) integer;
-            }
-
-            var y = TypeConverter.ToNumber(arguments[0]);
-            var m = SafeInteger(arguments[1]);
-            var dt = arguments.Length > 2 ? SafeInteger(arguments[2]) : 1;
-            var h = arguments.Length > 3 ? SafeInteger(arguments[3]) : 0;
-            var min = arguments.Length > 4 ? SafeInteger(arguments[4]) : 0;
-            var s = arguments.Length > 5 ? SafeInteger(arguments[5]) : 0;
-            var milli = arguments.Length > 6 ? SafeInteger(arguments[6]) : 0;
+                var finalDate = DatePrototype.MakeDate(
+                    DatePrototype.MakeDay(y, m, dt),
+                    DatePrototype.MakeTime(h, min, s, milli));
 
-            for (int i = 2; i < arguments.Length; i++)
-            {
-                if (double.IsNaN(TypeConverter.ToNumber(arguments[i])))
-                {
-                    return double.NaN;
-                }
+                return Construct(TimeClip(PrototypeObject.Utc(finalDate)));
             }
-
-            if (!double.IsNaN(y) && 0 <= TypeConverter.ToInteger(y) && TypeConverter.ToInteger(y) <= 99)
-            {
-                y += 1900;
-            }
-
-            var finalDate = DatePrototype.MakeDate(
-                DatePrototype.MakeDay(y, m, dt),
-                DatePrototype.MakeTime(h, min, s, milli));
-
-            return TimeClip(useUtc ? finalDate : PrototypeObject.Utc(finalDate));
         }
 
         public DatePrototype PrototypeObject { get; private set; }
@@ -235,7 +233,7 @@ namespace Jint.Native.Date
                 return double.NaN;
             }
 
-            return TypeConverter.ToInteger(time);
+            return TypeConverter.ToInteger(time) + 0;
         }
 
         public double FromDateTime(DateTime dt)

+ 21 - 5
Jint/Native/Date/DateInstance.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Globalization;
 using Jint.Native.Object;
 using Jint.Runtime;
 
@@ -15,18 +16,33 @@ namespace Jint.Native.Date
         public DateInstance(Engine engine)
             : base(engine, objectClass: "Date")
         {
+            PrimitiveValue = double.NaN;
         }
 
         public DateTime ToDateTime()
         {
-            if (double.IsNaN(PrimitiveValue) || PrimitiveValue > Max || PrimitiveValue < Min)
+            return DateTimeRangeValid 
+                ? DateConstructor.Epoch.AddMilliseconds(PrimitiveValue)
+                : ExceptionHelper.ThrowRangeError<DateTime>(Engine);
+        }
+
+        public double PrimitiveValue { get; set; }
+
+        internal bool DateTimeRangeValid => !double.IsNaN(PrimitiveValue) && PrimitiveValue <= Max && PrimitiveValue >= Min;
+
+        public override string ToString()
+        {
+            if (double.IsNaN(PrimitiveValue))
             {
-                return ExceptionHelper.ThrowRangeError<DateTime>(Engine);
+                return "NaN";
             }
 
-            return DateConstructor.Epoch.AddMilliseconds(PrimitiveValue);
-        }
+            if (double.IsInfinity(PrimitiveValue))
+            {
+                return "Infinity";
+            }
 
-        public double PrimitiveValue { get; set; }
+            return ToDateTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'K", CultureInfo.InvariantCulture);
+        }
     }
 }

+ 364 - 242
Jint/Native/Date/DatePrototype.cs

@@ -2,6 +2,8 @@
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using Jint.Collections;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -11,8 +13,14 @@ namespace Jint.Native.Date
     /// <summary>
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.5
     /// </summary>
-    public sealed class DatePrototype : DateInstance
+    public sealed class DatePrototype : ObjectInstance
     {
+        // ES6 section 20.3.1.1 Time Values and Time Range
+        private const double MinYear = -1000000.0;
+        private const double MaxYear = -MinYear;
+        private const double MinMonth = -10000000.0;
+        private const double MaxMonth = -MinMonth;
+        
         private DateConstructor _dateConstructor;
 
         private DatePrototype(Engine engine)
@@ -25,7 +33,6 @@ namespace Jint.Native.Date
             var obj = new DatePrototype(engine)
             {
                 _prototype = engine.Object.PrototypeObject,
-                PrimitiveValue = double.NaN,
                 _dateConstructor = dateConstructor
             };
 
@@ -34,57 +41,91 @@ namespace Jint.Native.Date
 
         protected override  void Initialize()
         {
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
+
             _properties = new StringDictionarySlim<PropertyDescriptor>(50)
             {
                 ["constructor"] = new PropertyDescriptor(_dateConstructor, PropertyFlag.NonEnumerable),
-                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0), true, false, true),
-                ["toDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toDateString", ToDateString, 0), true, false, true),
-                ["toTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toTimeString", ToTimeString, 0), true, false, true),
-                ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0), true, false, true),
-                ["toLocaleDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleDateString", ToLocaleDateString, 0), true, false, true),
-                ["toLocaleTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleTimeString", ToLocaleTimeString, 0), true, false, true),
-                ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0), true, false, true),
-                ["getTime"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getTime", GetTime, 0), true, false, true),
-                ["getFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getFullYear", GetFullYear, 0), true, false, true),
-                ["getYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getYear", GetYear, 0), true, false, true),
-                ["getUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCFullYear", GetUTCFullYear, 0), true, false, true),
-                ["getMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMonth", GetMonth, 0), true, false, true),
-                ["getUTCMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMonth", GetUTCMonth, 0), true, false, true),
-                ["getDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getDate", GetDate, 0), true, false, true),
-                ["getUTCDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCDate", GetUTCDate, 0), true, false, true),
-                ["getDay"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getDay", GetDay, 0), true, false, true),
-                ["getUTCDay"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCDay", GetUTCDay, 0), true, false, true),
-                ["getHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getHours", GetHours, 0), true, false, true),
-                ["getUTCHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCHours", GetUTCHours, 0), true, false, true),
-                ["getMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMinutes", GetMinutes, 0), true, false, true),
-                ["getUTCMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMInutes", GetUTCMinutes, 0), true, false, true),
-                ["getSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getSeconds", GetSeconds, 0), true, false, true),
-                ["getUTCSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCSeconds", GetUTCSeconds, 0), true, false, true),
-                ["getMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMilliseconds", GetMilliseconds, 0), true, false, true),
-                ["getUTCMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMilliseconds", GetUTCMilliseconds, 0), true, false, true),
-                ["getTimezoneOffset"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getTimezoneOffset", GetTimezoneOffset, 0), true, false, true),
-                ["setTime"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setTime", SetTime, 1), true, false, true),
-                ["setMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMilliseconds", SetMilliseconds, 1), true, false, true),
-                ["setUTCMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMilliseconds", SetUTCMilliseconds, 1), true, false, true),
-                ["setSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setSeconds", SetSeconds, 2), true, false, true),
-                ["setUTCSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCSeconds", SetUTCSeconds, 2), true, false, true),
-                ["setMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMinutes", SetMinutes, 3), true, false, true),
-                ["setUTCMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMinutes", SetUTCMinutes, 3), true, false, true),
-                ["setHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setHours", SetHours, 4), true, false, true),
-                ["setUTCHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCHours", SetUTCHours, 4), true, false, true),
-                ["setDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setDate", SetDate, 1), true, false, true),
-                ["setUTCDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCDate", SetUTCDate, 1), true, false, true),
-                ["setMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMonth", SetMonth, 2), true, false, true),
-                ["setUTCMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMonth", SetUTCMonth, 2), true, false, true),
-                ["setFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setFullYear", SetFullYear, 3), true, false, true),
-                ["setYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setYear", SetYear, 1), true, false, true),
-                ["setUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCFullYear     ", SetUTCFullYear, 3), true, false, true),
-                ["toUTCString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toUTCString", ToUtcString, 0), true, false, true),
-                ["toISOString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toISOString", ToISOString, 0), true, false, true),
-                ["toJSON"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toJSON", ToJSON, 1), true, false, true)
+                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, lengthFlags), propertyFlags),
+                ["toDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toDateString", ToDateString, 0, lengthFlags), propertyFlags),
+                ["toTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toTimeString", ToTimeString, 0, lengthFlags), propertyFlags),
+                ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, lengthFlags), propertyFlags),
+                ["toLocaleDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleDateString", ToLocaleDateString, 0, lengthFlags), propertyFlags),
+                ["toLocaleTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleTimeString", ToLocaleTimeString, 0, lengthFlags), propertyFlags),
+                ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, lengthFlags), propertyFlags),
+                ["getTime"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getTime", GetTime, 0, lengthFlags), propertyFlags),
+                ["getFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getFullYear", GetFullYear, 0, lengthFlags), propertyFlags),
+                ["getYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getYear", GetYear, 0, lengthFlags), propertyFlags),
+                ["getUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCFullYear", GetUTCFullYear, 0, lengthFlags), propertyFlags),
+                ["getMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMonth", GetMonth, 0, lengthFlags), propertyFlags),
+                ["getUTCMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMonth", GetUTCMonth, 0, lengthFlags), propertyFlags),
+                ["getDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getDate", GetDate, 0, lengthFlags), propertyFlags),
+                ["getUTCDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCDate", GetUTCDate, 0, lengthFlags), propertyFlags),
+                ["getDay"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getDay", GetDay, 0, lengthFlags), propertyFlags),
+                ["getUTCDay"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCDay", GetUTCDay, 0, lengthFlags), propertyFlags),
+                ["getHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getHours", GetHours, 0, lengthFlags), propertyFlags),
+                ["getUTCHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCHours", GetUTCHours, 0, lengthFlags), propertyFlags),
+                ["getMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMinutes", GetMinutes, 0, lengthFlags), propertyFlags),
+                ["getUTCMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMinutes", GetUTCMinutes, 0, lengthFlags), propertyFlags),
+                ["getSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getSeconds", GetSeconds, 0, lengthFlags), propertyFlags),
+                ["getUTCSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCSeconds", GetUTCSeconds, 0, lengthFlags), propertyFlags),
+                ["getMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMilliseconds", GetMilliseconds, 0, lengthFlags), propertyFlags),
+                ["getUTCMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMilliseconds", GetUTCMilliseconds, 0, lengthFlags), propertyFlags),
+                ["getTimezoneOffset"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getTimezoneOffset", GetTimezoneOffset, 0, lengthFlags), propertyFlags),
+                ["setTime"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setTime", SetTime, 1, lengthFlags), propertyFlags),
+                ["setMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMilliseconds", SetMilliseconds, 1, lengthFlags), propertyFlags),
+                ["setUTCMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMilliseconds", SetUTCMilliseconds, 1, lengthFlags), propertyFlags),
+                ["setSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setSeconds", SetSeconds, 2, lengthFlags), propertyFlags),
+                ["setUTCSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCSeconds", SetUTCSeconds, 2, lengthFlags), propertyFlags),
+                ["setMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMinutes", SetMinutes, 3, lengthFlags), propertyFlags),
+                ["setUTCMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMinutes", SetUTCMinutes, 3, lengthFlags), propertyFlags),
+                ["setHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setHours", SetHours, 4, lengthFlags), propertyFlags),
+                ["setUTCHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCHours", SetUTCHours, 4, lengthFlags), propertyFlags),
+                ["setDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setDate", SetDate, 1, lengthFlags), propertyFlags),
+                ["setUTCDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCDate", SetUTCDate, 1, lengthFlags), propertyFlags),
+                ["setMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMonth", SetMonth, 2, lengthFlags), propertyFlags),
+                ["setUTCMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMonth", SetUTCMonth, 2, lengthFlags), propertyFlags),
+                ["setFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setFullYear", SetFullYear, 3, lengthFlags), propertyFlags),
+                ["setYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setYear", SetYear, 1, lengthFlags), propertyFlags),
+                ["setUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCFullYear", SetUTCFullYear, 3, lengthFlags), propertyFlags),
+                ["toUTCString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toUTCString", ToUtcString, 0, lengthFlags), propertyFlags),
+                ["toISOString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toISOString", ToISOString, 0, lengthFlags), propertyFlags),
+                ["toJSON"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toJSON", ToJSON, 1, lengthFlags), propertyFlags),
+                [GlobalSymbolRegistry.ToPrimitive] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.toPrimitive]", ToPrimitive, 1, PropertyFlag.Configurable), PropertyFlag.Configurable),
             };
         }
 
+        private JsValue ToPrimitive(JsValue thisObject, JsValue[] arguments)
+        {
+            if (!(thisObject is ObjectInstance oi))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
+            }
+
+            var hint = arguments.At(0);
+            if (!hint.IsString())
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
+            }
+
+            Types tryFirst = Types.None;
+            if (hint == "default" || hint == "string")
+            {
+                tryFirst = Types.String;
+            }
+            else  if (hint == "number")
+            {
+                tryFirst = Types.Number;
+            }
+            else
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
+            }
+
+            return TypeConverter.OrdinaryToPrimitive(oi, tryFirst);
+        }
+
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         {
             return EnsureDateInstance(thisObj).PrimitiveValue;
@@ -96,7 +137,8 @@ namespace Jint.Native.Date
         /// </summary>
         private DateInstance EnsureDateInstance(JsValue thisObj)
         {
-            return thisObj as DateInstance ?? ExceptionHelper.ThrowTypeError<DateInstance>(_engine, "Invalid Date");
+            return thisObj as DateInstance 
+                   ?? ExceptionHelper.ThrowTypeError<DateInstance>(_engine, "this is not a Date object");
         }
 
         public JsValue ToString(JsValue thisObj, JsValue[] arg2)
@@ -106,7 +148,8 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
 
-            return ToLocalTime(dateInstance.ToDateTime()).ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'K", CultureInfo.InvariantCulture);
+            var t = ToLocalTime(dateInstance.ToDateTime());
+            return t.ToString("ddd MMM dd yyyy HH:mm:ss ", CultureInfo.InvariantCulture) + TimeZoneString(t);
         }
 
         private JsValue ToDateString(JsValue thisObj, JsValue[] arguments)
@@ -126,7 +169,16 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
 
-            return ToLocalTime(dateInstance.ToDateTime()).ToString("HH:mm:ss 'GMT'K", CultureInfo.InvariantCulture);
+            var t = ToLocalTime(dateInstance.ToDateTime());
+
+            var timeString = t.ToString("HH:mm:ss ", CultureInfo.InvariantCulture);
+            var timeZoneString = TimeZoneString(t);
+            return timeString + timeZoneString;
+        }
+
+        private static string TimeZoneString(DateTimeOffset t)
+        {
+            return t.ToString("'GMT'K", CultureInfo.InvariantCulture).Replace(":", "");
         }
 
         private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)
@@ -161,209 +213,191 @@ namespace Jint.Native.Date
 
         private JsValue GetTime(JsValue thisObj, JsValue[] arguments)
         {
-            if (double.IsNaN(EnsureDateInstance(thisObj).PrimitiveValue))
+            var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
-            return EnsureDateInstance(thisObj).PrimitiveValue;
+            return t;
         }
 
         private JsValue GetFullYear(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return YearFromTime(LocalTime(t));
         }
 
         private JsValue GetYear(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return YearFromTime(LocalTime(t)) - 1900;
         }
 
         private JsValue GetUTCFullYear(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return YearFromTime(t);
         }
 
         private JsValue GetMonth(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return MonthFromTime(LocalTime(t));
         }
 
         private JsValue GetUTCMonth(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return MonthFromTime(t);
         }
 
         private JsValue GetDate(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return DateFromTime(LocalTime(t));
         }
 
         private JsValue GetUTCDate(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return DateFromTime(t);
         }
 
         private JsValue GetDay(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return WeekDay(LocalTime(t));
         }
 
         private JsValue GetUTCDay(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return WeekDay(t);
         }
 
         private JsValue GetHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return HourFromTime(LocalTime(t));
         }
 
         private JsValue GetUTCHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return HourFromTime(t);
         }
 
         private JsValue GetMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return MinFromTime(LocalTime(t));
         }
 
         private JsValue GetUTCMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return MinFromTime(t);
         }
 
         private JsValue GetSeconds(JsValue thisObj, JsValue[] arguments)
         {
-            var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
-            if (double.IsNaN(t))
+            var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return SecFromTime(LocalTime(t));
         }
 
         private JsValue GetUTCSeconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return SecFromTime(t);
         }
 
         private JsValue GetMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return MsFromTime(LocalTime(t));
         }
 
         private JsValue GetUTCMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return MsFromTime(t);
         }
 
         private JsValue GetTimezoneOffset(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
-            if (double.IsNaN(t))
+            if (!IsFinite(t))
             {
                 return JsNumber.DoubleNaN;
             }
-
             return (int) (t - LocalTime(t))/MsPerMinute;
         }
 
@@ -375,6 +409,10 @@ namespace Jint.Native.Date
         private JsValue SetMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), TypeConverter.ToNumber(arguments.At(0)));
             var u = TimeClip(Utc(MakeDate(Day(t), time)));
             thisObj.As<DateInstance>().PrimitiveValue = u;
@@ -384,6 +422,12 @@ namespace Jint.Native.Date
         private JsValue SetUTCMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            
+            if (!IsFinite(t))
+            {
+                return double.NaN;
+            }
+
             var time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), TypeConverter.ToNumber(arguments.At(0)));
             var u = TimeClip(MakeDate(Day(t), time));
             thisObj.As<DateInstance>().PrimitiveValue = u;
@@ -393,6 +437,10 @@ namespace Jint.Native.Date
         private JsValue SetSeconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var s = TypeConverter.ToNumber(arguments.At(0));
             var milli = arguments.Length <= 1 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
@@ -404,6 +452,10 @@ namespace Jint.Native.Date
         private JsValue SetUTCSeconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var s = TypeConverter.ToNumber(arguments.At(0));
             var milli = arguments.Length <= 1 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
@@ -415,6 +467,10 @@ namespace Jint.Native.Date
         private JsValue SetMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var m = TypeConverter.ToNumber(arguments.At(0));
             var s = arguments.Length <= 1 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var milli = arguments.Length <= 2 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
@@ -427,6 +483,10 @@ namespace Jint.Native.Date
         private JsValue SetUTCMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var m = TypeConverter.ToNumber(arguments.At(0));
             var s = arguments.Length <= 1 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var milli = arguments.Length <= 2 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
@@ -439,6 +499,10 @@ namespace Jint.Native.Date
         private JsValue SetHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var h = TypeConverter.ToNumber(arguments.At(0));
             var m = arguments.Length <= 1 ? MinFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var s = arguments.Length <= 2 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
@@ -452,6 +516,10 @@ namespace Jint.Native.Date
         private JsValue SetUTCHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var h = TypeConverter.ToNumber(arguments.At(0));
             var m = arguments.Length <= 1 ? MinFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var s = arguments.Length <= 2 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
@@ -465,8 +533,13 @@ namespace Jint.Native.Date
         private JsValue SetDate(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var dt = TypeConverter.ToNumber(arguments.At(0));
-            var newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
+            var (year, month, __) = YearMonthDayFromTime(t);
+            var newDate = MakeDate(MakeDay(year, month, dt), TimeWithinDay(t));
             var u = TimeClip(Utc(newDate));
             thisObj.As<DateInstance>().PrimitiveValue = u;
             return u;
@@ -474,7 +547,11 @@ namespace Jint.Native.Date
 
         private JsValue SetUTCDate(JsValue thisObj, JsValue[] arguments)
         {
-            var t = (long) EnsureDateInstance(thisObj).PrimitiveValue;
+            var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var dt = TypeConverter.ToNumber(arguments.At(0));
             var newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
             var u = TimeClip(newDate);
@@ -485,6 +562,10 @@ namespace Jint.Native.Date
         private JsValue SetMonth(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var m = TypeConverter.ToNumber(arguments.At(0));
             var dt = arguments.Length <= 1 ? DateFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
@@ -495,7 +576,11 @@ namespace Jint.Native.Date
 
         private JsValue SetUTCMonth(JsValue thisObj, JsValue[] arguments)
         {
-            var t = (long) EnsureDateInstance(thisObj).PrimitiveValue;
+            var t = EnsureDateInstance(thisObj).PrimitiveValue;
+            if (!IsFinite(t))
+            {
+                return JsNumber.DoubleNaN;
+            }
             var m = TypeConverter.ToNumber(arguments.At(0));
             var dt = arguments.Length <= 1 ? DateFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
             var newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
@@ -555,18 +640,30 @@ namespace Jint.Native.Date
 
         private JsValue ToUtcString(JsValue thisObj, JsValue[] arguments)
         {
-            return (thisObj as DateInstance?? ExceptionHelper.ThrowTypeError<DateInstance>(_engine))
-            .ToDateTime().ToUniversalTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture);
+            var thisTime = EnsureDateInstance(thisObj);
+            if (!IsFinite(thisTime.PrimitiveValue))
+            {
+                return "Invalid Date";
+            }
+            return thisTime.ToDateTime().ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture);
         }
 
         private JsValue ToISOString(JsValue thisObj, JsValue[] arguments)
         {
-            var t = (thisObj as DateInstance ?? ExceptionHelper.ThrowTypeError<DateInstance>(_engine)).PrimitiveValue;
-
-            if (double.IsInfinity(t) || double.IsNaN(t))
+            var thisTime = EnsureDateInstance(thisObj);
+            var t = thisTime.PrimitiveValue;
+            if (!IsFinite(t))
             {
                 ExceptionHelper.ThrowRangeError(_engine);
             }
+
+            if (thisTime.DateTimeRangeValid)
+            {
+                // shortcut 
+                var dt = thisTime.ToDateTime();
+                return $"{dt.Year:0000}-{dt.Month:00}-{dt.Day:00}T{dt.Hour:00}:{dt.Minute:00}:{dt.Second:00}.{dt.Millisecond:000}Z";
+            }
+
             var h = HourFromTime(t);
             var m = MinFromTime(t);
             var s = SecFromTime(t);
@@ -575,7 +672,9 @@ namespace Jint.Native.Date
             if (m < 0) { m += MinutesPerHour; }
             if (s < 0) { s += SecondsPerMinute; }
             if (ms < 0) { ms += MsPerSecond; }
-            return $"{YearFromTime(t):0000}-{MonthFromTime(t) + 1:00}-{DateFromTime(t):00}T{h:00}:{m:00}:{s:00}.{ms:000}Z";
+
+            var (year, month, day) = YearMonthDayFromTime(t);
+            return $"{year:0000}-{month:00}-{day:00}T{h:00}:{m:00}:{s:00}.{ms:000}Z";
         }
 
         private JsValue ToJSON(JsValue thisObj, JsValue[] arguments)
@@ -615,7 +714,7 @@ namespace Jint.Native.Date
         /// <summary>
         /// 15.9.1.2
         /// </summary>
-        public static long TimeWithinDay(long t)
+        public static double TimeWithinDay(double t)
         {
             var result = t % MsPerDay;
 
@@ -632,22 +731,22 @@ namespace Jint.Native.Date
         /// </summary>
         public static int DaysInYear(double y)
         {
-            if (!(y%4).Equals(0))
+            if (y%4 != 0)
             {
                 return 365;
             }
 
-            if ((y%4).Equals(0) && !(y%100).Equals(0))
+            if (y%4 == 0 && y%100 != 0)
             {
                 return 366;
             }
 
-            if ((y%100).Equals(0) && !(y%400).Equals(0))
+            if (y%100 == 0 && y%400 != 0)
             {
                 return 365;
             }
 
-            if ((y%400).Equals(0))
+            if (y%400 == 0)
             {
                 return 366;
             }
@@ -679,39 +778,7 @@ namespace Jint.Native.Date
         /// </summary>
         public static int YearFromTime(double t)
         {
-            var sign = (t < 0) ? -1 : 1;
-            var year = (sign < 0) ? 1969 : 1970;
-            for (var timeToTimeZero = (long) t; ;)
-            {
-                //  Subtract the current year's time from the time that's left.
-                var timeInYear = DaysInYear(year) * MsPerDay;
-                timeToTimeZero -= sign * timeInYear;
-
-                //  If there's less than the current year's worth of time left, then break.
-                if (sign < 0)
-                {
-                    if (sign * timeToTimeZero <= 0)
-                    {
-                        break;
-                    }
-                    else
-                    {
-                        year += sign;
-                    }
-                }
-                else
-                {
-                    if (sign * timeToTimeZero < 0)
-                    {
-                        break;
-                    }
-                    else
-                    {
-                        year += sign;
-                    }
-                }
-            }
-
+            var (year, _, _) = YearMonthDayFromTime(t);
             return year;
         }
 
@@ -891,24 +958,29 @@ namespace Jint.Native.Date
 
         public long LocalTza => (long) Engine.Options._LocalTimeZone.BaseUtcOffset.TotalMilliseconds;
 
-        public long DaylightSavingTa(double t)
+        public double DaylightSavingTa(double t)
         {
-            var timeInYear = t - TimeFromYear(YearFromTime(t));
+            if (double.IsNaN(t))
+            {
+                return t;
+            }
+
+            var year = YearFromTime(t);
+            var timeInYear = t - TimeFromYear(year);
 
             if (double.IsInfinity(timeInYear) || double.IsNaN(timeInYear))
             {
                 return 0;
             }
 
-            var year = YearFromTime(t);
-            if (year < 9999 && year > -9999)
+            if (year < 9999 && year > -9999 && year != 0)
             {
                 // in DateTimeOffset range so we can use it
             }
             else
             {
                 // use similar leap-ed year
-                var isLeapYear = InLeapYear(t) == 1;
+                var isLeapYear = InLeapYear((long) t) == 1;
                 year = isLeapYear ? 2000 : 1999;
             }
 
@@ -930,9 +1002,14 @@ namespace Jint.Native.Date
             }
         }
 
-        public long LocalTime(double t)
+        public double LocalTime(double t)
         {
-            return (long) (t + LocalTza + DaylightSavingTa(t));
+            if (!IsFinite(t))
+            {
+                return double.NaN;
+            }
+            
+            return (long) (t + LocalTza + DaylightSavingTa((long) t));
         }
 
         public double Utc(double t)
@@ -988,58 +1065,6 @@ namespace Jint.Native.Date
             return (int) milli;
         }
 
-        public static int DayFromMonth(int year, int month)
-        {
-            int day = month * 30;
-
-            if (month >= 7)
-            {
-                day += month/2 - 1;
-            }
-            else if (month >= 2)
-            {
-                day += (month - 1)/2 - 1;
-            }
-            else
-            {
-                day += month;
-            }
-
-            if (month >= 2 && InLeapYear(year) == 1)
-            {
-                day++;
-            }
-
-            return day;
-        }
-
-
-        public static int DaysInMonth(int month, int leap)
-        {
-            month = month%12;
-
-            switch ((long) month)
-            {
-                case 0:
-                case 2:
-                case 4:
-                case 6:
-                case 7:
-                case 9:
-                case 11:
-                    return 31;
-                case 3:
-                case 5:
-                case 8:
-                case 10:
-                    return 30;
-                case 1:
-                    return 28 + leap;
-                default:
-                    return ExceptionHelper.ThrowArgumentOutOfRangeException<int>(nameof(month), "invalid month");
-            }
-        }
-
         public static double MakeTime(double hour, double min, double sec, double ms)
         {
             if (!AreFinite(hour, min, sec, ms))
@@ -1047,10 +1072,10 @@ namespace Jint.Native.Date
                 return double.NaN;
             }
 
-            var h = (long) hour;
-            var m = (long) min;
-            var s = (long) sec;
-            var milli = (long) ms;
+            var h = TypeConverter.ToInteger(hour);
+            var m = TypeConverter.ToInteger(min);
+            var s = TypeConverter.ToInteger(sec);
+            var milli = TypeConverter.ToInteger(ms);
             var t = h*MsPerHour + m*MsPerMinute + s*MsPerSecond + milli;
 
             return t;
@@ -1058,47 +1083,44 @@ namespace Jint.Native.Date
 
         public static double MakeDay(double year, double month, double date)
         {
-            if (!AreFinite(year, month, date))
+            if ((year < MinYear || year > MaxYear) || month < MinMonth  || month > MaxMonth || !AreFinite(year, month, date))
             {
                 return double.NaN;
             }
 
-            year = TypeConverter.ToInteger(year);
-            month = TypeConverter.ToInteger(month);
-            date = TypeConverter.ToInteger(date);
-
-            if (month < 0)
-            {
-                var m = (long) month;
-                year += (m - 11) / 12;
-                month = (12 + m % 12) % 12;
-            }
-
-            var sign = (year < 1970) ? -1 : 1;
-            long t = (year < 1970) ? 1 : 0;
-            int y;
-
-            if (sign == -1)
-            {
-                for (y = 1969; y >= year; y += sign)
-                {
-                    t += sign * DaysInYear(y) * MsPerDay;
-                }
-            }
-            else
-            {
-                for (y = 1970; y < year; y += sign)
-                {
-                    t += sign * DaysInYear(y) * MsPerDay;
-                }
-            }
-
-            for (var m = 0; m < month; m++)
-            {
-                t += DaysInMonth(m, InLeapYear(t)) * MsPerDay;
-            }
-
-            return Day(t) + date - 1;
+            var y = (long) TypeConverter.ToInteger(year);
+            var m = (long) TypeConverter.ToInteger(month);
+            y += m / 12;
+            m %= 12;
+            if (m < 0) {
+                m += 12;
+                y -= 1;
+            }
+            
+            // kYearDelta is an arbitrary number such that:
+            // a) kYearDelta = -1 (mod 400)
+            // b) year + kYearDelta > 0 for years in the range defined by
+            //    ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
+            //    Jan 1 1970. This is required so that we don't run into integer
+            //    division of negative numbers.
+            // c) there shouldn't be an overflow for 32-bit integers in the following
+            //    operations.
+            const int kYearDelta = 399999;
+            const int kBaseDay =
+                365 * (1970 + kYearDelta) + (1970 + kYearDelta) / 4 -
+                (1970 + kYearDelta) / 100 + (1970 + kYearDelta) / 400;
+            
+            long dayFromYear = 365 * (y + kYearDelta) + (y + kYearDelta) / 4 -
+                                (y + kYearDelta) / 100 + (y + kYearDelta) / 400 -  kBaseDay;
+
+            if ((y % 4 != 0) || (y % 100 == 0 && y % 400 != 0)) {
+                var dayFromMonth = new [] {0,   31,  59,  90,  120, 151,  181, 212, 243, 273, 304, 334};
+                dayFromYear += dayFromMonth[m];
+            } else {
+                var dayFromMonthLeapYear = new [] {0,   31,  60,  91,  121, 152, 182, 213, 244, 274, 305, 335};
+                dayFromYear += dayFromMonthLeapYear[m];
+            }
+            return dayFromYear - 1 + TypeConverter.ToInteger(date);
         }
 
         public static double MakeDate(double day, double time)
@@ -1113,7 +1135,7 @@ namespace Jint.Native.Date
 
         public static double TimeClip(double time)
         {
-            if (!AreFinite(time))
+            if (!IsFinite(time))
             {
                 return double.NaN;
             }
@@ -1127,24 +1149,124 @@ namespace Jint.Native.Date
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static bool AreFinite(double value)
+        private static bool IsFinite(double value)
         {
             return !double.IsNaN(value) && !double.IsInfinity(value);
         }
 
         private static bool AreFinite(double value1, double value2)
         {
-            return AreFinite(value1) && AreFinite(value2);
+            return IsFinite(value1) && IsFinite(value2);
         }
 
         private static bool AreFinite(double value1, double value2, double value3)
         {
-            return AreFinite(value1) && AreFinite(value2) &&  AreFinite(value3);
+            return IsFinite(value1) && IsFinite(value2) &&  IsFinite(value3);
         }
 
         private static bool AreFinite(double value1, double value2, double value3, double value4)
         {
-            return AreFinite(value1) && AreFinite(value2) &&  AreFinite(value3) && AreFinite(value4);
+            return IsFinite(value1) && IsFinite(value2) &&  IsFinite(value3) && IsFinite(value4);
+        }
+        private readonly struct Date
+        {
+            public Date(int year, int month, int day)
+            {
+                Year = year;
+                Month = month;
+                Day = day;
+            }
+
+            public readonly int Year;
+            public readonly int Month;
+            public readonly int Day;
+
+            public void Deconstruct(out int year, out int month, out int day)
+            {
+                year = Year;
+                month = Month;
+                day = Day;
+            }
+        }
+
+        private static readonly int[] kDaysInMonths = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+        private static Date YearMonthDayFromTime(double t) => YearMonthDayFromDays((long) (t / 1000 / 60 / 60 / 24));
+
+        private static Date YearMonthDayFromDays(long days)
+        {
+            const int kDaysIn4Years = 4 * 365 + 1;
+            const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
+            const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
+            const int kDays1970to2000 = 30 * 365 + 7;
+            const int kDaysOffset =
+                1000 * kDaysIn400Years + 5 * kDaysIn400Years - kDays1970to2000;
+            const int kYearsOffset = 400000;
+
+
+            days += kDaysOffset;
+            var year = 400 * (days / kDaysIn400Years) - kYearsOffset;
+            days %= kDaysIn400Years;
+
+            days--;
+            var yd1 = days / kDaysIn100Years;
+            days %= kDaysIn100Years;
+            year += 100 * yd1;
+
+            days++;
+            var yd2 = days / kDaysIn4Years;
+            days %= kDaysIn4Years;
+            year += 4 * yd2;
+
+            days--;
+            var yd3 = days / 365;
+            days %= 365;
+            year += yd3;
+
+            var is_leap = (yd1 == 0 || yd2 != 0) && yd3 == 0;
+
+            days += is_leap ? 1 : 0;
+            var month = 0;
+            var day = 0;
+
+            // Check if the date is after February.
+            if (days >= 31 + 28 + (is_leap ? 1 : 0))
+            {
+                days -= 31 + 28 + (is_leap ? 1 : 0);
+                // Find the date starting from March.
+                for (int i = 2; i < 12; i++)
+                {
+                    if (days < kDaysInMonths[i])
+                    {
+                        month = i;
+                        day = (int) (days + 1);
+                        break;
+                    }
+
+                    days -= kDaysInMonths[i];
+                }
+            }
+            else
+            {
+                // Check January and February.
+                if (days < 31)
+                {
+                    month = 0;
+                    day = (int) (days + 1);
+                }
+                else
+                {
+                    month = 1;
+                    day = (int) (days - 31 + 1);
+                }
+            }
+
+            return new Date((int) year, month, day);
+        }
+        
+        public override string ToString()
+        {
+            return "Date.prototype";
         }
     }
 }

+ 2 - 2
Jint/Native/JsNumber.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Globalization;
 using System.Runtime.CompilerServices;
 using Jint.Runtime;
 
@@ -29,6 +28,7 @@ namespace Jint.Native
         private static readonly JsNumber IntegerNegativeOne = new JsNumber(-1);
         internal static readonly JsNumber NegativeZero = new JsNumber(-0d);
         internal static readonly JsNumber PositiveZero = new JsNumber(+0);
+        internal static readonly JsNumber One = new JsNumber(1);
 
         internal static readonly JsNumber PI = new JsNumber(System.Math.PI);
 
@@ -164,7 +164,7 @@ namespace Jint.Native
 
         public override string ToString()
         {
-            return _value.ToString(CultureInfo.InvariantCulture);
+            return EsprimaExtensions.DoubleToString(_value);
         }
 
         public override bool Equals(JsValue obj)

+ 1 - 0
Jint/Native/JsString.cs

@@ -20,6 +20,7 @@ namespace Jint.Native
         internal static readonly JsString StringString = new JsString("string");
         internal static readonly JsString NumberString = new JsString("number");
         internal static readonly JsString LengthString = new JsString("length");
+        internal static readonly JsString DefaultString = new JsString("default");
 
         internal string _value;
 

+ 7 - 0
Jint/Native/Math/MathInstance.cs

@@ -364,6 +364,13 @@ namespace Jint.Native.Math
                 return JsNumber.DoubleNegativeInfinity;
             }
 
+#if NETFRAMEWORK
+            if (x < 0 && x > -1)
+            {
+                return JsNumber.NegativeZero;
+            }
+#endif
+            
             return System.Math.Ceiling(x);
         }
 

+ 0 - 97
Jint/Native/Object/ObjectInstance.cs

@@ -487,103 +487,6 @@ namespace Jint.Native.Object
             return false;
         }
 
-        /// <summary>
-        /// Hint is a String. Returns a default value for the object.
-        /// </summary>
-        public JsValue DefaultValue(Types hint)
-        {
-            EnsureInitialized();
-
-            if (hint == Types.String || (hint == Types.None && Class == "Date"))
-            {
-                var jsValue = Get(GlobalSymbolRegistry.ToPrimitive, this);
-                if (!jsValue.IsNullOrUndefined())
-                {
-                    if (jsValue is ICallable toPrimitive)
-                    {
-                        var str = toPrimitive.Call(this, Arguments.Empty);
-                        if (str.IsPrimitive())
-                        {
-                            return str;
-                        }
-
-                        if (str.IsObject())
-                        {
-                            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Cannot convert object to primitive value");
-                        }
-                    }
-
-                    const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
-                    return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
-                }
-                if (Get("toString", this) is ICallable toString)
-                {
-                    var str = toString.Call(this, Arguments.Empty);
-                    if (str.IsPrimitive())
-                    {
-                        return str;
-                    }
-                }
-
-                if (Get("valueOf", this) is ICallable valueOf)
-                {
-                    var val = valueOf.Call(this, Arguments.Empty);
-                    if (val.IsPrimitive())
-                    {
-                        return val;
-                    }
-                }
-
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
-            if (hint == Types.Number || hint == Types.None)
-            {
-                var jsValue = Get(GlobalSymbolRegistry.ToPrimitive, this);
-                if (!jsValue.IsNullOrUndefined())
-                {
-                    if (jsValue is ICallable toPrimitive)
-                    {
-                        var val = toPrimitive.Call(this, Arguments.Empty);
-                        if (val.IsPrimitive())
-                        {
-                            return val;
-                        }
-
-                        if (val.IsObject())
-                        {
-                            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Cannot convert object to primitive value");
-                        }
-                    }
-
-                    const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
-                    return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
-                }
-
-                if (Get("valueOf", this) is ICallable valueOf)
-                {
-                    var val = valueOf.Call(this, Arguments.Empty);
-                    if (val.IsPrimitive())
-                    {
-                        return val;
-                    }
-                }
-
-                if (Get("toString", this) is ICallable toString)
-                {
-                    var str = toString.Call(this, Arguments.Empty);
-                    if (str.IsPrimitive())
-                    {
-                        return str;
-                    }
-                }
-
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
-            return ToString();
-        }
-
         public bool DefinePropertyOrThrow(in Key propertyName, PropertyDescriptor desc)
         {
             if (!DefineOwnProperty(propertyName, desc))

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -60,7 +60,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                 if (property.Key is Literal literal)
                 {
-                    propName = literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
+                    propName = EsprimaExtensions.LiteralKeyToString(literal);
                 }
 
                 if (!property.Computed && property.Key is Identifier identifier)

+ 77 - 5
Jint/Runtime/TypeConverter.cs

@@ -9,6 +9,7 @@ using Jint.Native.Number;
 using Jint.Native.Number.Dtoa;
 using Jint.Native.Object;
 using Jint.Native.String;
+using Jint.Native.Symbol;
 using Jint.Pooling;
 
 namespace Jint.Runtime
@@ -65,19 +66,90 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.1
+        /// http://www.ecma-international.org/ecma-262/#sec-toprimitive
         /// </summary>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
-            if (input._type > InternalTypes.None && input._type < InternalTypes.Object)
+            if (!(input is ObjectInstance oi))
             {
                 return input;
             }
 
-            return input.AsObject().DefaultValue(preferredType);
+            var hint = preferredType switch
+            {
+                Types.String => JsString.StringString,
+                Types.Number => JsString.NumberString,
+                _ => JsString.DefaultString
+            };
+
+            var exoticToPrim  = GetMethod(oi, GlobalSymbolRegistry.ToPrimitive);
+            if (exoticToPrim is object)
+            {
+                var str = exoticToPrim.Call(oi, new JsValue[] { hint });
+                if (str.IsPrimitive())
+                {
+                    return str;
+                }
+
+                if (str.IsObject())
+                {
+                    return ExceptionHelper.ThrowTypeError<JsValue>(oi.Engine, "Cannot convert object to primitive value");
+                }
+            }
+
+            return OrdinaryToPrimitive(oi, preferredType == Types.None ? Types.Number :  preferredType);
+        }
+
+        private static readonly Key[] StringHintCallOrder = { (Key) "toString", (Key) "valueOf"};
+        private static readonly Key[] NumberHintCallOrder = { (Key) "valueOf", (Key) "toString"};
+        
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/#sec-ordinarytoprimitive
+        /// </summary>
+        internal static JsValue OrdinaryToPrimitive(ObjectInstance input, Types hint = Types.None)
+        {
+            var callOrder = ArrayExt.Empty<Key>();
+            if (hint == Types.String)
+            {
+                callOrder = StringHintCallOrder;
+            }
+
+            if (hint == Types.Number)
+            {
+                callOrder = NumberHintCallOrder;
+            }
+
+            foreach (var property in callOrder)
+            {
+                var method = input.Get(property) as ICallable;
+                if (method is object)
+                {
+                    var val = method.Call(input, Arguments.Empty);
+                    if (val.IsPrimitive())
+                    {
+                        return val;
+                    }
+                }
+ 
+            }
+
+            return ExceptionHelper.ThrowTypeError<JsValue>(input.Engine);
         }
 
+        internal static ICallable GetMethod(ObjectInstance v, in Key p)
+        {
+            var jsValue = v.Get(p);
+            if (jsValue.IsNullOrUndefined())
+            {
+                return null;
+            }
+
+            if (!(jsValue is ICallable callable))
+            {
+                return ExceptionHelper.ThrowTypeError<ICallable>(v.Engine, "Value returned for property '" + p.Name + "' of object is not a function");
+            }
+            return callable;
+        }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
@@ -246,7 +318,7 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
+        /// http://www.ecma-international.org/ecma-262/#sec-tointeger
         /// </summary>
         public static double ToInteger(JsValue o)
         {

+ 1 - 0
appveyor.yml

@@ -24,6 +24,7 @@ test_script:
   - dotnet test .\Jint.Tests.CommonScripts\Jint.Tests.CommonScripts.csproj -c Release -f netcoreapp3.0
   - dotnet test .\Jint.Tests.Ecma\Jint.Tests.Ecma.csproj -c Release -f netcoreapp3.0
   - dotnet test .\Jint.Tests.Test262\Jint.Tests.Test262.csproj -c Release -f netcoreapp3.0
+  - dotnet test .\Jint.Tests.Test262\Jint.Tests.Test262.csproj -c Release -f net452
 artifacts:
   - path: 'Jint\**\*.nupkg'
 deploy: