Merge pull request #3522 from henricm/fix-csharp-compiler-path-windows
[mono.git] / mcs / class / referencesource / mscorlib / system / globalization / datetimeparse.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 ////////////////////////////////////////////////////////////////////////////
7 //
8 //  Class:    DateTimeParse
9 //
10 //  Purpose:  This class is called by DateTime to parse a date/time string.
11 //
12 ////////////////////////////////////////////////////////////////////////////
13
14 namespace System {
15     using System;
16     using System.Text;
17     using System.Globalization;
18     using System.Threading;
19     using System.Collections;
20     using System.Runtime.CompilerServices;
21     using System.Runtime.InteropServices;
22     using System.Runtime.Versioning;
23     using System.Security;
24     using System.Diagnostics;
25     using System.Diagnostics.Contracts;
26
27     ////////////////////////////////////////////////////////////////////////
28
29     //This class contains only static members
30
31     internal static
32     class DateTimeParse {
33
34         internal const Int32 MaxDateTimeNumberDigits = 8;
35
36         internal delegate bool MatchNumberDelegate(ref __DTString str, int digitLen, out int result);
37
38         internal static MatchNumberDelegate m_hebrewNumberParser = new MatchNumberDelegate(DateTimeParse.MatchHebrewDigits);
39
40 #if !FEATURE_CORECLR && !MONO
41         [SecuritySafeCritical]
42         internal static bool GetAmPmParseFlag()
43         {
44             return DateTime.EnableAmPmParseAdjustment();
45         }
46
47         internal static bool enableAmPmParseAdjustment = GetAmPmParseFlag();
48 #endif
49
50         internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style) {
51             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
52             result.Init();
53             if (TryParseExact(s, format, dtfi, style, ref result)) {
54                 return result.parsedDate;
55             }
56             else {
57                 throw GetDateTimeParseException(ref result);
58             }
59         }
60
61         internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset) {
62             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
63             offset = TimeSpan.Zero;
64             result.Init();
65             result.flags |= ParseFlags.CaptureOffset;
66             if (TryParseExact(s, format, dtfi, style, ref result)) {
67                 offset = result.timeZoneOffset;
68                 return result.parsedDate;
69             }
70             else {
71                 throw GetDateTimeParseException(ref result);
72             }
73         }
74
75         internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result) {
76             result = DateTime.MinValue;
77             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
78             resultData.Init();
79             if (TryParseExact(s, format, dtfi, style, ref resultData)) {
80                 result = resultData.parsedDate;
81                 return true;
82             }
83             return false;
84         }
85
86         internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset) {
87             result = DateTime.MinValue;
88             offset = TimeSpan.Zero;
89             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
90             resultData.Init();
91             resultData.flags |= ParseFlags.CaptureOffset;
92             if (TryParseExact(s, format, dtfi, style, ref resultData)) {
93                 result = resultData.parsedDate;
94                 offset = resultData.timeZoneOffset;
95                 return true;
96             }
97             return false;
98         }
99
100         internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result) {
101             if (s == null) {
102                 result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, "s");
103                 return false;
104             }
105             if (format == null) {
106                 result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, "format");
107                 return false;
108             }
109             if (s.Length == 0) {
110                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
111                 return false;
112             }
113
114             if (format.Length == 0) {
115                 result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
116                 return false;
117             }
118
119             Contract.Assert(dtfi != null, "dtfi == null");
120
121             return DoStrictParse(s, format, style, dtfi, ref result);
122         }
123
124         internal static DateTime ParseExactMultiple(String s, String[] formats,
125                                                 DateTimeFormatInfo dtfi, DateTimeStyles style) {
126             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
127             result.Init();
128             if (TryParseExactMultiple(s, formats, dtfi, style, ref result)) {
129                 return result.parsedDate;
130             }
131             else {
132                 throw GetDateTimeParseException(ref result);
133             }
134         }
135
136
137         internal static DateTime ParseExactMultiple(String s, String[] formats,
138                                                 DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset) {
139             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
140             offset = TimeSpan.Zero;
141             result.Init();
142             result.flags |= ParseFlags.CaptureOffset;
143             if (TryParseExactMultiple(s, formats, dtfi, style, ref result)) {
144                 offset = result.timeZoneOffset;
145                 return result.parsedDate;
146             }
147             else {
148                 throw GetDateTimeParseException(ref result);
149             }
150         }
151
152         internal static bool TryParseExactMultiple(String s, String[] formats,
153                                                    DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset) {
154             result = DateTime.MinValue;
155             offset = TimeSpan.Zero;
156             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
157             resultData.Init();
158             resultData.flags |= ParseFlags.CaptureOffset;
159             if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData)) {
160                 result = resultData.parsedDate;
161                 offset = resultData.timeZoneOffset;
162                 return true;
163             }
164             return false;
165         }
166
167
168         internal static bool TryParseExactMultiple(String s, String[] formats,
169                                                    DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result) {
170             result = DateTime.MinValue;
171             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
172             resultData.Init();
173             if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData)) {
174                 result = resultData.parsedDate;
175                 return true;
176             }
177             return false;
178         }
179
180         internal static bool TryParseExactMultiple(String s, String[] formats,
181                                                 DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result) {
182             if (s == null) {
183                 result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, "s");
184                 return false;
185             }
186             if (formats == null) {
187                 result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, "formats");
188                 return false;
189             }
190
191             if (s.Length == 0) {
192                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
193                 return false;
194             }
195
196             if (formats.Length == 0) {
197                 result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
198                 return false;
199             }
200
201             Contract.Assert(dtfi != null, "dtfi == null");
202
203             //
204             // Do a loop through the provided formats and see if we can parse succesfully in
205             // one of the formats.
206             //
207             for (int i = 0; i < formats.Length; i++) {
208                 if (formats[i] == null || formats[i].Length == 0) {
209                     result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
210                     return false;
211                 }
212                 // Create a new result each time to ensure the runs are independent. Carry through
213                 // flags from the caller and return the result.
214                 DateTimeResult innerResult = new DateTimeResult();       // The buffer to store the parsing result.
215                 innerResult.Init();
216                 innerResult.flags = result.flags;                
217                 if (TryParseExact(s, formats[i], dtfi, style, ref innerResult)) {
218                     result.parsedDate = innerResult.parsedDate;
219                     result.timeZoneOffset = innerResult.timeZoneOffset;
220                     return (true);
221                 }
222             }
223             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
224             return (false);
225         }
226
227         ////////////////////////////////////////////////////////////////////////////
228         // Date Token Types
229         //
230         // Following is the set of tokens that can be generated from a date
231         // string. Notice that the legal set of trailing separators have been
232         // folded in with the date number, and month name tokens. This set
233         // of tokens is chosen to reduce the number of date parse states.
234         //
235         ////////////////////////////////////////////////////////////////////////////
236
237         internal enum DTT: int {
238
239             End               = 0,    // '\0'
240             NumEnd            = 1,    // Num[ ]*[\0]
241             NumAmpm           = 2,    // Num[ ]+AmPm
242             NumSpace          = 3,    // Num[ ]+^[Dsep|Tsep|'0\']
243             NumDatesep        = 4,    // Num[ ]*Dsep
244             NumTimesep        = 5,    // Num[ ]*Tsep
245             MonthEnd          = 6,    // Month[ ]*'\0'
246             MonthSpace        = 7,    // Month[ ]+^[Dsep|Tsep|'\0']
247             MonthDatesep      = 8,    // Month[ ]*Dsep
248             NumDatesuff       = 9,    // Month[ ]*DSuff
249             NumTimesuff       = 10,   // Month[ ]*TSuff
250             DayOfWeek         = 11,   // Day of week name
251             YearSpace         = 12,   // Year+^[Dsep|Tsep|'0\']
252             YearDateSep       = 13,  // Year+Dsep
253             YearEnd           = 14,  // Year+['\0']
254             TimeZone          = 15,  // timezone name
255             Era               = 16,  // era name
256             NumUTCTimeMark    = 17,      // Num + 'Z'
257             // When you add a new token which will be in the
258             // state table, add it after NumLocalTimeMark.
259             Unk               = 18,   // unknown
260             NumLocalTimeMark  = 19,    // Num + 'T'
261             Max               = 20,   // marker
262         }
263
264         internal enum TM {
265             NotSet  = -1,
266             AM      = 0,
267             PM      = 1,
268         }
269
270
271         ////////////////////////////////////////////////////////////////////////////
272         //
273         // DateTime parsing state enumeration (DS.*)
274         //
275         ////////////////////////////////////////////////////////////////////////////
276
277         internal enum DS {
278             BEGIN   = 0,
279             N       = 1,        // have one number
280             NN      = 2,        // have two numbers
281
282         // The following are known to be part of a date
283
284             D_Nd    = 3,        // date string: have number followed by date separator
285             D_NN    = 4,        // date string: have two numbers
286             D_NNd   = 5,        // date string: have two numbers followed by date separator
287
288             D_M     = 6,        // date string: have a month
289             D_MN    = 7,        // date string: have a month and a number
290             D_NM    = 8,        // date string: have a number and a month
291             D_MNd   = 9,        // date string: have a month and number followed by date separator
292             D_NDS   = 10,       // date string: have one number followed a date suffix.
293
294             D_Y     = 11,        // date string: have a year.
295             D_YN    = 12,        // date string: have a year and a number
296             D_YNd   = 13,        // date string: have a year and a number and a date separator
297             D_YM    = 14,        // date string: have a year and a month
298             D_YMd   = 15,        // date string: have a year and a month and a date separator
299             D_S     = 16,       // have numbers followed by a date suffix.
300             T_S     = 17,       // have numbers followed by a time suffix.
301
302         // The following are known to be part of a time
303
304             T_Nt    = 18,          // have num followed by time separator
305             T_NNt   = 19,       // have two numbers followed by time separator
306
307
308             ERROR   = 20,
309
310         // The following are terminal states. These all have an action
311         // associated with them; and transition back to BEGIN.
312
313             DX_NN   = 21,       // day from two numbers
314             DX_NNN  = 22,       // day from three numbers
315             DX_MN   = 23,       // day from month and one number
316             DX_NM   = 24,       // day from month and one number
317             DX_MNN  = 25,       // day from month and two numbers
318             DX_DS   = 26,       // a set of date suffixed numbers.
319             DX_DSN  = 27,       // day from date suffixes and one number.
320             DX_NDS  = 28,       // day from one number and date suffixes .
321             DX_NNDS = 29,       // day from one number and date suffixes .
322
323             DX_YNN  = 30,       // date string: have a year and two number
324             DX_YMN  = 31,       // date string: have a year, a month, and a number.
325             DX_YN   = 32,       // date string: have a year and one number
326             DX_YM   = 33,       // date string: have a year, a month.
327             TX_N    = 34,       // time from one number (must have ampm)
328             TX_NN   = 35,       // time from two numbers
329             TX_NNN  = 36,       // time from three numbers
330             TX_TS   = 37,       // a set of time suffixed numbers.
331             DX_NNY  = 38,
332         }
333
334         ////////////////////////////////////////////////////////////////////////////
335         //
336         // NOTE: The following state machine table is dependent on the order of the
337         // DS and DTT enumerations.
338         //
339         // For each non terminal state, the following table defines the next state
340         // for each given date token type.
341         //
342         ////////////////////////////////////////////////////////////////////////////
343
344 //          End       NumEnd      NumAmPm     NumSpace    NumDaySep   NumTimesep  MonthEnd    MonthSpace  MonthDSep   NumDateSuff NumTimeSuff     DayOfWeek     YearSpace   YearDateSep YearEnd     TimeZone   Era         UTCTimeMark   
345 private static DS[][] dateParsingStates = {
346 // DS.BEGIN                                                                             // DS.BEGIN
347 new DS[] { DS.BEGIN, DS.ERROR,   DS.TX_N,    DS.N,       DS.D_Nd,    DS.T_Nt,    DS.ERROR,   DS.D_M,     DS.D_M,     DS.D_S,     DS.T_S,         DS.BEGIN,     DS.D_Y,     DS.D_Y,     DS.ERROR,   DS.BEGIN,  DS.BEGIN,    DS.ERROR},
348
349 // DS.N                                                                                 // DS.N
350 new DS[] { DS.ERROR, DS.DX_NN,   DS.ERROR,   DS.NN,      DS.D_NNd,   DS.ERROR,   DS.DX_NM,   DS.D_NM,    DS.D_MNd,   DS.D_NDS,   DS.ERROR,       DS.N,         DS.D_YN,    DS.D_YNd,   DS.DX_YN,   DS.N,      DS.N,        DS.ERROR},
351
352 // DS.NN                                                                                // DS.NN
353 new DS[] { DS.DX_NN, DS.DX_NNN,  DS.TX_N,    DS.DX_NNN,  DS.ERROR,   DS.T_Nt,    DS.DX_MNN,  DS.DX_MNN,  DS.ERROR,   DS.ERROR,   DS.T_S,         DS.NN,        DS.DX_NNY,  DS.ERROR,   DS.DX_NNY,  DS.NN,     DS.NN,       DS.ERROR},
354
355 // DS.D_Nd                                                                              // DS.D_Nd
356 new DS[] { DS.ERROR, DS.DX_NN,   DS.ERROR,   DS.D_NN,    DS.D_NNd,   DS.ERROR,   DS.DX_NM,   DS.D_MN,    DS.D_MNd,   DS.ERROR,   DS.ERROR,       DS.D_Nd,      DS.D_YN,    DS.D_YNd,   DS.DX_YN,   DS.ERROR,  DS.D_Nd,     DS.ERROR},
357
358 // DS.D_NN                                                                              // DS.D_NN
359 new DS[] { DS.DX_NN, DS.DX_NNN,  DS.TX_N,    DS.DX_NNN,  DS.ERROR,   DS.T_Nt,    DS.DX_MNN,  DS.DX_MNN,  DS.ERROR,   DS.DX_DS,   DS.T_S,         DS.D_NN,     DS.DX_NNY,   DS.ERROR,   DS.DX_NNY,  DS.ERROR,  DS.D_NN,     DS.ERROR},
360
361 // DS.D_NNd                                                                             // DS.D_NNd
362 new DS[] { DS.ERROR, DS.DX_NNN,  DS.DX_NNN,  DS.DX_NNN,  DS.ERROR,   DS.ERROR,   DS.DX_MNN,  DS.DX_MNN,  DS.ERROR,   DS.DX_DS,   DS.ERROR,       DS.D_NNd,     DS.DX_NNY,  DS.ERROR,   DS.DX_NNY,  DS.ERROR,  DS.D_NNd,    DS.ERROR},
363
364 // DS.D_M                                                                               // DS.D_M
365 new DS[] { DS.ERROR, DS.DX_MN,   DS.ERROR,   DS.D_MN,    DS.D_MNd,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,       DS.D_M,       DS.D_YM,    DS.D_YMd,   DS.DX_YM,   DS.ERROR,  DS.D_M,      DS.ERROR},
366
367 // DS.D_MN                                                                              // DS.D_MN
368 new DS[] { DS.DX_MN, DS.DX_MNN,  DS.DX_MNN,  DS.DX_MNN,  DS.ERROR,   DS.T_Nt,    DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.DX_DS,   DS.T_S,         DS.D_MN,      DS.DX_YMN,  DS.ERROR,   DS.DX_YMN,  DS.ERROR,  DS.D_MN,     DS.ERROR},
369
370 // DS.D_NM                                                                              // DS.D_NM
371 new DS[] { DS.DX_NM, DS.DX_MNN,  DS.DX_MNN,  DS.DX_MNN,  DS.ERROR,   DS.T_Nt,    DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.DX_DS,   DS.T_S,         DS.D_NM,      DS.DX_YMN,  DS.ERROR,   DS.DX_YMN,  DS.ERROR,   DS.D_NM,    DS.ERROR},
372
373 // DS.D_MNd                                                                             // DS.D_MNd
374 new DS[] { DS.ERROR, DS.DX_MNN,  DS.ERROR,   DS.DX_MNN,  DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,       DS.D_MNd,     DS.DX_YMN,  DS.ERROR,   DS.DX_YMN,  DS.ERROR,   DS.D_MNd,   DS.ERROR},
375
376 // DS.D_NDS,                                                                            // DS.D_NDS,
377 new DS[] { DS.DX_NDS,DS.DX_NNDS, DS.DX_NNDS, DS.DX_NNDS, DS.ERROR,   DS.T_Nt,    DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_NDS,   DS.T_S,         DS.D_NDS,     DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_NDS,   DS.ERROR},
378
379 // DS.D_Y                                                                               // DS.D_Y
380 new DS[] { DS.ERROR, DS.DX_YN,   DS.ERROR,   DS.D_YN,    DS.D_YNd,   DS.ERROR,   DS.DX_YM,   DS.D_YM,    DS.D_YMd,   DS.D_YM,    DS.ERROR,       DS.D_Y,       DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_Y,     DS.ERROR},
381
382 // DS.D_YN                                                                              // DS.D_YN
383 new DS[] { DS.DX_YN, DS.DX_YNN,  DS.DX_YNN,  DS.DX_YNN,  DS.ERROR,   DS.ERROR,   DS.DX_YMN,  DS.DX_YMN,  DS.ERROR,   DS.ERROR,   DS.ERROR,       DS.D_YN,      DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_YN,    DS.ERROR},
384
385 // DS.D_YNd                                                                             // DS.D_YNd
386 new DS[] { DS.ERROR, DS.DX_YNN,  DS.DX_YNN,  DS.DX_YNN,  DS.ERROR,   DS.ERROR,   DS.DX_YMN,  DS.DX_YMN,  DS.ERROR,   DS.ERROR,   DS.ERROR,       DS.D_YN,      DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_YN,    DS.ERROR},
387
388 // DS.D_YM                                                                              // DS.D_YM
389 new DS[] { DS.DX_YM, DS.DX_YMN,  DS.DX_YMN,  DS.DX_YMN,  DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,       DS.D_YM,      DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_YM,    DS.ERROR},
390
391 // DS.D_YMd                                                                             // DS.D_YMd
392 new DS[] { DS.ERROR, DS.DX_YMN,  DS.DX_YMN,  DS.DX_YMN,  DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,       DS.D_YM,      DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_YM,    DS.ERROR},
393
394 // DS.D_S                                                                               // DS.D_S
395 new DS[] { DS.DX_DS, DS.DX_DSN,  DS.TX_N,    DS.T_Nt,    DS.ERROR,   DS.T_Nt,    DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_S,     DS.T_S,         DS.D_S,       DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_S,     DS.ERROR},
396
397 // DS.T_S                                                                               // DS.T_S
398 new DS[] { DS.TX_TS, DS.TX_TS,   DS.TX_TS,   DS.T_Nt,    DS.D_Nd,    DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.D_S,     DS.T_S,         DS.T_S,       DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.T_S,     DS.T_S,     DS.ERROR},
399
400 // DS.T_Nt                                                                              // DS.T_Nt
401 new DS[] { DS.ERROR, DS.TX_NN,   DS.TX_NN,   DS.TX_NN,   DS.ERROR,   DS.T_NNt,   DS.DX_NM,   DS.D_NM,    DS.ERROR,   DS.ERROR,   DS.T_S,         DS.ERROR,     DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.T_Nt,    DS.T_Nt,    DS.TX_NN},
402
403 // DS.T_NNt                                                                             // DS.T_NNt
404 new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.T_S,         DS.T_NNt,     DS.ERROR,   DS.ERROR,   DS.ERROR,   DS.T_NNt,   DS.T_NNt,   DS.TX_NNN},
405
406 };
407 //          End       NumEnd      NumAmPm     NumSpace    NumDaySep   NumTimesep  MonthEnd    MonthSpace  MonthDSep   NumDateSuff NumTimeSuff     DayOfWeek     YearSpace   YearDateSep YearEnd     TimeZone    Era        UTCMark
408
409         internal const String GMTName = "GMT";
410         internal const String ZuluName = "Z";
411
412         //
413         // Search from the index of str at str.Index to see if the target string exists in the str.
414         //
415         private static bool MatchWord(ref __DTString str, String target)
416         {
417             int length = target.Length;
418             if (length > (str.Value.Length - str.Index)) {
419                 return false;
420             }
421             
422             if (str.CompareInfo.Compare(str.Value, str.Index, length,
423                                         target, 0, length, CompareOptions.IgnoreCase)!=0) {
424                 return (false);
425             }
426
427             int nextCharIndex = str.Index + target.Length;
428
429             if (nextCharIndex < str.Value.Length) {
430                 char nextCh = str.Value[nextCharIndex];
431                 if (Char.IsLetter(nextCh)) {
432                     return (false);
433                 }
434             }
435             str.Index = nextCharIndex;
436             if (str.Index < str.len) {
437                 str.m_current = str.Value[str.Index];
438             }
439
440             return (true);
441         }
442
443
444         //
445         // Check the word at the current index to see if it matches GMT name or Zulu name.
446         //
447         private static bool GetTimeZoneName(ref __DTString str)
448         {
449             //
450             // <
451
452             if (MatchWord(ref str, GMTName)) {
453                 return (true);
454             }
455
456             if (MatchWord(ref str, ZuluName)) {
457                 return (true);
458             }
459
460             return (false);
461         }
462
463         internal static bool IsDigit(char ch) {
464             return (ch >= '0' && ch <= '9');
465         }
466
467
468         /*=================================ParseFraction==========================
469         **Action: Starting at the str.Index, which should be a decimal symbol.
470         ** if the current character is a digit, parse the remaining
471         **      numbers as fraction.  For example, if the sub-string starting at str.Index is "123", then
472         **      the method will return 0.123
473         **Returns:      The fraction number.
474         **Arguments:
475         **      str the parsing string
476         **Exceptions:
477         ============================================================================*/
478
479         private static bool ParseFraction(ref __DTString str, out double result) {
480             result = 0;
481             double decimalBase = 0.1;
482             int digits = 0;
483             char ch;
484             while (str.GetNext()
485                    && IsDigit(ch = str.m_current)) {
486                 result += (ch - '0') * decimalBase;
487                 decimalBase *= 0.1;
488                 digits++;
489             }
490             return (digits > 0);
491         }
492
493         /*=================================ParseTimeZone==========================
494         **Action: Parse the timezone offset in the following format:
495         **          "+8", "+08", "+0800", "+0800"
496         **        This method is used by DateTime.Parse().
497         **Returns:      The TimeZone offset.
498         **Arguments:
499         **      str the parsing string
500         **Exceptions:
501         **      FormatException if invalid timezone format is found.
502         ============================================================================*/
503
504         private static bool ParseTimeZone(ref __DTString str, ref TimeSpan result) {
505             // The hour/minute offset for timezone.
506             int hourOffset = 0;
507             int minuteOffset = 0;
508             DTSubString sub;
509
510             // Consume the +/- character that has already been read
511             sub = str.GetSubString();
512             if (sub.length != 1) {
513                 return false;
514             }
515             char offsetChar = sub[0];
516             if (offsetChar != '+' && offsetChar != '-') {
517                 return false;
518             }
519             str.ConsumeSubString(sub);
520
521             sub = str.GetSubString();
522             if (sub.type != DTSubStringType.Number) {
523                 return false;
524             }
525             int value = sub.value;
526             int length = sub.length;
527             if (length == 1 || length == 2) {
528                 // Parsing "+8" or "+08"
529                 hourOffset = value;
530                 str.ConsumeSubString(sub);
531                 // See if we have minutes
532                 sub = str.GetSubString();
533                 if (sub.length == 1 && sub[0] == ':') {
534                     // Parsing "+8:00" or "+08:00"
535                     str.ConsumeSubString(sub);
536                     sub = str.GetSubString();
537                     if (sub.type != DTSubStringType.Number || sub.length < 1 || sub.length > 2) {
538                         return false;
539                     }
540                     minuteOffset = sub.value;
541                     str.ConsumeSubString(sub);
542                 }
543             }
544             else if (length == 3 || length == 4) {
545                 // Parsing "+800" or "+0800"
546                 hourOffset = value / 100;
547                 minuteOffset = value % 100;
548                 str.ConsumeSubString(sub);
549             }
550             else {
551                 // Wrong number of digits
552                 return false;
553             }
554             Contract.Assert(hourOffset >= 0 && hourOffset <= 99, "hourOffset >= 0 && hourOffset <= 99");
555             Contract.Assert(minuteOffset >= 0 && minuteOffset <= 99, "minuteOffset >= 0 && minuteOffset <= 99");
556             if (minuteOffset < 0 || minuteOffset >= 60) {
557                 return false;
558             }
559
560             result = new TimeSpan(hourOffset, minuteOffset, 0);
561             if (offsetChar == '-') {
562                 result = result.Negate();
563             }
564             return true;
565         }
566
567         // This is the helper function to handle timezone in string in the format like +/-0800
568         private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result)
569         {
570             if ((str.Index < str.len - 1)) {
571                 char nextCh = str.Value[str.Index];
572                 // Skip whitespace, but don't update the index unless we find a time zone marker
573                 int whitespaceCount = 0;
574                 while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.len - 1) {
575                     whitespaceCount++;
576                     nextCh = str.Value[str.Index + whitespaceCount];
577                 }
578                 if (nextCh == '+' || nextCh == '-') {
579                     str.Index += whitespaceCount;
580                     if ((result.flags & ParseFlags.TimeZoneUsed) != 0) {
581                         // Should not have two timezone offsets.
582                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
583                         return false;
584                     }
585                     result.flags |= ParseFlags.TimeZoneUsed;
586                     if (!ParseTimeZone(ref str, ref result.timeZoneOffset)) {
587                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
588                         return false;
589                     }
590                 }
591             }                        
592             return true;
593         }
594         
595         //
596         // This is the lexer. Check the character at the current index, and put the found token in dtok and
597         // some raw date/time information in raw.
598         //
599         [System.Security.SecuritySafeCritical]  // auto-generated
600         private static Boolean Lex(DS dps, ref __DTString str, ref DateTimeToken dtok, ref DateTimeRawInfo raw, ref DateTimeResult result, ref DateTimeFormatInfo dtfi, DateTimeStyles styles)
601         {
602
603             TokenType tokenType;
604             int tokenValue;
605             int indexBeforeSeparator;
606             char charBeforeSeparator;
607
608             TokenType sep;
609             dtok.dtt = DTT.Unk;     // Assume the token is unkown.
610
611             str.GetRegularToken(out tokenType, out tokenValue, dtfi);
612
613 #if _LOGGING
614             // <
615
616
617
618
619
620
621
622
623             if (_tracingEnabled) {
624                 BCLDebug.Trace("DATETIME", "[DATETIME] Lex({0})\tpos:{1}({2}), {3}, DS.{4}", Hex(str.Value),
625                                str.Index, Hex(str.m_current), tokenType, dps);
626             }
627 #endif // _LOGGING
628
629             // Look at the regular token.
630             switch (tokenType) {
631                 case TokenType.NumberToken:
632                 case TokenType.YearNumberToken:
633                     if (raw.numCount == 3 || tokenValue == -1) {
634                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
635                         LexTraceExit("0010", dps);
636                         return false;
637                     }
638                     //
639                     // This is a digit.
640                     //
641                     // If the previous parsing state is DS.T_NNt (like 12:01), and we got another number,
642                     // so we will have a terminal state DS.TX_NNN (like 12:01:02).
643                     // If the previous parsing state is DS.T_Nt (like 12:), and we got another number,
644                     // so we will have a terminal state DS.TX_NN (like 12:01).
645                     //
646                     // Look ahead to see if the following character is a decimal point or timezone offset.
647                     // This enables us to parse time in the forms of:
648                     //  "11:22:33.1234" or "11:22:33-08".
649                     if (dps == DS.T_NNt) {
650                         if ((str.Index < str.len - 1)) {
651                             char nextCh = str.Value[str.Index];
652                             if (nextCh == '.') {
653                                 // While ParseFraction can fail, it just means that there were no digits after
654                                 // the dot. In this case ParseFraction just removes the dot. This is actually
655                                 // valid for cultures like Albanian, that join the time marker to the time with
656                                 // with a dot: e.g. "9:03.MD"
657                                 ParseFraction(ref str, out raw.fraction);
658                             }
659                         }
660                     }
661                     if (dps == DS.T_NNt || dps == DS.T_Nt) {
662                         if ((str.Index < str.len - 1)) {
663                             if (false == HandleTimeZone(ref str, ref result))
664                             {
665                                 LexTraceExit("0020 (value like \"12:01\" or \"12:\" followed by a non-TZ number", dps);
666                                 return false;
667                             }
668                         }                        
669                     }
670
671                     dtok.num = tokenValue;
672                     if (tokenType == TokenType.YearNumberToken)
673                     {
674                         if (raw.year == -1)
675                         {
676                             raw.year = tokenValue;
677                             //
678                             // If we have number which has 3 or more digits (like "001" or "0001"),
679                             // we assume this number is a year. Save the currnet raw.numCount in
680                             // raw.year.
681                             //
682                             switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) {
683                                 case TokenType.SEP_End:
684                                     dtok.dtt     = DTT.YearEnd;
685                                     break;
686                                 case TokenType.SEP_Am:
687                                 case TokenType.SEP_Pm:
688                                     if (raw.timeMark == TM.NotSet) {
689                                         raw.timeMark = (sep == TokenType.SEP_Am ? TM.AM : TM.PM);
690                                         dtok.dtt    = DTT.YearSpace;
691                                     } else {
692                                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
693                                         LexTraceExit("0030 (TM.AM/TM.PM Happened more than 1x)", dps);
694                                     }
695                                     break;
696                                 case TokenType.SEP_Space:
697                                     dtok.dtt    = DTT.YearSpace;
698                                     break;
699                                 case TokenType.SEP_Date:
700                                     dtok.dtt     = DTT.YearDateSep;
701                                     break;
702
703                                 case TokenType.SEP_Time:
704                                     if (!raw.hasSameDateAndTimeSeparators)
705                                     {
706                                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
707                                         LexTraceExit("0040 (Invalid separator after number)", dps);
708                                         return false;
709                                     }
710
711                                     // we have the date and time separators are same and getting a year number, then change the token to YearDateSep as 
712                                     // we are sure we are not parsing time.
713                                     dtok.dtt = DTT.YearDateSep;
714                                     break;
715
716                                 case TokenType.SEP_DateOrOffset:
717                                     // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
718                                     // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
719                                     if ((dateParsingStates[(int)dps][(int) DTT.YearDateSep] == DS.ERROR) 
720                                         && (dateParsingStates[(int)dps][(int) DTT.YearSpace] > DS.ERROR)) {
721                                         str.Index = indexBeforeSeparator;
722                                         str.m_current = charBeforeSeparator;
723                                         dtok.dtt = DTT.YearSpace;
724                                     }
725                                     else {
726                                         dtok.dtt = DTT.YearDateSep;
727                                     }
728                                     break;                                    
729                                 case TokenType.SEP_YearSuff:
730                                 case TokenType.SEP_MonthSuff:
731                                 case TokenType.SEP_DaySuff:
732                                     dtok.dtt    = DTT.NumDatesuff;
733                                     dtok.suffix = sep;
734                                     break;
735                                 case TokenType.SEP_HourSuff:
736                                 case TokenType.SEP_MinuteSuff:
737                                 case TokenType.SEP_SecondSuff:
738                                     dtok.dtt    = DTT.NumTimesuff;
739                                     dtok.suffix = sep;
740                                     break;
741                                 default:
742                                     // Invalid separator after number number.
743                                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
744                                     LexTraceExit("0040 (Invalid separator after number)", dps);
745                                     return false;
746                             }
747                             //
748                             // Found the token already. Return now.
749                             //
750                             LexTraceExit("0050 (success)", dps);
751                             return true;
752                         }
753                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
754                         LexTraceExit("0060", dps);
755                         return false;
756                     }
757                     switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
758                     {
759                         //
760                         // Note here we check if the numCount is less than three.
761                         // When we have more than three numbers, it will be caught as error in the state machine.
762                         //
763                         case TokenType.SEP_End:
764                             dtok.dtt = DTT.NumEnd;
765                             raw.AddNumber(dtok.num);
766                             break;
767                         case TokenType.SEP_Am:
768                         case TokenType.SEP_Pm:
769                             if (raw.timeMark == TM.NotSet) {
770                                 raw.timeMark = (sep == TokenType.SEP_Am ? TM.AM : TM.PM);
771                                 dtok.dtt = DTT.NumAmpm;
772                                 // Fix AM/PM parsing case, e.g. "1/10 5 AM"
773                                 if (dps == DS.D_NN 
774 #if !FEATURE_CORECLR && !MONO
775                                     && enableAmPmParseAdjustment
776 #endif
777                                 )
778                                 {
779                                     if (!ProcessTerminaltState(DS.DX_NN, ref result, ref styles, ref raw, dtfi))
780                                     {
781                                         return false;
782                                     }
783                                 }
784
785                                 raw.AddNumber(dtok.num);
786                             } else {
787                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
788                                 break;
789                             }
790                             if (dps == DS.T_NNt || dps == DS.T_Nt) {
791                                 if (false == HandleTimeZone(ref str, ref result))
792                                 {
793                                     LexTraceExit("0070 (HandleTimeZone returned false)", dps);
794                                     return false;
795                                 }
796                             }
797                             break;
798                         case TokenType.SEP_Space:
799                             dtok.dtt = DTT.NumSpace;
800                             raw.AddNumber(dtok.num);
801                             break;
802                         case TokenType.SEP_Date:
803                             dtok.dtt = DTT.NumDatesep;
804                             raw.AddNumber(dtok.num);
805                             break;
806                         case TokenType.SEP_DateOrOffset:
807                             // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
808                             // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
809                             if ((dateParsingStates[(int)dps][(int) DTT.NumDatesep] == DS.ERROR) 
810                                 && (dateParsingStates[(int)dps][(int) DTT.NumSpace] > DS.ERROR)) {
811                                 str.Index = indexBeforeSeparator;
812                                 str.m_current = charBeforeSeparator;
813                                 dtok.dtt = DTT.NumSpace;
814                             }
815                             else {                                
816                                 dtok.dtt = DTT.NumDatesep;
817                             }
818                             raw.AddNumber(dtok.num);
819                             break;                            
820                         case TokenType.SEP_Time:
821                             if (raw.hasSameDateAndTimeSeparators && 
822                                 (dps == DS.D_Y || dps == DS.D_YN || dps == DS.D_YNd || dps == DS.D_YM || dps == DS.D_YMd))
823                             {
824                                 // we are parsing a date and we have the time separator same as date separator, so we mark the token as date separator
825                                 dtok.dtt = DTT.NumDatesep;
826                                 raw.AddNumber(dtok.num);
827                                 break;
828                             }
829                             dtok.dtt = DTT.NumTimesep;
830                             raw.AddNumber(dtok.num);
831                             break;
832                         case TokenType.SEP_YearSuff:
833                             try {
834                                 dtok.num = dtfi.Calendar.ToFourDigitYear(tokenValue);
835                             }
836                             catch (ArgumentOutOfRangeException e) {
837                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
838                                 LexTraceExit("0075 (Calendar.ToFourDigitYear failed)", dps);
839                                 return false;
840                             }   
841                             dtok.dtt    = DTT.NumDatesuff;
842                             dtok.suffix = sep;
843                             break;
844                         case TokenType.SEP_MonthSuff:
845                         case TokenType.SEP_DaySuff:
846                             dtok.dtt    = DTT.NumDatesuff;
847                             dtok.suffix = sep;
848                             break;
849                         case TokenType.SEP_HourSuff:
850                         case TokenType.SEP_MinuteSuff:
851                         case TokenType.SEP_SecondSuff:
852                             dtok.dtt    = DTT.NumTimesuff;
853                             dtok.suffix = sep;
854                             break;
855                         case TokenType.SEP_LocalTimeMark:
856                             dtok.dtt = DTT.NumLocalTimeMark;
857                             raw.AddNumber(dtok.num);
858                             break;
859                         default:
860                             // Invalid separator after number number.
861                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
862                             LexTraceExit("0080", dps);
863                             return false;
864                     }
865                     break;
866                 case TokenType.HebrewNumber:
867                     if (tokenValue >= 100) {
868                         // This is a year number
869                         if (raw.year == -1) {
870                             raw.year = tokenValue;
871                             //
872                             // If we have number which has 3 or more digits (like "001" or "0001"),
873                             // we assume this number is a year. Save the currnet raw.numCount in
874                             // raw.year.
875                             //
876                             switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) {
877                                 case TokenType.SEP_End:
878                                     dtok.dtt = DTT.YearEnd;
879                                     break;
880                                 case TokenType.SEP_Space:
881                                     dtok.dtt = DTT.YearSpace;
882                                     break;
883                                 case TokenType.SEP_DateOrOffset:
884                                     // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
885                                     // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
886                                     if (dateParsingStates[(int)dps][(int) DTT.YearSpace] > DS.ERROR) {
887                                         str.Index = indexBeforeSeparator;
888                                         str.m_current = charBeforeSeparator;
889                                         dtok.dtt = DTT.YearSpace;
890                                         break;
891                                     }
892                                     goto default;
893                                 default:
894                                     // Invalid separator after number number.
895                                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
896                                     LexTraceExit("0090", dps);
897                                     return false;
898                             }
899                         } else {
900                             // Invalid separator after number number.
901                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
902                             LexTraceExit("0100", dps);
903                             return false;
904                         }
905                     } else {
906                         // This is a day number
907                         dtok.num = tokenValue;
908                         raw.AddNumber(dtok.num);
909
910                         switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) {
911                             //
912                             // Note here we check if the numCount is less than three.
913                             // When we have more than three numbers, it will be caught as error in the state machine.
914                             //
915                             case TokenType.SEP_End:
916                                 dtok.dtt = DTT.NumEnd;
917                                 break;
918                             case TokenType.SEP_Space:
919                             case TokenType.SEP_Date:
920                                 dtok.dtt = DTT.NumDatesep;
921                                 break;
922                             case TokenType.SEP_DateOrOffset:
923                                 // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
924                                 // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
925                                 if ((dateParsingStates[(int)dps][(int) DTT.NumDatesep] == DS.ERROR) 
926                                     && (dateParsingStates[(int)dps][(int) DTT.NumSpace] > DS.ERROR)) {
927                                     str.Index = indexBeforeSeparator;
928                                     str.m_current = charBeforeSeparator;
929                                     dtok.dtt = DTT.NumSpace;
930                                 }
931                                 else {                                
932                                     dtok.dtt = DTT.NumDatesep;
933                                 }
934                                 break;
935                             default:
936                                 // Invalid separator after number number.
937                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
938                                 LexTraceExit("0110", dps);
939                                 return false;
940                         }
941                     }
942                     break;
943                 case TokenType.DayOfWeekToken:
944                     if (raw.dayOfWeek == -1)
945                     {
946                         //
947                         // This is a day of week name.
948                         //
949                         raw.dayOfWeek = tokenValue;
950                         dtok.dtt = DTT.DayOfWeek;
951                     } else {
952                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
953                         LexTraceExit("0120 (DayOfWeek seen more than 1x)", dps);
954                         return false;
955                     }
956                     break;
957                 case TokenType.MonthToken:
958                     if (raw.month == -1)
959                     {
960                         //
961                         // This is a month name
962                         //
963                         switch(sep=str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
964                         {
965                             case TokenType.SEP_End:
966                                 dtok.dtt = DTT.MonthEnd;
967                                 break;
968                             case TokenType.SEP_Space:
969                                 dtok.dtt = DTT.MonthSpace;
970                                 break;
971                             case TokenType.SEP_Date:
972                                 dtok.dtt = DTT.MonthDatesep;
973                                 break;
974                             case TokenType.SEP_Time:
975                                 if (!raw.hasSameDateAndTimeSeparators)
976                                 {
977                                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
978                                     LexTraceExit("0130 (Invalid separator after month name)", dps);
979                                     return false;
980                                 }
981
982                                 // we have the date and time separators are same and getting a Month name, then change the token to MonthDatesep as 
983                                 // we are sure we are not parsing time.
984                                 dtok.dtt = DTT.MonthDatesep;
985                                 break;
986                             case TokenType.SEP_DateOrOffset:
987                                 // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
988                                 // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
989                                 if ((dateParsingStates[(int)dps][(int) DTT.MonthDatesep] == DS.ERROR) 
990                                     && (dateParsingStates[(int)dps][(int) DTT.MonthSpace] > DS.ERROR)) {
991                                     str.Index = indexBeforeSeparator;
992                                     str.m_current = charBeforeSeparator;
993                                     dtok.dtt = DTT.MonthSpace;
994                                 }
995                                 else {                                
996                                     dtok.dtt = DTT.MonthDatesep;
997                                 }
998                                 break;
999                             default:
1000                                 //Invalid separator after month name
1001                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1002                                 LexTraceExit("0130 (Invalid separator after month name)", dps);
1003                                 return false;
1004                         }
1005                         raw.month = tokenValue;
1006                     }  else {
1007                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1008                         LexTraceExit("0140 (MonthToken seen more than 1x)", dps);
1009                         return false;
1010                     }
1011                     break;
1012                 case TokenType.EraToken:
1013                     if (result.era != -1) {
1014                         result.era = tokenValue;
1015                         dtok.dtt = DTT.Era;
1016                     } else {
1017                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1018                         LexTraceExit("0150 (EraToken seen when result.era already set)", dps);
1019                         return false;
1020                     }
1021                     break;
1022                 case TokenType.JapaneseEraToken:
1023                     // Special case for Japanese.  We allow Japanese era name to be used even if the calendar is not Japanese Calendar.
1024                     result.calendar = JapaneseCalendar.GetDefaultInstance();
1025                     dtfi = DateTimeFormatInfo.GetJapaneseCalendarDTFI();
1026                     if (result.era != -1) {
1027                         result.era = tokenValue;
1028                         dtok.dtt = DTT.Era;
1029                     } else {
1030                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1031                         LexTraceExit("0160 (JapaneseEraToken seen when result.era already set)", dps);
1032                         return false;
1033                     }
1034                     break;
1035                 case TokenType.TEraToken:
1036 /*  */
1037                     result.calendar = TaiwanCalendar.GetDefaultInstance();
1038                     dtfi = DateTimeFormatInfo.GetTaiwanCalendarDTFI();
1039                     if (result.era != -1) {
1040                         result.era = tokenValue;
1041                         dtok.dtt = DTT.Era;
1042                     } else {
1043                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1044                         LexTraceExit("0170 (TEraToken seen when result.era already set)", dps);
1045                         return false;
1046                     }
1047                     break;
1048                 case TokenType.TimeZoneToken:
1049                     //
1050                     // This is a timezone designator
1051                     //
1052                     // NOTENOTE : for now, we only support "GMT" and "Z" (for Zulu time).
1053                     //
1054                     if ((result.flags & ParseFlags.TimeZoneUsed) != 0) {
1055                         // Should not have two timezone offsets.
1056                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1057                         LexTraceExit("0180 (seen GMT or Z more than 1x)", dps);
1058                         return false;
1059                     }
1060                     dtok.dtt = DTT.TimeZone;
1061                     result.flags |= ParseFlags.TimeZoneUsed;
1062                     result.timeZoneOffset = new TimeSpan(0);
1063                     result.flags |= ParseFlags.TimeZoneUtc;
1064                     break;
1065                 case TokenType.EndOfString:
1066                     dtok.dtt = DTT.End;
1067                     break;
1068                 case TokenType.DateWordToken:
1069                 case TokenType.IgnorableSymbol:
1070                     // Date words and ignorable symbols can just be skipped over
1071                     break;
1072                 case TokenType.Am:
1073                 case TokenType.Pm:
1074                     if (raw.timeMark == TM.NotSet) {
1075                         raw.timeMark = (TM)tokenValue;
1076                     } else {
1077                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1078                         LexTraceExit("0190 (AM/PM timeMark already set)", dps);
1079                         return false;
1080                     }
1081                     break;
1082                 case TokenType.UnknownToken:
1083                     if (Char.IsLetter(str.m_current)) {
1084                         result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_UnknowDateTimeWord",  str.Index);
1085                         LexTraceExit("0200", dps);
1086                         return (false);
1087                     }
1088
1089 #if !FEATURE_CORECLR  && !MONO
1090                     // If DateTimeParseIgnorePunctuation is defined, we want to have the V1.1 behavior of just
1091                     // ignoring any unrecognized punctuation and moving on to the next character
1092                     if (Environment.GetCompatibilityFlag(CompatibilityFlag.DateTimeParseIgnorePunctuation) && ((result.flags & ParseFlags.CaptureOffset) == 0)) {
1093                         str.GetNext();
1094                         LexTraceExit("0210 (success)", dps);
1095                         return true;
1096                     }
1097 #endif // FEATURE_CORECLR
1098                     
1099                     if ((str.m_current == '-' || str.m_current == '+') && ((result.flags & ParseFlags.TimeZoneUsed) == 0)) {
1100                         Int32 originalIndex = str.Index;
1101                         if (ParseTimeZone(ref str, ref result.timeZoneOffset)) {
1102                             result.flags |= ParseFlags.TimeZoneUsed;
1103                             LexTraceExit("0220 (success)", dps);
1104                             return true;
1105                         }
1106                         else {
1107                             // Time zone parse attempt failed. Fall through to punctuation handling.
1108                             str.Index = originalIndex;
1109                         }                                                
1110                     }
1111                     
1112                     // Visual Basic implements string to date conversions on top of DateTime.Parse:
1113                     //   CDate("#10/10/95#")
1114                     //
1115                     if (VerifyValidPunctuation(ref str)) {
1116                         LexTraceExit("0230 (success)", dps);
1117                         return true;                                
1118                     }
1119
1120                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1121                     LexTraceExit("0240", dps);
1122                     return false;
1123             }
1124
1125             LexTraceExit("0250 (success)", dps);
1126             return true;
1127         }
1128         
1129         private static Boolean VerifyValidPunctuation(ref __DTString str) {
1130             // Compatability Behavior. Allow trailing nulls and surrounding hashes
1131             Char ch = str.Value[str.Index];
1132             if (ch == '#') {
1133                 bool foundStart = false;
1134                 bool foundEnd = false;
1135                 for (int i = 0; i < str.len; i++) {
1136                     ch = str.Value[i];
1137                     if (ch == '#') {
1138                         if (foundStart) {
1139                             if (foundEnd) {
1140                                 // Having more than two hashes is invalid
1141                                 return false;
1142                             }
1143                             else {
1144                                 foundEnd = true;
1145                             }
1146                         }
1147                         else {
1148                             foundStart = true;
1149                         }
1150                     }
1151                     else if (ch == '\0') {
1152                         // Allow nulls only at the end
1153                         if (!foundEnd) {
1154                             return false;
1155                         }
1156                     }
1157                     else if ((!Char.IsWhiteSpace(ch))) {
1158                         // Anthyhing other than whitespace outside hashes is invalid
1159                         if (!foundStart || foundEnd) {
1160                             return false;
1161                         }
1162                     }
1163                 }
1164                 if (!foundEnd) {
1165                     // The has was un-paired
1166                     return false;
1167                 }
1168                 // Valid Hash usage: eat the hash and continue. 
1169                 str.GetNext();  
1170                 return true;                                                                    
1171             }
1172             else if (ch == '\0') {
1173                 for (int i = str.Index; i < str.len; i++) {
1174                     if (str.Value[i] != '\0') {
1175                         // Nulls are only valid if they are the only trailing character
1176                         return false;
1177                     }
1178                 }
1179                 // Move to the end of the string
1180                 str.Index = str.len;
1181                 return true;
1182             }
1183             return false;            
1184         }
1185
1186         private const int ORDER_YMD = 0;     // The order of date is Year/Month/Day.
1187         private const int ORDER_MDY = 1;     // The order of date is Month/Day/Year.
1188         private const int ORDER_DMY = 2;     // The order of date is Day/Month/Year.
1189         private const int ORDER_YDM = 3;     // The order of date is Year/Day/Month
1190         private const int ORDER_YM  = 4;     // Year/Month order.
1191         private const int ORDER_MY  = 5;     // Month/Year order.
1192         private const int ORDER_MD  = 6;     // Month/Day order.
1193         private const int ORDER_DM  = 7;     // Day/Month order.
1194
1195         //
1196         // Decide the year/month/day order from the datePattern.
1197         //
1198         // Return 0 for YMD, 1 for MDY, 2 for DMY, otherwise -1.
1199         //
1200         private static Boolean GetYearMonthDayOrder(String datePattern, DateTimeFormatInfo dtfi, out int order)
1201         {
1202             int yearOrder   = -1;
1203             int monthOrder  = -1;
1204             int dayOrder    = -1;
1205             int orderCount  =  0;
1206
1207             bool inQuote = false;
1208
1209             for (int i = 0; i < datePattern.Length && orderCount < 3; i++)
1210             {
1211                 char ch = datePattern[i];
1212                 if (ch == '\\' || ch == '%')
1213                 {
1214                     i++;
1215                     continue;  // Skip next character that is escaped by this backslash
1216                 }
1217
1218                 if (ch == '\'' || ch == '"')
1219                 {
1220                     inQuote = !inQuote;
1221                 }
1222
1223                 if (!inQuote)
1224                 {
1225                     if (ch == 'y')
1226                     {
1227                         yearOrder = orderCount++;
1228
1229                         //
1230                         // Skip all year pattern charaters.
1231                         //
1232                         for(; i+1 < datePattern.Length && datePattern[i+1] == 'y'; i++)
1233                         {
1234                             // Do nothing here.
1235                         }
1236                     }
1237                     else if (ch == 'M')
1238                     {
1239                         monthOrder = orderCount++;
1240                         //
1241                         // Skip all month pattern characters.
1242                         //
1243                         for(; i+1 < datePattern.Length && datePattern[i+1] == 'M'; i++)
1244                         {
1245                             // Do nothing here.
1246                         }
1247                     }
1248                     else if (ch == 'd')
1249                     {
1250
1251                         int patternCount = 1;
1252                         //
1253                         // Skip all day pattern characters.
1254                         //
1255                         for(; i+1 < datePattern.Length && datePattern[i+1] == 'd'; i++)
1256                         {
1257                             patternCount++;
1258                         }
1259                         //
1260                         // Make sure this is not "ddd" or "dddd", which means day of week.
1261                         //
1262                         if (patternCount <= 2)
1263                         {
1264                             dayOrder = orderCount++;
1265                         }
1266                     }
1267                 }
1268             }
1269
1270             if (yearOrder == 0 && monthOrder == 1 && dayOrder == 2)
1271             {
1272                 order = ORDER_YMD;
1273                 return true;
1274             }
1275             if (monthOrder == 0 && dayOrder == 1 && yearOrder == 2)
1276             {
1277                 order = ORDER_MDY;
1278                 return true;
1279             }
1280             if (dayOrder == 0 && monthOrder == 1 && yearOrder == 2)
1281             {
1282                 order = ORDER_DMY;
1283                 return true;
1284             }
1285             if (yearOrder == 0 && dayOrder == 1 && monthOrder == 2)
1286             {
1287                 order = ORDER_YDM;
1288                 return true;
1289             }
1290             order = -1;
1291             return false;
1292         }
1293
1294         //
1295         // Decide the year/month order from the pattern.
1296         //
1297         // Return 0 for YM, 1 for MY, otherwise -1.
1298         //
1299         private static Boolean GetYearMonthOrder(String pattern, DateTimeFormatInfo dtfi, out int order)
1300         {
1301             int yearOrder   = -1;
1302             int monthOrder  = -1;
1303             int orderCount  =  0;
1304
1305             bool inQuote = false;
1306             for (int i = 0; i < pattern.Length && orderCount < 2; i++)
1307             {
1308                 char ch = pattern[i];
1309                 if (ch == '\\' || ch == '%')
1310                 {
1311                     i++;
1312                     continue;  // Skip next character that is escaped by this backslash
1313                 }
1314
1315                 if (ch == '\'' || ch == '"')
1316                 {
1317                     inQuote = !inQuote;
1318                 }
1319
1320                 if (!inQuote)
1321                 {
1322                     if (ch == 'y')
1323                     {
1324                         yearOrder = orderCount++;
1325
1326                         //
1327                         // Skip all year pattern charaters.
1328                         //
1329                         for(; i+1 < pattern.Length && pattern[i+1] == 'y'; i++)
1330                         {
1331                         }
1332                     }
1333                     else if (ch == 'M')
1334                     {
1335                         monthOrder = orderCount++;
1336                         //
1337                         // Skip all month pattern characters.
1338                         //
1339                         for(; i+1 < pattern.Length && pattern[i+1] == 'M'; i++)
1340                         {
1341                         }
1342                     }
1343                 }
1344             }
1345
1346             if (yearOrder == 0 && monthOrder == 1)
1347             {
1348                 order = ORDER_YM;
1349                 return true;
1350             }
1351             if (monthOrder == 0 && yearOrder == 1)
1352             {
1353                 order = ORDER_MY;
1354                 return true;
1355             }
1356             order = -1;
1357             return false;
1358         }
1359
1360         //
1361         // Decide the month/day order from the pattern.
1362         //
1363         // Return 0 for MD, 1 for DM, otherwise -1.
1364         //
1365         private static Boolean GetMonthDayOrder(String pattern, DateTimeFormatInfo dtfi, out int order)
1366         {
1367             int monthOrder  = -1;
1368             int dayOrder    = -1;
1369             int orderCount  =  0;
1370
1371             bool inQuote = false;
1372             for (int i = 0; i < pattern.Length && orderCount < 2; i++)
1373             {
1374                 char ch = pattern[i];
1375                 if (ch == '\\' || ch == '%')
1376                 {
1377                     i++;
1378                     continue;  // Skip next character that is escaped by this backslash
1379                 }
1380
1381                 if (ch == '\'' || ch == '"')
1382                 {
1383                     inQuote = !inQuote;
1384                 }
1385
1386                 if (!inQuote)
1387                 {
1388                     if (ch == 'd')
1389                     {
1390                         int patternCount = 1;
1391                         //
1392                         // Skip all day pattern charaters.
1393                         //
1394                         for(; i+1 < pattern.Length && pattern[i+1] == 'd'; i++)
1395                         {
1396                             patternCount++;
1397                         }
1398
1399                         //
1400                         // Make sure this is not "ddd" or "dddd", which means day of week.
1401                         //
1402                         if (patternCount <= 2)
1403                         {
1404                             dayOrder = orderCount++;
1405                         }
1406
1407                     }
1408                     else if (ch == 'M')
1409                     {
1410                         monthOrder = orderCount++;
1411                         //
1412                         // Skip all month pattern characters.
1413                         //
1414                         for(; i+1 < pattern.Length && pattern[i+1] == 'M'; i++)
1415                         {
1416                         }
1417                     }
1418                 }
1419             }
1420
1421             if (monthOrder == 0 && dayOrder == 1)
1422             {
1423                 order = ORDER_MD;
1424                 return true;
1425             }
1426             if (dayOrder == 0 && monthOrder == 1)
1427             {
1428                 order = ORDER_DM;
1429                 return true;
1430             }
1431             order = -1;
1432             return false;
1433         }
1434
1435         //
1436         // Adjust the two-digit year if necessary.
1437         //
1438         private static bool TryAdjustYear(ref DateTimeResult result, int year, out int adjustedYear)
1439         {
1440             if (year < 100)
1441             {
1442                     try {
1443                         // the Calendar classes need some real work.  Many of the calendars that throw
1444                         // don't implement a fast/non-allocating (and non-throwing) IsValid{Year|Day|Month} method.
1445                         // we are making a targeted try/catch fix in the in-place release but will revisit this code
1446                         // in the next side-by-side release.
1447                         year = result.calendar.ToFourDigitYear(year);
1448                     }
1449                     catch (ArgumentOutOfRangeException) {
1450                         adjustedYear = -1;
1451                         return false;
1452                     }
1453             }
1454             adjustedYear = year;
1455             return true;
1456         }
1457
1458         private static bool SetDateYMD(ref DateTimeResult result, int year, int month, int day)
1459         {
1460             // Note, longer term these checks should be done at the end of the parse. This current
1461             // way of checking creates order dependence with parsing the era name.
1462             if (result.calendar.IsValidDay(year, month, day, result.era)) 
1463             {
1464                 result.SetDate(year, month, day);                           // YMD
1465                 return (true);
1466             }
1467             return (false);
1468         }
1469
1470         private static bool SetDateMDY(ref DateTimeResult result, int month, int day, int year)
1471         {
1472             return (SetDateYMD(ref result, year, month, day));
1473         }
1474
1475         private static bool SetDateDMY(ref DateTimeResult result, int day, int month, int year)
1476         {
1477             return (SetDateYMD(ref result, year, month, day));
1478         }
1479
1480         private static bool SetDateYDM(ref DateTimeResult result, int year, int day, int month)
1481         {
1482             return (SetDateYMD(ref result, year, month, day));
1483         }
1484         
1485         private static void GetDefaultYear(ref DateTimeResult result, ref DateTimeStyles styles) {
1486             result.Year = result.calendar.GetYear(GetDateTimeNow(ref result, ref styles));
1487             result.flags |= ParseFlags.YearDefault;
1488         }
1489
1490         // Processing teriminal case: DS.DX_NN
1491         private static Boolean GetDayOfNN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) {
1492
1493             if ((result.flags & ParseFlags.HaveDate) != 0) {
1494                 // Multiple dates in the input string
1495                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1496                 return false;
1497             }
1498
1499             int n1 = raw.GetNumber(0);
1500             int n2 = raw.GetNumber(1);
1501
1502             GetDefaultYear(ref result, ref styles);
1503
1504             int order;
1505             if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out order)) {
1506                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
1507                 return false;
1508             }
1509
1510             if (order == ORDER_MD)
1511             {
1512                 if (SetDateYMD(ref result, result.Year, n1, n2))                           // MD
1513                 {
1514                     result.flags |= ParseFlags.HaveDate;
1515                     return true;
1516                 }
1517             } else {
1518                 // ORDER_DM
1519                 if (SetDateYMD(ref result, result.Year, n2, n1))                           // DM
1520                 {
1521                     result.flags |= ParseFlags.HaveDate;
1522                     return true;
1523                 }
1524             }
1525             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1526             return false;
1527         }
1528
1529         // Processing teriminal case: DS.DX_NNN
1530         private static Boolean GetDayOfNNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
1531         {
1532             if ((result.flags & ParseFlags.HaveDate) != 0) {
1533                 // Multiple dates in the input string
1534                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1535                 return false;
1536             }
1537
1538             int n1 = raw.GetNumber(0);
1539             int n2 = raw.GetNumber(1);;
1540             int n3 = raw.GetNumber(2);
1541
1542             int order;
1543             if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) {
1544                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
1545                 return false;
1546             }
1547             int year;
1548
1549             if (order == ORDER_YMD) {
1550                 if (TryAdjustYear(ref result, n1, out year) && SetDateYMD(ref result, year, n2, n3))         // YMD
1551                 {
1552                     result.flags |= ParseFlags.HaveDate;
1553                     return true;
1554                 }
1555             } else if (order == ORDER_MDY) {
1556                 if (TryAdjustYear(ref result, n3, out year) && SetDateMDY(ref result, n1, n2, year))         // MDY
1557                 {
1558                     result.flags |= ParseFlags.HaveDate;
1559                     return true;
1560                 }
1561             } else if (order == ORDER_DMY) {
1562                 if (TryAdjustYear(ref result, n3, out year) && SetDateDMY(ref result, n1, n2, year))         // DMY
1563                 {
1564                     result.flags |= ParseFlags.HaveDate;
1565                     return true;
1566                 }
1567             } else if (order == ORDER_YDM) {
1568                 if (TryAdjustYear(ref result, n1, out year) && SetDateYDM(ref result, year, n2, n3))         // YDM
1569                 {
1570                     result.flags |= ParseFlags.HaveDate;
1571                     return true;
1572                 }
1573             }
1574             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1575             return false;
1576         }
1577
1578         private static Boolean GetDayOfMN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) {
1579
1580             if ((result.flags & ParseFlags.HaveDate) != 0) {
1581                 // Multiple dates in the input string
1582                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1583                 return false;
1584             }
1585
1586             // The interpretation is based on the MonthDayPattern and YearMonthPattern
1587             //
1588             //    MonthDayPattern   YearMonthPattern  Interpretation
1589             //    ---------------   ----------------  ---------------
1590             //    MMMM dd           MMMM yyyy         Day
1591             //    MMMM dd           yyyy MMMM         Day
1592             //    dd MMMM           MMMM yyyy         Year
1593             //    dd MMMM           yyyy MMMM         Day
1594             //
1595             // In the first and last cases, it could be either or neither, but a day is a better default interpretation
1596             // than a 2 digit year.
1597
1598             int monthDayOrder;
1599             if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) {
1600                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
1601                 return false;
1602             }
1603             if (monthDayOrder == ORDER_DM) {
1604                 int yearMonthOrder;
1605                 if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder)) {
1606                     result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern);
1607                     return false;
1608                 }
1609                 if (yearMonthOrder == ORDER_MY) {
1610                     int year;
1611                     if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1)) {
1612                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1613                         return false;
1614                     }
1615                     return true;
1616                 }
1617             }
1618
1619             GetDefaultYear(ref result, ref styles);            
1620             if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0))) {
1621                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1622                 return false;
1623             }
1624             return true;
1625
1626         }
1627
1628         ////////////////////////////////////////////////////////////////////////
1629         //  Actions:
1630         //  Deal with the terminal state for Hebrew Month/Day pattern
1631         //
1632         ////////////////////////////////////////////////////////////////////////
1633
1634         private static Boolean GetHebrewDayOfNM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
1635         {
1636             int monthDayOrder;
1637             if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) {
1638                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
1639                 return false;
1640             }
1641             result.Month = raw.month;
1642             if (monthDayOrder == ORDER_DM || monthDayOrder == ORDER_MD)
1643             {
1644                 if (result.calendar.IsValidDay(result.Year, result.Month, raw.GetNumber(0), result.era))
1645                 {
1646                     result.Day  = raw.GetNumber(0);
1647                     return true;
1648                 }
1649             }
1650             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1651             return false;
1652         }
1653
1654         private static Boolean GetDayOfNM(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
1655         {
1656             if ((result.flags & ParseFlags.HaveDate) != 0) {
1657                 // Multiple dates in the input string
1658                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1659                 return false;
1660             }
1661
1662             // The interpretation is based on the MonthDayPattern and YearMonthPattern
1663             //
1664             //    MonthDayPattern   YearMonthPattern  Interpretation
1665             //    ---------------   ----------------  ---------------
1666             //    MMMM dd           MMMM yyyy         Day
1667             //    MMMM dd           yyyy MMMM         Year
1668             //    dd MMMM           MMMM yyyy         Day
1669             //    dd MMMM           yyyy MMMM         Day
1670             //
1671             // In the first and last cases, it could be either or neither, but a day is a better default interpretation
1672             // than a 2 digit year.
1673
1674             int monthDayOrder;
1675             if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) {
1676                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
1677                 return false;
1678             }
1679             if (monthDayOrder == ORDER_MD) {
1680                 int yearMonthOrder;
1681                 if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder)) {
1682                     result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern);
1683                     return false;
1684                 }
1685                 if (yearMonthOrder == ORDER_YM) {
1686                     int year;
1687                     if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1)) {
1688                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1689                         return false;
1690                     }
1691                     return true;
1692                 }
1693             }
1694
1695             GetDefaultYear(ref result, ref styles);
1696             if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0))) {
1697                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1698                 return false;
1699             }
1700             return true;
1701         }
1702
1703         private static Boolean GetDayOfMNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
1704         {
1705             if ((result.flags & ParseFlags.HaveDate) != 0) {
1706                 // Multiple dates in the input string
1707                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1708                 return false;
1709             }
1710
1711             int n1 = raw.GetNumber(0);
1712             int n2 = raw.GetNumber(1);
1713
1714             int order;
1715             if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) {
1716                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
1717                 return false;
1718             }
1719             int year;
1720
1721             if (order == ORDER_MDY)
1722             {
1723                 if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era))
1724                 {
1725                     result.SetDate(year, raw.month, n1);      // MDY
1726                     result.flags |= ParseFlags.HaveDate;
1727                     return true;
1728                 }
1729                 else if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era))
1730                 {
1731                     result.SetDate(year, raw.month, n2);      // YMD
1732                     result.flags |= ParseFlags.HaveDate;
1733                     return true;
1734                 }
1735             }
1736             else if (order == ORDER_YMD)
1737             {
1738                 if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era))
1739                 {
1740                     result.SetDate(year, raw.month, n2);      // YMD
1741                     result.flags |= ParseFlags.HaveDate;
1742                     return true;
1743                 }
1744                 else if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era))
1745                 {
1746                     result.SetDate(year, raw.month, n1);      // DMY
1747                     result.flags |= ParseFlags.HaveDate;
1748                     return true;
1749                 }
1750             }
1751             else if (order == ORDER_DMY)
1752             {
1753                 if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era))
1754                 {
1755                     result.SetDate(year, raw.month, n1);      // DMY
1756                     result.flags |= ParseFlags.HaveDate;
1757                     return true;
1758                 }
1759                 else if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era))
1760                 {
1761                     result.SetDate(year, raw.month, n2);      // YMD
1762                     result.flags |= ParseFlags.HaveDate;
1763                     return true;
1764                 }
1765             }
1766
1767             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1768             return false;
1769         }
1770
1771         private static Boolean GetDayOfYNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) {
1772
1773             if ((result.flags & ParseFlags.HaveDate) != 0) {
1774                 // Multiple dates in the input string
1775                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1776                 return false;
1777             }
1778
1779             int n1 = raw.GetNumber(0);
1780             int n2 = raw.GetNumber(1);
1781             String pattern = dtfi.ShortDatePattern;
1782
1783             // For compatability, don't throw if we can't determine the order, but default to YMD instead
1784             int order;
1785             if (GetYearMonthDayOrder(pattern, dtfi, out order) && order == ORDER_YDM) {
1786                 if (SetDateYMD(ref result, raw.year, n2, n1)) {
1787                     result.flags |= ParseFlags.HaveDate;
1788                     return true; // Year + DM
1789                 }
1790             }
1791             else {
1792                 if (SetDateYMD(ref result, raw.year, n1, n2)) {
1793                     result.flags |= ParseFlags.HaveDate;
1794                     return true; // Year + MD
1795                 }
1796             }
1797             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1798             return false;
1799         }
1800
1801         private static Boolean GetDayOfNNY(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) {
1802
1803             if ((result.flags & ParseFlags.HaveDate) != 0) {
1804                 // Multiple dates in the input string
1805                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1806                 return false;
1807             }
1808
1809             int n1 = raw.GetNumber(0);
1810             int n2 = raw.GetNumber(1);
1811
1812             int order;
1813             if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) {
1814                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
1815                 return false;
1816             }
1817
1818             if (order == ORDER_MDY || order == ORDER_YMD) {
1819                 if (SetDateYMD(ref result, raw.year, n1, n2)) {
1820                     result.flags |= ParseFlags.HaveDate;
1821                     return true; // MD + Year
1822                 }
1823             } else {
1824                 if (SetDateYMD(ref result, raw.year, n2, n1)) {
1825                     result.flags |= ParseFlags.HaveDate;
1826                     return true; // DM + Year
1827                 }
1828             }
1829             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1830             return false;
1831         }
1832
1833
1834         private static Boolean GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) {
1835
1836             if ((result.flags & ParseFlags.HaveDate) != 0) {
1837                 // Multiple dates in the input string
1838                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1839                 return false;
1840             }
1841
1842             if (SetDateYMD(ref result, raw.year, raw.month, raw.GetNumber(0))) {
1843                 result.flags |= ParseFlags.HaveDate;
1844                 return true;
1845             }
1846             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1847             return false;
1848         }
1849
1850         private static Boolean GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
1851         {
1852             if ((result.flags & ParseFlags.HaveDate) != 0) {
1853                 // Multiple dates in the input string
1854                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1855                 return false;
1856             }
1857
1858             if (SetDateYMD(ref result, raw.year, raw.GetNumber(0), 1))
1859             {
1860                 result.flags |= ParseFlags.HaveDate;
1861                 return true;
1862             }
1863             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1864             return false;
1865         }
1866
1867         private static Boolean GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
1868         {
1869             if ((result.flags & ParseFlags.HaveDate) != 0) {
1870                 // Multiple dates in the input string
1871                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1872                 return false;
1873             }
1874
1875             if (SetDateYMD(ref result, raw.year, raw.month, 1))
1876             {
1877                 result.flags |= ParseFlags.HaveDate;
1878                 return true;
1879             }
1880             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1881             return false;
1882         }
1883
1884         private static void AdjustTimeMark(DateTimeFormatInfo dtfi, ref DateTimeRawInfo raw) {
1885             // Specail case for culture which uses AM as empty string.
1886             // E.g. af-ZA (0x0436)
1887             //    S1159                  \x0000
1888             //    S2359                  nm
1889             // In this case, if we are parsing a string like "2005/09/14 12:23", we will assume this is in AM.
1890
1891             if (raw.timeMark == TM.NotSet) {
1892                 if (dtfi.AMDesignator != null && dtfi.PMDesignator != null) {
1893                     if (dtfi.AMDesignator.Length == 0 && dtfi.PMDesignator.Length != 0) {
1894                         raw.timeMark = TM.AM;
1895                     }
1896                     if (dtfi.PMDesignator.Length == 0 && dtfi.AMDesignator.Length != 0) {
1897                         raw.timeMark = TM.PM;
1898                     }
1899                 }
1900             }
1901         }
1902
1903         //
1904         // Adjust hour according to the time mark.
1905         //
1906         private static Boolean AdjustHour(ref int hour, TM timeMark) {
1907             if (timeMark != TM.NotSet) {
1908
1909                 if (timeMark == TM.AM) {
1910                     if (hour < 0 || hour > 12) {
1911                         return false;
1912                     }
1913                     hour = (hour == 12) ? 0 : hour;
1914                 }
1915                 else {
1916                     if (hour < 0 || hour > 23) {
1917                         return false;
1918                     }
1919                     if (hour < 12) {
1920                         hour += 12;
1921                     }
1922                 }
1923             }
1924             return true;
1925         }
1926
1927         private static Boolean GetTimeOfN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
1928         {
1929             if ((result.flags & ParseFlags.HaveTime) != 0) {
1930                 // Multiple times in the input string
1931                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1932                 return false;
1933             }
1934             //
1935             // In this case, we need a time mark. Check if so.
1936             //
1937             if (raw.timeMark == TM.NotSet)
1938             {
1939                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1940                 return false;
1941             }
1942             result.Hour = raw.GetNumber(0);
1943             result.flags |= ParseFlags.HaveTime;
1944             return true;
1945         }
1946
1947         private static Boolean GetTimeOfNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
1948         {
1949             Contract.Assert(raw.numCount >= 2, "raw.numCount >= 2");
1950             if ((result.flags & ParseFlags.HaveTime) != 0) {
1951                 // Multiple times in the input string
1952                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1953                 return false;
1954             }
1955
1956             result.Hour     = raw.GetNumber(0);
1957             result.Minute   = raw.GetNumber(1);
1958             result.flags |= ParseFlags.HaveTime;
1959             return true;
1960         }
1961
1962         private static Boolean GetTimeOfNNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
1963         {
1964             if ((result.flags & ParseFlags.HaveTime) != 0) {
1965                 // Multiple times in the input string
1966                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1967                 return false;
1968             }
1969             Contract.Assert(raw.numCount >= 3, "raw.numCount >= 3");
1970             result.Hour = raw.GetNumber(0);
1971             result.Minute   = raw.GetNumber(1);
1972             result.Second   = raw.GetNumber(2);
1973             result.flags |= ParseFlags.HaveTime;
1974             return true;
1975         }
1976
1977         //
1978         // Processing terminal state: A Date suffix followed by one number.
1979         //
1980         private static Boolean GetDateOfDSN(ref DateTimeResult result, ref DateTimeRawInfo raw)
1981         {
1982             if (raw.numCount != 1 || result.Day != -1)
1983             {
1984                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1985                 return false;
1986             }
1987             result.Day = raw.GetNumber(0);
1988             return true;
1989         }
1990
1991         private static Boolean GetDateOfNDS(ref DateTimeResult result, ref DateTimeRawInfo raw)
1992         {
1993             if (result.Month == -1)
1994             {
1995                 //Should have a month suffix
1996                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
1997                 return false;
1998             }
1999             if (result.Year != -1)
2000             {
2001                 // Aleady has a year suffix
2002                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2003                 return false;
2004             }
2005             if (!TryAdjustYear(ref result, raw.GetNumber(0), out result.Year))
2006             {
2007                 // the year value is out of range
2008                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2009                 return false;            
2010             }
2011             result.Day = 1;
2012             return true;
2013         }
2014
2015         private static Boolean GetDateOfNNDS(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
2016         {
2017
2018             // For partial CJK Dates, the only valid formats are with a specified year, followed by two numbers, which
2019             // will be the Month and Day, and with a specified Month, when the numbers are either the year and day or
2020             // day and year, depending on the short date pattern.
2021
2022             if ((result.flags & ParseFlags.HaveYear) != 0) {
2023                 if (((result.flags & ParseFlags.HaveMonth) == 0) && ((result.flags & ParseFlags.HaveDay) == 0)) {
2024                     if (TryAdjustYear(ref result, raw.year, out result.Year) && SetDateYMD(ref result, result.Year, raw.GetNumber(0), raw.GetNumber(1))) {
2025                         return true;
2026                     }
2027                 }
2028             }
2029             else if ((result.flags & ParseFlags.HaveMonth) != 0) {
2030                 if (((result.flags & ParseFlags.HaveYear) == 0) && ((result.flags & ParseFlags.HaveDay) == 0)) {
2031                     int order;
2032                     if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) {
2033                         result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
2034                         return false;
2035                     }
2036                     int year;
2037                     if (order == ORDER_YMD) {
2038                         if (TryAdjustYear(ref result, raw.GetNumber(0), out year) && SetDateYMD(ref result, year, result.Month, raw.GetNumber(1))) {
2039                             return true;
2040                         }
2041                     }
2042                     else {
2043                         if (TryAdjustYear(ref result, raw.GetNumber(1), out year) && SetDateYMD(ref result, year, result.Month, raw.GetNumber(0))){
2044                             return true;
2045                         }
2046                     }
2047                 }
2048             }
2049             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2050             return false;
2051         }
2052
2053         //
2054         // A date suffix is found, use this method to put the number into the result.
2055         //
2056         private static bool ProcessDateTimeSuffix(ref DateTimeResult result, ref DateTimeRawInfo raw, ref DateTimeToken dtok)
2057         {
2058             switch (dtok.suffix)
2059             {
2060                 case TokenType.SEP_YearSuff:
2061                     if ((result.flags & ParseFlags.HaveYear) != 0) {
2062                         return false;
2063                     }
2064                     result.flags |= ParseFlags.HaveYear;
2065                     result.Year = raw.year = dtok.num;
2066                     break;
2067                 case TokenType.SEP_MonthSuff:
2068                     if ((result.flags & ParseFlags.HaveMonth) != 0) {
2069                         return false;
2070                     }
2071                     result.flags |= ParseFlags.HaveMonth;
2072                     result.Month= raw.month = dtok.num;
2073                     break;
2074                 case TokenType.SEP_DaySuff:
2075                     if ((result.flags & ParseFlags.HaveDay) != 0) {
2076                         return false;
2077                     }
2078                     result.flags |= ParseFlags.HaveDay;
2079                     result.Day  = dtok.num;
2080                     break;
2081                 case TokenType.SEP_HourSuff:
2082                     if ((result.flags & ParseFlags.HaveHour) != 0) {
2083                         return false;
2084                     }
2085                     result.flags |= ParseFlags.HaveHour;
2086                     result.Hour = dtok.num;
2087                     break;
2088                 case TokenType.SEP_MinuteSuff:
2089                     if ((result.flags & ParseFlags.HaveMinute) != 0) {
2090                         return false;
2091                     }
2092                     result.flags |= ParseFlags.HaveMinute;
2093                     result.Minute = dtok.num;
2094                     break;
2095                 case TokenType.SEP_SecondSuff:
2096                     if ((result.flags & ParseFlags.HaveSecond) != 0) {
2097                         return false;
2098                     }
2099                     result.flags |= ParseFlags.HaveSecond;
2100                     result.Second = dtok.num;
2101                     break;
2102             }
2103             return true;
2104
2105         }
2106
2107         ////////////////////////////////////////////////////////////////////////
2108         //
2109         // Actions:
2110         // This is used by DateTime.Parse().
2111         // Process the terminal state for the Hebrew calendar parsing.
2112         //
2113         ////////////////////////////////////////////////////////////////////////
2114
2115         internal static Boolean ProcessHebrewTerminalState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) {
2116             // The following are accepted terminal state for Hebrew date.
2117             switch (dps) {
2118                 case DS.DX_MNN:
2119                     // Deal with the default long/short date format when the year number is ambigous (i.e. year < 100).
2120                     raw.year = raw.GetNumber(1);
2121                     if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) {
2122                         result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
2123                         return false;
2124                     }
2125                     if (!GetDayOfMNN(ref result, ref raw, dtfi)) {
2126                         return false;
2127                     }
2128                     break;
2129                 case DS.DX_YMN:
2130                     // Deal with the default long/short date format when the year number is NOT ambigous (i.e. year >= 100).
2131                     if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) {
2132                         result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
2133                         return false;
2134                     }
2135                     if (!GetDayOfYMN(ref result, ref raw, dtfi)) {
2136                         return false;
2137                     }
2138                     break;
2139                 case DS.DX_NM:
2140                 case DS.DX_MN:
2141                     // Deal with Month/Day pattern.
2142                     GetDefaultYear(ref result, ref styles);
2143                     if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true)) {
2144                         result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
2145                         return false;
2146                     }
2147                     if (!GetHebrewDayOfNM(ref result, ref raw, dtfi)) {
2148                         return false;
2149                     }
2150                     break;
2151                 case DS.DX_YM:
2152                     // Deal with Year/Month pattern.
2153                     if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) {
2154                         result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
2155                         return false;
2156                     }
2157                     if (!GetDayOfYM(ref result, ref raw, dtfi)) {
2158                         return false;
2159                     }
2160                     break;
2161                 case DS.TX_N:
2162                     // Deal hour + AM/PM
2163                     if (!GetTimeOfN(dtfi, ref result, ref raw)) {
2164                         return false;
2165                     }
2166                     break;
2167                 case DS.TX_NN:
2168                     if (!GetTimeOfNN(dtfi, ref result, ref raw)) {
2169                         return false;
2170                     }
2171                     break;
2172                 case DS.TX_NNN:
2173                     if (!GetTimeOfNNN(dtfi, ref result, ref raw)) {
2174                         return false;
2175                     }
2176                     break;
2177                 default:
2178                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2179                     return false;
2180
2181             }
2182             if (dps > DS.ERROR)
2183             {
2184                 //
2185                 // We have reached a terminal state. Reset the raw num count.
2186                 //
2187                 raw.numCount = 0;
2188             }
2189             return true;
2190         }
2191
2192         //
2193         // A terminal state has been reached, call the appropriate function to fill in the parsing result.
2194         // Return true if the state is a terminal state.
2195         //
2196         internal static Boolean ProcessTerminaltState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
2197         {
2198
2199             bool passed = true;
2200             switch (dps)
2201             {
2202                 case DS.DX_NN:
2203                     passed = GetDayOfNN(ref result, ref styles, ref raw, dtfi);
2204                     break;
2205                 case DS.DX_NNN:
2206                     passed = GetDayOfNNN(ref result, ref raw, dtfi);
2207                     break;
2208                 case DS.DX_MN:
2209                     passed = GetDayOfMN(ref result, ref styles, ref raw, dtfi);
2210                     break;
2211                 case DS.DX_NM:
2212                     passed = GetDayOfNM(ref result, ref styles, ref raw, dtfi);
2213                     break;
2214                 case DS.DX_MNN:
2215                     passed = GetDayOfMNN(ref result, ref raw, dtfi);
2216                     break;
2217                 case DS.DX_DS:
2218                     // The result has got the correct value. No need to process.
2219                     passed = true;
2220                     break;
2221                 case DS.DX_YNN:
2222                     passed = GetDayOfYNN(ref result, ref raw, dtfi);
2223                     break;
2224                 case DS.DX_NNY:
2225                     passed = GetDayOfNNY(ref result, ref raw, dtfi);
2226                     break;
2227                 case DS.DX_YMN:
2228                     passed = GetDayOfYMN(ref result, ref raw, dtfi);
2229                     break;
2230                 case DS.DX_YN:
2231                     passed = GetDayOfYN(ref result, ref raw, dtfi);
2232                     break;
2233                 case DS.DX_YM:
2234                     passed = GetDayOfYM(ref result, ref raw, dtfi);
2235                     break;
2236                 case DS.TX_N:
2237                     passed = GetTimeOfN(dtfi, ref result, ref raw);
2238                     break;
2239                 case DS.TX_NN:
2240                     passed = GetTimeOfNN(dtfi, ref result, ref raw);
2241                     break;
2242                 case DS.TX_NNN:
2243                     passed = GetTimeOfNNN(dtfi, ref result, ref raw);
2244                     break;
2245                 case DS.TX_TS:
2246                     // The result has got the correct value. Nothing to do.
2247                     passed = true;
2248                     break;
2249                 case DS.DX_DSN:
2250                     passed = GetDateOfDSN(ref result, ref raw);
2251                     break;
2252                 case DS.DX_NDS:
2253                     passed = GetDateOfNDS(ref result, ref raw);
2254                     break;
2255                 case DS.DX_NNDS:
2256                     passed = GetDateOfNNDS(ref result, ref raw, dtfi);
2257                     break;
2258             }
2259
2260             PTSTraceExit(dps, passed);
2261             if (!passed) {
2262                 return false;
2263             }
2264
2265             if (dps > DS.ERROR)
2266             {
2267                 //
2268                 // We have reached a terminal state. Reset the raw num count.
2269                 //
2270                 raw.numCount = 0;
2271             }
2272             return true;
2273         }
2274
2275         internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles) {
2276             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
2277             result.Init();
2278             if (TryParse(s, dtfi, styles, ref result)) {
2279                 return result.parsedDate;
2280             }
2281             else {
2282                 throw GetDateTimeParseException(ref result);
2283             }
2284         }
2285
2286         internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset) {
2287             DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
2288             result.Init();
2289             result.flags |= ParseFlags.CaptureOffset;
2290             if (TryParse(s, dtfi, styles, ref result)) {
2291                 offset = result.timeZoneOffset;
2292                 return result.parsedDate;
2293             }
2294             else {
2295                 throw GetDateTimeParseException(ref result);
2296             }
2297         }
2298
2299
2300         internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result) {
2301             result = DateTime.MinValue;
2302             DateTimeResult resultData = new DateTimeResult();       // The buffer to store the parsing result.
2303             resultData.Init();
2304             if (TryParse(s, dtfi, styles, ref resultData)) {
2305                 result = resultData.parsedDate;
2306                 return true;
2307             }
2308             return false;
2309         }
2310   
2311         internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset) {
2312             result = DateTime.MinValue;
2313             offset = TimeSpan.Zero;
2314             DateTimeResult parseResult = new DateTimeResult();       // The buffer to store the parsing result.
2315             parseResult.Init();
2316             parseResult.flags |= ParseFlags.CaptureOffset;
2317             if (TryParse(s, dtfi, styles, ref parseResult)) {
2318                 result = parseResult.parsedDate;
2319                 offset = parseResult.timeZoneOffset;
2320                 return true;
2321             }
2322             return false;
2323         }
2324
2325   
2326         //
2327         // This is the real method to do the parsing work.
2328         //
2329         [System.Security.SecuritySafeCritical]  // auto-generated
2330         internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result) {
2331             if (s == null) {
2332                 result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, "s");
2333                 return false;
2334             }
2335             if (s.Length == 0) {
2336                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2337                 return false;
2338             }
2339
2340             Contract.Assert(dtfi != null, "dtfi == null");
2341
2342 #if _LOGGING
2343             DTFITrace(dtfi);
2344 #endif
2345
2346             DateTime time;
2347             //
2348             // First try the predefined format.
2349             //
2350             //<
2351
2352
2353
2354
2355
2356             DS dps             = DS.BEGIN;     // Date Parsing State.
2357             bool reachTerminalState = false;
2358
2359             DateTimeToken   dtok    = new DateTimeToken();      // The buffer to store the parsing token.
2360             dtok.suffix = TokenType.SEP_Unk;
2361             DateTimeRawInfo raw     = new DateTimeRawInfo();    // The buffer to store temporary parsing information.
2362             unsafe {
2363                 Int32 * numberPointer = stackalloc Int32[3];
2364                 raw.Init(numberPointer);
2365             }
2366             raw.hasSameDateAndTimeSeparators = dtfi.DateSeparator.Equals(dtfi.TimeSeparator, StringComparison.Ordinal);
2367
2368             result.calendar = dtfi.Calendar;
2369             result.era = Calendar.CurrentEra;
2370
2371             //
2372             // The string to be parsed. Use a __DTString wrapper so that we can trace the index which
2373             // indicates the begining of next token.
2374             //
2375             __DTString str = new __DTString(s, dtfi);
2376
2377             str.GetNext();
2378
2379             //
2380             // The following loop will break out when we reach the end of the str.
2381             //
2382             do {
2383                 //
2384                 // Call the lexer to get the next token.
2385                 //
2386                 // If we find a era in Lex(), the era value will be in raw.era.
2387                 if (!Lex(dps, ref str, ref dtok, ref raw, ref result, ref dtfi, styles))
2388                 {
2389                     TPTraceExit("0000", dps);
2390                     return false;
2391                 }
2392
2393                 //
2394                 // If the token is not unknown, process it.
2395                 // Otherwise, just discard it.
2396                 //
2397                 if (dtok.dtt != DTT.Unk)
2398                 {
2399                     //
2400                     // Check if we got any CJK Date/Time suffix.
2401                     // Since the Date/Time suffix tells us the number belongs to year/month/day/hour/minute/second,
2402                     // store the number in the appropriate field in the result.
2403                     //
2404                     if (dtok.suffix != TokenType.SEP_Unk)
2405                     {
2406                         if (!ProcessDateTimeSuffix(ref result, ref raw, ref dtok)) {
2407                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2408                             TPTraceExit("0010", dps);
2409                             return false;
2410                         }
2411
2412                         dtok.suffix = TokenType.SEP_Unk;  // Reset suffix to SEP_Unk;
2413                     }
2414
2415                     if (dtok.dtt == DTT.NumLocalTimeMark) {
2416                         if (dps == DS.D_YNd || dps == DS.D_YN) {
2417                             // Consider this as ISO 8601 format:
2418                             // "yyyy-MM-dd'T'HH:mm:ss"                 1999-10-31T02:00:00
2419                             TPTraceExit("0020", dps);
2420                             return (ParseISO8601(ref raw, ref str, styles, ref result));
2421                         }
2422                         else {
2423                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2424                             TPTraceExit("0030", dps);
2425                             return false;
2426                         }
2427                     }
2428
2429                     if (raw.hasSameDateAndTimeSeparators)
2430                     {
2431                         if (dtok.dtt == DTT.YearEnd || dtok.dtt == DTT.YearSpace || dtok.dtt == DTT.YearDateSep)
2432                         {
2433                             // When time and date separators are same and we are hitting a year number while the first parsed part of the string was recognized 
2434                             // as part of time (and not a date) DS.T_Nt, DS.T_NNt then change the state to be a date so we try to parse it as a date instead
2435                             if (dps == DS.T_Nt)
2436                             {
2437                                 dps = DS.D_Nd;
2438                             }
2439                             if (dps == DS.T_NNt)
2440                             {
2441                                 dps = DS.D_NNd;
2442                             }
2443                         }
2444
2445                         bool atEnd = str.AtEnd();
2446                         if (dateParsingStates[(int)dps][(int)dtok.dtt] == DS.ERROR || atEnd)
2447                         {
2448                             switch (dtok.dtt)
2449                             {
2450                                 // we have the case of Serbia have dates in forms 'd.M.yyyy.' so we can expect '.' after the date parts. 
2451                                 // changing the token to end with space instead of Date Separator will avoid failing the parsing.
2452
2453                                 case DTT.YearDateSep:  dtok.dtt = atEnd ? DTT.YearEnd  : DTT.YearSpace;  break;
2454                                 case DTT.NumDatesep:   dtok.dtt = atEnd ? DTT.NumEnd   : DTT.NumSpace;   break;
2455                                 case DTT.NumTimesep:   dtok.dtt = atEnd ? DTT.NumEnd   : DTT.NumSpace;   break;
2456                                 case DTT.MonthDatesep: dtok.dtt = atEnd ? DTT.MonthEnd : DTT.MonthSpace; break;
2457                             }
2458                         }
2459                     }
2460
2461                     //
2462                     // Advance to the next state, and continue
2463                     //
2464                     dps = dateParsingStates[(int)dps][(int)dtok.dtt];
2465                     
2466                     if (dps == DS.ERROR)
2467                     {
2468                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2469                         TPTraceExit("0040 (invalid state transition)", dps);
2470                         return false;
2471                     }
2472                     else if (dps > DS.ERROR)
2473                     {
2474                         if ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0) {
2475                             if (!ProcessHebrewTerminalState(dps, ref result, ref styles, ref raw, dtfi)) {
2476                                 TPTraceExit("0050 (ProcessHebrewTerminalState)", dps);
2477                                 return false;
2478                             }
2479                         } else {
2480                             if (!ProcessTerminaltState(dps, ref result, ref styles, ref raw, dtfi)) {
2481                                 TPTraceExit("0060 (ProcessTerminaltState)", dps);
2482                                 return false;
2483                             }
2484                         }
2485                         reachTerminalState = true;
2486
2487                         //
2488                         // If we have reached a terminal state, start over from DS.BEGIN again.
2489                         // For example, when we parsed "1999-12-23 13:30", we will reach a terminal state at "1999-12-23",
2490                         // and we start over so we can continue to parse "12:30".
2491                         //
2492                         dps = DS.BEGIN;
2493                     }
2494                 }
2495             } while (dtok.dtt != DTT.End && dtok.dtt != DTT.NumEnd && dtok.dtt != DTT.MonthEnd);
2496
2497             if (!reachTerminalState) {
2498                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2499                 TPTraceExit("0070 (did not reach terminal state)", dps);
2500                 return false;
2501             }
2502
2503             AdjustTimeMark(dtfi, ref raw);
2504             if (!AdjustHour(ref result.Hour, raw.timeMark)) {
2505                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2506                 TPTraceExit("0080 (AdjustHour)", dps);
2507                 return false;
2508             }
2509
2510             // Check if the parased string only contains hour/minute/second values.
2511             bool bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
2512
2513             //
2514             // Check if any year/month/day is missing in the parsing string.
2515             // If yes, get the default value from today's date.
2516             //
2517             if (!CheckDefaultDateTime(ref result, ref result.calendar, styles)) {
2518                 TPTraceExit("0090 (failed to fill in missing year/month/day defaults)", dps);
2519                 return false;
2520             }
2521
2522             if (!result.calendar.TryToDateTime(result.Year, result.Month, result.Day,
2523                     result.Hour, result.Minute, result.Second, 0, result.era, out time)) {
2524                 result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
2525                 TPTraceExit("0100 (result.calendar.TryToDateTime)", dps);
2526                 return false;
2527             }
2528             if (raw.fraction > 0) {
2529                 time = time.AddTicks((long)Math.Round(raw.fraction * Calendar.TicksPerSecond));
2530             }
2531
2532             //
2533             // We have to check day of week before we adjust to the time zone.
2534             // Otherwise, the value of day of week may change after adjustting to the time zone.
2535             //
2536             if (raw.dayOfWeek != -1) {
2537                 //
2538                 // Check if day of week is correct.
2539                 //
2540                 if (raw.dayOfWeek != (int)result.calendar.GetDayOfWeek(time)) {
2541                     result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
2542                     TPTraceExit("0110 (dayOfWeek check)", dps);
2543                     return false;
2544                 }
2545             }
2546
2547             result.parsedDate = time;
2548
2549             if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly)) {
2550                 TPTraceExit("0120 (DetermineTimeZoneAdjustments)", dps);
2551                 return false;
2552             }
2553             TPTraceExit("0130 (success)", dps);
2554             return true;
2555         }
2556
2557
2558         // Handles time zone adjustments and sets DateTimeKind values as required by the styles
2559         private static Boolean DetermineTimeZoneAdjustments(ref DateTimeResult result, DateTimeStyles styles, Boolean bTimeOnly) {
2560
2561             if ((result.flags & ParseFlags.CaptureOffset) != 0) {
2562                 // This is a DateTimeOffset parse, so the offset will actually be captured directly, and 
2563                 // no adjustment is required in most cases
2564                 return DateTimeOffsetTimeZonePostProcessing(ref result, styles);
2565             }
2566 #if FEATURE_CORECLR // on CoreCLR DateTime is also restricted to +- 14:00, just like DateTimeOffset
2567             else {
2568                 Int64 offsetTicks = result.timeZoneOffset.Ticks;
2569             
2570                 // the DateTime offset must be within +- 14:00 hours.
2571                 if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset) {
2572                     result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null);
2573                     return false;
2574                 }
2575             }
2576 #endif // FEATURE_CORECLR
2577
2578             // The flags AssumeUniveral and AssumeLocal only apply when the input does not have a time zone
2579             if ((result.flags & ParseFlags.TimeZoneUsed) == 0) {
2580
2581                 // If AssumeLocal or AssumeLocal is used, there will always be a kind specified. As in the
2582                 // case when a time zone is present, it will default to being local unless AdjustToUniversal
2583                 // is present. These comparisons determine whether setting the kind is sufficient, or if a
2584                 // time zone adjustment is required. For consistentcy with the rest of parsing, it is desirable
2585                 // to fall through to the Adjust methods below, so that there is consist handling of boundary
2586                 // cases like wrapping around on time-only dates and temporarily allowing an adjusted date
2587                 // to exceed DateTime.MaxValue
2588                 if ((styles & DateTimeStyles.AssumeLocal) != 0) {
2589                     if ((styles & DateTimeStyles.AdjustToUniversal) != 0) {
2590                         result.flags |= ParseFlags.TimeZoneUsed;
2591                         result.timeZoneOffset = TimeZoneInfo.GetLocalUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime);
2592                     }
2593                     else {
2594                         result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Local);
2595                         return true;
2596                     }
2597                 }
2598                 else if ((styles & DateTimeStyles.AssumeUniversal) != 0) {
2599                     if ((styles & DateTimeStyles.AdjustToUniversal) != 0) {
2600                         result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Utc);
2601                         return true;
2602                     }
2603                     else {
2604                         result.flags |= ParseFlags.TimeZoneUsed;
2605                         result.timeZoneOffset = TimeSpan.Zero;
2606                     }
2607                 }
2608                 else {
2609                     // No time zone and no Assume flags, so DateTimeKind.Unspecified is fine
2610                     Contract.Assert(result.parsedDate.Kind == DateTimeKind.Unspecified, "result.parsedDate.Kind == DateTimeKind.Unspecified");
2611                     return true;
2612                 }
2613             }
2614
2615             if (((styles & DateTimeStyles.RoundtripKind) != 0) && ((result.flags & ParseFlags.TimeZoneUtc) != 0)) {
2616                 result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Utc);
2617                 return true;
2618             }
2619
2620             if ((styles & DateTimeStyles.AdjustToUniversal) != 0) {
2621                 return (AdjustTimeZoneToUniversal(ref result));
2622             }
2623             return (AdjustTimeZoneToLocal(ref result, bTimeOnly));
2624         }
2625
2626         // Apply validation and adjustments specific to DateTimeOffset
2627         private static Boolean DateTimeOffsetTimeZonePostProcessing(ref DateTimeResult result, DateTimeStyles styles) {
2628
2629             // For DateTimeOffset, default to the Utc or Local offset when an offset was not specified by 
2630             // the input string.
2631             if ((result.flags & ParseFlags.TimeZoneUsed) == 0) {
2632                 if ((styles & DateTimeStyles.AssumeUniversal) != 0) {
2633                     // AssumeUniversal causes the offset to default to zero (0)
2634                     result.timeZoneOffset = TimeSpan.Zero;
2635                 }
2636                 else {
2637                     // AssumeLocal causes the offset to default to Local.  This flag is on by default for DateTimeOffset.
2638                     result.timeZoneOffset = TimeZoneInfo.GetLocalUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime);
2639                 }
2640             }
2641
2642             Int64 offsetTicks = result.timeZoneOffset.Ticks;
2643             
2644             // there should be no overflow, because the offset can be no more than -+100 hours and the date already
2645             // fits within a DateTime.
2646             Int64 utcTicks = result.parsedDate.Ticks - offsetTicks;
2647             
2648             // For DateTimeOffset, both the parsed time and the corresponding UTC value must be within the boundaries
2649             // of a DateTime instance.            
2650             if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks) {
2651                 result.SetFailure(ParseFailureKind.Format, "Format_UTCOutOfRange", null);
2652                 return false;
2653             }
2654
2655             // the offset must be within +- 14:00 hours.
2656             if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset) {
2657                 result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null);
2658                 return false;
2659             }
2660
2661             // DateTimeOffset should still honor the AdjustToUniversal flag for consistency with DateTime. It means you
2662             // want to return an adjusted UTC value, so store the utcTicks in the DateTime and set the offset to zero
2663             if ((styles & DateTimeStyles.AdjustToUniversal) != 0) {
2664                 if (((result.flags & ParseFlags.TimeZoneUsed) == 0) && ((styles & DateTimeStyles.AssumeUniversal) == 0)) {
2665                     // Handle the special case where the timeZoneOffset was defaulted to Local
2666                     Boolean toUtcResult = AdjustTimeZoneToUniversal(ref result);
2667                     result.timeZoneOffset = TimeSpan.Zero;
2668                     return toUtcResult;
2669                 }
2670
2671                 // The constructor should always succeed because of the range check earlier in the function
2672                 // Althought it is UTC, internally DateTimeOffset does not use this flag
2673                 result.parsedDate = new DateTime(utcTicks, DateTimeKind.Utc);
2674                 result.timeZoneOffset = TimeSpan.Zero;
2675             }
2676             
2677             return true;            
2678         }
2679         
2680
2681         //
2682         // Adjust the specified time to universal time based on the supplied timezone.
2683         // E.g. when parsing "2001/06/08 14:00-07:00",
2684         // the time is 2001/06/08 14:00, and timeZoneOffset = -07:00.
2685         // The result will be "2001/06/08 21:00"
2686         //
2687         private static Boolean AdjustTimeZoneToUniversal(ref DateTimeResult result) {
2688             long resultTicks = result.parsedDate.Ticks;
2689             resultTicks -= result.timeZoneOffset.Ticks;
2690             if (resultTicks < 0) {
2691                 resultTicks += Calendar.TicksPerDay;
2692             }
2693
2694             if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) {
2695                 result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null);
2696                 return false;
2697             }
2698             result.parsedDate = new DateTime(resultTicks, DateTimeKind.Utc);
2699             return true;
2700         }
2701
2702         //
2703         // Adjust the specified time to universal time based on the supplied timezone,
2704         // and then convert to local time.
2705         // E.g. when parsing "2001/06/08 14:00-04:00", and local timezone is GMT-7.
2706         // the time is 2001/06/08 14:00, and timeZoneOffset = -05:00.
2707         // The result will be "2001/06/08 11:00"
2708         //
2709         private static Boolean AdjustTimeZoneToLocal(ref DateTimeResult result, bool bTimeOnly) {
2710             long resultTicks = result.parsedDate.Ticks;
2711             // Convert to local ticks
2712             TimeZoneInfo tz = TimeZoneInfo.Local;
2713             Boolean isAmbiguousLocalDst = false;
2714             if (resultTicks < Calendar.TicksPerDay) {
2715                 //
2716                 // This is time of day.
2717                 //
2718
2719                 // Adjust timezone.
2720                 resultTicks -= result.timeZoneOffset.Ticks;
2721                 // If the time is time of day, use the current timezone offset.
2722                 resultTicks += tz.GetUtcOffset(bTimeOnly ? DateTime.Now: result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
2723
2724                 if (resultTicks < 0) {
2725                     resultTicks += Calendar.TicksPerDay;
2726                 }
2727             } else {
2728                 // Adjust timezone to GMT.
2729                 resultTicks -= result.timeZoneOffset.Ticks;
2730                 if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) {
2731                     // If the result ticks is greater than DateTime.MaxValue, we can not create a DateTime from this ticks.
2732                     // In this case, keep using the old code.
2733                     resultTicks += tz.GetUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
2734                 } else {
2735                     // Convert the GMT time to local time.
2736                     DateTime utcDt = new DateTime(resultTicks, DateTimeKind.Utc);
2737                     Boolean isDaylightSavings = false;
2738                     resultTicks += TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
2739                 }
2740             }
2741             if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) {
2742                 result.parsedDate = DateTime.MinValue;
2743                 result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null);
2744                 return false;
2745             }
2746             result.parsedDate = new DateTime(resultTicks, DateTimeKind.Local, isAmbiguousLocalDst);
2747             return true;
2748         }
2749
2750         //
2751         // Parse the ISO8601 format string found during Parse();
2752         //
2753         //
2754         private static bool ParseISO8601(ref DateTimeRawInfo raw, ref __DTString str, DateTimeStyles styles, ref DateTimeResult result) {
2755             if (raw.year < 0 || raw.GetNumber(0) < 0 || raw.GetNumber(1) < 0) {
2756             }
2757             str.Index--;
2758             int hour, minute;
2759             int second = 0;
2760             double partSecond = 0;
2761
2762             str.SkipWhiteSpaces();
2763             if (!ParseDigits(ref str, 2, out hour)) {
2764                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2765                 return false;
2766             }
2767             str.SkipWhiteSpaces();
2768             if (!str.Match(':')) {
2769                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2770                 return false;
2771             }
2772             str.SkipWhiteSpaces();
2773             if (!ParseDigits(ref str, 2, out minute)) {
2774                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2775                 return false;
2776             }
2777             str.SkipWhiteSpaces();
2778             if (str.Match(':')) {
2779                 str.SkipWhiteSpaces();
2780                 if (!ParseDigits(ref str, 2, out second)) {
2781                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2782                     return false;
2783                 }
2784                 if (str.Match('.')) {
2785                     if (!ParseFraction(ref str, out partSecond)) {
2786                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2787                         return false;
2788                     }
2789                     str.Index--;
2790                 }
2791                 str.SkipWhiteSpaces();
2792             }
2793             if (str.GetNext()) {
2794                 char ch = str.GetChar();
2795                 if (ch == '+' || ch == '-') {
2796                     result.flags |= ParseFlags.TimeZoneUsed;
2797                     if (!ParseTimeZone(ref str, ref result.timeZoneOffset)) {
2798                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2799                         return false;
2800                     }
2801                 } else if (ch == 'Z' || ch == 'z') {
2802                     result.flags |= ParseFlags.TimeZoneUsed;
2803                     result.timeZoneOffset = TimeSpan.Zero;
2804                     result.flags |= ParseFlags.TimeZoneUtc;
2805                 } else {
2806                     str.Index--;
2807                 }
2808                 str.SkipWhiteSpaces();
2809                 if (str.Match('#')) {
2810                     if (!VerifyValidPunctuation(ref str)) {
2811                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2812                         return false;                        
2813                     }
2814                     str.SkipWhiteSpaces();
2815                 }
2816                 if (str.Match('\0')) {
2817                     if (!VerifyValidPunctuation(ref str)) {
2818                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2819                         return false;                        
2820                     }
2821                 }
2822                 if (str.GetNext()) {                
2823                     // If this is true, there were non-white space characters remaining in the DateTime
2824                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
2825                     return false;
2826                 }
2827             }
2828
2829             DateTime time;
2830             Calendar calendar = GregorianCalendar.GetDefaultInstance();
2831             if (!calendar.TryToDateTime(raw.year, raw.GetNumber(0), raw.GetNumber(1),
2832                     hour, minute, second, 0, result.era, out time)) {
2833                 result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
2834                 return false;
2835             }
2836             
2837             time = time.AddTicks((long)Math.Round(partSecond * Calendar.TicksPerSecond));
2838             result.parsedDate = time;
2839             if (!DetermineTimeZoneAdjustments(ref result, styles, false)) {
2840                 return false;
2841             }
2842             return true;
2843         }
2844
2845
2846         ////////////////////////////////////////////////////////////////////////
2847         //
2848         // Actions:
2849         //    Parse the current word as a Hebrew number.
2850         //      This is used by DateTime.ParseExact().
2851         //
2852         ////////////////////////////////////////////////////////////////////////
2853
2854         internal static bool MatchHebrewDigits(ref __DTString str, int digitLen, out int number) {
2855             number = 0;
2856
2857             // Create a context object so that we can parse the Hebrew number text character by character.
2858             HebrewNumberParsingContext context = new HebrewNumberParsingContext(0);
2859
2860             // Set this to ContinueParsing so that we will run the following while loop in the first time.
2861             HebrewNumberParsingState state = HebrewNumberParsingState.ContinueParsing;
2862
2863             while (state == HebrewNumberParsingState.ContinueParsing && str.GetNext()) {
2864                 state = HebrewNumber.ParseByChar(str.GetChar(), ref context);
2865             }
2866
2867             if (state == HebrewNumberParsingState.FoundEndOfHebrewNumber) {
2868                 // If we have reached a terminal state, update the result and returns.
2869                 number = context.result;
2870                 return (true);
2871             }
2872
2873             // If we run out of the character before reaching FoundEndOfHebrewNumber, or
2874             // the state is InvalidHebrewNumber or ContinueParsing, we fail to match a Hebrew number.
2875             // Return an error.
2876             return false;
2877         }
2878
2879         /*=================================ParseDigits==================================
2880         **Action: Parse the number string in __DTString that are formatted using
2881         **        the following patterns:
2882         **        "0", "00", and "000..0"
2883         **Returns: the integer value
2884         **Arguments:    str: a __DTString.  The parsing will start from the
2885         **              next character after str.Index.
2886         **Exceptions: FormatException if error in parsing number.
2887         ==============================================================================*/
2888
2889         internal static bool ParseDigits(ref __DTString str, int digitLen, out int result) {
2890             if (digitLen == 1) {
2891                 // 1 really means 1 or 2 for this call
2892                 return ParseDigits(ref str, 1, 2, out result);
2893             }
2894             else {
2895                 return ParseDigits(ref str, digitLen, digitLen, out result);
2896             }
2897         }
2898
2899         internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result) {
2900             Contract.Assert(minDigitLen > 0, "minDigitLen > 0");
2901             Contract.Assert(maxDigitLen < 9, "maxDigitLen < 9");
2902             Contract.Assert(minDigitLen <= maxDigitLen, "minDigitLen <= maxDigitLen");
2903             result = 0;
2904             int startingIndex = str.Index;
2905             int tokenLength = 0;
2906             while (tokenLength < maxDigitLen) {
2907                 if (!str.GetNextDigit()) {
2908                     str.Index--;
2909                     break;
2910                 }
2911                 result = result * 10 + str.GetDigit();
2912                 tokenLength++;
2913             }
2914             if (tokenLength < minDigitLen) {
2915                 str.Index = startingIndex;
2916                 return false;
2917             }
2918             return true;
2919         }
2920
2921         /*=================================ParseFractionExact==================================
2922         **Action: Parse the number string in __DTString that are formatted using
2923         **        the following patterns:
2924         **        "0", "00", and "000..0"
2925         **Returns: the fraction value
2926         **Arguments:    str: a __DTString.  The parsing will start from the
2927         **              next character after str.Index.
2928         **Exceptions: FormatException if error in parsing number.
2929         ==============================================================================*/
2930
2931         private static bool ParseFractionExact(ref __DTString str, int maxDigitLen,  ref double result) {
2932             if (!str.GetNextDigit()) {
2933                 str.Index--;
2934                 return false;
2935             }
2936             result = str.GetDigit();
2937
2938             int digitLen = 1;
2939             for (; digitLen < maxDigitLen; digitLen++) {
2940                 if (!str.GetNextDigit()) {
2941                     str.Index--;
2942                     break;
2943                 }
2944                 result = result * 10 + str.GetDigit();
2945             }
2946
2947             result = ((double)result / Math.Pow(10, digitLen));
2948             return (digitLen == maxDigitLen);
2949         }
2950
2951         /*=================================ParseSign==================================
2952         **Action: Parse a positive or a negative sign.
2953         **Returns:      true if postive sign.  flase if negative sign.
2954         **Arguments:    str: a __DTString.  The parsing will start from the
2955         **              next character after str.Index.
2956         **Exceptions:   FormatException if end of string is encountered or a sign
2957         **              symbol is not found.
2958         ==============================================================================*/
2959
2960         private static bool ParseSign(ref __DTString str, ref bool result) {
2961             if (!str.GetNext()) {
2962                 // A sign symbol ('+' or '-') is expected. However, end of string is encountered.
2963                 return false;
2964             }
2965             char ch = str.GetChar();
2966             if (ch == '+') {
2967                 result = true;
2968                 return (true);
2969             } else if (ch == '-') {
2970                 result = false;
2971                 return (true);
2972             }
2973             // A sign symbol ('+' or '-') is expected.
2974             return false;
2975         }
2976
2977         /*=================================ParseTimeZoneOffset==================================
2978         **Action: Parse the string formatted using "z", "zz", "zzz" in DateTime.Format().
2979         **Returns: the TimeSpan for the parsed timezone offset.
2980         **Arguments:    str: a __DTString.  The parsing will start from the
2981         **              next character after str.Index.
2982         **              len: the repeated number of the "z"
2983         **Exceptions: FormatException if errors in parsing.
2984         ==============================================================================*/
2985
2986         private static bool ParseTimeZoneOffset(ref __DTString str, int len, ref TimeSpan result) {
2987             bool isPositive = true;
2988             int hourOffset;
2989             int minuteOffset = 0;
2990
2991             switch (len) {
2992                 case 1:
2993                 case 2:
2994                     if (!ParseSign(ref str, ref isPositive)) {
2995                         return (false);
2996                     }
2997                     if (!ParseDigits(ref str, len, out hourOffset)) {
2998                         return (false);
2999                     }
3000                     break;
3001                 default:
3002                     if (!ParseSign(ref str, ref isPositive)) {
3003                         return (false);
3004                     }
3005
3006                     // Parsing 1 digit will actually parse 1 or 2.
3007                     if (!ParseDigits(ref str, 1, out hourOffset)) {
3008                         return (false);
3009                     }
3010                     // ':' is optional.
3011                     if (str.Match(":")) {
3012                         // Found ':'
3013                         if (!ParseDigits(ref str, 2, out minuteOffset)) {
3014                             return (false);
3015                         }
3016                     } else {
3017                         // Since we can not match ':', put the char back.
3018                         str.Index--;
3019                         if (!ParseDigits(ref str, 2, out minuteOffset)) {
3020                             return (false);
3021                         }
3022                     }
3023                     break;
3024             }
3025             if (minuteOffset < 0 || minuteOffset >= 60) {
3026                 return false;
3027             }
3028
3029             result = (new TimeSpan(hourOffset, minuteOffset, 0));
3030             if (!isPositive) {
3031                 result = result.Negate();
3032             }
3033             return (true);
3034         }
3035
3036         /*=================================MatchAbbreviatedMonthName==================================
3037         **Action: Parse the abbreviated month name from string starting at str.Index.
3038         **Returns: A value from 1 to 12 for the first month to the twelveth month.
3039         **Arguments:    str: a __DTString.  The parsing will start from the
3040         **              next character after str.Index.
3041         **Exceptions: FormatException if an abbreviated month name can not be found.
3042         ==============================================================================*/
3043
3044         private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) {
3045             int maxMatchStrLen = 0;
3046             result = -1;
3047             if (str.GetNext()) {
3048                 //
3049                 // Scan the month names (note that some calendars has 13 months) and find
3050                 // the matching month name which has the max string length.
3051                 // We need to do this because some cultures (e.g. "cs-CZ") which have
3052                 // abbreviated month names with the same prefix.
3053                 //
3054                 int monthsInYear = (dtfi.GetMonthName(13).Length == 0 ? 12: 13);
3055                 for (int i = 1; i <= monthsInYear; i++) {
3056                     String searchStr = dtfi.GetAbbreviatedMonthName(i);
3057                     int matchStrLen = searchStr.Length;
3058                     if ( dtfi.HasSpacesInMonthNames
3059                             ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
3060                             : str.MatchSpecifiedWord(searchStr)) {
3061                         if (matchStrLen > maxMatchStrLen) {
3062                             maxMatchStrLen = matchStrLen;
3063                             result = i;
3064                         }
3065                     }
3066                 }
3067
3068                 // Search leap year form.
3069                 if ((dtfi.FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0) {
3070                     int tempResult = str.MatchLongestWords(dtfi.internalGetLeapYearMonthNames(), ref maxMatchStrLen);
3071                     // We found a longer match in the leap year month name.  Use this as the result.
3072                     // The result from MatchLongestWords is 0 ~ length of word array.
3073                     // So we increment the result by one to become the month value.
3074                     if (tempResult >= 0) {
3075                         result = tempResult + 1;
3076                     }
3077                 }
3078
3079
3080             }
3081             if (result > 0) {
3082                 str.Index += (maxMatchStrLen - 1);
3083                 return (true);
3084             }
3085             return false;
3086         }
3087
3088         /*=================================MatchMonthName==================================
3089         **Action: Parse the month name from string starting at str.Index.
3090         **Returns: A value from 1 to 12 indicating the first month to the twelveth month.
3091         **Arguments:    str: a __DTString.  The parsing will start from the
3092         **              next character after str.Index.
3093         **Exceptions: FormatException if a month name can not be found.
3094         ==============================================================================*/
3095
3096         private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) {
3097             int maxMatchStrLen = 0;
3098             result = -1;
3099             if (str.GetNext()) {
3100                 //
3101                 // Scan the month names (note that some calendars has 13 months) and find
3102                 // the matching month name which has the max string length.
3103                 // We need to do this because some cultures (e.g. "vi-VN") which have
3104                 // month names with the same prefix.
3105                 //
3106                 int monthsInYear = (dtfi.GetMonthName(13).Length == 0 ? 12: 13);
3107                 for (int i = 1; i <= monthsInYear; i++) {
3108                     String searchStr = dtfi.GetMonthName(i);
3109                     int matchStrLen = searchStr.Length;
3110                     if ( dtfi.HasSpacesInMonthNames
3111                             ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
3112                             : str.MatchSpecifiedWord(searchStr)) {
3113                         if (matchStrLen > maxMatchStrLen) {
3114                             maxMatchStrLen = matchStrLen;
3115                             result = i;
3116                         }
3117                     }
3118                 }
3119
3120                 // Search genitive form.
3121                 if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0) {
3122                     int tempResult = str.MatchLongestWords(dtfi.MonthGenitiveNames, ref maxMatchStrLen);
3123                     // We found a longer match in the genitive month name.  Use this as the result.
3124                     // The result from MatchLongestWords is 0 ~ length of word array.
3125                     // So we increment the result by one to become the month value.
3126                     if (tempResult >= 0) {
3127                         result = tempResult + 1;
3128                     }
3129                 }
3130
3131                 // Search leap year form.
3132                 if ((dtfi.FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0) {
3133                     int tempResult = str.MatchLongestWords(dtfi.internalGetLeapYearMonthNames(), ref maxMatchStrLen);
3134                     // We found a longer match in the leap year month name.  Use this as the result.
3135                     // The result from MatchLongestWords is 0 ~ length of word array.
3136                     // So we increment the result by one to become the month value.
3137                     if (tempResult >= 0) {
3138                         result = tempResult + 1;
3139                     }
3140                 }
3141
3142
3143             }
3144
3145             if (result > 0) {
3146                 str.Index += (maxMatchStrLen - 1);
3147                 return (true);
3148             }
3149             return false;
3150         }
3151
3152         /*=================================MatchAbbreviatedDayName==================================
3153         **Action: Parse the abbreviated day of week name from string starting at str.Index.
3154         **Returns: A value from 0 to 6 indicating Sunday to Saturday.
3155         **Arguments:    str: a __DTString.  The parsing will start from the
3156         **              next character after str.Index.
3157         **Exceptions: FormatException if a abbreviated day of week name can not be found.
3158         ==============================================================================*/
3159
3160         private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) {
3161             int maxMatchStrLen = 0;
3162             result = -1;
3163             if (str.GetNext()) {
3164                 for (DayOfWeek i = DayOfWeek.Sunday; i <= DayOfWeek.Saturday; i++) {
3165                     String searchStr = dtfi.GetAbbreviatedDayName(i);
3166                     int matchStrLen = searchStr.Length;
3167                     if ( dtfi.HasSpacesInDayNames
3168                             ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
3169                             : str.MatchSpecifiedWord(searchStr)) {
3170                         if (matchStrLen > maxMatchStrLen) {
3171                             maxMatchStrLen = matchStrLen;
3172                             result = (int)i;
3173                         }
3174                     }
3175                 }
3176             }
3177             if (result >= 0) {
3178                 str.Index += maxMatchStrLen - 1;
3179                 return (true);
3180             }
3181             return false;
3182         }
3183
3184         /*=================================MatchDayName==================================
3185         **Action: Parse the day of week name from string starting at str.Index.
3186         **Returns: A value from 0 to 6 indicating Sunday to Saturday.
3187         **Arguments:    str: a __DTString.  The parsing will start from the
3188         **              next character after str.Index.
3189         **Exceptions: FormatException if a day of week name can not be found.
3190         ==============================================================================*/
3191
3192         private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) {
3193             // Turkish (tr-TR) got day names with the same prefix.
3194             int maxMatchStrLen = 0;
3195             result = -1;
3196             if (str.GetNext()) {
3197                 for (DayOfWeek i = DayOfWeek.Sunday; i <= DayOfWeek.Saturday; i++) {
3198                     String searchStr = dtfi.GetDayName(i);
3199                     int matchStrLen = searchStr.Length;
3200                     if ( dtfi.HasSpacesInDayNames
3201                             ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
3202                             : str.MatchSpecifiedWord(searchStr)) {
3203                         if (matchStrLen > maxMatchStrLen) {
3204                             maxMatchStrLen = matchStrLen;
3205                             result = (int)i;
3206                         }
3207                     }
3208                 }
3209             }
3210             if (result >= 0) {
3211                 str.Index += maxMatchStrLen - 1;
3212                 return (true);
3213             }
3214             return false;
3215         }
3216
3217         /*=================================MatchEraName==================================
3218         **Action: Parse era name from string starting at str.Index.
3219         **Returns: An era value.
3220         **Arguments:    str: a __DTString.  The parsing will start from the
3221         **              next character after str.Index.
3222         **Exceptions: FormatException if an era name can not be found.
3223         ==============================================================================*/
3224
3225         private static bool MatchEraName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) {
3226             if (str.GetNext()) {
3227                 int[] eras = dtfi.Calendar.Eras;
3228
3229                 if (eras != null) {
3230                     for (int i = 0; i < eras.Length; i++) {
3231                         String searchStr = dtfi.GetEraName(eras[i]);
3232                         if (str.MatchSpecifiedWord(searchStr)) {
3233                             str.Index += (searchStr.Length - 1);
3234                             result = eras[i];
3235                             return (true);
3236                         }
3237                         searchStr = dtfi.GetAbbreviatedEraName(eras[i]);
3238                         if (str.MatchSpecifiedWord(searchStr)) {
3239                             str.Index += (searchStr.Length - 1);
3240                             result = eras[i];
3241                             return (true);
3242                         }
3243                     }
3244                 }
3245             }
3246             return false;
3247         }
3248
3249         /*=================================MatchTimeMark==================================
3250         **Action: Parse the time mark (AM/PM) from string starting at str.Index.
3251         **Returns: TM_AM or TM_PM.
3252         **Arguments:    str: a __DTString.  The parsing will start from the
3253         **              next character after str.Index.
3254         **Exceptions: FormatException if a time mark can not be found.
3255         ==============================================================================*/
3256
3257         private static bool MatchTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result) {
3258             result = TM.NotSet;
3259             // In some cultures have empty strings in AM/PM mark. E.g. af-ZA (0x0436), the AM mark is "", and PM mark is "nm".
3260             if (dtfi.AMDesignator.Length == 0) {
3261                 result = TM.AM;
3262             }
3263             if (dtfi.PMDesignator.Length == 0) {
3264                 result = TM.PM;
3265             }
3266
3267             if (str.GetNext()) {
3268                 String searchStr = dtfi.AMDesignator;
3269                 if (searchStr.Length > 0) {
3270                     if (str.MatchSpecifiedWord(searchStr)) {
3271                         // Found an AM timemark with length > 0.
3272                         str.Index += (searchStr.Length - 1);
3273                         result = TM.AM;
3274                         return (true);
3275                     }
3276                 }
3277                 searchStr = dtfi.PMDesignator;
3278                 if (searchStr.Length > 0) {
3279                     if (str.MatchSpecifiedWord(searchStr)) {
3280                         // Found a PM timemark with length > 0.
3281                         str.Index += (searchStr.Length - 1);
3282                         result = TM.PM;
3283                         return (true);
3284                     }
3285                 }
3286                 str.Index--; // Undo the GetNext call.
3287             }
3288             if (result != TM.NotSet) {
3289                 // If one of the AM/PM marks is empty string, return the result.
3290                 return (true);
3291             }
3292             return false;
3293         }
3294
3295         /*=================================MatchAbbreviatedTimeMark==================================
3296         **Action: Parse the abbreviated time mark (AM/PM) from string starting at str.Index.
3297         **Returns: TM_AM or TM_PM.
3298         **Arguments:    str: a __DTString.  The parsing will start from the
3299         **              next character after str.Index.
3300         **Exceptions: FormatException if a abbreviated time mark can not be found.
3301         ==============================================================================*/
3302
3303         private static bool MatchAbbreviatedTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result) {
3304             // NOTENOTE : the assumption here is that abbreviated time mark is the first
3305             // character of the AM/PM designator.  If this invariant changes, we have to
3306             // change the code below.
3307             if (str.GetNext())
3308             {
3309                 if (str.GetChar() == dtfi.AMDesignator[0]) {
3310                     result = TM.AM;
3311                     return (true);
3312                 }
3313                 if (str.GetChar() == dtfi.PMDesignator[0]) {
3314                     result = TM.PM;
3315                     return (true);
3316                 }
3317             }
3318             return false;
3319         }
3320
3321         /*=================================CheckNewValue==================================
3322         **Action: Check if currentValue is initialized.  If not, return the newValue.
3323         **        If yes, check if the current value is equal to newValue.  Return false
3324         **        if they are not equal.  This is used to check the case like "d" and "dd" are both
3325         **        used to format a string.
3326         **Returns: the correct value for currentValue.
3327         **Arguments:
3328         **Exceptions:
3329         ==============================================================================*/
3330
3331         private static bool CheckNewValue(ref int currentValue, int newValue, char patternChar, ref DateTimeResult result) {
3332             if (currentValue == -1) {
3333                 currentValue = newValue;
3334                 return (true);
3335             } else {
3336                 if (newValue != currentValue) {
3337                     result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", patternChar);
3338                     return (false);
3339                 }
3340             }
3341             return (true);
3342         }
3343
3344         private static DateTime GetDateTimeNow(ref DateTimeResult result, ref DateTimeStyles styles) {
3345             if ((result.flags & ParseFlags.CaptureOffset) != 0) {
3346                 if ((result.flags & ParseFlags.TimeZoneUsed) != 0) {
3347                     // use the supplied offset to calculate 'Now'
3348                     return new DateTime(DateTime.UtcNow.Ticks + result.timeZoneOffset.Ticks, DateTimeKind.Unspecified);
3349                 }
3350                 else if ((styles & DateTimeStyles.AssumeUniversal) != 0) {
3351                     // assume the offset is Utc
3352                     return DateTime.UtcNow;
3353                 }
3354             }
3355
3356             // assume the offset is Local            
3357             return DateTime.Now;
3358         }
3359
3360         private static bool CheckDefaultDateTime(ref DateTimeResult result, ref Calendar cal, DateTimeStyles styles) {
3361
3362             if ((result.flags & ParseFlags.CaptureOffset) != 0) {
3363                 // DateTimeOffset.Parse should allow dates without a year, but only if there is also no time zone marker;
3364                 // e.g. "May 1 5pm" is OK, but "May 1 5pm -08:30" is not.  This is somewhat pragmatic, since we would
3365                 // have to rearchitect parsing completely to allow this one case to correctly handle things like leap
3366                 // years and leap months.  Is is an extremely corner case, and DateTime is basically incorrect in that
3367                 // case today.
3368                 //
3369                 // values like "11:00Z" or "11:00 -3:00" are also acceptable
3370                 //
3371                 // if ((month or day is set) and (year is not set and time zone is set))
3372                 //
3373                 if (  ((result.Month != -1) || (result.Day != -1)) 
3374                     && ((result.Year == -1 || ((result.flags & ParseFlags.YearDefault) != 0)) && (result.flags & ParseFlags.TimeZoneUsed) != 0) ) {
3375                     result.SetFailure(ParseFailureKind.Format, "Format_MissingIncompleteDate", null);
3376                     return false;
3377                 }
3378             }
3379
3380
3381             if ((result.Year == -1) || (result.Month == -1) || (result.Day == -1)) {
3382                 /*
3383                 The following table describes the behaviors of getting the default value
3384                 when a certain year/month/day values are missing.
3385
3386                 An "X" means that the value exists.  And "--" means that value is missing.
3387
3388                 Year    Month   Day =>  ResultYear  ResultMonth     ResultDay       Note
3389
3390                 X       X       X       Parsed year Parsed month    Parsed day
3391                 X       X       --      Parsed Year Parsed month    First day       If we have year and month, assume the first day of that month.
3392                 X       --      X       Parsed year First month     Parsed day      If the month is missing, assume first month of that year.
3393                 X       --      --      Parsed year First month     First day       If we have only the year, assume the first day of that year.
3394
3395                 --      X       X       CurrentYear Parsed month    Parsed day      If the year is missing, assume the current year.
3396                 --      X       --      CurrentYear Parsed month    First day       If we have only a month value, assume the current year and current day.
3397                 --      --      X       CurrentYear First month     Parsed day      If we have only a day value, assume current year and first month.
3398                 --      --      --      CurrentYear Current month   Current day     So this means that if the date string only contains time, you will get current date.
3399
3400                 */
3401                 
3402                 DateTime now = GetDateTimeNow(ref result, ref styles);
3403                 if (result.Month == -1 && result.Day == -1) {
3404                     if (result.Year == -1) {
3405                         if ((styles & DateTimeStyles.NoCurrentDateDefault) != 0) {
3406                             // If there is no year/month/day values, and NoCurrentDateDefault flag is used,
3407                             // set the year/month/day value to the beginning year/month/day of DateTime().
3408                             // Note we should be using Gregorian for the year/month/day.
3409                             cal = GregorianCalendar.GetDefaultInstance();
3410                             result.Year = result.Month = result.Day = 1;
3411                         } else {
3412                             // Year/Month/Day are all missing.
3413                             result.Year = cal.GetYear(now);
3414                             result.Month = cal.GetMonth(now);
3415                             result.Day = cal.GetDayOfMonth(now);
3416                         }
3417                     } else {
3418                         // Month/Day are both missing.
3419                         result.Month = 1;
3420                         result.Day = 1;
3421                     }
3422                 } else {
3423                     if (result.Year == -1) {
3424                         result.Year = cal.GetYear(now);
3425                     }
3426                     if (result.Month == -1) {
3427                         result.Month = 1;
3428                     }
3429                     if (result.Day == -1) {
3430                         result.Day = 1;
3431                     }
3432                 }
3433             }
3434             // Set Hour/Minute/Second to zero if these value are not in str.
3435             if (result.Hour   == -1) result.Hour = 0;
3436             if (result.Minute == -1) result.Minute = 0;
3437             if (result.Second == -1) result.Second = 0;
3438             if (result.era == -1) result.era = Calendar.CurrentEra;
3439             return true;
3440         }
3441
3442         // Expand a pre-defined format string (like "D" for long date) to the real format that
3443         // we are going to use in the date time parsing.
3444         // This method also set the dtfi according/parseInfo to some special pre-defined
3445         // formats.
3446         //
3447         private static String ExpandPredefinedFormat(String format, ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result) {
3448             //
3449             // Check the format to see if we need to override the dtfi to be InvariantInfo,
3450             // and see if we need to set up the userUniversalTime flag.
3451             //
3452             switch (format[0]) {
3453                 case 'o':
3454                 case 'O':       // Round Trip Format
3455                     parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
3456                     dtfi = DateTimeFormatInfo.InvariantInfo;
3457                     break;
3458                 case 'r':
3459                 case 'R':       // RFC 1123 Standard.  (in Universal time)
3460                     parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
3461                     dtfi = DateTimeFormatInfo.InvariantInfo;
3462                     
3463                     if ((result.flags & ParseFlags.CaptureOffset) != 0) {
3464                         result.flags |= ParseFlags.Rfc1123Pattern;
3465                     }
3466                     break;
3467                 case 's':       // Sortable format (in local time)
3468                     dtfi = DateTimeFormatInfo.InvariantInfo;
3469                     parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
3470                     break;
3471                 case 'u':       // Universal time format in sortable format.
3472                     parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
3473                     dtfi = DateTimeFormatInfo.InvariantInfo;
3474
3475                     if ((result.flags & ParseFlags.CaptureOffset) != 0) {
3476                         result.flags |= ParseFlags.UtcSortPattern;
3477                     }
3478                     break;
3479                 case 'U':       // Universal time format with culture-dependent format.
3480                     parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
3481                     result.flags |= ParseFlags.TimeZoneUsed;
3482                     result.timeZoneOffset = new TimeSpan(0);
3483                     result.flags |= ParseFlags.TimeZoneUtc;
3484                     if (dtfi.Calendar.GetType() != typeof(GregorianCalendar)) {
3485                         dtfi = (DateTimeFormatInfo)dtfi.Clone();
3486                         dtfi.Calendar = GregorianCalendar.GetDefaultInstance();
3487                     }
3488                     break;
3489             }
3490
3491             //
3492             // Expand the pre-defined format character to the real format from DateTimeFormatInfo.
3493             //
3494             return (DateTimeFormat.GetRealFormat(format, dtfi));
3495         }
3496
3497
3498
3499
3500
3501         // Given a specified format character, parse and update the parsing result.
3502         //
3503         private static bool ParseByFormat(
3504             ref __DTString str,
3505             ref __DTString format,
3506             ref ParsingInfo parseInfo,
3507             DateTimeFormatInfo dtfi,
3508             ref DateTimeResult result) {
3509
3510             int tokenLen = 0;
3511             int tempYear = 0, tempMonth = 0, tempDay = 0, tempDayOfWeek = 0, tempHour = 0, tempMinute = 0, tempSecond = 0;
3512             double tempFraction = 0;
3513             TM tempTimeMark = 0;
3514
3515             char ch = format.GetChar();
3516
3517             switch (ch) {
3518                 case 'y':
3519                     tokenLen = format.GetRepeatCount();
3520                     bool parseResult;
3521                     if (dtfi.HasForceTwoDigitYears) {
3522                         parseResult = ParseDigits(ref str, 1, 4, out tempYear);
3523                     }
3524                     else {
3525                         if (tokenLen <= 2) {
3526                             parseInfo.fUseTwoDigitYear = true;
3527                         }
3528                         parseResult = ParseDigits(ref str, tokenLen, out tempYear);
3529                     }
3530                     if (!parseResult && parseInfo.fCustomNumberParser) {
3531                         parseResult = parseInfo.parseNumberDelegate(ref str, tokenLen, out tempYear);
3532                     }
3533                     if (!parseResult) {
3534                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3535                         return (false);
3536                     }
3537                     if (!CheckNewValue(ref result.Year, tempYear, ch, ref result)) {
3538                         return (false);
3539                     }
3540                     break;
3541                 case 'M':
3542                     tokenLen = format.GetRepeatCount();
3543                     if (tokenLen <= 2) {
3544                         if (!ParseDigits(ref str, tokenLen, out tempMonth)) {
3545                             if (!parseInfo.fCustomNumberParser ||
3546                                 !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth)) {
3547                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3548                                 return (false);
3549                             }
3550                         }
3551                     } else {
3552                         if (tokenLen == 3) {
3553                             if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth)) {
3554                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3555                                 return (false);
3556                             }
3557                         } else {
3558                             if (!MatchMonthName(ref str, dtfi, ref tempMonth)) {
3559                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3560                                 return (false);
3561                             }
3562                         }
3563                         result.flags |= ParseFlags.ParsedMonthName;
3564                     }
3565                     if (!CheckNewValue(ref result.Month, tempMonth, ch, ref result)) {
3566                         return (false);
3567                     }
3568                     break;
3569                 case 'd':
3570                     // Day & Day of week
3571                     tokenLen = format.GetRepeatCount();
3572                     if (tokenLen <= 2) {
3573                         // "d" & "dd"
3574
3575                         if (!ParseDigits(ref str, tokenLen, out tempDay)) {
3576                             if (!parseInfo.fCustomNumberParser ||
3577                                 !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay)) {
3578                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3579                                 return (false);
3580                             }
3581                         }
3582                         if (!CheckNewValue(ref result.Day, tempDay, ch, ref result)) {
3583                             return (false);
3584                         }
3585                     } else {
3586                         if (tokenLen == 3) {
3587                             // "ddd"
3588                             if (!MatchAbbreviatedDayName(ref str, dtfi, ref tempDayOfWeek)) {
3589                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3590                                 return (false);
3591                             }
3592                         } else {
3593                             // "dddd*"
3594                             if (!MatchDayName(ref str, dtfi, ref tempDayOfWeek)) {
3595                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3596                                 return (false);
3597                             }
3598                         }
3599                         if (!CheckNewValue(ref parseInfo.dayOfWeek, tempDayOfWeek, ch, ref result)) {
3600                             return (false);
3601                         }
3602                     }
3603                     break;
3604                 case 'g':
3605                     tokenLen = format.GetRepeatCount();
3606                     // Put the era value in result.era.
3607                     if (!MatchEraName(ref str, dtfi, ref result.era)) {
3608                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3609                         return (false);
3610                     }
3611                     break;
3612                 case 'h':
3613                     parseInfo.fUseHour12 = true;
3614                     tokenLen = format.GetRepeatCount();
3615                     if (!ParseDigits(ref str, (tokenLen < 2? 1 : 2), out tempHour)) {
3616                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3617                         return (false);
3618                     }
3619                     if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result)) {
3620                         return (false);
3621                     }
3622                     break;
3623                 case 'H':
3624                     tokenLen = format.GetRepeatCount();
3625                     if (!ParseDigits(ref str, (tokenLen < 2? 1 : 2), out tempHour)) {
3626                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3627                         return (false);
3628                     }
3629                     if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result)) {
3630                         return (false);
3631                     }
3632                     break;
3633                 case 'm':
3634                     tokenLen = format.GetRepeatCount();
3635                     if (!ParseDigits(ref str, (tokenLen < 2? 1 : 2), out tempMinute)) {
3636                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3637                         return (false);
3638                     }
3639                     if (!CheckNewValue(ref result.Minute, tempMinute, ch, ref result)) {
3640                         return (false);
3641                     }
3642                     break;
3643                 case 's':
3644                     tokenLen = format.GetRepeatCount();
3645                     if (!ParseDigits(ref str, (tokenLen < 2? 1 : 2), out tempSecond)) {
3646                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3647                         return (false);
3648                     }
3649                     if (!CheckNewValue(ref result.Second, tempSecond, ch, ref result)) {
3650                         return (false);
3651                     }
3652                     break;
3653                 case 'f':
3654                 case 'F':
3655                     tokenLen = format.GetRepeatCount();
3656                     if (tokenLen <= DateTimeFormat.MaxSecondsFractionDigits) {
3657                         if (!ParseFractionExact(ref str, tokenLen, ref tempFraction)) {
3658                             if (ch == 'f') {
3659                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3660                                 return (false);
3661                             }
3662                         }
3663                         if (result.fraction < 0) {
3664                             result.fraction = tempFraction;
3665                         } else {
3666                             if (tempFraction != result.fraction) {
3667                                 result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch);
3668                                 return (false);
3669                             }
3670                         }
3671                     } else {
3672                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3673                         return (false);
3674                     }
3675                     break;
3676             case 't':
3677                     // AM/PM designator
3678                     tokenLen = format.GetRepeatCount();
3679                     if (tokenLen == 1) {
3680                         if (!MatchAbbreviatedTimeMark(ref str, dtfi, ref tempTimeMark)) {
3681                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3682                             return (false);
3683                         }
3684                     } else {
3685                         if (!MatchTimeMark(ref str, dtfi, ref tempTimeMark)) {
3686                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3687                             return (false);
3688                         }
3689                     }
3690
3691                     if (parseInfo.timeMark == TM.NotSet) {
3692                         parseInfo.timeMark = tempTimeMark;
3693                     }
3694                     else {
3695                         if (parseInfo.timeMark != tempTimeMark) {
3696                             result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch);
3697                             return (false);
3698                         }
3699                     }
3700                     break;
3701                 case 'z':
3702                     // timezone offset
3703                     tokenLen = format.GetRepeatCount();
3704                     {
3705                         TimeSpan tempTimeZoneOffset = new TimeSpan(0);
3706                         if (!ParseTimeZoneOffset(ref str, tokenLen, ref tempTimeZoneOffset)) {
3707                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3708                             return (false);
3709                         }
3710                         if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset) {
3711                             result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'z');
3712                             return (false);
3713                         }
3714                         result.timeZoneOffset = tempTimeZoneOffset;
3715                         result.flags |= ParseFlags.TimeZoneUsed;
3716                     }
3717                     break;
3718                 case 'Z':
3719                     if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero) {
3720                         result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'Z');
3721                         return (false);
3722                     }
3723
3724                     result.flags |= ParseFlags.TimeZoneUsed;
3725                     result.timeZoneOffset = new TimeSpan(0);
3726                     result.flags |= ParseFlags.TimeZoneUtc;
3727
3728                     // The updating of the indexes is to reflect that ParseExact MatchXXX methods assume that
3729                     // they need to increment the index and Parse GetXXX do not. Since we are calling a Parse
3730                     // method from inside ParseExact we need to adjust this. Long term, we should try to
3731                     // eliminate this discrepancy.
3732                     str.Index++;
3733                     if (!GetTimeZoneName(ref str)) {
3734                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3735                         return false;
3736                     }
3737                     str.Index--;
3738                     break;
3739                 case 'K':
3740                     // This should parse either as a blank, the 'Z' character or a local offset like "-07:00"
3741                     if (str.Match('Z')) {
3742                         if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero) {
3743                             result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K');
3744                             return (false);
3745                         }
3746
3747                         result.flags |= ParseFlags.TimeZoneUsed;
3748                         result.timeZoneOffset = new TimeSpan(0);
3749                         result.flags |= ParseFlags.TimeZoneUtc;
3750                     }
3751                     else if (str.Match('+') || str.Match('-')) {
3752                         str.Index--; // Put the character back for the parser
3753                         TimeSpan tempTimeZoneOffset = new TimeSpan(0);
3754                         if (!ParseTimeZoneOffset(ref str, 3, ref tempTimeZoneOffset)) {
3755                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3756                             return (false);
3757                         }
3758                         if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset) {
3759                             result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K');
3760                             return (false);
3761                         }
3762                         result.timeZoneOffset = tempTimeZoneOffset;
3763                         result.flags |= ParseFlags.TimeZoneUsed;
3764                     }
3765                     // Otherwise it is unspecified and we consume no characters
3766                     break;
3767                 case ':':
3768                     // We match the separator in time pattern with the character in the time string if both equal to ':' or the date separator is matching the characters in the date string
3769                     // We have to exclude the case when the time separator is more than one character and starts with ':' something like "::" for instance.
3770                     if (((dtfi.TimeSeparator.Length > 1 && dtfi.TimeSeparator[0] == ':') || !str.Match(':')) && 
3771                         !str.Match(dtfi.TimeSeparator)) {
3772                         // A time separator is expected.
3773                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3774                         return false;
3775                     }
3776                     break;
3777                 case '/':
3778                     // We match the separator in date pattern with the character in the date string if both equal to '/' or the date separator is matching the characters in the date string
3779                     // We have to exclude the case when the date separator is more than one character and starts with '/' something like "//" for instance.
3780                     if (((dtfi.DateSeparator.Length > 1 && dtfi.DateSeparator[0] == '/') || !str.Match('/')) && 
3781                         !str.Match(dtfi.DateSeparator))
3782                     {
3783                         // A date separator is expected.
3784                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3785                         return false;
3786                     }
3787                     break;
3788                 case '\"':
3789                 case '\'':
3790                     StringBuilder enquotedString = new StringBuilder();
3791                     // Use ParseQuoteString so that we can handle escape characters within the quoted string.
3792                     if (!TryParseQuoteString(format.Value, format.Index, enquotedString, out tokenLen)) {
3793                         result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadQuote", ch);
3794                         return (false);
3795                     }
3796                     format.Index += tokenLen - 1;
3797
3798                     // Some cultures uses space in the quoted string.  E.g. Spanish has long date format as:
3799                     // "dddd, dd' de 'MMMM' de 'yyyy".  When inner spaces flag is set, we should skip whitespaces if there is space
3800                     // in the quoted string.
3801                     String quotedStr = enquotedString.ToString();
3802
3803                     for (int i = 0; i < quotedStr.Length; i++) {
3804                         if (quotedStr[i] == ' ' && parseInfo.fAllowInnerWhite) {
3805                             str.SkipWhiteSpaces();
3806                         } else if (!str.Match(quotedStr[i])) {
3807                             // Can not find the matching quoted string.
3808                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3809                             return false;
3810                         }
3811                     }
3812
3813                     // The "r" and "u" formats incorrectly quoted 'GMT' and 'Z', respectively.  We cannot
3814                     // correct this mistake for DateTime.ParseExact for compatibility reasons, but we can 
3815                     // fix it for DateTimeOffset.ParseExact as DateTimeOffset has not been publically released
3816                     // with this issue.
3817                     if ((result.flags & ParseFlags.CaptureOffset) != 0) {
3818                         if ((result.flags & ParseFlags.Rfc1123Pattern) != 0 && quotedStr == GMTName) {
3819                             result.flags |= ParseFlags.TimeZoneUsed;
3820                             result.timeZoneOffset = TimeSpan.Zero;
3821                         }
3822                         else if ((result.flags & ParseFlags.UtcSortPattern) != 0 && quotedStr == ZuluName) {
3823                             result.flags |= ParseFlags.TimeZoneUsed;
3824                             result.timeZoneOffset = TimeSpan.Zero;
3825                         }
3826                     }
3827
3828                     break;
3829                 case '%':
3830                     // Skip this so we can get to the next pattern character.
3831                     // Used in case like "%d", "%y"
3832
3833                     // Make sure the next character is not a '%' again.
3834                     if (format.Index >= format.Value.Length - 1 || format.Value[format.Index + 1] == '%') {
3835                         result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
3836                         return false;
3837                     }
3838                     break;
3839                 case '\\':
3840                     // Escape character. For example, "\d".
3841                     // Get the next character in format, and see if we can
3842                     // find a match in str.
3843                     if (format.GetNext()) {
3844                         if (!str.Match(format.GetChar())) {
3845                             // Can not find a match for the escaped character.
3846                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3847                             return false;
3848                         }
3849                     } else {
3850                         result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
3851                         return false;
3852                     }
3853                     break;
3854                 case '.': 
3855                     if (!str.Match(ch)) {
3856                         if (format.GetNext()) {
3857                             // If we encounter the pattern ".F", and the dot is not present, it is an optional
3858                             // second fraction and we can skip this format.
3859                             if (format.Match('F')) {
3860                                 format.GetRepeatCount();
3861                                 break;
3862                             }
3863                         }
3864                         result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3865                         return false;
3866                     }
3867                     break;
3868                 default:
3869                     if (ch == ' ') {
3870                         if (parseInfo.fAllowInnerWhite) {
3871                             // Skip whitespaces if AllowInnerWhite.
3872                             // Do nothing here.
3873                         } else {
3874                             if (!str.Match(ch)) {
3875                                 // If the space does not match, and trailing space is allowed, we do
3876                                 // one more step to see if the next format character can lead to
3877                                 // successful parsing.
3878                                 // This is used to deal with special case that a empty string can match
3879                                 // a specific pattern.
3880                                 // The example here is af-ZA, which has a time format like "hh:mm:ss tt".  However,
3881                                 // its AM symbol is "" (empty string).  If fAllowTrailingWhite is used, and time is in
3882                                 // the AM, we will trim the whitespaces at the end, which will lead to a failure
3883                                 // when we are trying to match the space before "tt".
3884                                 if (parseInfo.fAllowTrailingWhite) {
3885                                     if (format.GetNext()) {
3886                                         if (ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result)) {
3887                                             return (true);
3888                                         }
3889                                     }
3890                                 }
3891                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3892                                 return false;
3893                             }
3894                             // Found a macth.
3895                         }
3896                     } else {
3897                         if (format.MatchSpecifiedWord(GMTName)) {
3898                             format.Index += (GMTName.Length - 1);
3899                             // Found GMT string in format.  This means the DateTime string
3900                             // is in GMT timezone.
3901                             result.flags |= ParseFlags.TimeZoneUsed;
3902                             result.timeZoneOffset = TimeSpan.Zero;
3903                             if (!str.Match(GMTName)) {
3904                                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3905                                 return false;
3906                             }
3907                         } else if (!str.Match(ch)) {
3908                             // ch is expected.
3909                             result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
3910                             return false;
3911                         }
3912                     }
3913                     break;
3914             } // switch
3915             return (true);
3916         }
3917
3918         //
3919         // The pos should point to a quote character. This method will
3920         // get the string enclosed by the quote character.
3921         //
3922         internal static bool TryParseQuoteString(String format, int pos, StringBuilder result, out int returnValue) {
3923             //
3924             // NOTE : pos will be the index of the quote character in the 'format' string.
3925             //
3926             returnValue = 0;
3927             int formatLen = format.Length;
3928             int beginPos = pos;
3929             char quoteChar = format[pos++]; // Get the character used to quote the following string.
3930
3931             bool foundQuote = false;
3932             while (pos < formatLen) {
3933                 char ch = format[pos++];
3934                 if (ch == quoteChar) {
3935                     foundQuote = true;
3936                     break;
3937                 }
3938                 else if (ch == '\\') {
3939                     // The following are used to support escaped character.
3940                     // Escaped character is also supported in the quoted string.
3941                     // Therefore, someone can use a format like "'minute:' mm\"" to display:
3942                     //  minute: 45"
3943                     // because the second double quote is escaped.
3944                     if (pos < formatLen) {
3945                         result.Append(format[pos++]);
3946                     } else {
3947                         //
3948                         // This means that '\' is at the end of the formatting string.
3949                         //
3950                         return false;
3951                     }
3952                 } else {
3953                     result.Append(ch);
3954                 }
3955             }
3956
3957             if (!foundQuote) {
3958                 // Here we can't find the matching quote.
3959                 return false;
3960             }
3961
3962             //
3963             // Return the character count including the begin/end quote characters and enclosed string.
3964             //
3965             returnValue = (pos - beginPos);
3966             return true;
3967         }
3968
3969
3970
3971
3972         /*=================================DoStrictParse==================================
3973         **Action: Do DateTime parsing using the format in formatParam.
3974         **Returns: The parsed DateTime.
3975         **Arguments:
3976         **Exceptions:
3977         **
3978         **Notes:
3979         **  When the following general formats are used, InvariantInfo is used in dtfi:
3980         **      'r', 'R', 's'.
3981         **  When the following general formats are used, the time is assumed to be in Universal time.
3982         **
3983         **Limitations:
3984         **  Only GregarianCalendar is supported for now.
3985         **  Only support GMT timezone.
3986         ==============================================================================*/
3987
3988         private static bool DoStrictParse(
3989             String s,
3990             String formatParam,
3991             DateTimeStyles styles,
3992             DateTimeFormatInfo dtfi,
3993             ref DateTimeResult result) {
3994
3995
3996
3997             ParsingInfo parseInfo = new ParsingInfo();
3998             parseInfo.Init();
3999
4000             parseInfo.calendar = dtfi.Calendar;
4001             parseInfo.fAllowInnerWhite = ((styles & DateTimeStyles.AllowInnerWhite) != 0);
4002             parseInfo.fAllowTrailingWhite = ((styles & DateTimeStyles.AllowTrailingWhite) != 0);
4003
4004 #if !MONO
4005             // We need the original values of the following two below.
4006             String originalFormat = formatParam;
4007 #endif
4008
4009             if (formatParam.Length == 1) {
4010                 if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U') {
4011                     // The 'U' format is not allowed for DateTimeOffset
4012                     result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
4013                     return false;
4014                 }
4015                 formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result);
4016             }
4017             
4018             bool bTimeOnly = false;
4019             result.calendar = parseInfo.calendar;
4020
4021             if (parseInfo.calendar.ID == Calendar.CAL_HEBREW) {
4022                 parseInfo.parseNumberDelegate = m_hebrewNumberParser;
4023                 parseInfo.fCustomNumberParser = true;
4024             }
4025
4026             // Reset these values to negative one so that we could throw exception
4027             // if we have parsed every item twice.
4028             result.Hour = result.Minute = result.Second = -1;
4029
4030             __DTString format = new __DTString(formatParam, dtfi, false);
4031             __DTString str = new __DTString(s, dtfi, false);
4032
4033             if (parseInfo.fAllowTrailingWhite) {
4034                 // Trim trailing spaces if AllowTrailingWhite.
4035                 format.TrimTail();
4036                 format.RemoveTrailingInQuoteSpaces();
4037                 str.TrimTail();
4038             }
4039
4040             if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) {
4041                 format.SkipWhiteSpaces();
4042                 format.RemoveLeadingInQuoteSpaces();
4043                 str.SkipWhiteSpaces();
4044             }
4045
4046             //
4047             // Scan every character in format and match the pattern in str.
4048             //
4049             while (format.GetNext()) {
4050                 // We trim inner spaces here, so that we will not eat trailing spaces when
4051                 // AllowTrailingWhite is not used.
4052                 if (parseInfo.fAllowInnerWhite) {
4053                     str.SkipWhiteSpaces();
4054                 }
4055                 if (!ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result)) {
4056                    return (false);
4057                 }
4058             }                
4059
4060             if (str.Index < str.Value.Length - 1) {
4061                 // There are still remaining character in str.
4062                 result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
4063                 return false;
4064             }
4065
4066             if (parseInfo.fUseTwoDigitYear && ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) == 0)) {
4067                 // A two digit year value is expected. Check if the parsed year value is valid.
4068                 if (result.Year >= 100) {
4069                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
4070                     return false;
4071                 }
4072                 try {
4073                     result.Year = parseInfo.calendar.ToFourDigitYear(result.Year);
4074                 }
4075                 catch (ArgumentOutOfRangeException e) {
4076                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
4077                     return false;
4078                 }
4079             }
4080
4081             if (parseInfo.fUseHour12) {
4082                 if (parseInfo.timeMark == TM.NotSet) {
4083                     // hh is used, but no AM/PM designator is specified.
4084                     // Assume the time is AM.
4085                     // Don't throw exceptions in here becasue it is very confusing for the caller.
4086                     // I always got confused myself when I use "hh:mm:ss" to parse a time string,
4087                     // and ParseExact() throws on me (because I didn't use the 24-hour clock 'HH').
4088                     parseInfo.timeMark = TM.AM;
4089                 }
4090                 if (result.Hour > 12) {
4091                     // AM/PM is used, but the value for HH is too big.
4092                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
4093                     return false;
4094                 }
4095                 if (parseInfo.timeMark == TM.AM) {
4096                     if (result.Hour == 12) {
4097                         result.Hour = 0;
4098                     }
4099                 } else {
4100                     result.Hour = (result.Hour == 12) ? 12 : result.Hour + 12;
4101                 }
4102             }
4103             else
4104             {
4105                  // Military (24-hour time) mode
4106                  //
4107                  // AM cannot be set with a 24-hour time like 17:15.
4108                  // PM cannot be set with a 24-hour time like 03:15.
4109                  if (  (parseInfo.timeMark == TM.AM && result.Hour >= 12)
4110                      ||(parseInfo.timeMark == TM.PM && result.Hour <  12)) {
4111                     result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
4112                     return false;
4113                 }
4114             }
4115
4116
4117             // Check if the parased string only contains hour/minute/second values.
4118             bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
4119             if (!CheckDefaultDateTime(ref result, ref parseInfo.calendar, styles)) {
4120                 return false;
4121             }
4122
4123             if (!bTimeOnly && dtfi.HasYearMonthAdjustment) {
4124                 if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0))) {
4125                     result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
4126                     return false;
4127                 }
4128             }
4129             if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day,
4130                     result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate)) {
4131                 result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
4132                 return false;
4133             }
4134             if (result.fraction > 0) {
4135                 result.parsedDate = result.parsedDate.AddTicks((long)Math.Round(result.fraction * Calendar.TicksPerSecond));
4136             }
4137
4138             //
4139             // We have to check day of week before we adjust to the time zone.
4140             // It is because the value of day of week may change after adjusting
4141             // to the time zone.
4142             //
4143             if (parseInfo.dayOfWeek != -1) {
4144                 //
4145                 // Check if day of week is correct.
4146                 //
4147                 if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate)) {
4148                     result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
4149                     return false;
4150                 }
4151             }
4152
4153
4154             if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly)) {
4155                 return false;
4156             }
4157             return true;
4158         }
4159
4160         private static Exception GetDateTimeParseException(ref DateTimeResult result) {
4161             switch (result.failure) {
4162                 case ParseFailureKind.ArgumentNull:
4163                     return new ArgumentNullException(result.failureArgumentName, Environment.GetResourceString(result.failureMessageID));
4164                 case ParseFailureKind.Format:
4165                     return new FormatException(Environment.GetResourceString(result.failureMessageID));
4166                 case ParseFailureKind.FormatWithParameter:
4167                     return new FormatException(Environment.GetResourceString(result.failureMessageID, result.failureMessageFormatArgument));
4168                 case ParseFailureKind.FormatBadDateTimeCalendar:
4169                     return new FormatException(Environment.GetResourceString(result.failureMessageID, result.calendar));
4170                 default:
4171                     Contract.Assert(false, "Unkown DateTimeParseFailure: " + result);
4172                     return null;
4173
4174             }
4175         }
4176
4177         // <
4178
4179
4180
4181
4182
4183
4184
4185
4186         [Pure]
4187         [Conditional("_LOGGING")]
4188         [ResourceExposure(ResourceScope.None)]
4189         internal static void LexTraceExit(string message, DS dps) {
4190 #if _LOGGING
4191             if (!_tracingEnabled)
4192                 return;
4193             BCLDebug.Trace("DATETIME", "[DATETIME] Lex return {0}, DS.{1}", message, dps);
4194 #endif // _LOGGING
4195         }
4196         [Pure]
4197         [Conditional("_LOGGING")]
4198         [ResourceExposure(ResourceScope.None)]
4199         internal static void PTSTraceExit(DS dps, bool passed) {
4200 #if _LOGGING
4201             if (!_tracingEnabled)
4202                 return;
4203             BCLDebug.Trace("DATETIME", "[DATETIME] ProcessTerminalState {0} @ DS.{1}", passed ? "passed" : "failed", dps);
4204 #endif // _LOGGING
4205         }
4206         [Pure]
4207         [Conditional("_LOGGING")]
4208         [ResourceExposure(ResourceScope.None)]
4209         internal static void TPTraceExit(string message, DS dps) {
4210 #if _LOGGING
4211             if (!_tracingEnabled)
4212                 return;
4213             BCLDebug.Trace("DATETIME", "[DATETIME] TryParse return {0}, DS.{1}", message, dps);
4214 #endif // _LOGGING
4215         }
4216         [Pure]
4217         [Conditional("_LOGGING")]
4218         [ResourceExposure(ResourceScope.None)]
4219         internal static void DTFITrace(DateTimeFormatInfo dtfi) {
4220 #if _LOGGING
4221             if (!_tracingEnabled)
4222                 return;
4223
4224             BCLDebug.Trace("DATETIME", "[DATETIME] DateTimeFormatInfo Properties");
4225             BCLDebug.Trace("DATETIME", " NativeCalendarName {0}", Hex(dtfi.NativeCalendarName));
4226             BCLDebug.Trace("DATETIME", "       AMDesignator {0}", Hex(dtfi.AMDesignator));
4227             BCLDebug.Trace("DATETIME", "       PMDesignator {0}", Hex(dtfi.PMDesignator));
4228             BCLDebug.Trace("DATETIME", "      TimeSeparator {0}", Hex(dtfi.TimeSeparator));
4229             BCLDebug.Trace("DATETIME", "      AbbrvDayNames {0}", Hex(dtfi.AbbreviatedDayNames));
4230             BCLDebug.Trace("DATETIME", "   ShortestDayNames {0}", Hex(dtfi.ShortestDayNames));
4231             BCLDebug.Trace("DATETIME", "           DayNames {0}", Hex(dtfi.DayNames));
4232             BCLDebug.Trace("DATETIME", "    AbbrvMonthNames {0}", Hex(dtfi.AbbreviatedMonthNames));
4233             BCLDebug.Trace("DATETIME", "         MonthNames {0}", Hex(dtfi.MonthNames));
4234             BCLDebug.Trace("DATETIME", " AbbrvMonthGenNames {0}", Hex(dtfi.AbbreviatedMonthGenitiveNames));
4235             BCLDebug.Trace("DATETIME", "      MonthGenNames {0}", Hex(dtfi.MonthGenitiveNames));
4236 #endif // _LOGGING
4237         }
4238 #if _LOGGING
4239         [Pure]
4240         [ResourceExposure(ResourceScope.None)]
4241         // return a string in the form: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
4242         internal static string Hex(string[] strs) {
4243             if (strs == null || strs.Length == 0)
4244                 return String.Empty;
4245             if (strs.Length == 1)
4246                 return Hex(strs[0]);
4247          
4248             int curLineLength  =  0;
4249             int maxLineLength  = 55; 
4250             int newLinePadding = 20;
4251
4252
4253             //invariant: strs.Length >= 2
4254             StringBuilder buffer = new StringBuilder();           
4255             buffer.Append(Hex(strs[0]));
4256             curLineLength = buffer.Length;
4257             String s;
4258
4259             for (int i = 1; i < strs.Length-1; i++) {
4260                 s = Hex(strs[i]);
4261
4262                 if (s.Length > maxLineLength || (curLineLength + s.Length + 2)  > maxLineLength) {
4263                     buffer.Append(',');
4264                     buffer.Append(Environment.NewLine);
4265                     buffer.Append(' ', newLinePadding);
4266                     curLineLength = 0;
4267                 }
4268                 else {
4269                     buffer.Append(", ");
4270                     curLineLength += 2;
4271                 }
4272                 buffer.Append(s);
4273                 curLineLength += s.Length;
4274             }
4275
4276             buffer.Append(',');
4277             s = Hex(strs[strs.Length-1]);
4278             if (s.Length > maxLineLength || (curLineLength + s.Length + 6)  > maxLineLength) {
4279                 buffer.Append(Environment.NewLine);
4280                 buffer.Append(' ', newLinePadding);
4281             }
4282             else {
4283                 buffer.Append(' ');
4284             }
4285             buffer.Append(s);
4286             return buffer.ToString();
4287         }
4288         [Pure]
4289         [ResourceExposure(ResourceScope.None)]
4290         // return a string in the form: "Sun"
4291         internal static string Hex(string str) {
4292             StringBuilder buffer = new StringBuilder();
4293             buffer.Append("\"");
4294             for (int i = 0; i < str.Length; i++) {
4295                 if (str[i] <= '\x007f')
4296                     buffer.Append(str[i]);
4297                 else
4298                     buffer.Append("\\u" + ((int)str[i]).ToString("x4", CultureInfo.InvariantCulture));
4299             }
4300             buffer.Append("\"");
4301             return buffer.ToString();
4302         }
4303         [Pure]
4304         [ResourceExposure(ResourceScope.None)]
4305         // return an unicode escaped string form of char c
4306         internal static String Hex(char c) {
4307             if (c <= '\x007f')
4308                 return c.ToString(CultureInfo.InvariantCulture);
4309             else
4310                 return "\\u" + ((int)c).ToString("x4", CultureInfo.InvariantCulture);
4311         }
4312
4313         internal static bool _tracingEnabled = BCLDebug.CheckEnabled("DATETIME");
4314 #endif // _LOGGING
4315    }
4316
4317
4318     //
4319     // This is a string parsing helper which wraps a String object.
4320     // It has a Index property which tracks
4321     // the current parsing pointer of the string.
4322     //
4323     internal
4324     struct __DTString
4325     {
4326
4327         //
4328         // Value propery: stores the real string to be parsed.
4329         //
4330         internal String Value;
4331
4332         //
4333         // Index property: points to the character that we are currently parsing.
4334         //
4335         internal int Index;
4336
4337         // The length of Value string.
4338         internal int len;
4339
4340         // The current chracter to be looked at.
4341         internal char m_current;
4342
4343         private CompareInfo m_info;
4344         // Flag to indicate if we encouter an digit, we should check for token or not.
4345         // In some cultures, such as mn-MN, it uses "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440" in month names.
4346         private bool m_checkDigitToken;
4347         
4348         internal __DTString(String str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this (str, dtfi)
4349         {
4350             m_checkDigitToken = checkDigitToken;
4351         }
4352
4353         internal __DTString(String str, DateTimeFormatInfo dtfi)
4354         {
4355             Index = -1;
4356             Value = str;
4357             len = Value.Length;
4358
4359             m_current = '\0';            
4360             if (dtfi != null)
4361             {
4362                 m_info = dtfi.CompareInfo;
4363                 m_checkDigitToken = ((dtfi.FormatFlags & DateTimeFormatFlags.UseDigitPrefixInTokens) != 0);
4364             } else
4365             {
4366                 m_info = Thread.CurrentThread.CurrentCulture.CompareInfo;
4367                 m_checkDigitToken = false;
4368             }
4369         }
4370
4371         internal CompareInfo CompareInfo
4372         {
4373             get { return m_info; }
4374         }
4375
4376         //
4377         // Advance the Index.
4378         // Return true if Index is NOT at the end of the string.
4379         //
4380         // Typical usage:
4381         // while (str.GetNext())
4382         // {
4383         //     char ch = str.GetChar()
4384         // }
4385         internal bool GetNext() {
4386             Index++;
4387             if (Index < len) {
4388                 m_current = Value[Index];
4389                 return (true);
4390             }
4391             return (false);
4392         }
4393
4394         internal bool AtEnd()
4395         {
4396             return Index < len ? false : true;
4397         }
4398
4399         internal bool Advance(int count) {
4400             Contract.Assert(Index + count <= len, "__DTString::Advance: Index + count <= len");
4401             Index += count;
4402             if (Index < len) {
4403                 m_current = Value[Index];
4404                 return (true);
4405             }
4406             return (false);
4407         }
4408
4409
4410         // Used by DateTime.Parse() to get the next token.
4411         [System.Security.SecurityCritical]  // auto-generated
4412         internal void GetRegularToken(out TokenType tokenType, out int tokenValue, DateTimeFormatInfo dtfi) {
4413             tokenValue = 0;
4414             if (Index >= len) {
4415                 tokenType = TokenType.EndOfString;
4416                 return;
4417             }
4418
4419             tokenType = TokenType.UnknownToken;
4420
4421 Start:
4422             if (DateTimeParse.IsDigit(m_current)) {
4423                 // This is a digit.
4424                 tokenValue = m_current - '0';
4425                 int value;
4426                 int start = Index;
4427
4428                 //
4429                 // Collect other digits.
4430                 //
4431                 while (++Index < len)
4432                 {
4433                     m_current = Value[Index];
4434                     value = m_current - '0';
4435                     if (value >= 0 && value <= 9) {
4436                         tokenValue = tokenValue * 10 + value;
4437                     } else {
4438                         break;
4439                     }
4440                 }
4441                 if (Index - start > DateTimeParse.MaxDateTimeNumberDigits) {
4442                     tokenType = TokenType.NumberToken;
4443                     tokenValue = -1;
4444                 } else if (Index - start < 3) {
4445                     tokenType = TokenType.NumberToken;
4446                 } else {
4447                     // If there are more than 3 digits, assume that it's a year value.
4448                     tokenType = TokenType.YearNumberToken;
4449                 }
4450                 if (m_checkDigitToken)
4451                 {
4452                     int save = Index;
4453                     char saveCh = m_current;
4454                     // Re-scan using the staring Index to see if this is a token.
4455                     Index = start;  // To include the first digit.
4456                     m_current = Value[Index];
4457                     TokenType tempType;
4458                     int tempValue;
4459                     // This DTFI has tokens starting with digits.
4460                     // E.g. mn-MN has month name like "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440"
4461                     if (dtfi.Tokenize(TokenType.RegularTokenMask, out tempType, out tempValue, ref this))
4462                     {
4463                         tokenType = tempType;
4464                         tokenValue = tempValue;
4465                         // This is a token, so the Index has been advanced propertly in DTFI.Tokenizer().
4466                     } else
4467                     {
4468                         // Use the number token value.
4469                         // Restore the index.
4470                         Index = save;
4471                         m_current = saveCh;
4472                     }
4473                     
4474                 } 
4475                 
4476             } else if (Char.IsWhiteSpace( m_current)) {
4477                 // Just skip to the next character.
4478                 while (++Index < len) {
4479                     m_current = Value[Index];
4480                     if (!(Char.IsWhiteSpace(m_current))) {
4481                         goto Start;
4482                     }
4483                 }
4484                 // We have reached the end of string.
4485                 tokenType = TokenType.EndOfString;
4486             } else {
4487                 dtfi.Tokenize(TokenType.RegularTokenMask, out tokenType, out tokenValue, ref this);
4488             }
4489         }
4490
4491         [System.Security.SecurityCritical]  // auto-generated
4492         internal TokenType GetSeparatorToken(DateTimeFormatInfo dtfi, out int indexBeforeSeparator, out char charBeforeSeparator) {
4493             indexBeforeSeparator = Index;
4494             charBeforeSeparator = m_current;
4495             TokenType tokenType;
4496             if (!SkipWhiteSpaceCurrent()) {
4497                 // Reach the end of the string.
4498                 return (TokenType.SEP_End);
4499             }
4500             if (!DateTimeParse.IsDigit(m_current)) {
4501                 // Not a digit.  Tokenize it.
4502                 int tokenValue;
4503                 bool found = dtfi.Tokenize(TokenType.SeparatorTokenMask, out tokenType, out tokenValue, ref this);
4504                 if (!found) {
4505                     tokenType = TokenType.SEP_Space;
4506                 }
4507             } else {
4508                 // Do nothing here.  If we see a number, it will not be a separator. There is no need wasting time trying to find the
4509                 // separator token.
4510                 tokenType = TokenType.SEP_Space;
4511             }
4512             return (tokenType);
4513         }
4514
4515         internal bool MatchSpecifiedWord(String target) {
4516             return MatchSpecifiedWord(target, target.Length + Index);
4517         }
4518
4519         internal bool MatchSpecifiedWord(String target, int endIndex) {
4520             int count = endIndex - Index;
4521
4522             if (count != target.Length) {
4523                 return false;
4524             }
4525
4526             if (Index + count > len) {
4527                 return false;
4528             }
4529
4530             return (m_info.Compare(Value, Index, count, target, 0, count, CompareOptions.IgnoreCase)==0);
4531         }
4532
4533         private static Char[] WhiteSpaceChecks = new Char[] { ' ', '\u00A0' };
4534
4535         internal bool MatchSpecifiedWords(String target, bool checkWordBoundary, ref int matchLength) {
4536             int valueRemaining = Value.Length - Index;
4537             matchLength = target.Length;
4538
4539             if (matchLength > valueRemaining || m_info.Compare(Value, Index, matchLength, target, 0, matchLength, CompareOptions.IgnoreCase) !=0) {
4540                 // Check word by word
4541                 int targetPosition = 0;                 // Where we are in the target string
4542                 int thisPosition = Index;         // Where we are in this string
4543                 int wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition);
4544                 if (wsIndex == -1) {
4545                     return false;
4546                 }
4547                 do {
4548                     int segmentLength = wsIndex - targetPosition;
4549                     if (thisPosition >= Value.Length - segmentLength) { // Subtraction to prevent overflow.
4550                         return false;
4551                     }
4552                     if (segmentLength == 0) {
4553                         // If segmentLength == 0, it means that we have leading space in the target string.
4554                         // In that case, skip the leading spaces in the target and this string.
4555                         matchLength--;
4556                     } else {
4557                         // Make sure we also have whitespace in the input string
4558                         if (!Char.IsWhiteSpace(Value[thisPosition + segmentLength])) {
4559                             return false;
4560                         }
4561                         if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) !=0) {
4562                             return false;
4563                         }
4564                         // Advance the input string
4565                         thisPosition = thisPosition + segmentLength + 1;
4566                     }
4567                     // Advance our target string
4568                     targetPosition = wsIndex + 1;
4569
4570
4571                     // Skip past multiple whitespace
4572                     while (thisPosition < Value.Length && Char.IsWhiteSpace(Value[thisPosition])) {
4573                         thisPosition++;
4574                         matchLength++;
4575                     }
4576                 } while ((wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition)) >= 0);
4577                 // now check the last segment;
4578                 if (targetPosition < target.Length) {
4579                     int segmentLength = target.Length - targetPosition;
4580                     if (thisPosition > Value.Length - segmentLength) {
4581                         return false;
4582                     }
4583                     if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) !=0) {
4584                         return false;
4585                     }
4586                 }
4587             }
4588
4589             if (checkWordBoundary) {
4590                 int nextCharIndex = Index + matchLength;
4591                 if (nextCharIndex < Value.Length) {
4592                     if (Char.IsLetter(Value[nextCharIndex])) {
4593                         return (false);
4594                     }
4595                 }
4596             }
4597             return (true);
4598         }
4599
4600         //
4601         // Check to see if the string starting from Index is a prefix of
4602         // str.
4603         // If a match is found, true value is returned and Index is updated to the next character to be parsed.
4604         // Otherwise, Index is unchanged.
4605         //
4606         internal bool Match(String str) {
4607             if (++Index >= len) {
4608                 return (false);
4609             }
4610
4611             if (str.Length > (Value.Length - Index)) {
4612                 return false;
4613             }
4614
4615             if (m_info.Compare(Value, Index, str.Length, str, 0, str.Length, CompareOptions.Ordinal)==0) {
4616                 // Update the Index to the end of the matching string.
4617                 // So the following GetNext()/Match() opeartion will get
4618                 // the next character to be parsed.
4619                 Index += (str.Length - 1);
4620                 return (true);
4621             }
4622             return (false);
4623         }
4624
4625         internal bool Match(char ch) {
4626             if (++Index >= len) {
4627                 return (false);
4628             }
4629             if (Value[Index] == ch) {
4630                 m_current = ch;
4631                 return (true);
4632             }
4633             Index--;
4634             return (false);
4635         }
4636
4637         //
4638         //  Actions: From the current position, try matching the longest word in the specified string array.
4639         //      E.g. words[] = {"AB", "ABC", "ABCD"}, if the current position points to a substring like "ABC DEF",
4640         //          MatchLongestWords(words, ref MaxMatchStrLen) will return 1 (the index), and maxMatchLen will be 3.
4641         //  Returns:
4642         //      The index that contains the longest word to match
4643         //  Arguments:
4644         //      words   The string array that contains words to search.
4645         //      maxMatchStrLen  [in/out] the initailized maximum length.  This parameter can be used to
4646         //          find the longest match in two string arrays.
4647         //
4648         internal int MatchLongestWords(String[] words, ref int maxMatchStrLen) {
4649             int result = -1;
4650             for (int i = 0; i < words.Length; i++) {
4651                 String word = words[i];
4652                 int matchLength = word.Length;
4653                 if (MatchSpecifiedWords(word, false, ref matchLength)) {
4654                     if (matchLength > maxMatchStrLen) {
4655                         maxMatchStrLen = matchLength;
4656                         result = i;
4657                     }
4658                 }
4659             }
4660
4661             return (result);
4662         }
4663
4664         //
4665         // Get the number of repeat character after the current character.
4666         // For a string "hh:mm:ss" at Index of 3. GetRepeatCount() = 2, and Index
4667         // will point to the second ':'.
4668         //
4669         internal int GetRepeatCount() {
4670             char repeatChar = Value[Index];
4671             int pos = Index + 1;
4672             while ((pos < len) && (Value[pos] == repeatChar)) {
4673                 pos++;
4674             }
4675             int repeatCount = (pos - Index);
4676             // Update the Index to the end of the repeated characters.
4677             // So the following GetNext() opeartion will get
4678             // the next character to be parsed.
4679             Index = pos - 1;
4680             return (repeatCount);
4681         }
4682
4683         // Return false when end of string is encountered or a non-digit character is found.
4684         internal bool GetNextDigit() {
4685             if (++Index >= len) {
4686                 return (false);
4687             }
4688             return (DateTimeParse.IsDigit(Value[Index]));
4689         }
4690
4691         //
4692         // Get the current character.
4693         //
4694         internal char GetChar() {
4695             Contract.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
4696             return (Value[Index]);
4697         }
4698
4699         //
4700         // Convert the current character to a digit, and return it.
4701         //
4702         internal int GetDigit() {
4703             Contract.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
4704             Contract.Assert(DateTimeParse.IsDigit(Value[Index]), "IsDigit(Value[Index])");
4705             return (Value[Index] - '0');
4706         }
4707
4708         //
4709         // Eat White Space ahead of the current position
4710         //
4711         // Return false if end of string is encountered.
4712         //
4713         internal void SkipWhiteSpaces()
4714         {
4715             // Look ahead to see if the next character
4716             // is a whitespace.
4717             while (Index+1 < len)
4718             {
4719                 char ch = Value[Index+1];
4720                 if (!Char.IsWhiteSpace(ch)) {
4721                     return;
4722                 }
4723                 Index++;
4724             }
4725             return;
4726         }
4727
4728         //
4729         // Skip white spaces from the current position
4730         //
4731         // Return false if end of string is encountered.
4732         //
4733         internal bool SkipWhiteSpaceCurrent()
4734         {
4735             if (Index >= len) {
4736                 return (false);
4737             }
4738
4739             if (!Char.IsWhiteSpace(m_current))
4740             {
4741                 return (true);
4742             }
4743
4744             while (++Index < len)
4745             {
4746                 m_current = Value[Index];
4747                 if (!Char.IsWhiteSpace(m_current))
4748                 {
4749                     return (true);
4750                 }
4751                 // Nothing here.
4752             }
4753             return (false);
4754         }
4755
4756         internal void TrimTail() {
4757             int i = len - 1;
4758             while (i >= 0 && Char.IsWhiteSpace(Value[i])) {
4759                 i--;
4760             }
4761             Value = Value.Substring(0, i + 1);
4762             len = Value.Length;
4763         }
4764
4765         // Trim the trailing spaces within a quoted string.
4766         // Call this after TrimTail() is done.
4767         internal void RemoveTrailingInQuoteSpaces() {
4768             int i = len - 1;
4769             if (i <= 1) {
4770                 return;
4771             }
4772             char ch = Value[i];
4773             // Check if the last character is a quote.
4774             if (ch == '\'' || ch == '\"') {
4775                 if (Char.IsWhiteSpace(Value[i-1])) {
4776                     i--;
4777                     while (i >= 1 && Char.IsWhiteSpace(Value[i-1])) {
4778                         i--;
4779                     }
4780                     Value = Value.Remove(i, Value.Length - 1 - i);
4781                     len = Value.Length;
4782                 }
4783             }
4784         }
4785
4786         // Trim the leading spaces within a quoted string.
4787         // Call this after the leading spaces before quoted string are trimmed.
4788         internal void RemoveLeadingInQuoteSpaces() {
4789             if (len <= 2) {
4790                 return;
4791             }
4792             int i = 0;
4793             char ch = Value[i];
4794             // Check if the last character is a quote.
4795             if (ch == '\'' || ch == '\"') {
4796                 while ((i + 1) < len && Char.IsWhiteSpace(Value[i+1])) {
4797                     i++;
4798                 }
4799                 if (i != 0) {
4800                     Value = Value.Remove(1, i);
4801                     len = Value.Length;
4802                 }
4803             }
4804         }
4805
4806         internal DTSubString GetSubString() {
4807             DTSubString sub = new DTSubString();
4808             sub.index = Index;
4809             sub.s = Value;
4810             while (Index + sub.length < len) {
4811                 DTSubStringType currentType;
4812                 Char ch = Value[Index + sub.length];
4813                 if (ch >= '0' && ch <= '9') {
4814                     currentType = DTSubStringType.Number;
4815                 }
4816                 else {
4817                     currentType = DTSubStringType.Other;
4818                 }
4819
4820                 if (sub.length == 0) {
4821                     sub.type = currentType;
4822                 }
4823                 else {
4824                     if (sub.type != currentType) {
4825                         break;
4826                     }
4827                 }
4828                 sub.length++;
4829                 if (currentType == DTSubStringType.Number) {
4830                     // Incorporate the number into the value
4831                     // Limit the digits to prevent overflow
4832                     if (sub.length > DateTimeParse.MaxDateTimeNumberDigits) {
4833                         sub.type = DTSubStringType.Invalid;
4834                         return sub;
4835                     }
4836                     int number = ch - '0';
4837                     Contract.Assert(number >= 0 && number <= 9, "number >= 0 && number <= 9");
4838                     sub.value = sub.value * 10 + number;
4839                 }
4840                 else {
4841                     // For non numbers, just return this length 1 token. This should be expanded
4842                     // to more types of thing if this parsing approach is used for things other
4843                     // than numbers and single characters
4844                     break;
4845                 }
4846             }
4847             if (sub.length == 0) {
4848                 sub.type = DTSubStringType.End;
4849                 return sub;
4850             }
4851
4852             return sub;
4853         }
4854
4855         internal void ConsumeSubString(DTSubString sub) {
4856             Contract.Assert(sub.index == Index, "sub.index == Index");
4857             Contract.Assert(sub.index + sub.length <= len, "sub.index + sub.length <= len");
4858             Index = sub.index + sub.length;
4859             if (Index < len) {
4860                 m_current = Value[Index];
4861             }
4862         }
4863     }
4864
4865     internal enum DTSubStringType {
4866         Unknown = 0,
4867         Invalid = 1,
4868         Number = 2,
4869         End = 3,
4870         Other = 4,
4871     }
4872
4873     internal struct DTSubString {
4874         internal String s;
4875         internal Int32 index;
4876         internal Int32 length;
4877         internal DTSubStringType type;
4878         internal Int32 value;
4879
4880         internal Char this[Int32 relativeIndex] {
4881             get {
4882                 return s[index + relativeIndex];
4883             }
4884         }
4885     }
4886
4887     //
4888     // The buffer to store the parsing token.
4889     //
4890     internal
4891     struct DateTimeToken {
4892         internal DateTimeParse.DTT dtt;    // Store the token
4893         internal TokenType suffix; // Store the CJK Year/Month/Day suffix (if any)
4894         internal int num;    // Store the number that we are parsing (if any)
4895     }
4896
4897     //
4898     // The buffer to store temporary parsing information.
4899     //
4900     internal
4901     unsafe struct DateTimeRawInfo {
4902         [SecurityCritical]
4903         private  int* num;
4904         internal int numCount;
4905         internal int month;
4906         internal int year;
4907         internal int dayOfWeek;
4908         internal int era;
4909         internal DateTimeParse.TM timeMark;
4910         internal double fraction;
4911         internal bool hasSameDateAndTimeSeparators;
4912         //
4913         // <
4914
4915
4916         internal bool timeZone;
4917
4918         [System.Security.SecurityCritical]  // auto-generated
4919         internal void Init(int * numberBuffer) {
4920             month      = -1;
4921             year       = -1;
4922             dayOfWeek  = -1;
4923             era        = -1;
4924             timeMark   = DateTimeParse.TM.NotSet;
4925             fraction = -1;
4926             num = numberBuffer;
4927         }
4928         [System.Security.SecuritySafeCritical]  // auto-generated
4929         internal unsafe void AddNumber(int value) {
4930             num[numCount++] = value;
4931         }
4932         [System.Security.SecuritySafeCritical]  // auto-generated
4933         internal unsafe int GetNumber(int index) {
4934             return num[index];
4935         }
4936     }
4937
4938     internal enum ParseFailureKind {
4939         None = 0,
4940         ArgumentNull = 1,
4941         Format = 2,
4942         FormatWithParameter = 3,
4943         FormatBadDateTimeCalendar = 4,  // FormatException when ArgumentOutOfRange is thrown by a Calendar.TryToDateTime().
4944     };
4945
4946     [Flags]
4947     internal enum ParseFlags {
4948         HaveYear        = 0x00000001,
4949         HaveMonth       = 0x00000002,
4950         HaveDay         = 0x00000004,
4951         HaveHour        = 0x00000008,
4952         HaveMinute      = 0x00000010,
4953         HaveSecond      = 0x00000020,
4954         HaveTime        = 0x00000040,
4955         HaveDate        = 0x00000080,
4956         TimeZoneUsed    = 0x00000100,
4957         TimeZoneUtc     = 0x00000200,
4958         ParsedMonthName = 0x00000400,
4959         CaptureOffset   = 0x00000800,
4960         YearDefault     = 0x00001000,
4961         Rfc1123Pattern  = 0x00002000,
4962         UtcSortPattern  = 0x00004000,
4963     }
4964
4965     //
4966     // This will store the result of the parsing.  And it will be eventually
4967     // used to construct a DateTime instance.
4968     //
4969     internal
4970     struct DateTimeResult
4971     {
4972         internal int Year;
4973         internal int Month;
4974         internal int Day;
4975         //
4976         // Set time defualt to 00:00:00.
4977         //
4978         internal int Hour;
4979         internal int Minute;
4980         internal int Second;
4981         internal double fraction;
4982
4983         internal int era;
4984
4985         internal ParseFlags flags;
4986
4987         internal TimeSpan timeZoneOffset;
4988
4989         internal Calendar calendar;
4990
4991         internal DateTime parsedDate;
4992
4993         internal ParseFailureKind failure;
4994         internal string failureMessageID;
4995         internal object failureMessageFormatArgument;
4996         internal string failureArgumentName;
4997
4998         internal void Init() {
4999             Year    = -1;
5000             Month   = -1;
5001             Day     = -1;
5002             fraction = -1;
5003             era = -1;
5004         }
5005
5006         internal void SetDate(int year, int month, int day)
5007         {
5008             Year = year;
5009             Month = month;
5010             Day = day;
5011         }
5012         internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) {
5013             this.failure = failure;
5014             this.failureMessageID = failureMessageID;
5015             this.failureMessageFormatArgument = failureMessageFormatArgument;
5016         }
5017
5018         internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, string failureArgumentName) {
5019             this.failure = failure;
5020             this.failureMessageID = failureMessageID;
5021             this.failureMessageFormatArgument = failureMessageFormatArgument;
5022             this.failureArgumentName = failureArgumentName;
5023         }
5024
5025
5026
5027
5028     }
5029
5030     // This is the helper data structure used in ParseExact().
5031     internal struct ParsingInfo {
5032    
5033         internal Calendar calendar;
5034         internal int dayOfWeek;
5035         internal DateTimeParse.TM timeMark;
5036
5037         internal bool fUseHour12;
5038         internal bool fUseTwoDigitYear;
5039         internal bool fAllowInnerWhite;
5040         internal bool fAllowTrailingWhite;
5041         internal bool fCustomNumberParser;
5042         internal DateTimeParse.MatchNumberDelegate parseNumberDelegate;
5043         
5044         internal void Init() {
5045             dayOfWeek = -1;
5046             timeMark = DateTimeParse.TM.NotSet;
5047         }
5048
5049     }
5050
5051     //
5052     // The type of token that will be returned by DateTimeFormatInfo.Tokenize().
5053     //
5054     internal enum TokenType {
5055         // The valid token should start from 1.
5056
5057         // Regular tokens. The range is from 0x00 ~ 0xff.
5058         NumberToken     = 1,    // The number.  E.g. "12"
5059         YearNumberToken = 2,    // The number which is considered as year number, which has 3 or more digits.  E.g. "2003"
5060         Am              = 3,    // AM timemark. E.g. "AM"
5061         Pm              = 4,    // PM timemark. E.g. "PM"
5062         MonthToken      = 5,    // A word (or words) that represents a month name.  E.g. "[....]"
5063         EndOfString     = 6,    // End of string
5064         DayOfWeekToken  = 7,    // A word (or words) that represents a day of week name.  E.g. "Monday" or "Mon"
5065         TimeZoneToken   = 8,    // A word that represents a timezone name. E.g. "GMT"
5066         EraToken        = 9,    // A word that represents a era name. E.g. "A.D."
5067         DateWordToken   = 10,   // A word that can appear in a DateTime string, but serves no parsing semantics.  E.g. "de" in Spanish culture.
5068         UnknownToken    = 11,   // An unknown word, which signals an error in parsing.
5069         HebrewNumber    = 12,   // A number that is composed of Hebrew text.  Hebrew calendar uses Hebrew digits for year values, month values, and day values.
5070         JapaneseEraToken= 13,   // Era name for JapaneseCalendar
5071         TEraToken       = 14,   // Era name for TaiwanCalendar
5072         IgnorableSymbol = 15,   // A separator like "," that is equivalent to whitespace
5073
5074
5075         // Separator tokens.
5076         SEP_Unk        = 0x100,         // Unknown separator.
5077         SEP_End        = 0x200,    // The end of the parsing string.
5078         SEP_Space      = 0x300,    // Whitespace (including comma).
5079         SEP_Am         = 0x400,    // AM timemark. E.g. "AM"
5080         SEP_Pm         = 0x500,    // PM timemark. E.g. "PM"
5081         SEP_Date       = 0x600,    // date separator. E.g. "/"
5082         SEP_Time       = 0x700,    // time separator. E.g. ":"
5083         SEP_YearSuff   = 0x800,    // Chinese/Japanese/Korean year suffix.
5084         SEP_MonthSuff  = 0x900,    // Chinese/Japanese/Korean month suffix.
5085         SEP_DaySuff    = 0xa00,    // Chinese/Japanese/Korean day suffix.
5086         SEP_HourSuff   = 0xb00,   // Chinese/Japanese/Korean hour suffix.
5087         SEP_MinuteSuff = 0xc00,   // Chinese/Japanese/Korean minute suffix.
5088         SEP_SecondSuff = 0xd00,   // Chinese/Japanese/Korean second suffix.
5089         SEP_LocalTimeMark = 0xe00,   // 'T', used in ISO 8601 format.
5090         SEP_DateOrOffset = 0xf00,   // '-' which could be a date separator or start of a time zone offset
5091
5092         RegularTokenMask = 0x00ff,
5093         SeparatorTokenMask = 0xff00,
5094     }
5095 }