private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref double result) { if (!str.GetNextDigit()) { str.Index--; return false; } result = (double)str.GetDigit(); int i; for (i = 1; i < maxDigitLen; i++) { if (!str.GetNextDigit()) { str.Index--; break; } result = result * 10.0 + (double)str.GetDigit(); } result /= Math.Pow(10.0, (double)i); return i == maxDigitLen; }
/*=================================ParseFractionExact================================== **Action: Parse the number string in __DTString that are formatted using ** the following patterns: ** "0", "00", and "000..0" **Returns: the fraction value **Arguments: str: a __DTString. The parsing will start from the ** next character after str.Index. **Exceptions: FormatException if error in parsing number. ==============================================================================*/ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref double result) { if (!str.GetNextDigit()) { str.Index--; return false; } result = str.GetDigit(); int digitLen = 1; for (; digitLen < maxDigitLen; digitLen++) { if (!str.GetNextDigit()) { str.Index--; break; } result = result * 10 + str.GetDigit(); } result = ((double)result / Math.Pow(10, digitLen)); return (digitLen == maxDigitLen); }
internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result) { result = 0; int index = str.Index; int i; for (i = 0; i < maxDigitLen; i++) { if (!str.GetNextDigit()) { str.Index--; break; } result = result * 10 + str.GetDigit(); } if (i < minDigitLen) { str.Index = index; return false; } return true; }
private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref double result) { if (!str.GetNextDigit()) { str.Index--; return false; } result = str.GetDigit(); int num = 1; while (num < maxDigitLen) { if (!str.GetNextDigit()) { str.Index--; break; } result = (result * 10.0) + str.GetDigit(); num++; } result = ((double) result) / Math.Pow(10.0, (double) num); return (num == maxDigitLen); }
internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result) { Contract.Assert(minDigitLen > 0, "minDigitLen > 0"); Contract.Assert(maxDigitLen < 9, "maxDigitLen < 9"); Contract.Assert(minDigitLen <= maxDigitLen, "minDigitLen <= maxDigitLen"); result = 0; int startingIndex = str.Index; int tokenLength = 0; while (tokenLength < maxDigitLen) { if (!str.GetNextDigit()) { str.Index--; break; } result = result * 10 + str.GetDigit(); tokenLength++; } if (tokenLength < minDigitLen) { str.Index = startingIndex; return false; } return true; }
internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result) { result = 0; int index = str.Index; int num2 = 0; while (num2 < maxDigitLen) { if (!str.GetNextDigit()) { str.Index--; break; } result = (result * 10) + str.GetDigit(); num2++; } if (num2 < minDigitLen) { str.Index = index; return false; } return true; }
// // This is the lexer. Check the character at the current index, and put the found token in dtok and // some raw date/time information in raw. // private static void Lex( int dps, __DTString str, DateTimeToken dtok, DateTimeRawInfo raw, DateTimeResult result, ref DateTimeFormatInfo dtfi) { int sep; dtok.dtt = DTT_Unk; // Assume the token is unkown. // // Skip any white spaces. // if (!str.SkipWhiteSpaceComma()) { // // SkipWhiteSpaceComma() will return true when end of string is reached. // dtok.dtt = DTT_End; return; } char ch = str.GetChar(); if (Char.IsLetter(ch)) { // // This is a letter. // int month, dayOfWeek, era, timeMark; // // Check if this is a beginning of a month name. // And check if this is a day of week name. // if (raw.month == -1 && (month = GetMonthNumber(str, dtfi)) >= 1) { // // This is a month name // switch(sep=GetSeparator(str, raw, dtfi)) { case SEP_End: dtok.dtt = DTT_MonthEnd; break; case SEP_Space: dtok.dtt = DTT_MonthSpace; break; case SEP_Date: dtok.dtt = DTT_MonthDatesep; break; default: //Invalid separator after month name throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } raw.month = month; } else if (raw.dayOfWeek == -1 && (dayOfWeek = GetDayOfWeekNumber(str, dtfi)) >= 0) { // // This is a day of week name. // raw.dayOfWeek = dayOfWeek; dtok.dtt = DTT_DayOfWeek; // // Discard the separator. // GetSeparator(str, raw, dtfi); } else if (GetTimeZoneName(str)) { // // This is a timezone designator // // NOTENOTE : for now, we only support "GMT" and "Z" (for Zulu time). // dtok.dtt = DTT_TimeZone; result.timeZoneUsed = true; result.timeZoneOffset = new TimeSpan(0); } else if ((raw.era == -1) && ((era = GetEra(str, result, ref dtfi)) != -1)) { raw.era = era; dtok.dtt = DTT_Era; } else if (raw.timeMark == -1 && (timeMark = GetTimeMark(str, dtfi)) != -1) { raw.timeMark = timeMark; GetSeparator(str, raw, dtfi); } else { // // Not a month name, not a day of week name. Check if this is one of the // known date words. This is used to deal case like Spanish cultures, which // uses 'de' in their Date string. // // if (!str.MatchWords(dtfi.DateWords)) { throw new FormatException( String.Format(Environment.GetResourceString("Format_UnknowDateTimeWord"), str.Index)); } GetSeparator(str, raw, dtfi); } } else if (Char.IsDigit(ch)) { if (raw.numCount == 3) { throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } // // This is a digit. // int number = ch - '0'; int digitCount = 1; // // Collect other digits. // while (str.GetNextDigit()) { number = number * 10 + str.GetDigit(); digitCount++; } // If the previous parsing state is DS_T_NNt (like 12:01), and we got another number, // so we will have a terminal state DS_TX_NNN (like 12:01:02). // If the previous parsing state is DS_T_Nt (like 12:), and we got another number, // so we will have a terminal state DS_TX_NN (like 12:01:02). // // Look ahead to see if the following character is a decimal point or timezone offset. // This enables us to parse time in the forms of: // "11:22:33.1234" or "11:22:33-08". if (dps == DS_T_NNt || dps == DS_T_Nt) { char nextCh; if ((str.Index < str.len - 1)) { nextCh = str.Value[str.Index]; switch (nextCh) { case '.': if (dps == DS_T_NNt) { // Yes, advance to the next character. str.Index++; // Collect the second fraction. raw.fraction = ParseFraction(str); } break; case '+': case '-': if (result.timeZoneUsed) { // Should not have two timezone offsets. throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } result.timeZoneUsed = true; result.timeZoneOffset = ParseTimeZone(str, nextCh); break; } } } if (number >= 0) { dtok.num = number; if (digitCount >= 3) { if (raw.year == -1) { raw.year = number; // // If we have number which has 3 or more digits (like "001" or "0001"), // we assume this number is a year. Save the currnet raw.numCount in // raw.year. // switch (sep = GetSeparator(str, raw, dtfi)) { case SEP_End: dtok.dtt = DTT_YearEnd; break; case SEP_Am: case SEP_Pm: case SEP_Space: dtok.dtt = DTT_YearSpace; break; case SEP_Date: dtok.dtt = DTT_YearDateSep; break; case SEP_YearSuff: case SEP_MonthSuff: case SEP_DaySuff: dtok.dtt = DTT_NumDatesuff; dtok.suffix = sep; break; case SEP_HourSuff: case SEP_MinuteSuff: case SEP_SecondSuff: dtok.dtt = DTT_NumTimesuff; dtok.suffix = sep; break; default: // Invalid separator after number number. throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } // // Found the token already. Let's bail. // return; } throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } } else { // // number is overflowed. // throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } switch (sep = GetSeparator(str, raw, dtfi)) { // // Note here we check if the numCount is less than three. // When we have more than three numbers, it will be caught as error in the state machine. // case SEP_End: dtok.dtt = DTT_NumEnd; raw.num[raw.numCount++] = dtok.num; break; case SEP_Am: case SEP_Pm: dtok.dtt = DTT_NumAmpm; raw.num[raw.numCount++] = dtok.num; break; case SEP_Space: dtok.dtt = DTT_NumSpace; raw.num[raw.numCount++] = dtok.num; break; case SEP_Date: dtok.dtt = DTT_NumDatesep; raw.num[raw.numCount++] = dtok.num; break; case SEP_Time: if (!result.timeZoneUsed) { dtok.dtt = DTT_NumTimesep; raw.num[raw.numCount++] = dtok.num; } else { // If we already got timezone, there should be no // time separator again. throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } break; case SEP_YearSuff: dtok.num = dtfi.Calendar.ToFourDigitYear(number); dtok.dtt = DTT_NumDatesuff; dtok.suffix = sep; break; case SEP_MonthSuff: case SEP_DaySuff: dtok.dtt = DTT_NumDatesuff; dtok.suffix = sep; break; case SEP_HourSuff: case SEP_MinuteSuff: case SEP_SecondSuff: dtok.dtt = DTT_NumTimesuff; dtok.suffix = sep; break; case SEP_LocalTimeMark: dtok.dtt = DTT_NumLocalTimeMark; raw.num[raw.numCount++] = dtok.num; break; default: // Invalid separator after number number. throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } } else { // // Not a letter, not a digit. Just ignore it. // str.Index++; } return; }
/*=================================ParseTimeZone========================== **Action: Parse the timezone offset in the following format: ** "+8", "+08", "+0800", "+0800" ** This method is used by DateTime.Parse(). **Returns: The TimeZone offset. **Arguments: ** str the parsing string **Exceptions: ** FormatException if invalid timezone format is found. ============================================================================*/ private static TimeSpan ParseTimeZone(__DTString str, char offsetChar) { // The hour/minute offset for timezone. int hourOffset = 0; int minuteOffset = 0; if (str.GetNextDigit()) { // Get the first digit, Try if we can parse timezone in the form of "+8". hourOffset = str.GetDigit(); if (str.GetNextDigit()) { // Parsing "+18" hourOffset *= 10; hourOffset += str.GetDigit(); if (str.GetNext()) { char ch; if (Char.IsDigit(ch = str.GetChar())) { // Parsing "+1800" // Put the char back, since we already get the char in the previous GetNext() call. str.Index--; if (ParseDigits(str, 2, true, out minuteOffset)) { // ParseDigits() does not advance the char for us, so do it here. str.Index++; } else { throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } } else if (ch == ':') { // Parsing "+18:00" if (ParseDigits(str, 2, true, out minuteOffset)) { str.Index++; } else { throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } } else { // Not a digit, not a colon, put this char back. str.Index--; } } } // The next char is not a digit, so we get the timezone in the form of "+8". } else { // Invalid timezone: No numbers after +/-. throw new FormatException(Environment.GetResourceString("Format_BadDateTime")); } TimeSpan timezoneOffset = new TimeSpan(hourOffset, minuteOffset, 0); if (offsetChar == '-') { timezoneOffset = timezoneOffset.Negate(); } return (timezoneOffset); }
/*=================================ParseFractionExact================================== **Action: Parse the number string in __DTString that are formatted using ** the following patterns: ** "0", "00", and "000..0" **Returns: the fraction value **Arguments: str: a __DTString. The parsing will start from the ** next character after str.Index. **Exceptions: FormatException if error in parsing number. ==============================================================================*/ private static bool ParseFractionExact(__DTString str, int digitLen, bool isThrowExp, ref double result) { if (!str.GetNextDigit()) { return (ParseFormatError(isThrowExp, "Format_BadDateTime")); } result = str.GetDigit(); for (int i = 1; i < digitLen; i++) { if (!str.GetNextDigit()) { return (ParseFormatError(isThrowExp, "Format_BadDateTime")); } result = result * 10 + str.GetDigit(); } result = ((double)result / Math.Pow(10, digitLen)); return (true); }
/*=================================ParseDigits================================== **Action: Parse the number string in __DTString that are formatted using ** the following patterns: ** "0", "00", and "000..0" **Returns: the integer value **Arguments: str: a __DTString. The parsing will start from the ** next character after str.Index. **Exceptions: FormatException if error in parsing number. ==============================================================================*/ private static bool ParseDigits(__DTString str, int digitLen, bool isThrowExp, out int result) { result = 0; if (!str.GetNextDigit()) { return (ParseFormatError(isThrowExp, "Format_BadDateTime")); } result = str.GetDigit(); if (digitLen == 1) { // When digitLen == 1, we should able to parse number like "9" and "19". However, // we won't go beyond two digits. // // So let's look ahead one character to see if it is a digit. If yes, add it to result. if (str.GetNextDigit()) { result = result * 10 + str.GetDigit(); } else { // Not a digit, let's roll back the Index. str.Index--; } } else if (digitLen == 2) { if (!str.GetNextDigit()) { return (ParseFormatError(isThrowExp, "Format_BadDateTime")); } result = result * 10 + str.GetDigit(); } else { for (int i = 1; i < digitLen; i++) { if (!str.GetNextDigit()) { return (ParseFormatError(isThrowExp, "Format_BadDateTime")); } result = result * 10 + str.GetDigit(); } } return (true); }