Skip to content

Commit e01ef6d

Browse files
authored
Merge pull request #801 from Unity-Technologies/unity-master-tz-fix-build (case 988428) (case 943047)
Support timezones without daylight savings time
2 parents 4d85d0e + eee354c commit e01ef6d

File tree

1 file changed

+84
-97
lines changed

1 file changed

+84
-97
lines changed

mcs/class/corlib/System/TimeZoneInfo.Unity.cs

Lines changed: 84 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,13 @@
11
//
2-
// System.TimeZoneInfo helper for MonoTouch
3-
// because the devices cannot access the file system to read the data
2+
// System.TimeZoneInfo helper for Unity
3+
// because the devices cannot access the file system to read the data
44
//
55
// Authors:
6-
// Sebastien Pouliot <[email protected]>
7-
//
8-
// Copyright 2011-2013 Xamarin Inc.
9-
//
10-
// The class can be either constructed from a string (from user code)
11-
// or from a handle (from iphone-sharp.dll internal calls). This
12-
// delays the creation of the actual managed string until actually
13-
// required
14-
//
15-
// Permission is hereby granted, free of charge, to any person obtaining
16-
// a copy of this software and associated documentation files (the
17-
// "Software"), to deal in the Software without restriction, including
18-
// without limitation the rights to use, copy, modify, merge, publish,
19-
// distribute, sublicense, and/or sell copies of the Software, and to
20-
// permit persons to whom the Software is furnished to do so, subject to
21-
// the following conditions:
22-
//
23-
// The above copyright notice and this permission notice shall be
24-
// included in all copies or substantial portions of the Software.
25-
//
26-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27-
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28-
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29-
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30-
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31-
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32-
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6+
// Michael DeRoy <[email protected]>
7+
// Jonathan Chambers <[email protected]>
338
//
9+
// Copyright 2018 Unity Technologies, Inc.
10+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3411

3512
#if UNITY
3613

