@@ -22,8 +22,8 @@ namespace System {
22
22
public partial class TimeZoneInfo {
23
23
enum TimeZoneData
24
24
{
25
- DaylightSavingStartIdx ,
26
- DaylightSavingEndIdx ,
25
+ DaylightSavingFirstTransitionIdx ,
26
+ DaylightSavingSecondTransitionIdx ,
27
27
UtcOffsetIdx ,
28
28
AdditionalDaylightOffsetIdx
29
29
} ;
@@ -34,12 +34,14 @@ enum TimeZoneNames
34
34
DaylightNameIdx
35
35
} ;
36
36
37
- static AdjustmentRule CreateAdjustmentRule ( int year , out Int64 [ ] data , out string [ ] names , string standardNameCurrentYear , string daylightNameCurrentYear )
37
+ static List < AdjustmentRule > CreateAdjustmentRule ( int year , out Int64 [ ] data , out string [ ] names , string standardNameCurrentYear , string daylightNameCurrentYear )
38
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 ] ) ;
39
+ List < AdjustmentRule > rulesForYear = new List < AdjustmentRule > ( ) ;
40
+ bool dst_inverted ;
41
+ if ( ! System . CurrentSystemTimeZone . GetTimeZoneData ( year , out data , out names , out dst_inverted ) )
42
+ return rulesForYear ;
43
+ var firstTransition = new DateTime ( data [ ( int ) TimeZoneData . DaylightSavingFirstTransitionIdx ] ) ;
44
+ var secondTransition = new DateTime ( data [ ( int ) TimeZoneData . DaylightSavingSecondTransitionIdx ] ) ;
43
45
var daylightOffset = new TimeSpan ( data [ ( int ) TimeZoneData . AdditionalDaylightOffsetIdx ] ) ;
44
46
45
47
/* C# TimeZoneInfo does not support timezones the same way as unix. In unix, timezone files are specified by region such as
@@ -48,73 +50,116 @@ static AdjustmentRule CreateAdjustmentRule(int year, out Int64[] data, out strin
48
50
* 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
51
*/
50
52
if ( standardNameCurrentYear != names [ ( int ) TimeZoneNames . StandardNameIdx ] )
51
- return null ;
53
+ return rulesForYear ;
52
54
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 ;
55
+ return rulesForYear ;
56
+
57
+ var beginningOfYear = new DateTime ( year , 1 , 1 , 0 , 0 , 0 , 0 ) ;
58
+ var endOfYearDay = new DateTime ( year , 12 , DateTime . DaysInMonth ( year , 12 ) ) ;
59
+ var endOfYearMaxTimeout = new DateTime ( year , 12 , DateTime . DaysInMonth ( year , 12 ) , 23 , 59 , 59 , 999 ) ;
60
+
61
+ if ( ! dst_inverted ) {
62
+ // For daylight savings time that happens between jan and dec, create a rule from jan 1 to dec 31 (the entire year)
63
+
64
+ // This rule (for the whole year) specifies the starting and ending months of daylight savings time.
65
+ var startOfDaylightSavingsTime = TransitionTime . CreateFixedDateRule ( new DateTime ( 1 , 1 , 1 ) . Add ( firstTransition . TimeOfDay ) ,
66
+ firstTransition . Month , firstTransition . Day ) ;
67
+ var endOfDaylightSavingsTime = TransitionTime . CreateFixedDateRule ( new DateTime ( 1 , 1 , 1 ) . Add ( secondTransition . TimeOfDay ) ,
68
+ secondTransition . Month , secondTransition . Day ) ;
69
+
70
+ var fullYearRule = TimeZoneInfo . AdjustmentRule . CreateAdjustmentRule ( beginningOfYear ,
71
+ endOfYearDay ,
72
+ daylightOffset ,
73
+ startOfDaylightSavingsTime ,
74
+ endOfDaylightSavingsTime ) ;
75
+ rulesForYear . Add ( fullYearRule ) ;
76
+ } else {
77
+ // Some timezones (Australia/Sydney) have daylight savings over the new year.
78
+ // Our icall returns the transitions for the current year, so we need two adjustment rules each year for this case
79
+
80
+ // The first rule specifies daylight savings starting at jan 1 and ending at the first transition.
81
+ var startOfFirstDaylightSavingsTime = TransitionTime . CreateFixedDateRule ( new DateTime ( 1 , 1 , 1 ) , 1 , 1 ) ;
82
+ var endOfFirstDaylightSavingsTime = TransitionTime . CreateFixedDateRule ( new DateTime ( 1 , 1 , 1 ) . Add ( firstTransition . TimeOfDay ) ,
83
+ firstTransition . Month , firstTransition . Day ) ;
84
+
85
+ var transitionOutOfDaylightSavingsRule = TimeZoneInfo . AdjustmentRule . CreateAdjustmentRule (
86
+ new DateTime ( year , 1 , 1 ) ,
87
+ new DateTime ( firstTransition . Year , firstTransition . Month , firstTransition . Day ) ,
88
+ daylightOffset ,
89
+ startOfFirstDaylightSavingsTime ,
90
+ endOfFirstDaylightSavingsTime ) ;
91
+ rulesForYear . Add ( transitionOutOfDaylightSavingsRule ) ;
92
+
93
+ // The second rule specifies daylight savings time starting the day after we transition out of daylight savings
94
+ // and ending at the end of the year, with daylight savings starting near the end and ending on the last day of the year
95
+ var startOfSecondDaylightSavingsTime = TransitionTime . CreateFixedDateRule ( new DateTime ( 1 , 1 , 1 ) . Add ( secondTransition . TimeOfDay ) ,
96
+ secondTransition . Month , secondTransition . Day ) ;
97
+ var endOfSecondDaylightSavingsTime = TransitionTime . CreateFixedDateRule ( new DateTime ( 1 , 1 , 1 ) . Add ( endOfYearMaxTimeout . TimeOfDay ) ,
98
+ endOfYearMaxTimeout . Month , endOfYearMaxTimeout . Day ) ;
99
+
100
+ var transitionIntoDaylightSavingsRule = TimeZoneInfo . AdjustmentRule . CreateAdjustmentRule (
101
+ new DateTime ( firstTransition . Year , firstTransition . Month , firstTransition . Day ) . AddDays ( 1 ) ,
102
+ endOfYearDay ,
103
+ daylightOffset ,
104
+ startOfSecondDaylightSavingsTime ,
105
+ endOfSecondDaylightSavingsTime ) ;
106
+ rulesForYear . Add ( transitionIntoDaylightSavingsRule ) ;
107
+ }
108
+ return rulesForYear ;
66
109
}
67
110
68
111
static TimeZoneInfo CreateLocalUnity ( )
69
112
{
70
113
Int64 [ ] data ;
71
114
string [ ] names ;
115
+ //Some timezones start in DST on january first and disable it during the summer
116
+ bool dst_inverted ;
72
117
int currentYear = DateTime . UtcNow . Year ;
73
- if ( ! System . CurrentSystemTimeZone . GetTimeZoneData ( currentYear , out data , out names ) )
118
+ if ( ! System . CurrentSystemTimeZone . GetTimeZoneData ( currentYear , out data , out names , out dst_inverted ) )
74
119
throw new NotSupportedException ( "Can't get timezone name." ) ;
75
120
76
- var utcOffsetTS = TimeSpan . FromTicks ( data [ ( int ) TimeZoneData . UtcOffsetIdx ] ) ;
121
+ var utcOffsetTS = TimeSpan . FromTicks ( data [ ( int ) TimeZoneData . UtcOffsetIdx ] ) ;
77
122
char utcOffsetSign = ( utcOffsetTS >= TimeSpan . Zero ) ? '+' : '-' ;
78
- string displayName = "(GMT" + utcOffsetSign + utcOffsetTS . ToString ( @"hh\:mm" ) + ") Local Time" ;
123
+ string displayName = "(GMT" + utcOffsetSign + utcOffsetTS . ToString ( @"hh\:mm" ) + ") Local Time" ;
79
124
string standardDisplayName = names [ ( int ) TimeZoneNames . StandardNameIdx ] ;
80
125
string daylightDisplayName = names [ ( int ) TimeZoneNames . DaylightNameIdx ] ;
81
126
82
- var adjustmentList = new List < AdjustmentRule > ( ) ;
83
- bool disableDaylightSavings = data [ ( int ) TimeZoneData . AdditionalDaylightOffsetIdx ] < = 0 ;
127
+ var adjustmentRulesList = new List < AdjustmentRule > ( ) ;
128
+ bool disableDaylightSavings = data [ ( int ) TimeZoneData . AdditionalDaylightOffsetIdx ] = = 0 ;
84
129
//If the timezone supports daylight savings time, generate adjustment rules for the timezone
85
- if ( ! disableDaylightSavings )
86
- {
130
+ if ( ! disableDaylightSavings ) {
87
131
//the icall only supports years from 1970 through 2037.
88
132
int firstSupportedDate = 1971 ;
89
133
int lastSupportedDate = 2037 ;
90
134
91
135
//first, generate rules from the current year until the last year mktime is guaranteed to supports
92
- for ( int year = currentYear ; year <= lastSupportedDate ; year ++ )
93
- {
94
- var rule = CreateAdjustmentRule ( year , out data , out names , standardDisplayName , daylightDisplayName ) ;
95
- //breakout if timezone changes, or fails
96
- if ( rule == null )
136
+ for ( int year = currentYear ; year <= lastSupportedDate ; year ++ ) {
137
+ var rulesForCurrentYear = CreateAdjustmentRule ( year , out data , out names , standardDisplayName , daylightDisplayName ) ;
138
+ //breakout if no more rules
139
+ if ( rulesForCurrentYear . Count > 0 )
140
+ adjustmentRulesList . AddRange ( rulesForCurrentYear ) ;
141
+ else
97
142
break ;
98
- adjustmentList . Add ( rule ) ;
143
+
99
144
}
100
145
101
- for ( int year = currentYear - 1 ; year >= firstSupportedDate ; year -- )
102
- {
103
- var rule = CreateAdjustmentRule ( year , out data , out names , standardDisplayName , daylightDisplayName ) ;
104
- //breakout if timezone changes, or fails
105
- if ( rule == null )
146
+ for ( int year = currentYear - 1 ; year >= firstSupportedDate ; year -- ) {
147
+ var rulesForCurrentYear = CreateAdjustmentRule ( year , out data , out names , standardDisplayName , daylightDisplayName ) ;
148
+ //breakout if no more rules
149
+ if ( rulesForCurrentYear . Count > 0 )
150
+ adjustmentRulesList . AddRange ( rulesForCurrentYear ) ;
151
+ else
106
152
break ;
107
- adjustmentList . Add ( rule ) ;
108
153
}
109
154
110
- adjustmentList . Sort ( ( rule1 , rule2 ) => rule1 . DateStart . CompareTo ( rule2 . DateStart ) ) ;
155
+ adjustmentRulesList . Sort ( ( rule1 , rule2 ) => rule1 . DateStart . CompareTo ( rule2 . DateStart ) ) ;
111
156
}
112
- return TimeZoneInfo . CreateCustomTimeZone ( "Local" ,
157
+ return TimeZoneInfo . CreateCustomTimeZone ( "Local" ,
113
158
utcOffsetTS ,
114
159
displayName ,
115
160
standardDisplayName ,
116
161
daylightDisplayName ,
117
- adjustmentList . ToArray ( ) ,
162
+ adjustmentRulesList . ToArray ( ) ,
118
163
disableDaylightSavings ) ;
119
164
}
120
165
}
0 commit comments