From: Marcos Henrich Date: Tue, 28 Apr 2015 15:36:08 +0000 (+0100) Subject: Merge pull request #1696 from esdrubal/tzrefactor X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=a27db5252facf504e7995e6abe41ab5f1d3ec506;hp=a8747582f3d24c5f185539331ee879f0dec0bae0;p=mono.git Merge pull request #1696 from esdrubal/tzrefactor TimeZone Refactoring. --- diff --git a/mcs/class/corlib/System/AndroidPlatform.cs b/mcs/class/corlib/System/AndroidPlatform.cs index 75e3d6a855f..ae59c7c9c5a 100644 --- a/mcs/class/corlib/System/AndroidPlatform.cs +++ b/mcs/class/corlib/System/AndroidPlatform.cs @@ -36,7 +36,6 @@ namespace System { static readonly Func getDefaultSyncContext; static readonly Func getDefaultTimeZone; - static readonly Func getCurrentSystemTimeZone; static AndroidPlatform () { @@ -51,11 +50,6 @@ namespace System { Delegate.CreateDelegate (typeof(Func), androidRuntime.GetMethod ("GetDefaultTimeZone", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)); - - MethodInfo mi = androidRuntime.GetMethod ("GetCurrentSystemTimeZone", - System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); - if (mi != null) - getCurrentSystemTimeZone = (Func) Delegate.CreateDelegate (typeof(Func), mi); } internal static SynchronizationContext GetDefaultSyncContext () @@ -67,13 +61,6 @@ namespace System { { return getDefaultTimeZone (); } - - internal static TimeZone GetCurrentSystemTimeZone () - { - if (getCurrentSystemTimeZone == null) - return null; - return getCurrentSystemTimeZone (); - } } } #endif diff --git a/mcs/class/corlib/System/TimeZone.cs b/mcs/class/corlib/System/TimeZone.cs index 61d088a6593..1cfa2ee9bcc 100644 --- a/mcs/class/corlib/System/TimeZone.cs +++ b/mcs/class/corlib/System/TimeZone.cs @@ -73,11 +73,7 @@ namespace System lock (tz_lock) { if (tz == null || Math.Abs (now - timezone_check) > TimeSpan.TicksPerMinute) { -#if MONODROID - tz = AndroidPlatform.GetCurrentSystemTimeZone (); - if (tz == null) -#endif - tz = new CurrentSystemTimeZone (now); + tz = new CurrentSystemTimeZone (); timezone_check = now; currentTimeZone = tz; @@ -169,205 +165,50 @@ namespace System { currentTimeZone = null; } - - // - // This routine returns the TimeDiff that would have to be - // added to "time" to turn it into a local time. This would - // be equivalent to call ToLocalTime. - // - // There is one important consideration: - // - // This information is only valid during the minute it - // was called. - // - // This only works with a real time, not one of the boundary - // cases like DateTime.MaxValue, so validation must be done - // before. - // - // This is intended to be used by DateTime.Now - // - // We use a minute, just to be conservative and cope with - // any potential time zones that might be defined in the future - // that might not nicely fit in hour or half-hour steps. - // - internal TimeSpan GetLocalTimeDiff (DateTime time) - { - return GetLocalTimeDiff (time, GetUtcOffset (time)); - } - - // - // This routine is intended to be called by GetLocalTimeDiff(DatetTime) - // or by ToLocalTime after validation has been performed - // - // time is the time to map, utc_offset is the utc_offset that - // has been computed for calling GetUtcOffset on time. - // - // When called by GetLocalTime, utc_offset is assumed to come - // from a time constructed by new DateTime (DateTime.GetNow ()), that - // is a valid time. - // - // When called by ToLocalTime ranges are checked before this is - // called. - // - internal TimeSpan GetLocalTimeDiff (DateTime time, TimeSpan utc_offset) - { - DaylightTime dlt = GetDaylightChanges (time.Year); - - if (dlt.Delta.Ticks == 0) - return utc_offset; - - DateTime local = time.Add (utc_offset); - if (local < dlt.End && dlt.End.Subtract (dlt.Delta) <= local) - return utc_offset; - - if (local >= dlt.Start && dlt.Start.Add (dlt.Delta) > local) - return utc_offset - dlt.Delta; - - return GetUtcOffset (local); - } } [Serializable] - internal class CurrentSystemTimeZone : TimeZone, IDeserializationCallback { + internal class CurrentSystemTimeZone : TimeZone { - // Fields - private string m_standardName; - private string m_daylightName; - - // A yearwise cache of DaylightTime. - private Dictionary m_CachedDaylightChanges = new Dictionary (1); - - internal enum TimeZoneData - { - DaylightSavingStartIdx, - DaylightSavingEndIdx, - UtcOffsetIdx, - AdditionalDaylightOffsetIdx - }; - - internal enum TimeZoneNames - { - StandardNameIdx, - DaylightNameIdx - }; - - // Internal method to get timezone data. - // data[0]: start of daylight saving time (in DateTime ticks). - // data[1]: end of daylight saving time (in DateTime ticks). - // data[2]: utcoffset (in TimeSpan ticks). - // data[3]: additional offset when daylight saving (in TimeSpan ticks). - // name[0]: name of this timezone when not daylight saving. - // name[1]: name of this timezone when daylight saving. - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern bool GetTimeZoneData (int year, out Int64[] data, out string[] names); + readonly TimeZoneInfo LocalTimeZone; // Constructor internal CurrentSystemTimeZone () { + LocalTimeZone = TimeZoneInfo.Local; } - // - // Initialized by the constructor - // - static int this_year; - static DaylightTime this_year_dlt; - - // - // The "lnow" parameter must be the current time, I could have moved - // the code here, but I do not want to interfere with serialization - // which is why I kept the other constructor around - // - internal CurrentSystemTimeZone (long lnow) - { - Int64[] data; - string[] names; - - DateTime now = new DateTime (lnow); - if (!GetTimeZoneData (now.Year, out data, out names)) - throw new NotSupportedException (Locale.GetText ("Can't get timezone name.")); - - m_standardName = Locale.GetText (names[(int)TimeZoneNames.StandardNameIdx]); - m_daylightName = Locale.GetText (names[(int)TimeZoneNames.DaylightNameIdx]); - - DaylightTime dlt = GetDaylightTimeFromData (data); - m_CachedDaylightChanges.Add (now.Year, dlt); - OnDeserialization (dlt); - } - - // Properties public override string DaylightName { - get { return m_daylightName; } + get { + return LocalTimeZone.DaylightName; + } } public override string StandardName { - get { return m_standardName; } + get { + return LocalTimeZone.StandardName; + } } - // Methods - public override DaylightTime GetDaylightChanges (int year) + public override System.Globalization.DaylightTime GetDaylightChanges (int year) { - if (year < 1 || year > 9999) - throw new ArgumentOutOfRangeException ("year", year + - Locale.GetText (" is not in a range between 1 and 9999.")); - - // - // First we try the case for this year, very common, and is used - // by DateTime.Now (a popular call) indirectly. - // - if (year == this_year) - return this_year_dlt; - - lock (m_CachedDaylightChanges) { - DaylightTime dlt; - if (!m_CachedDaylightChanges.TryGetValue (year, out dlt)) { - Int64[] data; - string[] names; - - if (!GetTimeZoneData (year, out data, out names)) - throw new ArgumentException (Locale.GetText ("Can't get timezone data for " + year)); - - dlt = GetDaylightTimeFromData (data); - m_CachedDaylightChanges.Add (year, dlt); - } - return dlt; - } + return LocalTimeZone.GetDaylightChanges (year); } - public override TimeSpan GetUtcOffset (DateTime time) + public override TimeSpan GetUtcOffset (DateTime dateTime) { - if (time.Kind == DateTimeKind.Utc) + if (dateTime.Kind == DateTimeKind.Utc) return TimeSpan.Zero; - return TimeZoneInfo.Local.GetUtcOffset (time); - } - - void IDeserializationCallback.OnDeserialization (object sender) - { - OnDeserialization (null); + return LocalTimeZone.GetUtcOffset (dateTime); } - private void OnDeserialization (DaylightTime dlt) + public override bool IsDaylightSavingTime (DateTime dateTime) { - if (dlt == null) { - Int64[] data; - string[] names; - - this_year = DateTime.Now.Year; - if (!GetTimeZoneData (this_year, out data, out names)) - throw new ArgumentException (Locale.GetText ("Can't get timezone data for " + this_year)); - dlt = GetDaylightTimeFromData (data); - } else - this_year = dlt.Start.Year; - - this_year_dlt = dlt; - } + if (dateTime.Kind == DateTimeKind.Utc) + return false; - private DaylightTime GetDaylightTimeFromData (long[] data) - { - return new DaylightTime (new DateTime (data[(int)TimeZoneData.DaylightSavingStartIdx]), - new DateTime (data[(int)TimeZoneData.DaylightSavingEndIdx]), - new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx])); + return LocalTimeZone.IsDaylightSavingTime (dateTime); } - } } diff --git a/mcs/class/corlib/System/TimeZoneInfo.cs b/mcs/class/corlib/System/TimeZoneInfo.cs index dadd2b7003e..0c17412242c 100644 --- a/mcs/class/corlib/System/TimeZoneInfo.cs +++ b/mcs/class/corlib/System/TimeZoneInfo.cs @@ -880,6 +880,68 @@ namespace System throw new NotImplementedException (); } + internal DaylightTime GetDaylightChanges (int year) + { + DateTime start = DateTime.MinValue, end = DateTime.MinValue; + TimeSpan delta = new TimeSpan (); + + if (transitions != null) { + end = DateTime.MaxValue; + for (var i = transitions.Count - 1; i >= 0; i--) { + var pair = transitions [i]; + DateTime ttime = pair.Key; + TimeType ttype = pair.Value; + + if (ttype.IsDst) { + // DaylightTime.Delta is relative to the current BaseUtcOffset. + var d = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset; + // Handle DST gradients + if (start != DateTime.MinValue && delta != d) + end = start; + + start = ttime; + delta = d; + + if (ttime.Year <= year) + break; + } else { + if (ttime.Year < year) + break; + + end = ttime; + start = DateTime.MinValue; + } + } + + // DaylightTime.Start is relative to the Standard time. + if (start != DateTime.MinValue) + start += BaseUtcOffset; + + // DaylightTime.End is relative to the DST time. + if (end != DateTime.MaxValue) + end += BaseUtcOffset + delta; + } else { + AdjustmentRule rule = null; + foreach (var r in GetAdjustmentRules ()) { + if (r.DateEnd.Year < year) + continue; + if (r.DateStart.Year > year) + break; + rule = r; + } + if (rule != null) { + start = TransitionPoint (rule.DaylightTransitionStart, year); + end = TransitionPoint (rule.DaylightTransitionEnd, year); + delta = rule.DaylightDelta; + } + } + + if (start == DateTime.MinValue || end == DateTime.MinValue) + return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ()); + + return new DaylightTime (start, end, delta); + } + public bool IsInvalidTime (DateTime dateTime) { if (dateTime.Kind == DateTimeKind.Utc) diff --git a/mcs/class/corlib/Test/System/TimeZoneTest.cs b/mcs/class/corlib/Test/System/TimeZoneTest.cs index 965d2e078e3..19e2cb5c49d 100644 --- a/mcs/class/corlib/Test/System/TimeZoneTest.cs +++ b/mcs/class/corlib/Test/System/TimeZoneTest.cs @@ -27,13 +27,13 @@ public class TimeZoneTest { Assert.AreEqual("CEST", t1.DaylightName, "A02"); DaylightTime d1 = t1.GetDaylightChanges (2002); - Assert.AreEqual("03/31/2002 02:00:00", d1.Start.ToString ("G"), "A03"); - Assert.AreEqual("10/27/2002 03:00:00", d1.End.ToString ("G"), "A04"); + Assert.AreEqual("03/31/2002 02:00:00", d1.Start.ToString ("G", CultureInfo.InvariantCulture), "A03"); + Assert.AreEqual("10/27/2002 03:00:00", d1.End.ToString ("G", CultureInfo.InvariantCulture), "A04"); Assert.AreEqual(36000000000L, d1.Delta.Ticks, "A05"); DaylightTime d2 = t1.GetDaylightChanges (1996); - Assert.AreEqual("03/31/1996 02:00:00", d2.Start.ToString ("G"), "A06"); - Assert.AreEqual("10/27/1996 03:00:00", d2.End.ToString ("G"), "A07"); + Assert.AreEqual("03/31/1996 02:00:00", d2.Start.ToString ("G", CultureInfo.InvariantCulture), "A06"); + Assert.AreEqual("10/27/1996 03:00:00", d2.End.ToString ("G", CultureInfo.InvariantCulture), "A07"); Assert.AreEqual(36000000000L, d2.Delta.Ticks, "A08"); DateTime d3 = new DateTime (2002,2,25); @@ -46,6 +46,11 @@ public class TimeZoneTest { Assert.AreEqual(36000000000L, t1.GetUtcOffset (d3).Ticks, "A12"); Assert.AreEqual(72000000000L, t1.GetUtcOffset (d4).Ticks, "A13"); Assert.AreEqual(36000000000L, t1.GetUtcOffset (d5).Ticks, "A14"); + + // Test TimeZone methods with UTC DateTime in DST. + DateTime d6 = d4.ToUniversalTime (); + Assert.AreEqual(false, t1.IsDaylightSavingTime (d6), "A15"); + Assert.AreEqual(0, t1.GetUtcOffset (d6).Ticks, "A16"); } private void EST (TimeZone t1) @@ -55,25 +60,31 @@ public class TimeZoneTest { //Assert.AreEqual("Eastern Daylight Time", t1.DaylightName, "B02"); DaylightTime d1 = t1.GetDaylightChanges (2002); - Assert.AreEqual("04/07/2002 02:00:00", d1.Start.ToString ("G"), "B03"); - Assert.AreEqual("10/27/2002 02:00:00", d1.End.ToString ("G"), "B04"); + Assert.AreEqual("04/07/2002 02:00:00", d1.Start.ToString ("G", CultureInfo.InvariantCulture), "B03"); + Assert.AreEqual("10/27/2002 02:00:00", d1.End.ToString ("G", CultureInfo.InvariantCulture), "B04"); Assert.AreEqual(36000000000L, d1.Delta.Ticks, "B05"); DaylightTime d2 = t1.GetDaylightChanges (1996); - Assert.AreEqual("04/07/1996 02:00:00", d2.Start.ToString ("G"), "B06"); - Assert.AreEqual("10/27/1996 02:00:00", d2.End.ToString ("G"), "B07"); + Assert.AreEqual("04/07/1996 02:00:00", d2.Start.ToString ("G", CultureInfo.InvariantCulture), "B06"); + Assert.AreEqual("10/27/1996 02:00:00", d2.End.ToString ("G", CultureInfo.InvariantCulture), "B07"); Assert.AreEqual(36000000000L, d2.Delta.Ticks, "B08"); DateTime d3 = new DateTime (2002,2,25); Assert.AreEqual(false, t1.IsDaylightSavingTime (d3), "B09"); DateTime d4 = new DateTime (2002,4,8); Assert.AreEqual(true, t1.IsDaylightSavingTime (d4), "B10"); + DateTime d5 = new DateTime (2002,11,4); Assert.AreEqual(false, t1.IsDaylightSavingTime (d5), "B11"); Assert.AreEqual(-180000000000L, t1.GetUtcOffset (d3).Ticks, "B12"); Assert.AreEqual(-144000000000L, t1.GetUtcOffset (d4).Ticks, "B13"); Assert.AreEqual(-180000000000L, t1.GetUtcOffset (d5).Ticks, "B14"); + + // Test TimeZone methods with UTC DateTime in DST. + DateTime d6 = d4.ToUniversalTime (); + Assert.AreEqual(false, t1.IsDaylightSavingTime (d6), "B15"); + Assert.AreEqual(0, t1.GetUtcOffset (d6).Ticks, "B16"); } private void TST (TimeZone t1) @@ -101,13 +112,13 @@ public class TimeZoneTest { Assert.IsTrue("BST" == t1.DaylightName || "IST" == t1.DaylightName, "D02"); DaylightTime d1 = t1.GetDaylightChanges (2002); - Assert.AreEqual("03/31/2002 01:00:00", d1.Start.ToString ("G"), "D03"); - Assert.AreEqual("10/27/2002 02:00:00", d1.End.ToString ("G"), "D04"); + Assert.AreEqual("03/31/2002 01:00:00", d1.Start.ToString ("G", CultureInfo.InvariantCulture), "D03"); + Assert.AreEqual("10/27/2002 02:00:00", d1.End.ToString ("G", CultureInfo.InvariantCulture), "D04"); Assert.AreEqual(36000000000L, d1.Delta.Ticks, "D05"); DaylightTime d2 = t1.GetDaylightChanges (1996); - Assert.AreEqual("03/31/1996 01:00:00", d2.Start.ToString ("G"), "D06"); - Assert.AreEqual("10/27/1996 02:00:00", d2.End.ToString ("G"), "D07"); + Assert.AreEqual("03/31/1996 01:00:00", d2.Start.ToString ("G", CultureInfo.InvariantCulture), "D06"); + Assert.AreEqual("10/27/1996 02:00:00", d2.End.ToString ("G", CultureInfo.InvariantCulture), "D07"); Assert.AreEqual(36000000000L, d2.Delta.Ticks, "D08"); DateTime d3 = new DateTime (2002,2,25); @@ -120,6 +131,11 @@ public class TimeZoneTest { Assert.AreEqual(0L, t1.GetUtcOffset (d3).Ticks, "D12"); Assert.AreEqual(36000000000L, t1.GetUtcOffset (d4).Ticks, "D13"); Assert.AreEqual(0L, t1.GetUtcOffset (d5).Ticks, "D14"); + + // Test TimeZone methods with UTC DateTime in DST. + DateTime d6 = d4.ToUniversalTime (); + Assert.AreEqual(false, t1.IsDaylightSavingTime (d6), "D15"); + Assert.AreEqual(0, t1.GetUtcOffset (d6).Ticks, "D16"); } private void NZST(TimeZone t1) { @@ -127,13 +143,13 @@ public class TimeZoneTest { Assert.AreEqual("NZDT", t1.DaylightName, "E02"); DaylightTime d1 = t1.GetDaylightChanges (2013); - Assert.AreEqual("09/29/2013 02:00:00", d1.Start.ToString ("G"), "E03"); - Assert.AreEqual("04/07/2013 03:00:00", d1.End.ToString ("G"), "E04"); + Assert.AreEqual("09/29/2013 02:00:00", d1.Start.ToString ("G", CultureInfo.InvariantCulture), "E03"); + Assert.AreEqual("04/07/2013 03:00:00", d1.End.ToString ("G", CultureInfo.InvariantCulture), "E04"); Assert.AreEqual(36000000000L, d1.Delta.Ticks, "E05"); DaylightTime d2 = t1.GetDaylightChanges (2001); - Assert.AreEqual("10/07/2001 02:00:00", d2.Start.ToString ("G"), "E06"); - Assert.AreEqual("03/18/2001 03:00:00", d2.End.ToString ("G"), "E07"); + Assert.AreEqual("10/07/2001 02:00:00", d2.Start.ToString ("G", CultureInfo.InvariantCulture), "E06"); + Assert.AreEqual("03/18/2001 03:00:00", d2.End.ToString ("G", CultureInfo.InvariantCulture), "E07"); Assert.AreEqual(36000000000L, d2.Delta.Ticks, "E08"); DateTime d3 = new DateTime(2013,02,15); @@ -146,10 +162,14 @@ public class TimeZoneTest { Assert.AreEqual(36000000000L /*hour*/ * 13L, t1.GetUtcOffset (d3).Ticks, "E12"); Assert.AreEqual(36000000000L /*hour*/ * 12L, t1.GetUtcOffset (d4).Ticks, "E13"); Assert.AreEqual(36000000000L /*hour*/ * 13L, t1.GetUtcOffset (d5).Ticks, "E14"); + + // Test TimeZone methods with UTC DateTime in DST. + DateTime d6 = d5.ToUniversalTime (); + Assert.AreEqual(false, t1.IsDaylightSavingTime (d6), "E15"); + Assert.AreEqual(0, t1.GetUtcOffset (d6).Ticks, "E16"); } [Test] - [Culture ("")] public void TestCtors () { TimeZone t1 = TimeZone.CurrentTimeZone; diff --git a/mono/metadata/icall-def.h b/mono/metadata/icall-def.h index ec4396cfb18..f80a9c00949 100644 --- a/mono/metadata/icall-def.h +++ b/mono/metadata/icall-def.h @@ -129,9 +129,6 @@ ICALL(CONSOLE_3, "SetBreak", ves_icall_System_ConsoleDriver_SetBreak ) ICALL(CONSOLE_4, "SetEcho", ves_icall_System_ConsoleDriver_SetEcho ) ICALL(CONSOLE_5, "TtySetup", ves_icall_System_ConsoleDriver_TtySetup ) -ICALL_TYPE(TZONE, "System.CurrentSystemTimeZone", TZONE_1) -ICALL(TZONE_1, "GetTimeZoneData", ves_icall_System_CurrentSystemTimeZone_GetTimeZoneData) - ICALL_TYPE(DTIME, "System.DateTime", DTIME_1) ICALL(DTIME_1, "GetSystemTimeAsFileTime", mono_100ns_datetime) diff --git a/mono/metadata/icall.c b/mono/metadata/icall.c index 62040ceb6d9..6ac14185a1b 100644 --- a/mono/metadata/icall.c +++ b/mono/metadata/icall.c @@ -5613,257 +5613,6 @@ ves_icall_System_Delegate_SetMulticastInvoke (MonoDelegate *this) this->invoke_impl = mono_runtime_create_delegate_trampoline (this->object.vtable->klass); } -/* - * Magic number to convert a time which is relative to - * Jan 1, 1970 into a value which is relative to Jan 1, 0001. - */ -#define EPOCH_ADJUST ((guint64)62135596800LL) - -/* - * Magic number to convert FILETIME base Jan 1, 1601 to DateTime - base Jan, 1, 0001 - */ -#define FILETIME_ADJUST ((guint64)504911232000000000LL) - -#ifdef HOST_WIN32 -/* convert a SYSTEMTIME which is of the form "last thursday in october" to a real date */ -static void -convert_to_absolute_date(SYSTEMTIME *date) -{ -#define IS_LEAP(y) ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) - static int days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - static int leap_days_in_month[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - /* from the calendar FAQ */ - int a = (14 - date->wMonth) / 12; - int y = date->wYear - a; - int m = date->wMonth + 12 * a - 2; - int d = (1 + y + y/4 - y/100 + y/400 + (31*m)/12) % 7; - - /* d is now the day of the week for the first of the month (0 == Sunday) */ - - int day_of_week = date->wDayOfWeek; - - /* set day_in_month to the first day in the month which falls on day_of_week */ - int day_in_month = 1 + (day_of_week - d); - if (day_in_month <= 0) - day_in_month += 7; - - /* wDay is 1 for first weekday in month, 2 for 2nd ... 5 means last - so work that out allowing for days in the month */ - date->wDay = day_in_month + (date->wDay - 1) * 7; - if (date->wDay > (IS_LEAP(date->wYear) ? leap_days_in_month[date->wMonth - 1] : days_in_month[date->wMonth - 1])) - date->wDay -= 7; -} -#endif - -#ifndef HOST_WIN32 -/* - * Return's the offset from GMT of a local time. - * - * tm is a local time - * t is the same local time as seconds. - */ -static int -gmt_offset(struct tm *tm, time_t t) -{ -#if defined (HAVE_TM_GMTOFF) - return tm->tm_gmtoff; -#else - struct tm g; - time_t t2; - g = *gmtime(&t); - g.tm_isdst = tm->tm_isdst; - t2 = mktime(&g); - return (int)difftime(t, t2); -#endif -} -#endif -/* - * This is heavily based on zdump.c from glibc 2.2. - * - * * data[0]: start of daylight saving time (in DateTime ticks). - * * data[1]: end of daylight saving time (in DateTime ticks). - * * data[2]: utcoffset (in TimeSpan ticks). - * * data[3]: additional offset when daylight saving (in TimeSpan ticks). - * * name[0]: name of this timezone when not daylight saving. - * * name[1]: name of this timezone when daylight saving. - * - * FIXME: This only works with "standard" Unix dates (years between 1900 and 2100) while - * the class library allows years between 1 and 9999. - * - * Returns true on success and zero on failure. - */ -ICALL_EXPORT guint32 -ves_icall_System_CurrentSystemTimeZone_GetTimeZoneData (guint32 year, MonoArray **data, MonoArray **names) -{ -#ifndef HOST_WIN32 - MonoDomain *domain = mono_domain_get (); - struct tm start, tt; - time_t t; - - long int gmtoff, gmtoff_after, gmtoff_st, gmtoff_ds; - int day, transitioned; - char tzone [64]; - - gmtoff_st = gmtoff_ds = transitioned = 0; - - MONO_CHECK_ARG_NULL (data, 0); - MONO_CHECK_ARG_NULL (names, 0); - - mono_gc_wbarrier_generic_store (data, (MonoObject*) mono_array_new (domain, mono_defaults.int64_class, 4)); - mono_gc_wbarrier_generic_store (names, (MonoObject*) mono_array_new (domain, mono_defaults.string_class, 2)); - - /* - * no info is better than crashing: we'll need our own tz data - * to make this work properly, anyway. The range is probably - * reduced to 1970 .. 2037 because that is what mktime is - * guaranteed to support (we get into an infinite loop - * otherwise). - */ - - memset (&start, 0, sizeof (start)); - - start.tm_mday = 1; - start.tm_year = year-1900; - - t = mktime (&start); - - if ((year < 1970) || (year > 2037) || (t == -1)) { - t = time (NULL); - tt = *localtime (&t); - strftime (tzone, sizeof (tzone), "%Z", &tt); - mono_array_setref ((*names), 0, mono_string_new (domain, tzone)); - mono_array_setref ((*names), 1, mono_string_new (domain, tzone)); - return 1; - } - - gmtoff = gmt_offset (&start, t); - - /* For each day of the year, calculate the tm_gmtoff. */ - for (day = 0; day < 365 && transitioned < 2; day++) { - - t += 3600*24; - tt = *localtime (&t); - - gmtoff_after = gmt_offset(&tt, t); - - /* Daylight saving starts or ends here. */ - if (gmtoff_after != gmtoff) { - struct tm tt1; - time_t t1; - - /* Try to find the exact hour when daylight saving starts/ends. */ - t1 = t; - do { - t1 -= 3600; - tt1 = *localtime (&t1); - } while (gmt_offset (&tt1, t1) != gmtoff); - - /* Try to find the exact minute when daylight saving starts/ends. */ - do { - t1 += 60; - tt1 = *localtime (&t1); - } while (gmt_offset (&tt1, t1) == gmtoff); - t1+=gmtoff; - strftime (tzone, sizeof (tzone), "%Z", &tt); - - /* Write data, if we're already in daylight saving, we're done. */ - if (tt.tm_isdst) { - mono_array_setref ((*names), 1, mono_string_new (domain, tzone)); - mono_array_set ((*data), gint64, 0, ((gint64)t1 + EPOCH_ADJUST) * 10000000L); - if (gmtoff_ds == 0) { - gmtoff_st = gmtoff; - gmtoff_ds = gmtoff_after; - } - transitioned++; - } else { - mono_array_setref ((*names), 0, mono_string_new (domain, tzone)); - mono_array_set ((*data), gint64, 1, ((gint64)t1 + EPOCH_ADJUST) * 10000000L); - if (gmtoff_ds == 0) { - gmtoff_st = gmtoff_after; - gmtoff_ds = gmtoff; - } - transitioned++; - } - - /* This is only set once when we enter daylight saving. */ - if (tt1.tm_isdst) { - mono_array_set ((*data), gint64, 2, (gint64)gmtoff_st * 10000000L); - mono_array_set ((*data), gint64, 3, (gint64)(gmtoff_ds - gmtoff_st) * 10000000L); - } - gmtoff = gmt_offset (&tt, t); - } - } - - if (transitioned < 2) { - strftime (tzone, sizeof (tzone), "%Z", &tt); - mono_array_setref ((*names), 0, mono_string_new (domain, tzone)); - mono_array_setref ((*names), 1, mono_string_new (domain, tzone)); - mono_array_set ((*data), gint64, 0, 0); - mono_array_set ((*data), gint64, 1, 0); - mono_array_set ((*data), gint64, 2, (gint64) gmtoff * 10000000L); - mono_array_set ((*data), gint64, 3, 0); - } - - return 1; -#else - MonoDomain *domain = mono_domain_get (); - TIME_ZONE_INFORMATION tz_info; - FILETIME ft; - int i; - int err, tz_id; - - tz_id = GetTimeZoneInformation (&tz_info); - if (tz_id == TIME_ZONE_ID_INVALID) - return 0; - - MONO_CHECK_ARG_NULL (data, 0); - MONO_CHECK_ARG_NULL (names, 0); - - mono_gc_wbarrier_generic_store (data, mono_array_new (domain, mono_defaults.int64_class, 4)); - mono_gc_wbarrier_generic_store (names, mono_array_new (domain, mono_defaults.string_class, 2)); - - for (i = 0; i < 32; ++i) - if (!tz_info.DaylightName [i]) - break; - mono_array_setref ((*names), 1, mono_string_new_utf16 (domain, tz_info.DaylightName, i)); - for (i = 0; i < 32; ++i) - if (!tz_info.StandardName [i]) - break; - mono_array_setref ((*names), 0, mono_string_new_utf16 (domain, tz_info.StandardName, i)); - - if ((year <= 1601) || (year > 30827)) { - /* - * According to MSDN, the MS time functions can't handle dates outside - * this interval. - */ - return 1; - } - - /* even if the timezone has no daylight savings it may have Bias (e.g. GMT+13 it seems) */ - if (tz_id != TIME_ZONE_ID_UNKNOWN) { - tz_info.StandardDate.wYear = year; - convert_to_absolute_date(&tz_info.StandardDate); - err = SystemTimeToFileTime (&tz_info.StandardDate, &ft); - //g_assert(err); - if (err == 0) - return 0; - - mono_array_set ((*data), gint64, 1, FILETIME_ADJUST + (((guint64)ft.dwHighDateTime<<32) | ft.dwLowDateTime)); - tz_info.DaylightDate.wYear = year; - convert_to_absolute_date(&tz_info.DaylightDate); - err = SystemTimeToFileTime (&tz_info.DaylightDate, &ft); - //g_assert(err); - if (err == 0) - return 0; - - mono_array_set ((*data), gint64, 0, FILETIME_ADJUST + (((guint64)ft.dwHighDateTime<<32) | ft.dwLowDateTime)); - } - mono_array_set ((*data), gint64, 2, (tz_info.Bias + tz_info.StandardBias) * -600000000LL); - mono_array_set ((*data), gint64, 3, (tz_info.DaylightBias - tz_info.StandardBias) * -600000000LL); - - return 1; -#endif -} - /* System.Buffer */ static inline gint32