public static string ToTimeString(this TimeSpan ts)
        {
            if (ts <= TimeSpan.MinValue)
            {
                return(StringEx.Localize("min"));
            }
            if (ts >= TimeSpan.MaxValue)
            {
                return(StringEx.Localize("max"));
            }
            if (ts == TimeSpan.Zero)
            {
                return(StringEx.Localize("zero"));
            }

            double years = ts.TotalDays / c_daysinyear;
            int    i     = (int)Math.Floor(years);

            if (years == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("years") : StringEx.Localize("year")));
            }

            double months = ts.TotalDays / c_daysinmonth;

            i = (int)Math.Floor(months);
            if (months == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("months") : StringEx.Localize("month")));
            }

            double weeks = ts.TotalDays / 7;

            i = (int)Math.Floor(weeks);
            if (weeks == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("weeks") : StringEx.Localize("week")));
            }

            i = (int)Math.Floor(ts.TotalDays);
            if (ts.TotalDays == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("days") : StringEx.Localize("day")));
            }

            i = (int)Math.Floor(ts.TotalHours);
            if (ts.TotalHours == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("hours") : StringEx.Localize("hour")));
            }

            i = (int)Math.Floor(ts.TotalMinutes);
            if (ts.TotalMinutes == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("minutes") : StringEx.Localize("minute")));
            }

            i = (int)Math.Floor(ts.TotalSeconds);
            if (ts.TotalSeconds == i)
            {
                return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("seconds") : StringEx.Localize("second")));
            }

            i = (int)Math.Floor(ts.TotalMilliseconds);
            return(String.Concat(i, ' ', (Math.Abs(i) > 1) ? StringEx.Localize("milliseconds") : StringEx.Localize("millisecond")));
        }
        public static TimeSpan?ParseTime(string val)
        {
            if (String.IsNullOrWhiteSpace(val))
            {
                return(null);
            }
            string[]       v    = val.Split(StringEx.WhiteSpaceSplit, StringSplitOptions.RemoveEmptyEntries);
            double         n    = 0;
            bool           nok  = v.Length == 2 && NumberEx.TryFromServer(v[0], out n, 0.0);
            int            span = (int)Math.Floor(n);
            DateTimeOffset now  = DateTimeOffset.Now;
            string         ts   = v[v.Length - 1].ToLower();

            switch (ts)
            {
            case "past": case "min": return(TimeSpan.MinValue);

            case "future": case "max": return(TimeSpan.MaxValue);

            case "now":
            case "zero":
            case "0": return(TimeSpan.Zero);

            case "millisecond":
            case "milliseconds": return(new TimeSpan(0, 0, 0, 0, span));

            case "second":
            case "seconds": return(new TimeSpan(0, 0, span));

            case "minute":
            case "minutes": return(new TimeSpan(0, span, 0));

            case "hour":
            case "hours": return(new TimeSpan(span, 0, 0));

            case "day":
            case "days": return(new TimeSpan(span, 0, 0, 0));

            case "week":
            case "weeks": return(new TimeSpan(span * 7, 0, 0, 0));

            case "month":
            case "months": return(new TimeSpan((span * 30) + (int)Math.Floor(span * 10.5 / 24.0), (int)Math.Floor((span * 10.5) % 24.0), (int)Math.Floor((((span * 10.5) % 24.0) % 1) * 60), 0));                  // 30 days, 10.5 hours per month = 365.25 days

            case "year":
            case "years": return(new TimeSpan((span * 365) + (int)Math.Floor(span / 4.0), (int)Math.Floor((span % 4.0) * 6), 0, 0));                 // 365.25 days in a year factoring in leap year

            default:
                if (String.Compare(StringEx.Localize("past"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "past";
                }
                if (String.Compare(StringEx.Localize("min"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "min";
                }
                if (String.Compare(StringEx.Localize("future"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "future";
                }
                if (String.Compare(StringEx.Localize("max"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "max";
                }
                if (String.Compare(StringEx.Localize("now"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "now";
                }
                if (String.Compare(StringEx.Localize("zero"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "zero";
                }
                if (String.Compare(StringEx.Localize("millisecond"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "millisecond";
                }
                if (String.Compare(StringEx.Localize("milliseconds"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "milliseconds";
                }
                if (String.Compare(StringEx.Localize("minute"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "minute";
                }
                if (String.Compare(StringEx.Localize("hour"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "hour";
                }
                if (String.Compare(StringEx.Localize("day"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "day";
                }
                if (String.Compare(StringEx.Localize("week"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "week";
                }
                if (String.Compare(StringEx.Localize("weeks"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "weeks";
                }
                if (String.Compare(StringEx.Localize("month"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "month";
                }
                if (String.Compare(StringEx.Localize("months"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "months";
                }
                if (String.Compare(StringEx.Localize("year"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "year";
                }
                if (String.Compare(StringEx.Localize("years"), ts, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    goto case "years";
                }
                return(null);
            }
        }