private static bool DoParse(string input, string format, bool exact, out DateTimeOffset result, DateTimeFormatInfo dfi, DateTimeStyles styles) { if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) { format = format.TrimStart(null); input = input.TrimStart(null); } if ((styles & DateTimeStyles.AllowTrailingWhite) != 0) { format = format.TrimEnd(null); input = input.TrimEnd(null); } bool allow_white_spaces = false; if ((styles & DateTimeStyles.AllowInnerWhite) != 0) { allow_white_spaces = true; } result = DateTimeOffset.MinValue; bool useutc = false, use_invariants = false; if (format.Length == 1) { format = DateTimeUtils.GetStandardPattern(format[0], dfi, out useutc, out use_invariants, true); if (format == null) { return(false); } } int year = -1; int month = -1; int day = -1; int partial_hour = -1; // for 'hh tt' formats int hour = -1; int minute = -1; int second = -1; double fraction = -1; int temp_int = -1; TimeSpan offset = TimeSpan.MinValue; int fi = 0; //format iterator int ii = 0; //input iterator while (fi < format.Length) { int tokLen; char ch = format[fi]; switch (ch) { case 'd': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (day != -1 || tokLen > 4) { return(false); } if (tokLen <= 2) { ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out day); } else { ii += ParseEnum(input, ii, tokLen == 3 ? dfi.AbbreviatedDayNames : dfi.DayNames, allow_white_spaces, out temp_int); } break; case 'f': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); ii += ParseNumber(input, ii, tokLen, true, allow_white_spaces, out temp_int); if (fraction >= 0 || tokLen > 7 || temp_int == -1) { return(false); } fraction = (double)temp_int / Math.Pow(10, tokLen); break; case 'F': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); int digits; int read = ParseNumber(input, ii, tokLen, true, allow_white_spaces, out temp_int, out digits); if (temp_int == -1) { ii += ParseNumber(input, ii, digits, true, allow_white_spaces, out temp_int); } else { ii += read; } if (fraction >= 0 || tokLen > 7 || temp_int == -1) { return(false); } fraction = (double)temp_int / Math.Pow(10, digits); break; case 'h': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (hour != -1 || tokLen > 2) { return(false); } ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out temp_int); if (temp_int == -1) { return(false); } if (partial_hour == -1) { partial_hour = temp_int; } else { hour = partial_hour + temp_int; } break; case 'H': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (hour != -1 || tokLen > 2) { return(false); } ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out hour); break; case 'm': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (minute != -1 || tokLen > 2) { return(false); } ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out minute); break; case 'M': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (month != -1 || tokLen > 4) { return(false); } if (tokLen <= 2) { ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out month); } else { ii += ParseEnum(input, ii, tokLen == 3 ? dfi.AbbreviatedMonthNames : dfi.MonthNames, allow_white_spaces, out month); month += 1; } break; case 's': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (second != -1 || tokLen > 2) { return(false); } ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out second); break; case 't': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (hour != -1 || tokLen > 2) { return(false); } ii += ParseEnum(input, ii, tokLen == 1 ? new string[] { new string (dfi.AMDesignator[0], 1), new string (dfi.PMDesignator[0], 0) } : new string[] { dfi.AMDesignator, dfi.PMDesignator }, allow_white_spaces, out temp_int); if (temp_int == -1) { return(false); } if (partial_hour == -1) { partial_hour = temp_int * 12; } else { hour = partial_hour + temp_int * 12; } break; case 'y': if (year != -1) { return(false); } tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (tokLen <= 2) { ii += ParseNumber(input, ii, 2, tokLen == 2, allow_white_spaces, out year); if (year != -1) { year = dfi.Calendar.ToFourDigitYear(year); } } else if (tokLen <= 4) { // yyy and yyyy accept up to 5 digits with leading 0 int digit_parsed; ii += ParseNumber(input, ii, 5, false, allow_white_spaces, out year, out digit_parsed); if (digit_parsed < tokLen || (digit_parsed > tokLen && (year / Math.Pow(10, digit_parsed - 1) < 1))) { return(false); } } else { ii += ParseNumber(input, ii, tokLen, true, allow_white_spaces, out year); } break; // The documentation is incorrect, they claim that K is the same as 'zz', but // it actually allows the format to contain 4 digits for the offset case 'K': tokLen = 1; int off_h, off_m = 0, sign; temp_int = 0; ii += ParseEnum(input, ii, new string[] { "-", "+" }, allow_white_spaces, out sign); ii += ParseNumber(input, ii, 4, false, false, out off_h); if (off_h == -1 || off_m == -1 || sign == -1) { return(false); } if (sign == 0) { sign = -1; } offset = new TimeSpan(sign * off_h, sign * off_m, 0); break; case 'z': tokLen = DateTimeUtils.CountRepeat(format, fi, ch); if (offset != TimeSpan.MinValue || tokLen > 3) { return(false); } off_m = 0; temp_int = 0; ii += ParseEnum(input, ii, new string[] { "-", "+" }, allow_white_spaces, out sign); ii += ParseNumber(input, ii, 2, tokLen != 1, false, out off_h); if (tokLen == 3) { ii += ParseEnum(input, ii, new string[] { dfi.TimeSeparator }, false, out temp_int); ii += ParseNumber(input, ii, 2, true, false, out off_m); } if (off_h == -1 || off_m == -1 || sign == -1) { return(false); } if (sign == 0) { sign = -1; } offset = new TimeSpan(sign * off_h, sign * off_m, 0); break; case ':': tokLen = 1; ii += ParseEnum(input, ii, new string[] { dfi.TimeSeparator }, false, out temp_int); if (temp_int == -1) { return(false); } break; case '/': tokLen = 1; ii += ParseEnum(input, ii, new string[] { dfi.DateSeparator }, false, out temp_int); if (temp_int == -1) { return(false); } break; case '%': tokLen = 1; if (fi != 0) { return(false); } break; case ' ': tokLen = 1; ii += ParseChar(input, ii, ' ', false, out temp_int); if (temp_int == -1) { return(false); } break; case '\\': tokLen = 2; ii += ParseChar(input, ii, format[fi + 1], allow_white_spaces, out temp_int); if (temp_int == -1) { return(false); } break; case '\'': case '"': tokLen = 1; while (ii < input.Length) { var ftoken = format[fi + tokLen]; ++tokLen; if (ftoken == format[fi]) { if (useutc && tokLen == 5 && input[ii - 3] == 'G' && input[ii - 2] == 'M' && input[ii - 1] == 'T') { offset = TimeSpan.Zero; } break; } if (ftoken != input[ii++]) { return(false); } } break; default: //Console.WriteLine ("un-parsed character: {0}", ch); tokLen = 1; ii += ParseChar(input, ii, format[fi], allow_white_spaces, out temp_int); if (temp_int == -1) { return(false); } break; } fi += tokLen; } //Console.WriteLine ("{0}-{1}-{2} {3}:{4} {5}", year, month, day, hour, minute, offset); if (offset == TimeSpan.MinValue) { if ((styles & DateTimeStyles.AssumeUniversal) != 0) { offset = TimeSpan.Zero; } else if ((styles & DateTimeStyles.AssumeLocal) != 0) { offset = use_invariants ? TimeSpan.Zero : TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now); } } if (hour < 0) { hour = 0; } if (minute < 0) { minute = 0; } if (second < 0) { second = 0; } if (fraction < 0) { fraction = 0; } if (year > 0 && month > 0 && day > 0) { result = new DateTimeOffset(year, month, day, hour, minute, second, 0, offset); result = result.AddSeconds(fraction); if ((styles & DateTimeStyles.AdjustToUniversal) != 0) { result = result.ToUniversalTime(); } return(true); } return(false); }
public static string ToString(DateTime dt, TimeSpan?utc_offset, string format, DateTimeFormatInfo dfi) { // the length of the format is usually a good guess of the number // of chars in the result. Might save us a few bytes sometimes // Add + 10 for cases like mmmm dddd StringBuilder result = new StringBuilder(format.Length + 10); int i = 0; bool saw_day_specifier = false; while (i < format.Length) { int tokLen; bool omitZeros = false; char ch = format[i]; switch (ch) { // // Time Formats // case 'h': // hour, [1, 12] tokLen = DateTimeUtils.CountRepeat(format, i, ch); int hr = dt.Hour % 12; if (hr == 0) { hr = 12; } DateTimeUtils.ZeroPad(result, hr, tokLen == 1 ? 1 : 2); break; case 'H': // hour, [0, 23] tokLen = DateTimeUtils.CountRepeat(format, i, ch); DateTimeUtils.ZeroPad(result, dt.Hour, tokLen == 1 ? 1 : 2); break; case 'm': // minute, [0, 59] tokLen = DateTimeUtils.CountRepeat(format, i, ch); DateTimeUtils.ZeroPad(result, dt.Minute, tokLen == 1 ? 1 : 2); break; case 's': // second [0, 29] tokLen = DateTimeUtils.CountRepeat(format, i, ch); DateTimeUtils.ZeroPad(result, dt.Second, tokLen == 1 ? 1 : 2); break; case 'F': omitZeros = true; goto case 'f'; case 'f': // fraction of second, to same number of // digits as there are f's tokLen = DateTimeUtils.CountRepeat(format, i, ch); if (tokLen > 7) { throw new FormatException("Invalid Format String"); } int dec = (int)((long)(dt.Ticks % TimeSpan.TicksPerSecond) / (long)Math.Pow(10, 7 - tokLen)); int startLen = result.Length; DateTimeUtils.ZeroPad(result, dec, tokLen); if (omitZeros) { while (result.Length > startLen && result[result.Length - 1] == '0') { result.Length--; } // when the value was 0, then trim even preceding '.' (!) It is fixed character. if (dec == 0 && startLen > 0 && result[startLen - 1] == '.') { result.Length--; } } break; case 't': // AM/PM. t == first char, tt+ == full tokLen = DateTimeUtils.CountRepeat(format, i, ch); string desig = dt.Hour < 12 ? dfi.AMDesignator : dfi.PMDesignator; if (tokLen == 1) { if (desig.Length >= 1) { result.Append(desig[0]); } } else { result.Append(desig); } break; case 'z': // timezone. t = +/-h; tt = +/-hh; ttt+=+/-hh:mm tokLen = DateTimeUtils.CountRepeat(format, i, ch); TimeSpan offset = utc_offset ?? TimeZone.CurrentTimeZone.GetUtcOffset(dt); if (offset.Ticks >= 0) { result.Append('+'); } else { result.Append('-'); } switch (tokLen) { case 1: result.Append(Math.Abs(offset.Hours)); break; case 2: result.Append(Math.Abs(offset.Hours).ToString("00")); break; default: result.Append(Math.Abs(offset.Hours).ToString("00")); result.Append(':'); result.Append(Math.Abs(offset.Minutes).ToString("00")); break; } break; case 'K': // 'Z' (UTC) or zzz (Local) tokLen = 1; if (utc_offset != null || dt.Kind == DateTimeKind.Local) { offset = utc_offset ?? TimeZone.CurrentTimeZone.GetUtcOffset(dt); if (offset.Ticks >= 0) { result.Append('+'); } else { result.Append('-'); } result.Append(Math.Abs(offset.Hours).ToString("00")); result.Append(':'); result.Append(Math.Abs(offset.Minutes).ToString("00")); } else if (dt.Kind == DateTimeKind.Utc) { result.Append('Z'); } break; // // Date tokens // case 'd': // day. d(d?) = day of month (leading 0 if two d's) // ddd = three leter day of week // dddd+ full day-of-week tokLen = DateTimeUtils.CountRepeat(format, i, ch); if (tokLen <= 2) { DateTimeUtils.ZeroPad(result, dfi.Calendar.GetDayOfMonth(dt), tokLen == 1 ? 1 : 2); } else if (tokLen == 3) { result.Append(dfi.GetAbbreviatedDayName(dfi.Calendar.GetDayOfWeek(dt))); } else { result.Append(dfi.GetDayName(dfi.Calendar.GetDayOfWeek(dt))); } saw_day_specifier = true; break; case 'M': // Month.m(m?) = month # (with leading 0 if two mm) // mmm = 3 letter name // mmmm+ = full name tokLen = DateTimeUtils.CountRepeat(format, i, ch); int month = dfi.Calendar.GetMonth(dt); if (tokLen <= 2) { DateTimeUtils.ZeroPad(result, month, tokLen); } else if (tokLen == 3) { result.Append(dfi.GetAbbreviatedMonthName(month)); } else { // Handles MMMM dd format if (!saw_day_specifier) { for (int ii = i + 1; ii < format.Length; ++ii) { ch = format[ii]; if (ch == 'd') { saw_day_specifier = true; break; } if (ch == '\'' || ch == '"') { ii += ParseQuotedString(format, ii, null) - 1; } } } // NOTE: .NET ignores quoted 'd' and reads it as day specifier but I think // that's wrong #if NETCF result.Append(/*saw_day_specifier ? dfi.GetMonthGenitiveName (month) :*/ dfi.GetMonthName(month)); #else result.Append(saw_day_specifier ? dfi.GetMonthGenitiveName(month) : dfi.GetMonthName(month)); #endif } break; case 'y': // Year. y(y?) = two digit year, with leading 0 if yy // yyy+ full year with leading zeros if needed. tokLen = DateTimeUtils.CountRepeat(format, i, ch); if (tokLen <= 2) { DateTimeUtils.ZeroPad(result, dfi.Calendar.GetYear(dt) % 100, tokLen); } else { DateTimeUtils.ZeroPad(result, dfi.Calendar.GetYear(dt), tokLen); } break; case 'g': // Era name tokLen = DateTimeUtils.CountRepeat(format, i, ch); result.Append(dfi.GetEraName(dfi.Calendar.GetEra(dt))); break; // // Other // case ':': result.Append(dfi.TimeSeparator); tokLen = 1; break; case '/': result.Append(dfi.DateSeparator); tokLen = 1; break; case '\'': case '"': tokLen = ParseQuotedString(format, i, result); break; case '%': if (i >= format.Length - 1) { throw new FormatException("% at end of date time string"); } if (format[i + 1] == '%') { throw new FormatException("%% in date string"); } // Look for the next char tokLen = 1; break; case '\\': // C-Style escape if (i >= format.Length - 1) { throw new FormatException("\\ at end of date time string"); } result.Append(format[i + 1]); tokLen = 2; break; default: // catch all result.Append(ch); tokLen = 1; break; } i += tokLen; } return(result.ToString()); }