private void AdjustForShortenedRange(Tuple <string, string> input,
                                      ArchiveDate date, bool dmy, DateMonthStyle monthStyle)
 {
     // if no match for Min/Max but we're inside a range,
     // try with shortened ranges
     // DMY can shorten Min
     if (dmy)
     {
         AdjustDmyForShortenedRange(input, date, monthStyle);
     }
     else
     {
         AdjustYmdForShortenedRange(input, date, monthStyle);
     }
 }
        /// <summary>
        /// Parses the specified text, representing one or more dates or dates
        /// ranges.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns>date(s) parsed</returns>
        /// <exception cref="ArgumentNullException">null text</exception>
        public IList <ArchiveDate> Parse(string text)
        {
            if (text == null)
            {
                throw new ArgumentNullException(nameof(text));
            }

            // split at `;` (TODO: prefilter to replace `,` as ranges
            // separator with `;`)
            DateMonthStyle     monthStyle = DateMonthStyle.Undefined;
            char               ymdSep     = '\0';
            List <ArchiveDate> dates      = new List <ArchiveDate>();

            // several dates (or dates ranges) are separated by ;
            foreach (string part in text.Split(new[] { ';' },
                                               StringSplitOptions.RemoveEmptyEntries))
            {
                // remove [ ] to simplify styles detection
                string purgedPart = _squaresRegex.Replace(part, "");

                // detect month style (unless already detected in another part)
                if (monthStyle == DateMonthStyle.Undefined)
                {
                    monthStyle = DetectMonthStyle(purgedPart);
                }

                // detect numeric M style (unless already detected in another part)
                if ((monthStyle == DateMonthStyle.Numeric ||
                     monthStyle == DateMonthStyle.Undefined) &&
                    ymdSep == '\0')
                {
                    ymdSep = DetectYmdSeparator(purgedPart);
                }

                // detect YMD order
                bool dmy = _dmyRegex.IsMatch(purgedPart) ||
                           _dmyNamedRegex.IsMatch(purgedPart);

                // split range pair if any
                var t = SplitRange(part, ymdSep);
                if (t != null)
                {
                    ArchiveDate date = new ArchiveDate
                    {
                        A = ParsePoint(t.Item1, dmy, monthStyle),
                        B = ParsePoint(t.Item2, dmy, monthStyle)
                    };

                    // corner case: shortened ranges
                    AdjustForShortenedRange(t, date, dmy, monthStyle);

                    dates.Add(date);
                } // rng
                else
                {
                    // ante/post can appear only before single-point dates
                    string pointText = part;
                    Match  m = _antePostRegex.Match(part);
                    bool   min = true, max = true;
                    if (m.Success)
                    {
                        pointText = part.Substring(m.Length);
                        if (string.Equals(m.Groups[1].Value, "ante",
                                          StringComparison.InvariantCultureIgnoreCase))
                        {
                            min = false;
                        }
                        else
                        {
                            max = false;
                        }
                    }

                    ArchiveDatePoint point = ParsePoint(pointText, dmy, monthStyle);
                    if (point != null)
                    {
                        ArchiveDate date = new ArchiveDate();
                        if (min && max)
                        {
                            date.A = point;
                            date.B = point.Clone();
                        }
                        else
                        {
                            if (min)
                            {
                                date.A = point;
                            }
                            else
                            {
                                date.B = point;
                            }
                        }
                        dates.Add(date);
                    }
                } // !rng
            }

            return(dates);
        }
        private void AdjustDmyForShortenedRange(Tuple <string, string> input,
                                                ArchiveDate date, DateMonthStyle monthStyle)
        {
            if (date.A != null || date.B?.ValueType != DateValueType.Year ||
                date.B.Month == 0)
            {
                return;
            }

            int   i     = (int)(monthStyle == 0 ? 0 : monthStyle - 1);
            Match match = _shortenedDmyRegexes[i].Match(input.Item1);

            if (match.Success)
            {
                // copy YM from Max
                date.A = new ArchiveDatePoint
                {
                    ValueType       = DateValueType.Year,
                    Value           = date.B.Value,
                    IsYearInferred  = date.B.IsYearInferred,
                    Month           = date.B.Month,
                    IsMonthInferred = date.B.IsMonthInferred
                };

                // style N has groups d/m or md
                if (i == 0)
                {
                    if (match.Groups["md"].Length > 0)
                    {
                        if (date.B.Day > 0)
                        {
                            date.A.Day = short.Parse(match.Groups["md"].Value,
                                                     CultureInfo.InvariantCulture);
                        }
                        else
                        {
                            date.A.Month = short.Parse(match.Groups["md"].Value,
                                                       CultureInfo.InvariantCulture);
                        }
                    }
                    else
                    {
                        date.A.Month = short.Parse(match.Groups["m"].Value,
                                                   CultureInfo.InvariantCulture);
                        date.A.Day = short.Parse(match.Groups["d"].Value,
                                                 CultureInfo.InvariantCulture);
                    }
                } // N
                else
                {
                    // styles S/F have groups m/d
                    if (match.Groups["m"].Length > 0)
                    {
                        YmdToken m = YmdToken.Parse(match.Groups["m"].Value, 'm');
                        date.A.Month = m.Value;
                    }
                    if (match.Groups["d"].Length > 0)
                    {
                        YmdToken d = YmdToken.Parse(match.Groups["d"].Value, 'd');
                        date.A.Day = d.Value;
                    }
                } // !N
            }
        }