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();
        }
Example #2
0
        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);
        }