1
1
//
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
4
4
//
5
5
// 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] >
33
8
//
9
+ // Copyright 2018 Unity Technologies, Inc.
10
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
34
11
35
12
#if UNITY
36
13
@@ -45,90 +22,100 @@ namespace System {
45
22
public partial class TimeZoneInfo {
46
23
enum TimeZoneData
47
24
{
48
- DaylightSavingStartIdx ,
49
- DaylightSavingEndIdx ,
50
- UtcOffsetIdx ,
51
- AdditionalDaylightOffsetIdx
25
+ DaylightSavingStartIdx ,
26
+ DaylightSavingEndIdx ,
27
+ UtcOffsetIdx ,
28
+ AdditionalDaylightOffsetIdx
52
29
} ;
53
30
54
31
enum TimeZoneNames
55
32
{
56
- StandardNameIdx ,
57
- DaylightNameIdx
33
+ StandardNameIdx ,
34
+ DaylightNameIdx
58
35
} ;
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
+
59
68
static TimeZoneInfo CreateLocalUnity ( )
60
69
{
61
70
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 ) )
64
74
throw new NotSupportedException ( "Can't get timezone name." ) ;
65
75
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 ] ;
85
81
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 ;
88
90
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 ++ )
95
93
{
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 ) ;
102
99
}
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 -- )
113
102
{
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 ) ;
122
108
}
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 ) ;
132
119
}
133
120
}
134
121
}
0 commit comments