private static DateTime?Parse(string input, DateTimeFormatInfo formatInfo, bool throwOnError = true) { Argument.IsNotNull(() => formatInfo); var year = 0; var month = 0; var day = 0; var hour = 0; var minute = 0; var second = 0; var amPm = default(string); var separator = default(string); var i = 0; var partValue = default(string); for (i = 0; i < 7; i++) { separator = formatInfo.GetSeparator(i); if (separator != null && separator.Length > 0 && input.Length > 0) { if (!input.StartsWith(separator)) { return(ThrowOnError <FormatException>("Invalid value. Value does not match format", null, throwOnError)); } input = input.Substring(separator.Length); } if (separator == null && i == (formatInfo.MaxPosition + 1) && input.Length > 0) { // We have come to the end of format but there is still some characters left in the input. // They are probably AM/PM values even though the format is 24 hours. We will try and parse them correctly formatInfo.AmPmPosition = i; input = input.Trim(); if (input.Length == 2) { formatInfo.AmPmFormat = "tt"; } else if (input.Length == 1) { formatInfo.AmPmFormat = "t"; } } if (i == formatInfo.YearPosition) { partValue = new string(input.TakeWhile(x => char.IsDigit(x)).ToArray()); if (formatInfo.YearFormat.Length == 1 && (partValue.Length < 1 || partValue.Length > 2)) // 'y' { return(ThrowOnError <FormatException>("Invalid year value. Year must contain 1 or 2 digits", null, throwOnError)); } else if (formatInfo.YearFormat.Length == 2 && partValue.Length != 2) // 'yy' { return(ThrowOnError <FormatException>("Invalid year value. Year must contain 2 digits", null, throwOnError)); } else if (formatInfo.YearFormat.Length == 3 && (partValue.Length < 3 || partValue.Length > 5)) // 'yyy' { return(ThrowOnError <FormatException>("Invalid year value. Year must contain 3 or 4 or 5 digits", null, throwOnError)); } else if (formatInfo.YearFormat.Length == 4 && (partValue.Length < 4 || partValue.Length > 5)) // 'yyyy' { return(ThrowOnError <FormatException>("Invalid year value. Year must contain 4 or 5 digits", null, throwOnError)); } else if (formatInfo.YearFormat.Length == 5 && partValue.Length != 5) // 'yyyyy' { return(ThrowOnError <FormatException>("Invalid year value. Year must contain 5 digits", null, throwOnError)); } year = int.Parse(partValue); // There is no reason to fail. year += formatInfo.IsYearShortFormat == true ? 2000 : 0; input = input.Substring(partValue.Length); } else if (i == formatInfo.MonthPosition) { partValue = new string(input.TakeWhile(x => char.IsDigit(x)).ToArray()); if (formatInfo.MonthFormat.Length == 1 && (partValue.Length < 1 || partValue.Length > 2)) // 'M' { return(ThrowOnError <FormatException>("Invalid month value. Month must contain 1 or 2 digits", null, throwOnError)); } else if (formatInfo.MonthFormat.Length == 2 && partValue.Length != 2) // 'MM' { return(ThrowOnError <FormatException>("Invalid month value. Month must contain 2 digits", null, throwOnError)); } month = int.Parse(partValue); // There is no reason to fail. input = input.Substring(partValue.Length); } else if (i == formatInfo.DayPosition) { partValue = new string(input.TakeWhile(x => char.IsDigit(x)).ToArray()); if (formatInfo.DayFormat.Length == 1 && (partValue.Length < 1 || partValue.Length > 2)) // 'd' { return(ThrowOnError <FormatException>("Invalid day value. Day must contain 1 or 2 digits", null, throwOnError)); } else if (formatInfo.DayFormat.Length == 2 && partValue.Length != 2) // 'dd' { return(ThrowOnError <FormatException>("Invalid day value. Day must contain 2 digits", null, throwOnError)); } day = int.Parse(partValue); // There is no reason to fail. input = input.Substring(partValue.Length); } else if (i == formatInfo.HourPosition) { partValue = new string(input.TakeWhile(x => char.IsDigit(x)).ToArray()); if (formatInfo.HourFormat.Length == 1 && (partValue.Length < 1 || partValue.Length > 2)) // 'h' or 'H' { return(ThrowOnError <FormatException>("Invalid hour value. Hour must contain 1 or 2 digits", null, throwOnError)); } else if (formatInfo.HourFormat.Length == 2 && partValue.Length != 2) // 'hh' or 'HH' { return(ThrowOnError <FormatException>("Invalid hour value. Hour must contain 2 digits", null, throwOnError)); } hour = int.Parse(partValue); // There is no reason to fail. if (hour < 0 && hour >= 24) { return(ThrowOnError <FormatException>("Invalid hour value. Hour must be in range <0, 24> for short format", null, throwOnError)); } input = input.Substring(partValue.Length); } else if (i == formatInfo.MinutePosition) { partValue = new string(input.TakeWhile(x => char.IsDigit(x)).ToArray()); if (formatInfo.MinuteFormat.Length == 1 && (partValue.Length < 1 || partValue.Length > 2)) // 'm' { return(ThrowOnError <FormatException>("Invalid minute value. Minute must contain 1 or 2 digits", null, throwOnError)); } else if (formatInfo.MinuteFormat.Length == 2 && partValue.Length != 2) // 'mm' { return(ThrowOnError <FormatException>("Invalid minute value. Minute must contain 2 digits", null, throwOnError)); } minute = int.Parse(partValue); // There is no reason to fail. input = input.Substring(partValue.Length); } else if (i == formatInfo.SecondPosition) { partValue = new string(input.TakeWhile(x => char.IsDigit(x)).ToArray()); if (formatInfo.SecondFormat.Length == 1 && (partValue.Length < 1 || partValue.Length > 2)) { return(ThrowOnError <FormatException>("Invalid second value. Second must contain 1 or 2 digits", null, throwOnError)); // 's' } else if (formatInfo.SecondFormat.Length == 2 && partValue.Length != 2) // 'ss' { return(ThrowOnError <FormatException>("Invalid second value. Second must contain 2 digits", null, throwOnError)); } second = int.Parse(partValue); // There is no reason to fail. input = input.Substring(partValue.Length); } else if (i == formatInfo.AmPmPosition) { partValue = new string(input.Take(formatInfo.AmPmFormat.Length).ToArray()); if (partValue.Length <= 0) { // We allow the input string to not have AM/PM even if the format specifies them continue; } if (formatInfo.IsAmPmShortFormat == true && !(Meridiems.IsShortAm(partValue) || Meridiems.IsShortPm(partValue))) { return(ThrowOnError <FormatException>("Invalid AM/PM designator value", null, throwOnError)); } else if (formatInfo.IsAmPmShortFormat == false && !(Meridiems.IsLongAm(partValue) || Meridiems.IsLongPm(partValue))) { return(ThrowOnError <FormatException>("Invalid AM/PM designator value", null, throwOnError)); } amPm = partValue; // We need to make sure hour is less than 12, because we could accept values like: 05/02/2017 16:41:24 PM hour += (Meridiems.IsPm(amPm) && hour < 12) ? 12 : 0; input = input.Substring(partValue.Length); } } separator = formatInfo.GetSeparator(i); if (separator != null && separator.Length > 0) { if (!input.StartsWith(separator)) { return(ThrowOnError <FormatException>("Invalid value. Value does not match to format", null, throwOnError)); } } try { return(new DateTime(year, month, day, hour, minute, second)); } catch (Exception exc) { return(ThrowOnError <FormatException>("Invalid value. It's not possible to build new DateTime object", exc, throwOnError)); } }
private static DateTime?Parse(string input, DateTimeFormatInfo formatInfo, bool throwOnError) { Argument.IsNotNull(() => formatInfo); var year = 0; var month = 0; var day = 0; var hour = 0; var minute = 0; var second = 0; string separator; int i; for (i = 0; i < 7; i++) { separator = formatInfo.GetSeparator(i); if (!string.IsNullOrEmpty(separator) && input.Length > 0) { if (!input.StartsWith(separator)) { return(ThrowOnError <FormatException>("Invalid value. Value does not match format", null, throwOnError)); } input = input.Substring(separator.Length); } if (separator is null && i == formatInfo.MaxPosition + 1 && input.Length > 0) { input = ParseAmPmFormat(input, formatInfo, i); } if (i == formatInfo.YearPosition) { var partValue = new string(input.TakeWhile(char.IsDigit).ToArray()); if (!formatInfo.CheckYearFormat(partValue, out var errorMessage)) { return(ThrowOnError <FormatException>(errorMessage, null, throwOnError)); } input = input.ChunkIntValue(partValue, out year); year += formatInfo.IsYearShortFormat ? 2000 : 0; continue; } if (i == formatInfo.MonthPosition) { var partValue = new string(input.TakeWhile(char.IsDigit).ToArray()); if (!formatInfo.CheckMonthFormat(partValue, out var errorMessage)) { return(ThrowOnError <FormatException>(errorMessage, null, throwOnError)); } input = input.ChunkIntValue(partValue, out month); continue; } if (i == formatInfo.DayPosition) { var partValue = new string(input.TakeWhile(char.IsDigit).ToArray()); if (!formatInfo.CheckDayFormat(partValue, out var errorMessage)) { return(ThrowOnError <FormatException>(errorMessage, null, throwOnError)); } input = input.ChunkIntValue(partValue, out day); continue; } if (i == formatInfo.HourPosition) { var partValue = new string(input.TakeWhile(char.IsDigit).ToArray()); if (!formatInfo.CheckHourFormat(partValue, out var errorMessage)) { return(ThrowOnError <FormatException>(errorMessage, null, throwOnError)); } input = input.ChunkIntValue(partValue, out hour); if (hour < 0 || hour >= 24) { return(ThrowOnError <FormatException>("Invalid hour value. Hour must be in range <0, 24> for short format", null, throwOnError)); } continue; } if (i == formatInfo.MinutePosition) { var partValue = new string(input.TakeWhile(char.IsDigit).ToArray()); if (!formatInfo.CheckMinuteFormat(partValue, out var errorMessage)) { return(ThrowOnError <FormatException>(errorMessage, null, throwOnError)); } input = input.ChunkIntValue(partValue, out minute); continue; } if (i == formatInfo.SecondPosition) { var partValue = new string(input.TakeWhile(char.IsDigit).ToArray()); if (!formatInfo.CheckSecondFormat(partValue, out var errorMessage)) { return(ThrowOnError <FormatException>(errorMessage, null, throwOnError)); } input = input.ChunkIntValue(partValue, out second); continue; } if (i == formatInfo.AmPmPosition) { var partValue = new string(input.Take(formatInfo.AmPmFormat.Length).ToArray()); if (partValue.Length <= 0) { // We allow the input string to not have AM/PM even if the format specifies them continue; } if (!formatInfo.CheckIsAmPmShortFormat(partValue, out var amPmErrorMessage)) { return(ThrowOnError <FormatException>(amPmErrorMessage, null, throwOnError)); } var amPm = partValue; // We need to make sure hour is less than 12, because we could accept values like: 05/02/2017 16:41:24 PM hour += Meridiems.IsPm(amPm) && hour < 12 ? 12 : 0; input = input.Substring(partValue.Length); } } separator = formatInfo.GetSeparator(i); if (!string.IsNullOrEmpty(separator) && !input.StartsWith(separator)) { return(ThrowOnError <FormatException>("Invalid value. Value does not match to format", null, throwOnError)); } try { return(new DateTime(year, month, day, hour, minute, second)); } catch (Exception exc) { return(ThrowOnError <FormatException>("Invalid value. It's not possible to build new DateTime object", exc, throwOnError)); } }