@@ -45,90 +22,100 @@ namespace System {
4522
public partial class TimeZoneInfo {
4623
enum TimeZoneData
4724
{
48-
DaylightSavingStartIdx,
49-
DaylightSavingEndIdx,
50-
UtcOffsetIdx,
51-
AdditionalDaylightOffsetIdx
25+
DaylightSavingStartIdx,
26+
DaylightSavingEndIdx,
27+
UtcOffsetIdx,
28+
AdditionalDaylightOffsetIdx
5229
};
5330

5431
enum TimeZoneNames
5532
{
56-
StandardNameIdx,
57-
DaylightNameIdx
33+
StandardNameIdx,
34+
DaylightNameIdx
5835
};
36+
37+
static AdjustmentRule CreateAdjustmentRule(int year, out Int64[] data, out string[] names, string standardNameCurrentYear, string daylightNameCurrentYear)
38+
{
39+
if(!System.CurrentSystemTimeZone.GetTimeZoneData(year, out data, out names))
40+
return null;
41+
var startTime = new DateTime (data[(int)TimeZoneData.DaylightSavingStartIdx]);
42+
var endTime = new DateTime (data[(int)TimeZoneData.DaylightSavingEndIdx]);
43+
var daylightOffset = new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]);
44+
45+
/* C# TimeZoneInfo does not support timezones the same way as unix. In unix, timezone files are specified by region such as
46+
* America/New_York or Asia/Singapore. If a region like Asia/Singapore changes it's timezone from +0730 to +08, the UTC offset
47+
* has changed, but there is no support in the C# code to transition to this new UTC offset except for the case of daylight
48+
* savings time. As such we'll only generate timezone rules for a region at the times associated with the timezone of the current year.
49+
*/
50+
if(standardNameCurrentYear != names[(int)TimeZoneNames.StandardNameIdx])
51+
return null;
52+
if(daylightNameCurrentYear != names[(int)TimeZoneNames.DaylightNameIdx])
53+
return null;
54+
55+
var dlsTransitionStart = TransitionTime.CreateFixedDateRule(new DateTime(1,1,1).Add(startTime.TimeOfDay),
56+
startTime.Month, startTime.Day);
57+
var dlsTransitionEnd = TransitionTime.CreateFixedDateRule(new DateTime(1,1,1).Add(endTime.TimeOfDay),
58+
endTime.Month, endTime.Day);
59+
60+
var rule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(year, 1, 1),
61+
new DateTime(year, 12, DateTime.DaysInMonth(year, 12)),
62+
daylightOffset,
63+
dlsTransitionStart,
64+
dlsTransitionEnd);
65+
return rule;
66+
}
67+
5968
static TimeZoneInfo CreateLocalUnity ()
6069
{
6170
Int64[] data;
62-
string[] names;
63-
if (!System.CurrentSystemTimeZone.GetTimeZoneData (1973, out data, out names))
71+
string[] names;
72+
int currentYear = DateTime.UtcNow.Year;
73+
if (!System.CurrentSystemTimeZone.GetTimeZoneData (currentYear, out data, out names))
6474
throw new NotSupportedException ("Can't get timezone name.");
6575

66-
TimeSpan utcOffsetTS = TimeSpan.FromTicks(data[(int)TimeZoneData.UtcOffsetIdx]);
67-
char utcOffsetSign = (utcOffsetTS >= TimeSpan.Zero) ? '+' : '-';
68-
string displayName = "(GMT" + utcOffsetSign + utcOffsetTS.ToString(@"hh\:mm") + ") Local Time";
69-
string standardDisplayName = names[(int)TimeZoneNames.StandardNameIdx];
70-
string daylightDisplayName = names[(int)TimeZoneNames.DaylightNameIdx];
71-
72-
//Create The Adjustment Rules For This TimeZoneInfo.
73-
var adjustmentList = new List<TimeZoneInfo.AdjustmentRule>();
74-
for(int year = 1973; year <= 2037; year++)
75-
{
76-
if (!System.CurrentSystemTimeZone.GetTimeZoneData (year, out data, out names))
77-
continue;
78-
79-
DaylightTime dlt = new DaylightTime (new DateTime (data[(int)TimeZoneData.DaylightSavingStartIdx]),
80-
new DateTime (data[(int)TimeZoneData.DaylightSavingEndIdx]),
81-
new TimeSpan (data[(int)TimeZoneData.AdditionalDaylightOffsetIdx]));
82-
83-
DateTime dltStartTime = new DateTime(1, 1, 1).Add(dlt.Start.TimeOfDay);
84-
DateTime dltEndTime = new DateTime(1, 1, 1).Add(dlt.End.TimeOfDay);
76+
var utcOffsetTS = TimeSpan.FromTicks(data[(int)TimeZoneData.UtcOffsetIdx]);
77+
char utcOffsetSign = (utcOffsetTS >= TimeSpan.Zero) ? '+' : '-';
78+
string displayName = "(GMT" + utcOffsetSign + utcOffsetTS.ToString(@"hh\:mm") + ") Local Time";
79+
string standardDisplayName = names[(int)TimeZoneNames.StandardNameIdx];
80+
string daylightDisplayName = names[(int)TimeZoneNames.DaylightNameIdx];
8581

86-
if (dlt.Start == dlt.End)
87-
continue;
82+
var adjustmentList = new List<AdjustmentRule>();
83+
bool disableDaylightSavings = data[(int)TimeZoneData.AdditionalDaylightOffsetIdx] <= 0;
84+
//If the timezone supports daylight savings time, generate adjustment rules for the timezone
85+
if(!disableDaylightSavings)
86+
{
87+
//the icall only supports years from 1970 through 2037.
88+
int firstSupportedDate = 1970;
89+
int lastSupportedDate = 2037;
8890

89-
TimeZoneInfo.TransitionTime startTime = TimeZoneInfo.TransitionTime.CreateFixedDateRule(dltStartTime, dlt.Start.Month, dlt.Start.Day);
90-
TimeZoneInfo.TransitionTime endTime = TimeZoneInfo.TransitionTime.CreateFixedDateRule(dltEndTime, dlt.End.Month, dlt.End.Day);
91-
92-
93-
//mktime only supports dates starting in 1973, so create an adjustment rule for years before 1973 following 1973s rules
94-
if (year == 1973)
91+
//first, generate rules from the current year until the last year mktime is guaranteed to supports
92+
for(int year = currentYear; year <= lastSupportedDate; year++)
9593
{
96-
TimeZoneInfo.AdjustmentRule firstRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(DateTime.MinValue,
97-
new DateTime(1969, 12, 31),
98-
dlt.Delta,
99-
startTime,
100-
endTime);
101-
adjustmentList.Add(firstRule);
94+
var rule = CreateAdjustmentRule(year, out data, out names, standardDisplayName, daylightDisplayName);
95+
//breakout if timezone changes, or fails
96+
if(rule == null)
97+
break;
98+
adjustmentList.Add(rule);
10299
}
103-
104-
TimeZoneInfo.AdjustmentRule rule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(year, 1, 1),
105-
new DateTime(year, 12, 31),
106-
dlt.Delta,
107-
startTime,
108-
endTime);
109-
adjustmentList.Add(rule);
110-
111-
//mktime only supports dates up to 2037, so create an adjustment rule for years after 2037 following 2037s rules
112-
if (year == 2037)
100+
101+
for(int year = currentYear - 1; year >= firstSupportedDate; year--)
113102
{
114-
// create a max date that does not include any time of day offset to make CreateAdjustmentRule happy
115-
var maxDate = new DateTime(DateTime.MaxValue.Year, DateTime.MaxValue.Month, DateTime.MaxValue.Day);
116-
TimeZoneInfo.AdjustmentRule lastRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(2038, 1, 1),
117-
maxDate,
118-
dlt.Delta,
119-
startTime,
120-
endTime);
121-
adjustmentList.Add(lastRule);
103+
var rule = CreateAdjustmentRule(year, out data, out names, standardDisplayName, daylightDisplayName);
104+
//breakout if timezone changes, or fails
105+
if(rule == null)
106+
break;
107+
adjustmentList.Add(rule);
122108
}
123-
}
124-
125-
return TimeZoneInfo.CreateCustomTimeZone("local",
126-
utcOffsetTS,
127-
displayName,
128-
standardDisplayName,
129-
daylightDisplayName,
130-
adjustmentList.ToArray(),
131-
false);
109+
110+
adjustmentList.Sort( (rule1, rule2) => rule1.DateStart.CompareTo(rule2.DateStart) );
111+
}
112+
return TimeZoneInfo.CreateCustomTimeZone("Local",
113+
utcOffsetTS,
114+
displayName,
115+
standardDisplayName,
116+
daylightDisplayName,
117+
adjustmentList.ToArray(),
118+
disableDaylightSavings);
132119
}
133120
}
134121
}

0 commit comments

Comments
 (0)