Browse Source

Adding a way to override the local timezone

Brian Beard 11 years ago
parent
commit
f2d64b7196

+ 61 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Globalization;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Threading;
 using Jint.Native.Number;
@@ -738,5 +739,65 @@ namespace Jint.Tests.Runtime
             }
         }
 
+        [Fact]
+        public void ShouldUseLocalTimeZoneOverride()
+        {
+            //var all = TimeZoneInfo.GetSystemTimeZones();
+            //var skip = new[]
+            //{
+            //    "Dateline Standard Time",
+            //    "UTC-11",
+            //    "Hawaiian Standard Time"
+            //};
+            //foreach (var localTimeZone in all)
+            //{
+
+            //    if (skip.Contains(localTimeZone.Id))
+            //    {
+            //        continue;
+            //    }
+
+            //    //var localTimeZone = TimeZoneInfo.Local;
+
+            //    var customHoursOffsetFromLocal = 1;
+            //    var expectedToStringValue = "Mon Feb 02 1990 15:00:00 GMT";
+            //    var expectedGetHoursValue = 15d;
+            //    if (localTimeZone.BaseUtcOffset.Hours > 11)
+            //    {
+            //        customHoursOffsetFromLocal = -1;
+            //        expectedToStringValue = "Mon Feb 02 1990 13:00:00 GMT";
+            //        expectedGetHoursValue = 13d;
+            //    }
+
+                const string customName = "Custom Time";
+                //var customUtcOffset = localTimeZone.BaseUtcOffset.Add(new TimeSpan(customHoursOffsetFromLocal, 0, 0));
+            var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName,
+                customName, customName, null, false);
+
+                //var d = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
+
+                //var u = TimeZoneInfo.ConvertTime(d, localTimeZone, TimeZoneInfo.Utc);
+                //var l = TimeZoneInfo.ConvertTime(u, customTimeZone);
+
+                var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
+
+
+                var d1 = engine.Execute("var d = new Date(0); d.getTime();").GetCompletionValue().AsNumber();
+                var d2 = engine.Execute("var d = new Date(Date.UTC(1970,0,1)); d.getTime();").GetCompletionValue().AsNumber();
+
+
+                var result = engine.Execute("var d1 = new Date(0); var d2 = new Date(Date.UTC(1970,0,1)); d1.getTime() === d2.getTime();").GetCompletionValue().AsBoolean();
+                Assert.Equal(true, result);
+
+                var toStringResult = engine.Execute("var d = new Date(946684800000); d.toString();").GetCompletionValue().AsString();
+                Assert.Equal("Sat Jan 01 2000 00:11:00 GMT", toStringResult);
+
+                var getMinutesResult = engine.Execute("var d = new Date(946684800000); d.getMinutes();").GetCompletionValue().AsNumber();
+                Assert.Equal(11, getMinutesResult);
+
+            //}
+
+        }
+
     }
 }

+ 1 - 1
Jint/Native/Date/DateConstructor.cs

@@ -147,7 +147,7 @@ namespace Jint.Native.Date
                 var finalDate = DatePrototype.MakeDate(DatePrototype.MakeDay(y, m, dt),
                     DatePrototype.MakeTime(h, min, s, milli));
 
-                return Construct(DatePrototype.TimeClip(DatePrototype.Utc(finalDate)));
+                return Construct(DatePrototype.TimeClip(DatePrototype.Utc(finalDate, Engine.Options)));
             }
         }
 

+ 63 - 36
Jint/Native/Date/DatePrototype.cs

@@ -85,7 +85,7 @@ namespace Jint.Native.Date
 
         public JsValue ToString(JsValue thisObj, JsValue[] arg2)
         {
-            return thisObj.TryCast<DateInstance>().ToDateTime().ToLocalTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'K", CultureInfo.InvariantCulture);
+            return ToLocalTime(thisObj.TryCast<DateInstance>().ToDateTime()).ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'K", CultureInfo.InvariantCulture);
         }
 
         private static JsValue ToDateString(JsValue thisObj, JsValue[] arguments)
@@ -123,7 +123,7 @@ namespace Jint.Native.Date
             return thisObj.TryCast<DateInstance>().PrimitiveValue;
         }
 
