summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/CurrentSystemTimeZone.cs
blob: 2d848397a9b3e0fdab12081a70cda35306cfe937 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*============================================================
**
**
**
** Purpose: 
** This class represents the current system timezone.  It is
** the only meaningful implementation of the TimeZone class 
** available in this version.
**
** The only TimeZone that we support in version 1 is the 
** CurrentTimeZone as determined by the system timezone.
**
**
============================================================*/

using System;
using System.Diagnostics.Contracts;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

namespace System
{
    [Obsolete("System.CurrentSystemTimeZone has been deprecated.  Please investigate the use of System.TimeZoneInfo.Local instead.")]
    [Serializable]
    internal partial class CurrentSystemTimeZone : TimeZone
    {
        // Standard offset in ticks to the Universal time if
        // no daylight saving is in used.
        // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000.
        // (1 millisecond = 10000 ticks)
        private long m_ticksOffset;
        private String m_standardName;
        private String m_daylightName;

        internal CurrentSystemTimeZone()
        {
            TimeZoneInfo local = TimeZoneInfo.Local;

            m_ticksOffset = local.BaseUtcOffset.Ticks;
            m_standardName = local.StandardName;
            m_daylightName = local.DaylightName;
        }

        public override String StandardName
        {
            get
            {
                return m_standardName;
            }
        }

        public override String DaylightName
        {
            get
            {
                return m_daylightName;
            }
        }

        internal long GetUtcOffsetFromUniversalTime(DateTime time, ref Boolean isAmbiguousLocalDst)
        {
            // Get the daylight changes for the year of the specified time.
            TimeSpan offset = new TimeSpan(m_ticksOffset);
            DaylightTime daylightTime = GetDaylightChanges(time.Year);
            isAmbiguousLocalDst = false;

            if (daylightTime == null || daylightTime.Delta.Ticks == 0)
            {
                return offset.Ticks;
            }

            // The start and end times represent the range of universal times that are in DST for that year.                
            // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
            // the unusual case of a negative daylight savings delta.
            DateTime startTime = daylightTime.Start - offset;
            DateTime endTime = daylightTime.End - offset - daylightTime.Delta;
            DateTime ambiguousStart;
            DateTime ambiguousEnd;

            if (daylightTime.Delta.Ticks > 0)
            {
                ambiguousStart = endTime - daylightTime.Delta;
                ambiguousEnd = endTime;
            }
            else
            {
                ambiguousStart = startTime;
                ambiguousEnd = startTime - daylightTime.Delta;
            }

            Boolean isDst = false;
            if (startTime > endTime)
            {
                // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
                // Note, the summer in the southern hemisphere begins late in the year.
                isDst = (time < endTime || time >= startTime);
            }
            else
            {
                // In northern hemisphere, the daylight saving time starts in the middle of the year.
                isDst = (time >= startTime && time < endTime);
            }

            if (isDst)
            {
                offset += daylightTime.Delta;

                // See if the resulting local time becomes ambiguous. This must be captured here or the
                // DateTime will not be able to round-trip back to UTC accurately.
                if (time >= ambiguousStart && time < ambiguousEnd)
                {
                    isAmbiguousLocalDst = true;
                }
            }
            return offset.Ticks;
        }

        public override DateTime ToLocalTime(DateTime time)
        {
            if (time.Kind == DateTimeKind.Local)
            {
                return time;
            }
            Boolean isAmbiguousLocalDst = false;
            Int64 offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
            long tick = time.Ticks + offset;
            if (tick > DateTime.MaxTicks)
            {
                return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
            }
            if (tick < DateTime.MinTicks)
            {
                return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
            }
            return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
        }

        public override DaylightTime GetDaylightChanges(int year)
        {
            if (year < 1 || year > 9999)
            {
                throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, 1, 9999));
            }

            return GetCachedDaylightChanges(year);
        }

        private static DaylightTime CreateDaylightChanges(int year)
        {
            DaylightTime currentDaylightChanges = null;

            if (TimeZoneInfo.Local.SupportsDaylightSavingTime)
            {
                DateTime start;
                DateTime end;
                TimeSpan delta;

                foreach (var rule in TimeZoneInfo.Local.GetAdjustmentRules())
                {
                    if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero)
                    {
                        start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
                        end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
                        delta = rule.DaylightDelta;

                        currentDaylightChanges = new DaylightTime(start, end, delta);
                        break;
                    }
                }
            }

            if (currentDaylightChanges == null)
            {
                currentDaylightChanges = new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero);
            }

            return currentDaylightChanges;
        }

        public override TimeSpan GetUtcOffset(DateTime time)
        {
            if (time.Kind == DateTimeKind.Utc)
            {
                return TimeSpan.Zero;
            }
            else
            {
                return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset);
            }
        }
    } // class CurrentSystemTimeZone
}