From ce0036bbd3b65fe5dcc88b17ef4fa55a0b74f188 Mon Sep 17 00:00:00 2001 From: nilproject Date: Wed, 30 May 2018 21:07:43 +0300 Subject: [PATCH] Fixes and tests for #133 --- IntegrationTests/BaseLibrary/DateTests.cs | 295 +++++++++++++++++++++- NiL.JS/BaseLibrary/Date.cs | 80 +++--- 2 files changed, 336 insertions(+), 39 deletions(-) diff --git a/IntegrationTests/BaseLibrary/DateTests.cs b/IntegrationTests/BaseLibrary/DateTests.cs index 0304113b8..ff21117f9 100644 --- a/IntegrationTests/BaseLibrary/DateTests.cs +++ b/IntegrationTests/BaseLibrary/DateTests.cs @@ -33,7 +33,7 @@ public void NewDateShouldContainCurrentTime() Assert.AreEqual(date.getDate(), dateTime.Day); Assert.AreEqual((int)date.getMonth().Value + 1, dateTime.Month); - Assert.AreEqual(date.getYear(), dateTime.Year); + Assert.AreEqual(date.getYear(), dateTime.Year - 1900); Assert.AreEqual(date.getHours(), dateTime.Hour); Assert.AreEqual(date.getMinutes(), dateTime.Minute); @@ -81,5 +81,298 @@ public void ShouldCorrectHandleSwitchFromDstToStandardBySetTime_MoscowTime() d.setTime((long)d.getTime() + 86400 * 1000); Assert.AreEqual(-240, d.getTimezoneOffset().As()); } + + [TestMethod] + public void ShouldCorrectParseIsoTimeInCurrentTimezone_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual(1283090400000, (long)d.valueOf()); + } + + [TestMethod] + public void ShouldReturnsCorrectUtcDateForParsedIsoTime_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual(29, (int)d.getUTCDate()); + } + + [TestMethod] + public void ShouldReturnsCorrectUtcDayForParsedIsoTime_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual(0, (int)d.getUTCDay()); + } + + [TestMethod] + public void ShouldReturnsCorrectUtcHoursForParsedIsoTime_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual(14, (int)d.getUTCHours()); + } + + [TestMethod] + public void ShouldReturnsCorrectUtcYearForParsedIsoTime_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual(110, (int)d.getYear()); + } + + [TestMethod] + public void ShouldReturnsCorrectIsoStringForParsedIsoTime_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual("2010-08-29T14:00:00.000Z", d.toISOString().ToString()); + } + + [TestMethod] + public void ShouldReturnsCorrectJsonForParsedIsoTime_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual("2010-08-29T14:00:00.000Z", d.toJSON().ToString()); + } + + [TestMethod] + public void SetMonthShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setMonth(5, null); + + Assert.AreEqual(1277820000000, (long)result); + } + + [TestMethod] + public void SetUtcMonthShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setUTCMonth(5, null); + + Assert.AreEqual(1277820000000, (long)result); + } + + [TestMethod] + public void SetUtcMillisecondsShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setUTCMilliseconds(555); + + Assert.AreEqual(1283090400555, (long)result); + } + + [TestMethod] + public void SetUtcHoursShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setUTCHours(4, null, null, null); + + Assert.AreEqual(1283054400000, (long)result); + } + + [TestMethod] + public void SetUtcDateShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setUTCDate(15); + + Assert.AreEqual(1281880800000, (long)result); + } + + [TestMethod] + public void SetUtcMinutesShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setUTCMinutes(34, null, null); + + Assert.AreEqual(1283092440000, (long)result); + } + + [TestMethod] + public void SetMillisecondsShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setMilliseconds(555); + + Assert.AreEqual(1283090400555, (long)result); + } + + [TestMethod] + public void SetFullYearShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setFullYear(2005, null, null); + + Assert.AreEqual(1125324000000, (long)result); + } + + [TestMethod] + public void SetUtcFullYearShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setUTCFullYear(2004, null, null); + + Assert.AreEqual(1093788000000, (long)result); + } + + [TestMethod] + public void SetMinutesShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setMinutes(34, null, null); + + Assert.AreEqual(1283092440000, (long)result); + } + + [TestMethod] + public void SetTimeShouldWorkCorrectlyAndReturnTickValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setTime(0); + + Assert.AreEqual(0, (long)result); + } + + [TestMethod] + public void SetTimeShouldRecalcTimezoneOffset_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var d = new Date(new Arguments { "2010-08-30T00:00:00" }); + + Assert.AreEqual(-600, (long)d.getTimezoneOffset()); + d.setTime(0); + Assert.AreEqual(-660, (long)d.getTimezoneOffset()); + } + + [TestMethod] + public void SetTimeShouldNotOffsetValueWithTimezone_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).setTime(1525898414436); + + Assert.AreEqual(1525898414436, (long)result); + } + + [TestMethod] + public void ToLocaleDateStringShouldCorrectFormatValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).toLocaleDateString(); + + Assert.AreEqual("Mon Aug 30 2010", result.ToString()); + } + + [TestMethod] + public void ToLocaleTimeStringShouldCorrectFormatValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).toLocaleTimeString(); + + var shouldBe = "00:00:00 GMT+1000 ("; + Assert.IsTrue(result.ToString().StartsWith(shouldBe), result.ToString() + " != " + shouldBe); + } + + [TestMethod] + public void ToStringShouldCorrectFormatValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).toString(); + + var shouldBe = "Mon Aug 30 2010 00:00:00 GMT+1000 ("; + Assert.IsTrue(result.ToString().StartsWith(shouldBe), result.ToString() + " != " + shouldBe); + } + + [TestMethod] + public void ToTimeStringShouldCorrectFormatValue_SidneyTimeZone() + { + var timezones = TimeZoneInfo.GetSystemTimeZones().Where(x => x.BaseUtcOffset.Ticks == 10 * 3600 * 10000000L).ToArray(); + var timezone = timezones.First(x => x.Id.IndexOf("AUS", StringComparison.OrdinalIgnoreCase) != -1); + Date.CurrentTimeZone = timezone; + + var result = new Date(new Arguments { "2010-08-30T00:00:00" }).toTimeString(); + + var shouldBe = "00:00:00 GMT+1000 ("; + Assert.IsTrue(result.ToString().StartsWith(shouldBe), result.ToString() + " != " + shouldBe); + } } } diff --git a/NiL.JS/BaseLibrary/Date.cs b/NiL.JS/BaseLibrary/Date.cs index 22e33c28c..4a95bcd79 100644 --- a/NiL.JS/BaseLibrary/Date.cs +++ b/NiL.JS/BaseLibrary/Date.cs @@ -198,7 +198,7 @@ private static long getTimeZoneOffset(long time) return offset; } - private void offsetTimeValue(JSValue value, long amort, long mul) + private void offsetTimeValue(JSValue value, long amort, long mul, bool correctTimeWithTimezone = true) { if (value == null || !value.Defined @@ -214,7 +214,10 @@ private void offsetTimeValue(JSValue value, long amort, long mul) var oldTzo = _timeZoneOffset; _timeZoneOffset = getTimeZoneOffset(_time); - _time -= _timeZoneOffset - oldTzo; + if (correctTimeWithTimezone) + { + _time -= _timeZoneOffset - oldTzo; + } } } @@ -254,7 +257,17 @@ public JSValue getTimezoneOffset() [DoNotEnumerate] public JSValue getYear() { - return getFullYear(); + var jsYear = getFullYear(); + if (jsYear._valueType == JSValueType.Integer) + { + jsYear._iValue -= 1900; + } + else if (jsYear._valueType == JSValueType.Double) + { + jsYear._dValue -= 1900; + } + + return jsYear; } [DoNotEnumerate] @@ -492,20 +505,21 @@ public JSValue setTime(JSValue time) { _error = true; this._time = 0; + this._timeZoneOffset = 0; } else { - this.offsetTimeValue(time, _time - _unixTimeBase, 1); + this.offsetTimeValue(time, _time - _unixTimeBase, 1, false); } - return getTime(); + return valueOf(); } [DoNotEnumerate] public JSValue setMilliseconds(JSValue milliseconds) { offsetTimeValue(milliseconds, getMillisecondsImpl(), 1); - return getMilliseconds(); + return valueOf(); } [DoNotEnumerate] @@ -541,13 +555,19 @@ public JSValue setMinutes(JSValue minutes, JSValue seconds, JSValue milliseconds if (!_error) setSeconds(seconds, milliseconds); - return getMinutes(); + return valueOf(); } [DoNotEnumerate] public JSValue setUTCMinutes(JSValue minutes, JSValue seconds, JSValue milliseconds) { - return setMinutes(minutes, seconds, milliseconds); + if (minutes != null && minutes.Exists) + offsetTimeValue(minutes, getMinutesImpl(false), _minuteMillisecond); + + if (!_error) + setUTCSeconds(seconds, milliseconds); + + return valueOf(); } [DoNotEnumerate] @@ -564,7 +584,12 @@ public JSValue setHours(JSValue hours, JSValue minutes, JSValue seconds, JSValue [DoNotEnumerate] public JSValue setUTCHours(JSValue hours, JSValue minutes, JSValue seconds, JSValue milliseconds) { - return setHours(hours, minutes, seconds, milliseconds); + if (hours != null && hours.Exists) + offsetTimeValue(hours, getHoursImpl(false), _hourMilliseconds); + + setUTCMinutes(minutes, seconds, milliseconds); + + return valueOf(); } [DoNotEnumerate] @@ -679,22 +704,13 @@ public DateTime ToDateTime() [DoNotEnumerate] public JSValue toLocaleString() { - var dt = ToDateTime(); -#if !(PORTABLE || NETCORE) - return dt.ToLongDateString() + " " + dt.ToLongTimeString(); -#else - return dt.ToString(); -#endif + return stringifyDate(true, false) + " " + stringifyTime(true, false); } [DoNotEnumerate] public JSValue toLocaleTimeString() { - var res = - getHoursImpl(true).ToString("00:") - + getMinutesImpl(true).ToString("00:") - + getSecondsImpl().ToString("00"); - return res; + return stringifyTime(true, false); } [DoNotEnumerate] @@ -756,7 +772,7 @@ private string stringifyTime(bool withTzo, bool rfc1123) return "Invalid date"; var offset = new TimeSpan(_timeZoneOffset * _timeAccuracy); - var timeName = CurrentTimeZone.IsDaylightSavingTime(new DateTimeOffset(_time * _timeAccuracy, offset)) ? TimeZoneInfo.Local.DaylightName : TimeZoneInfo.Local.StandardName; + var timeName = CurrentTimeZone.IsDaylightSavingTime(new DateTimeOffset(_time * _timeAccuracy, offset)) ? CurrentTimeZone.DaylightName : CurrentTimeZone.StandardName; var res = getHoursImpl(withTzo).ToString("00:") + getMinutesImpl(withTzo).ToString("00:") @@ -772,7 +788,8 @@ public override string ToString() } [DoNotEnumerate] - public JSValue toJSON(JSValue obj) + [ArgumentsCount(1)] + public JSValue toJSON() { return toISOString(); } @@ -804,20 +821,7 @@ public JSValue toDateString() [DoNotEnumerate] public JSValue toLocaleDateString() { - var y = getYearImpl(true); - while (y > 2800) - y -= 2800; - while (y < 0) - y += 2800; - var dt = new DateTime(0); - dt = dt.AddDays(getDateImpl(true)); - dt = dt.AddMonths(getMonthImpl(true)); - dt = dt.AddYears(y); -#if (PORTABLE || NETCORE) - return dt.ToString(); -#else - return dt.ToLongDateString(); -#endif + return stringifyDate(true, false); } [DoNotEnumerate] @@ -1271,7 +1275,7 @@ private static bool parseIso8601(string timeStr, out long time, out long timeZon if (char.ToLowerInvariant(timeStr[j]) != 't' && !char.IsWhiteSpace(timeStr[j])) return false; - computeTzo = char.ToLowerInvariant(timeStr[j]) != 't'; + computeTzo = char.ToLowerInvariant(timeStr[j]) == 't'; part++; break; @@ -1330,7 +1334,7 @@ private static bool parseDateTime(string timeString, out long time, out long tzo { var dateTime = DateTime.Parse(timeString); time = dateTime.Ticks / _timeAccuracy; - tzo = TimeZoneInfo.Local.GetUtcOffset(dateTime).Ticks / _timeAccuracy; + tzo = CurrentTimeZone.GetUtcOffset(dateTime).Ticks / _timeAccuracy; return true; } catch (FormatException)