-        private static JsValue GetFullYear(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetFullYear(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -134,7 +134,7 @@ namespace Jint.Native.Date
             return YearFromTime(LocalTime(t));
         }
 
-        private static JsValue GetYear(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetYear(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -156,7 +156,7 @@ namespace Jint.Native.Date
             return YearFromTime(t);
         }
 
-        private static JsValue GetMonth(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetMonth(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -178,7 +178,7 @@ namespace Jint.Native.Date
             return MonthFromTime(t);
         }
 
-        private static JsValue GetDate(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetDate(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -200,7 +200,7 @@ namespace Jint.Native.Date
             return DateFromTime(t);
         }
 
-        private static JsValue GetDay(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetDay(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -222,7 +222,7 @@ namespace Jint.Native.Date
             return WeekDay(t);
         }
 
-        private static JsValue GetHours(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -244,7 +244,7 @@ namespace Jint.Native.Date
             return HourFromTime(t);
         }
 
-        private static JsValue GetMinutes(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -266,7 +266,7 @@ namespace Jint.Native.Date
             return MinFromTime(t);
         }
 
-        private static JsValue GetSeconds(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetSeconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -288,7 +288,7 @@ namespace Jint.Native.Date
             return SecFromTime(t);
         }
 
-        private static JsValue GetMilliseconds(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -310,7 +310,7 @@ namespace Jint.Native.Date
             return MsFromTime(t);
         }
 
-        private static JsValue GetTimezoneOffset(JsValue thisObj, JsValue[] arguments)
+        private JsValue GetTimezoneOffset(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.TryCast<DateInstance>().PrimitiveValue;
             if (double.IsNaN(t))
@@ -326,7 +326,7 @@ namespace Jint.Native.Date
             return thisObj.As<DateInstance>().PrimitiveValue = TimeClip(TypeConverter.ToNumber(arguments.At(0)));
         }
 
-        private static JsValue SetMilliseconds(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(thisObj.As<DateInstance>().PrimitiveValue);
             var time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), TypeConverter.ToNumber(arguments.At(0)));
@@ -335,7 +335,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetUTCMilliseconds(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetUTCMilliseconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.As<DateInstance>().PrimitiveValue;
             var time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), TypeConverter.ToNumber(arguments.At(0)));
@@ -344,7 +344,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetSeconds(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetSeconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(thisObj.As<DateInstance>().PrimitiveValue);
             var s = TypeConverter.ToNumber(arguments.At(0));
@@ -355,7 +355,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetUTCSeconds(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetUTCSeconds(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.As<DateInstance>().PrimitiveValue;
             var s = TypeConverter.ToNumber(arguments.At(0));
@@ -366,7 +366,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetMinutes(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(thisObj.As<DateInstance>().PrimitiveValue);
             var m = TypeConverter.ToNumber(arguments.At(0));
@@ -378,7 +378,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetUTCMinutes(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetUTCMinutes(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.As<DateInstance>().PrimitiveValue;
             var m = TypeConverter.ToNumber(arguments.At(0));
@@ -390,7 +390,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetHours(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(thisObj.As<DateInstance>().PrimitiveValue);
             var h = TypeConverter.ToNumber(arguments.At(0));
@@ -403,7 +403,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetUTCHours(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetUTCHours(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.As<DateInstance>().PrimitiveValue;
             var h = TypeConverter.ToNumber(arguments.At(0));
@@ -416,7 +416,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetDate(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetDate(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(thisObj.As<DateInstance>().PrimitiveValue);
             var dt = TypeConverter.ToNumber(arguments.At(0));
@@ -426,7 +426,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetUTCDate(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetUTCDate(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.As<DateInstance>().PrimitiveValue;
             var dt = TypeConverter.ToNumber(arguments.At(0));
@@ -436,7 +436,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetMonth(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetMonth(JsValue thisObj, JsValue[] arguments)
         {
             var t = LocalTime(thisObj.As<DateInstance>().PrimitiveValue);
             var m = TypeConverter.ToNumber(arguments.At(0));
@@ -447,7 +447,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetUTCMonth(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetUTCMonth(JsValue thisObj, JsValue[] arguments)
         {
             var t = thisObj.As<DateInstance>().PrimitiveValue;
             var m = TypeConverter.ToNumber(arguments.At(0));
@@ -458,7 +458,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetFullYear(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetFullYear(JsValue thisObj, JsValue[] arguments)
         {
             var thisTime = thisObj.As<DateInstance>().PrimitiveValue;
             var t = double.IsNaN(thisTime) ? +0 : LocalTime(thisTime);
@@ -471,7 +471,7 @@ namespace Jint.Native.Date
             return u;
         }
 
-        private static JsValue SetYear(JsValue thisObj, JsValue[] arguments)
+        private JsValue SetYear(JsValue thisObj, JsValue[] arguments)
         {
             var thisTime = thisObj.As<DateInstance>().PrimitiveValue;
             var t = double.IsNaN(thisTime) ? +0 : LocalTime(thisTime);
@@ -830,15 +830,22 @@ namespace Jint.Native.Date
             return (Day(t) + 4)%7;
         }
 
-        public static double LocalTza
+        private double LocalTza()
         {
-            get
-            {
-                return TimeZoneInfo.Local.BaseUtcOffset.TotalMilliseconds;
-            }
+            return LocalTza(Engine.Options);
+        }
+
+        public static double LocalTza(Options options)
+        {
+            return options.GetLocalTimeZone().BaseUtcOffset.TotalMilliseconds;
         }
 
-        public static double DaylightSavingTa(double t)
+        private double DaylightSavingTa(double t)
+        {
+            return DaylightSavingTa(t, Engine.Options);
+        }
+
+        public static double DaylightSavingTa(double t, Options options)
         {
             var timeInYear = t - TimeFromYear(YearFromTime(t));
 
@@ -861,17 +868,37 @@ namespace Jint.Native.Date
 
             var dateTime = new DateTime((int)year, 1, 1).AddMilliseconds(timeInYear);
 
-            return TimeZoneInfo.Local.IsDaylightSavingTime(dateTime) ? MsPerHour : 0;
+            return options.GetLocalTimeZone().IsDaylightSavingTime(dateTime) ? MsPerHour : 0;
+        }
+
+        public DateTime ToLocalTime(DateTime t)
+        {
+            if (t.Kind == DateTimeKind.Local)
+            {
+                return t;
+            }
+
+            if (t.Kind == DateTimeKind.Unspecified)
+            {
+                t = DateTime.SpecifyKind(t, DateTimeKind.Utc);
+            }
+
+            return TimeZoneInfo.ConvertTime(t, Engine.Options.GetLocalTimeZone());
+        }
+
+        public double LocalTime(double t)
+        {
+            return t + LocalTza() + DaylightSavingTa(t);
         }
 
-        public static double LocalTime(double t)
+        private double Utc(double t)
         {
-            return t + LocalTza + DaylightSavingTa(t);
+            return Utc(t, Engine.Options);
         }
 
-        public static double Utc(double t)
+        public static double Utc(double t, Options options)
         {
-            return t - LocalTza - DaylightSavingTa(t - LocalTza);
+            return t - LocalTza(options) - DaylightSavingTa(t - LocalTza(options), options);
         }
 
         public static double HourFromTime(double t)

+ 14 - 1
Jint/Options.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Reflection;
@@ -17,6 +18,7 @@ namespace Jint
         private readonly List<IObjectConverter> _objectConverters = new List<IObjectConverter>();
         private int _maxStatements;
         private CultureInfo _culture = CultureInfo.CurrentCulture;
+        private TimeZoneInfo _localTimeZone = TimeZoneInfo.Local;
         private List<Assembly> _lookupAssemblies = new List<Assembly>(); 
 
         /// <summary>
@@ -92,6 +94,12 @@ namespace Jint
             return this;
         }
 
+        public Options LocalTimeZone(TimeZoneInfo timeZoneInfo)
+        {
+            _localTimeZone = timeZoneInfo;
+            return this;
+        }
+
         internal bool GetDiscardGlobal()
         {
             return _discardGlobal;
@@ -136,5 +144,10 @@ namespace Jint
         {
             return _culture;
         }
+
+        internal TimeZoneInfo GetLocalTimeZone()
+        {
+            return _localTimeZone;
+        }
     }
 }