private static ArchiveDatePoint ParseDecade(string text)
        {
            ArchiveDatePoint point = new ArchiveDatePoint
            {
                ValueType = DateValueType.Decade
            };

            switch (text.ToLowerInvariant())
            {
            case "dieci":
                point.Value = 191;
                break;

            case "venti":
                point.Value = 192;
                break;

            case "trenta":
                point.Value = 193;
                break;

            case "quaranta":
                point.Value = 194;
                break;

            case "cinquanta":
                point.Value = 195;
                break;

            case "sessanta":
                point.Value = 196;
                break;

            case "settanta":
                point.Value = 197;
                break;

            case "ottanta":
                point.Value = 198;
                break;

            case "novanta":
                point.Value = 199;
                break;

            default:
                // 'NN or NNNN
                point.Value = text[0] == '\''
                        ? (short)(short.Parse(text.Substring(1),
                                              CultureInfo.InvariantCulture) / 10 + 190)
                        : (short)
                              (short.Parse(text, CultureInfo.InvariantCulture) / 10);
                break;
            }
            return(point);
        }
        /// <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 ArchiveDatePoint ParsePoint(string text, bool dmy,
                                            DateMonthStyle monthStyle)
        {
            // senza data or s.d.
            if (_sineDataRegex.IsMatch(text))
            {
                return(null);
            }

            // century
            if (text.IndexOf("secolo", StringComparison.OrdinalIgnoreCase) > -1 ||
                text.IndexOf("sec.", StringComparison.CurrentCultureIgnoreCase) > -1)
            {
                return(ParseCenturyPoint(text));
            }

            // decade
            Match match = _decadeRegex.Match(text);

            if (match.Success)
            {
                return(ParseDecade(match.Groups[1].Value));
            }

            // DMY/YMD
            ArchiveDatePoint point = new ArchiveDatePoint();

            // about
            if (_aboutRegex.IsMatch(text))
            {
                point.Approximation = ApproximationType.About;
            }

            int   i = (int)(monthStyle == 0 ? 0 : monthStyle - 1);
            Regex r = dmy ? _dmyRegexes[i] : _ymdRegexes[i];

            match = r.Match(text);
            if (!match.Success)
            {
                return(null);
            }

            YmdToken y = YmdToken.Parse(match.Groups["y"].Value, 'y');
            YmdToken m = YmdToken.Parse(match.Groups["m"].Value, 'm');
            YmdToken d = YmdToken.Parse(match.Groups["d"].Value, 'd');

            // take square brackets into account: this depends on the DMY/YMD order
            MarkInferred(dmy ? new[] { d, m, y } : new[] { y, m, d });

            point.Value          = y.Value;
            point.IsYearInferred = y.IsInferred;

            if (m != null)
            {
                point.Month           = m.Value;
                point.IsMonthInferred = m.IsInferred;
            }

            if (d != null)
            {
                point.Day           = d.Value;
                point.IsDayInferred = d.IsInferred;
            }

            return(point);
        }
        private ArchiveDatePoint ParseCenturyPoint(string text)
        {
            ArchiveDatePoint point = new ArchiveDatePoint
            {
                ValueType = DateValueType.Century
            };

            // prefix modifiers
            Match m = _centPrefixModRegex.Match(text);

            if (m.Success)
            {
                switch (_wsRegex.Replace(m.Groups[1].Value, " ").ToLowerInvariant())
                {
                case "ca.":
                case "circa":
                    point.Approximation = ApproximationType.About;
                    break;

                case "inizio":
                    point.Approximation = ApproximationType.Beginning;
                    break;

                case "i metà":
                    point.Approximation = ApproximationType.FirstHalf;
                    break;

                case "metà":
                    point.Approximation = ApproximationType.Mid;
                    break;

                case "ii metà":
                    point.Approximation = ApproximationType.SecondHalf;
                    break;

                case "fine":
                    point.Approximation = ApproximationType.End;
                    break;
                }
                // remove matched prefix
                text = text.Substring(m.Length);
            }

            // postfix modifiers
            if (point.Approximation == ApproximationType.None)
            {
                m = _centSuffixModRegex.Match(text);
                if (m.Success)
                {
                    switch (m.Groups[1].Value.ToLowerInvariant())
                    {
                    case "in.":
                        point.Approximation = ApproximationType.Beginning;
                        break;

                    case "ex.":
                        point.Approximation = ApproximationType.End;
                        break;
                    }
                    // remove matched postfix
                    text = text.Remove(m.Groups[1].Index, m.Groups[1].Length);
                }
            }

            // parse value
            m = _centuryRegex.Match(text);
            if (!m.Success)
            {
                throw new ArgumentException(
                          LocalizedStrings.Format(
                              Properties.Resources.NoCenturyValue, text));
            }

            point.Value = (short)RomanNumber.FromRoman(m.Value);
            if (_acRegex.IsMatch(text))
            {
                point.Value = (short)-point.Value;
            }

            return(point);
        }