private static void CommitDateComponent(ref int dateComponentIndex, ref bool hasSeasonComponent, int flags, List <char> componentBuffer, ref ExtendedDateTime extendedDateTime) { if (componentBuffer.Count == 0) { return; } var componentString = new string(componentBuffer.ToArray()); if (dateComponentIndex == 0) // We expect a year to appear first. { var isLongFormYear = false; var isExponent = false; var isPrecision = false; var digits = new List <char>(); for (int i = 0; i < componentString.Length; i++) { if (i == 0 && componentString[i] == 'y') // Component is long-form year. { isLongFormYear = true; } else if (char.IsDigit(componentString[i]) || componentString[i] == '-') // Character is year digit or negative sign. { digits.Add(componentString[i]); } else if (isLongFormYear && componentString[i] == 'e') // Component indicates exponent. { extendedDateTime.Year = int.Parse(new string(digits.ToArray())); digits.Clear(); isExponent = true; } else if (componentString[i] == 'p' && isExponent) // Component indicates precision. { extendedDateTime.YearExponent = int.Parse(new string(digits.ToArray())); digits.Clear(); isPrecision = true; isExponent = false; } else { throw new ParseException("The year is invalid.", componentString); } } if (isExponent) { extendedDateTime.YearExponent = int.Parse(new string(digits.ToArray())); } else if (isPrecision) { extendedDateTime.YearPrecision = int.Parse(new string(digits.ToArray())); } else { if (digits.Count == 0 || (digits.Count == 1 && digits[0] == '-')) { throw new ParseException("The year must have more than zero digits.", componentString); } var year = int.Parse(new string(digits.ToArray())); if (isLongFormYear) { if (isExponent && year == 0) { throw new ParseException("The significand of a long year cannot be zero.", componentString); } else if (digits.Count < 4 || (digits[0] == '-' && digits.Count < 5)) { throw new ParseException("The long year must have at least four digits.", componentString); } } else if (digits.Count < 4 || (digits[0] == '-' && digits.Count < 5) || year < -9999 || year > 9999) { throw new ParseException("The year must have four digits.", componentString); } extendedDateTime.Year = year; } extendedDateTime.YearFlags = (YearFlags)flags; dateComponentIndex++; } else if (dateComponentIndex == 1) // We expect either a month or a season to appear second. { if (componentString[0] == '2') // The component is a season. { if (componentString.Contains('^')) // Check for season qualifier. { var seasonComponentStrings = componentString.Split('^'); extendedDateTime.SeasonQualifier = seasonComponentStrings[1]; componentString = seasonComponentStrings[0]; } if (componentString.Length != 2) { throw new ParseException("The season must have two digits (excluding the qualifier).", componentString); } else if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The season must be a number (excluding the qualifier).", componentString); } var seasonInteger = int.Parse(componentString); if (seasonInteger < 21 || seasonInteger > 24) { throw new ParseException("The season must be between 21 and 24.", componentString); } extendedDateTime.Season = (Season)seasonInteger; extendedDateTime.SeasonFlags = (SeasonFlags)flags; hasSeasonComponent = true; } else // The component is a month { if (componentString.Length != 2) { throw new ParseException("The month must have two digits.", componentString); } if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The month must be a number.", componentString); } var monthInteger = int.Parse(componentString); if (monthInteger < 1 || monthInteger > 12) { throw new ParseException("The month must be between 1 and 12.", componentString); } extendedDateTime.Month = monthInteger; extendedDateTime.Precision++; extendedDateTime.MonthFlags = (MonthFlags)flags; } dateComponentIndex++; } else if (dateComponentIndex == 2) // We expect a day. { if (hasSeasonComponent) { throw new ParseException("A season and day cannot coexist.", componentString); } if (componentString.Length != 2) { throw new ParseException("The day must have two digits.", componentString); } if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The day must be a number.", componentString); } var dayInteger = int.Parse(componentString); if (dayInteger < 1 || dayInteger > 31) { throw new ParseException("The day must be between 1 and 31.", componentString); } var daysInMonth = ExtendedDateTimeCalculator.DaysInMonth(extendedDateTime.Year, extendedDateTime.Month); if (dayInteger > daysInMonth) { throw new ParseException("The month " + extendedDateTime.Month + " in the year " + extendedDateTime.Year + " has only " + daysInMonth + " days.", componentString); } extendedDateTime.Day = dayInteger; extendedDateTime.Precision++; extendedDateTime.DayFlags = (DayFlags)flags; dateComponentIndex++; } else if (dateComponentIndex > 2) { throw new ParseException("The date can have at most three components.", componentString); } componentBuffer.Clear(); }
internal static ExtendedDateTimePossibilityCollection ToPossibilityCollection(UnspecifiedExtendedDateTime unspecifiedExtendedDateTime) { var extendedDateTimePossibilityCollection = new ExtendedDateTimePossibilityCollection(); var extendedDateTimeRange = new ExtendedDateTimeRange(); var startExtendedDateTime = new ExtendedDateTime(); var endExtendedDateTime = new ExtendedDateTime(); extendedDateTimeRange.Start = startExtendedDateTime; extendedDateTimeRange.End = endExtendedDateTime; extendedDateTimePossibilityCollection.Add(extendedDateTimeRange); if (unspecifiedExtendedDateTime.Year.Length != 4) // Year { throw new ConversionException("An UnspecifiedExtendedDateTime year must be four characters long."); } var yearStartBuffer = new List <char>(); var yearEndBuffer = new List <char>(); for (int i = 0; i < 4; i++) { if (unspecifiedExtendedDateTime.Year[0] == 'u') { if (i == 0) { yearStartBuffer.Add('-'); } yearStartBuffer.Add('9'); yearEndBuffer.Add('9'); } else if (unspecifiedExtendedDateTime.Year[i] == 'u') { yearStartBuffer.Add('0'); yearEndBuffer.Add('9'); } else { yearStartBuffer.Add(unspecifiedExtendedDateTime.Year[i]); yearEndBuffer.Add(unspecifiedExtendedDateTime.Year[i]); } } var yearStart = int.Parse(new string(yearStartBuffer.ToArray())); var yearEnd = int.Parse(new string(yearEndBuffer.ToArray())); if (unspecifiedExtendedDateTime.Month == null) // Month { startExtendedDateTime.Year = yearStart; endExtendedDateTime.Year = yearEnd; return(extendedDateTimePossibilityCollection); } if (unspecifiedExtendedDateTime.Month.Length != 2) { throw new ConversionException("A month must be two characters long."); } var monthStartBuffer = new List <char>(); var monthEndBuffer = new List <char>(); var isFirstMonthDigitUnspecified = false; if (unspecifiedExtendedDateTime.Month[0] == 'u') { monthStartBuffer.Add('0'); monthEndBuffer.Add('1'); isFirstMonthDigitUnspecified = true; } else { monthStartBuffer.Add(unspecifiedExtendedDateTime.Month[0]); monthEndBuffer.Add(unspecifiedExtendedDateTime.Month[0]); } if (unspecifiedExtendedDateTime.Month[1] == 'u') { if (isFirstMonthDigitUnspecified) { monthStartBuffer.Add('1'); monthEndBuffer.Add('2'); } else { var firstDigit = int.Parse(unspecifiedExtendedDateTime.Month[0].ToString()); if (firstDigit == 0) { monthStartBuffer.Add('1'); monthEndBuffer.Add('9'); } else if (firstDigit == 1) { monthStartBuffer.Add('0'); monthEndBuffer.Add('2'); } else { throw new ConversionException("A month must be between 1 and 12."); } } } else { if (isFirstMonthDigitUnspecified) { var secondDigit = int.Parse(unspecifiedExtendedDateTime.Month[1].ToString()); if (secondDigit > 2) // Month must be in the range of 01 to 09 { monthEndBuffer[0] = '0'; } } monthStartBuffer.Add(unspecifiedExtendedDateTime.Month[1]); monthEndBuffer.Add(unspecifiedExtendedDateTime.Month[1]); } var monthStart = int.Parse(new string(monthStartBuffer.ToArray())); var monthEnd = int.Parse(new string(monthEndBuffer.ToArray())); if (unspecifiedExtendedDateTime.Day == null) // Day { startExtendedDateTime.Year = yearStart; startExtendedDateTime.Month = monthStart; endExtendedDateTime.Year = yearEnd; endExtendedDateTime.Month = monthEnd; return(extendedDateTimePossibilityCollection); } if (unspecifiedExtendedDateTime.Day.Length != 2) { throw new ConversionException("A day must be two characters long."); } var dayStartBuffer = new List <char>(); var dayEndBuffer = new List <char>(); var daysInEndMonth = ExtendedDateTimeCalculator.DaysInMonth(yearEnd, monthEnd); var isFirstDayDigitUnspecified = false; if (unspecifiedExtendedDateTime.Day[0] == 'u') { dayStartBuffer.Add('0'); dayEndBuffer.Add(daysInEndMonth.ToString()[0]); isFirstDayDigitUnspecified = true; } else { dayStartBuffer.Add(unspecifiedExtendedDateTime.Day[0]); dayEndBuffer.Add(unspecifiedExtendedDateTime.Day[0]); } if (unspecifiedExtendedDateTime.Day[1] == 'u') { if (isFirstDayDigitUnspecified) { dayStartBuffer.Add('1'); dayEndBuffer.Add(daysInEndMonth.ToString()[1]); } else { var firstDigit = int.Parse(unspecifiedExtendedDateTime.Day[0].ToString()); if (firstDigit == 0) // Day is 01 to 09 { dayStartBuffer.Add('1'); dayEndBuffer.Add('9'); } else if (firstDigit == 1) // Day is 10 to 19 { dayStartBuffer.Add('0'); dayEndBuffer.Add('9'); } else if (firstDigit == 2) // Day is 20 to 28 (if end month is February in a non-leap year) or 29 { dayStartBuffer.Add('0'); if (daysInEndMonth == 28) { dayEndBuffer.Add('8'); } else { dayEndBuffer.Add('9'); } } else if (firstDigit == 3) // Day is 30 to 30 or 31 (depending on end month) { dayStartBuffer.Add('0'); if (daysInEndMonth == 30) { dayEndBuffer.Add('0'); } else { dayEndBuffer.Add('1'); } } else { throw new ConversionException("A day must be between 1 and either 28, 29, 30, or 31 depending on the month."); } } } else { if (isFirstDayDigitUnspecified) { var secondDigit = int.Parse(unspecifiedExtendedDateTime.Day[1].ToString()); if (secondDigit > daysInEndMonth.ToString()[1]) // Decrement the first digit of the end day. { dayEndBuffer[0] = (int.Parse(dayEndBuffer[0].ToString()) - 1).ToString()[0]; } } dayStartBuffer.Add(unspecifiedExtendedDateTime.Day[1]); dayEndBuffer.Add(unspecifiedExtendedDateTime.Day[1]); } var dayStart = int.Parse(new string(dayStartBuffer.ToArray())); var dayEnd = int.Parse(new string(dayEndBuffer.ToArray())); var rangeBuffer = new List <ExtendedDateTime>(); // Collects consecutive dates, which are then converted into an ExtendedDateTimeRange. extendedDateTimePossibilityCollection.Clear(); for (var year = yearStart; year <= yearEnd; year++) { for (var month = monthStart; month <= monthEnd; month++) { for (var day = dayStart; day <= dayEnd; day++) { if (day > ExtendedDateTimeCalculator.DaysInMonth(year, month)) { if (rangeBuffer.Count == 1) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTime(year, month, day)); rangeBuffer.Clear(); } else if (rangeBuffer.Count > 0) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTimeRange(rangeBuffer.First(), rangeBuffer.Last())); rangeBuffer.Clear(); } } else { rangeBuffer.Add(new ExtendedDateTime(year, month, day)); } if (day == dayEnd) { if (rangeBuffer.Count == 1) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTime(year, month, day)); rangeBuffer.Clear(); } else if (rangeBuffer.Count > 0) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTimeRange(rangeBuffer.First(), rangeBuffer.Last())); rangeBuffer.Clear(); } } } } } return(extendedDateTimePossibilityCollection); }