C# .NET 中的模糊日期时间选择器控件?

发布于 2024-07-21 06:28:02 字数 445 浏览 11 评论 0原文

我正在 C# 中为 winforms 应用程序实现模糊日期控件。 模糊日期应该能够采用模糊值,例如

  • 去年六月
  • 2 小时前
  • 2 个月前
  • 上周
  • 昨天
  • 去年

是否有“模糊”日期时间选择器的示例实现?

任何实现这种控制的想法将不胜感激

PS: 我知道此处在这里,我真的在寻找开发此类控件的任何想法和灵感

I am implementing a Fuzzy Date control in C# for a winforms application. The Fuzzy Date should be able to take fuzzy values like

  • Last June
  • 2 Hours ago
  • 2 Months ago
  • Last week
  • Yesterday
  • Last year

and the like

Are there any sample implementations of "Fuzzy" Date Time Pickers?

Any ideas to implement such a control would be appreciated

PS:
I am aware of the fuzzy date algorithm spoken about here and here, I am really looking for any ideas and inspirations for developing such a control

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

东风软 2024-07-28 06:28:02

解析非常简单。 它可以作为一堆正则表达式和一些日期计算来实现。

下面的示例可以轻松扩展以满足您的需求。
我粗略地测试过它,它至少适用于以下字符串:

  • 下个月、明年、
  • 下 4 个月、下 3 天
  • 、3 天前、明天 5 小时前
  • 、去年
  • 昨天、上个月、
  • 上周二、下周五
  • 去年六月,明年五月,
  • 2008年1月,2009年1月1日,
  • 2019年6月,2009/01/01

辅助类:

class FuzzyDateTime
{

    static List<string> dayList = new List<string>() { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
    static List<IDateTimePattern> parsers = new List<IDateTimePattern>()
    {
       new RegexDateTimePattern (
            @"next +([2-9]\d*) +months",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(val);
            }
       ),
       new RegexDateTimePattern (
            @"next +month",
            delegate (Match m) { 
                return DateTime.Now.AddMonths(1);
            }
       ),           
       new RegexDateTimePattern (
            @"next +([2-9]\d*) +days",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddDays(val);
            }
       ),

       new RegexDateTimePattern (
            @"([2-9]\d*) +months +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(-val);
            }
       ),
       new RegexDateTimePattern (
            @"([2-9]\d*) days +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddDays(-val);
            }
       ),
       new RegexDateTimePattern (
            @"([2-9]\d*) *h(ours)? +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(-val);
            }
       ),
       new RegexDateTimePattern (
            @"tomorrow",
            delegate (Match m) {
                return DateTime.Now.AddDays(1);
            }
       ),
       new RegexDateTimePattern (
            @"today",
            delegate (Match m) {
                return DateTime.Now;
            }
       ),
       new RegexDateTimePattern (
            @"yesterday",
            delegate (Match m) {
                return DateTime.Now.AddDays(-1);
            }
       ),
       new RegexDateTimePattern (
            @"(last|next) *(year|month)",
            delegate (Match m) {
                int direction = (m.Groups[1].Value == "last")? -1 :1;
                switch(m.Groups[2].Value) 
                {
                    case "year":
                        return new DateTime(DateTime.Now.Year+direction, 1,1);
                    case "month":
                        return new DateTime(DateTime.Now.Year, DateTime.Now.Month+direction, 1);
                }
                return DateTime.MinValue;
            }
       ),
       new RegexDateTimePattern (
            String.Format(@"(last|next) *({0}).*", String.Join("|", dayList.ToArray())), //handle weekdays
            delegate (Match m) {
                var val = m.Groups[2].Value;
                var direction = (m.Groups[1].Value == "last")? -1 :1;
                var dayOfWeek = dayList.IndexOf(val.Substring(0,3));
                if (dayOfWeek >= 0) {
                    var diff = direction*(dayOfWeek - (int)DateTime.Today.DayOfWeek);
                    if (diff <= 0 ) { 
                        diff = 7 + diff;
                    }
                    return DateTime.Today.AddDays(direction * diff);
                }
                return DateTime.MinValue;
            }
       ),

       new RegexDateTimePattern (
            @"(last|next) *(.+)", // to parse months using DateTime.TryParse
            delegate (Match m) {
                DateTime dt;
                int direction = (m.Groups[1].Value == "last")? -1 :1;
                var s = String.Format("{0} {1}",m.Groups[2].Value, DateTime.Now.Year + direction);
                if (DateTime.TryParse(s, out dt)) {
                    return dt;
                } else {
                    return DateTime.MinValue;
                }
            }
       ),
       new RegexDateTimePattern (
            @".*", //as final resort parse using DateTime.TryParse
            delegate (Match m) {
                DateTime dt;
                var s = m.Groups[0].Value;
                if (DateTime.TryParse(s, out dt)) {
                    return dt;
                } else {
                    return DateTime.MinValue;
                }
            }
       ),
    };

    public static DateTime Parse(string text)
    {
        text = text.Trim().ToLower();
        var dt = DateTime.Now;
        foreach (var parser in parsers)
        {
            dt = parser.Parse(text);
            if (dt != DateTime.MinValue)
                break;
        }
        return dt;
    }
}
interface IDateTimePattern
{
    DateTime Parse(string text);
}

class RegexDateTimePattern : IDateTimePattern
{
    public delegate DateTime Interpreter(Match m);
    protected Regex regEx;
    protected Interpreter inter;
    public RegexDateTimePattern(string re, Interpreter inter)
    {
        this.regEx = new Regex(re);
        this.inter = inter;
    }
    public DateTime Parse(string text)
    {
        var m = regEx.Match(text);

        if (m.Success)
        {
            return inter(m);
        }
        return DateTime.MinValue;
    }
}

用法示例:

var val = FuzzyDateTime.Parse(textBox1.Text);
if (val != DateTime.MinValue)
   label1.Text = val.ToString();
else
   label1.Text = "unknown value";

The parsing is quite easy. It can be implemented as bunch of regexps and some date calculations.

The sample below can be easily extended to suit your needs.
I've roughly tested it and it works at least for the following strings:

  • next month, next year,
  • next 4 months, next 3 days
  • 3 days ago, 5 hours ago
  • tomorrow, yesterday
  • last year, last month,
  • last tue, next fri
  • last june, next may,
  • jan 2008, 01 january 2009,
  • june 2019, 2009/01/01

The helper class:

class FuzzyDateTime
{

    static List<string> dayList = new List<string>() { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
    static List<IDateTimePattern> parsers = new List<IDateTimePattern>()
    {
       new RegexDateTimePattern (
            @"next +([2-9]\d*) +months",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(val);
            }
       ),
       new RegexDateTimePattern (
            @"next +month",
            delegate (Match m) { 
                return DateTime.Now.AddMonths(1);
            }
       ),           
       new RegexDateTimePattern (
            @"next +([2-9]\d*) +days",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddDays(val);
            }
       ),

       new RegexDateTimePattern (
            @"([2-9]\d*) +months +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(-val);
            }
       ),
       new RegexDateTimePattern (
            @"([2-9]\d*) days +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddDays(-val);
            }
       ),
       new RegexDateTimePattern (
            @"([2-9]\d*) *h(ours)? +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(-val);
            }
       ),
       new RegexDateTimePattern (
            @"tomorrow",
            delegate (Match m) {
                return DateTime.Now.AddDays(1);
            }
       ),
       new RegexDateTimePattern (
            @"today",
            delegate (Match m) {
                return DateTime.Now;
            }
       ),
       new RegexDateTimePattern (
            @"yesterday",
            delegate (Match m) {
                return DateTime.Now.AddDays(-1);
            }
       ),
       new RegexDateTimePattern (
            @"(last|next) *(year|month)",
            delegate (Match m) {
                int direction = (m.Groups[1].Value == "last")? -1 :1;
                switch(m.Groups[2].Value) 
                {
                    case "year":
                        return new DateTime(DateTime.Now.Year+direction, 1,1);
                    case "month":
                        return new DateTime(DateTime.Now.Year, DateTime.Now.Month+direction, 1);
                }
                return DateTime.MinValue;
            }
       ),
       new RegexDateTimePattern (
            String.Format(@"(last|next) *({0}).*", String.Join("|", dayList.ToArray())), //handle weekdays
            delegate (Match m) {
                var val = m.Groups[2].Value;
                var direction = (m.Groups[1].Value == "last")? -1 :1;
                var dayOfWeek = dayList.IndexOf(val.Substring(0,3));
                if (dayOfWeek >= 0) {
                    var diff = direction*(dayOfWeek - (int)DateTime.Today.DayOfWeek);
                    if (diff <= 0 ) { 
                        diff = 7 + diff;
                    }
                    return DateTime.Today.AddDays(direction * diff);
                }
                return DateTime.MinValue;
            }
       ),

       new RegexDateTimePattern (
            @"(last|next) *(.+)", // to parse months using DateTime.TryParse
            delegate (Match m) {
                DateTime dt;
                int direction = (m.Groups[1].Value == "last")? -1 :1;
                var s = String.Format("{0} {1}",m.Groups[2].Value, DateTime.Now.Year + direction);
                if (DateTime.TryParse(s, out dt)) {
                    return dt;
                } else {
                    return DateTime.MinValue;
                }
            }
       ),
       new RegexDateTimePattern (
            @".*", //as final resort parse using DateTime.TryParse
            delegate (Match m) {
                DateTime dt;
                var s = m.Groups[0].Value;
                if (DateTime.TryParse(s, out dt)) {
                    return dt;
                } else {
                    return DateTime.MinValue;
                }
            }
       ),
    };

    public static DateTime Parse(string text)
    {
        text = text.Trim().ToLower();
        var dt = DateTime.Now;
        foreach (var parser in parsers)
        {
            dt = parser.Parse(text);
            if (dt != DateTime.MinValue)
                break;
        }
        return dt;
    }
}
interface IDateTimePattern
{
    DateTime Parse(string text);
}

class RegexDateTimePattern : IDateTimePattern
{
    public delegate DateTime Interpreter(Match m);
    protected Regex regEx;
    protected Interpreter inter;
    public RegexDateTimePattern(string re, Interpreter inter)
    {
        this.regEx = new Regex(re);
        this.inter = inter;
    }
    public DateTime Parse(string text)
    {
        var m = regEx.Match(text);

        if (m.Success)
        {
            return inter(m);
        }
        return DateTime.MinValue;
    }
}

Usage example:

var val = FuzzyDateTime.Parse(textBox1.Text);
if (val != DateTime.MinValue)
   label1.Text = val.ToString();
else
   label1.Text = "unknown value";
小梨窩很甜 2024-07-28 06:28:02

我们的用户使用的系统之一允许他们输入如下日期:

  • T // 今天
  • T + 1 // 今天加/减天数
  • T + 1w // 今天加/减周数
  • T + 1m //今天加/减几个月
  • T + 1y // 今天加/减几年

他们似乎喜欢它,并在我们的应用程序中请求它,所以我想出了以下代码。 ParseDateToString 将采用上述形式之一的字符串,加上其他一些形式,计算日期,并以“MM/DD/YYYY”格式返回。 可以很容易地更改它以返回实际的 DateTime 对象,以及添加对小时、分钟、秒或任何您想要的内容的支持。

using System;
using System.Text.RegularExpressions;

namespace Utils
{
    class DateParser
    {
        private static readonly DateTime sqlMinDate = DateTime.Parse("01/01/1753");
        private static readonly DateTime sqlMaxDate = DateTime.Parse("12/31/9999");
        private static readonly Regex todayPlusOrMinus = new Regex(@"^\s*t(\s*[\-\+]\s*\d{1,4}([dwmy])?)?\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); // T +/- number of days
        private static readonly Regex dateWithoutSlashies = new Regex(@"^\s*(\d{6}|\d{8})\s*$", RegexOptions.Compiled); // Date in MMDDYY or MMDDYYYY format

        private const string DATE_FORMAT = "MM/dd/yyyy";

        private const string ERROR_INVALID_SQL_DATE_FORMAT = "Date must be between {0} and {1}!";
        private const string ERROR_DATE_ABOVE_MAX_FORMAT = "Date must be on or before {0}!";
        private const string ERROR_USAGE = @"Unable to determine date! Please enter a valid date as either:
    MMDDYY
    MMDDYYYY
    MM/DD/YY
    MM/DD/YYYY

You may also use the following:
    T (Today's date)
    T + 1 (Today plus/minus a number of days)
    T + 1w (Today plus/minus a number of weeks)
    T + 1m (Today plus/minus a number of months)
    T + 1y (Today plus/minus a number of years)";

        public static DateTime SqlMinDate
        {
            get { return sqlMinDate; }
        }

        public static DateTime SqlMaxDate
        {
            get { return sqlMaxDate; }
        }

        /// <summary>
        /// Determine if user input string can become a valid date, and if so, returns it as a short date (MM/dd/yyyy) string.
        /// </summary>
        /// <param name="dateString"></param>
        /// <returns></returns>
        public static string ParseDateToString(string dateString)
        {
            return ParseDateToString(dateString, sqlMaxDate);
        }

        /// <summary>
        /// Determine if user input string can become a valid date, and if so, returns it as a short date (MM/dd/yyyy) string. Date must be on or before maxDate.
        /// </summary>
        /// <param name="dateString"></param>
        /// <param name="maxDate"></param>
        /// <returns></returns>
        public static string ParseDateToString(string dateString, DateTime maxDate)
        {
            if (null == dateString || 0 == dateString.Trim().Length)
            {
                return null;
            }

            dateString = dateString.ToLower();

            DateTime dateToReturn;

            if (todayPlusOrMinus.IsMatch(dateString))
            {
                dateToReturn = DateTime.Today;

                int amountToAdd;
                string unitsToAdd;

                GetAmountAndUnitsToModifyDate(dateString, out amountToAdd, out unitsToAdd);

                switch (unitsToAdd)
                {
                    case "y":
                        {
                            dateToReturn = dateToReturn.AddYears(amountToAdd);
                            break;
                        }
                    case "m":
                        {
                            dateToReturn = dateToReturn.AddMonths(amountToAdd);
                            break;
                        }
                    case "w":
                        {
                            dateToReturn = dateToReturn.AddDays(7 * amountToAdd);
                            break;
                        }
                    default:
                        {
                            dateToReturn = dateToReturn.AddDays(amountToAdd);
                            break;
                        }
                }
            }
            else
            {
                if (dateWithoutSlashies.IsMatch(dateString))
                {
                    /*
                    * It was too hard to deal with 3, 4, 5, and 7 digit date strings without slashes,
                    * so I limited it to 6 (MMDDYY) or 8 (MMDDYYYY) to avoid ambiguity.
                    * For example, 12101 could be:
                    *       1/21/01 => Jan 21, 2001
                    *       12/1/01 => Dec 01, 2001
                    *       12/10/1 => Dec 10, 2001
                    * 
                    * Limiting it to 6 or 8 digits is much easier to deal with. Boo hoo if they have to
                    * enter leading zeroes.
                    */

                    // All should parse without problems, since we ensured it was a string of digits
                    dateString = dateString.Insert(4, "/").Insert(2, "/");
                }

                try
                {
                    dateToReturn = DateTime.Parse(dateString);
                }
                catch
                {
                    throw new FormatException(ERROR_USAGE);
                }
            }

            if (IsDateSQLValid(dateToReturn))
            {
                if (dateToReturn <= maxDate)
                {
                    return dateToReturn.ToString(DATE_FORMAT);
                }

                throw new ApplicationException(string.Format(ERROR_DATE_ABOVE_MAX_FORMAT, maxDate.ToString(DATE_FORMAT)));
            }

            throw new ApplicationException(String.Format(ERROR_INVALID_SQL_DATE_FORMAT, SqlMinDate.ToString(DATE_FORMAT), SqlMaxDate.ToString(DATE_FORMAT)));
        }

        /// <summary>
        /// Converts a string of the form:
        /// 
        /// "T [+-] \d{1,4}[dwmy]" (spaces optional, case insensitive)
        /// 
        /// to a number of days/weeks/months/years to add/subtract from the current date.
        /// </summary>
        /// <param name="dateString"></param>
        /// <param name="amountToAdd"></param>
        /// <param name="unitsToAdd"></param>
        private static void GetAmountAndUnitsToModifyDate(string dateString, out int amountToAdd, out string unitsToAdd)
        {
            GroupCollection groups = todayPlusOrMinus.Match(dateString).Groups;

            amountToAdd = 0;
            unitsToAdd = "d";

            string amountWithPossibleUnits = groups[1].Value;
            string possibleUnits = groups[2].Value;

            if (null == amountWithPossibleUnits ||
                0 == amountWithPossibleUnits.Trim().Length)
            {
                return;
            }

            // Strip out the whitespace
            string stripped = Regex.Replace(amountWithPossibleUnits, @"\s", "");

            if (null == possibleUnits ||
                0 == possibleUnits.Trim().Length)
            {
                amountToAdd = Int32.Parse(stripped);
                return;
            }

            // Should have a parseable integer followed by a units indicator (d/w/m/y)
            // Remove the units indicator from the end, so we have a parseable integer.
            stripped = stripped.Remove(stripped.LastIndexOf(possibleUnits));

            amountToAdd = Int32.Parse(stripped);
            unitsToAdd = possibleUnits;
        }

        public static bool IsDateSQLValid(string dt) { return IsDateSQLValid(DateTime.Parse(dt)); }

        /// <summary>
        /// Make sure the range of dates is valid for SQL Server
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static bool IsDateSQLValid(DateTime dt)
        {
            return (dt >= SqlMinDate && dt <= SqlMaxDate);
        }
    }
}

列表中唯一可能比较困难的示例是“Last June”,但您可以通过计算自去年 6 月以来已经过去了多少个月来计算要传入的字符串。

int monthDiff = (DateTime.Now.Month + 6) % 12;

if(monthDiff == 0) monthDiff = 12;
string lastJuneCode = string.Format("T - {0}m", monthDiff);

当然,这取决于 DateTime 的 AddMonths 函数的准确性,而且我还没有真正测试过边缘情况。 它应该为您提供去年六月的日期时间,您可以使用它来查找该月的第一天和最后一天。

其他一切都应该很容易使用正则表达式进行映射或解析。 例如:

  • 上周 => “t - 1w”
  • 昨天 => “t - 1d”
  • 去年 => “t - 1y”
  • 下周 => “t+1w”
  • 明天=> “t + 1d”
  • 明年 => “t+1年”

One of the systems our users use allows them to enter dates like so:

  • T // Today
  • T + 1 // Today plus/minus a number of days
  • T + 1w // Today plus/minus a number of weeks
  • T + 1m // Today plus/minus a number of months
  • T + 1y // Today plus/minus a number of years

They seem to like it, and requested it in our app, so I came up with the following code. ParseDateToString will take a string of one of the forms above, plus a few others, calculate the date, and return it in "MM/DD/YYYY" format. It's easy enough to change it to return the actual DateTime object, as well as to add support for hours, minutes, seconds, or whatever you want.

using System;
using System.Text.RegularExpressions;

namespace Utils
{
    class DateParser
    {
        private static readonly DateTime sqlMinDate = DateTime.Parse("01/01/1753");
        private static readonly DateTime sqlMaxDate = DateTime.Parse("12/31/9999");
        private static readonly Regex todayPlusOrMinus = new Regex(@"^\s*t(\s*[\-\+]\s*\d{1,4}([dwmy])?)?\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); // T +/- number of days
        private static readonly Regex dateWithoutSlashies = new Regex(@"^\s*(\d{6}|\d{8})\s*$", RegexOptions.Compiled); // Date in MMDDYY or MMDDYYYY format

        private const string DATE_FORMAT = "MM/dd/yyyy";

        private const string ERROR_INVALID_SQL_DATE_FORMAT = "Date must be between {0} and {1}!";
        private const string ERROR_DATE_ABOVE_MAX_FORMAT = "Date must be on or before {0}!";
        private const string ERROR_USAGE = @"Unable to determine date! Please enter a valid date as either:
    MMDDYY
    MMDDYYYY
    MM/DD/YY
    MM/DD/YYYY

You may also use the following:
    T (Today's date)
    T + 1 (Today plus/minus a number of days)
    T + 1w (Today plus/minus a number of weeks)
    T + 1m (Today plus/minus a number of months)
    T + 1y (Today plus/minus a number of years)";

        public static DateTime SqlMinDate
        {
            get { return sqlMinDate; }
        }

        public static DateTime SqlMaxDate
        {
            get { return sqlMaxDate; }
        }

        /// <summary>
        /// Determine if user input string can become a valid date, and if so, returns it as a short date (MM/dd/yyyy) string.
        /// </summary>
        /// <param name="dateString"></param>
        /// <returns></returns>
        public static string ParseDateToString(string dateString)
        {
            return ParseDateToString(dateString, sqlMaxDate);
        }

        /// <summary>
        /// Determine if user input string can become a valid date, and if so, returns it as a short date (MM/dd/yyyy) string. Date must be on or before maxDate.
        /// </summary>
        /// <param name="dateString"></param>
        /// <param name="maxDate"></param>
        /// <returns></returns>
        public static string ParseDateToString(string dateString, DateTime maxDate)
        {
            if (null == dateString || 0 == dateString.Trim().Length)
            {
                return null;
            }

            dateString = dateString.ToLower();

            DateTime dateToReturn;

            if (todayPlusOrMinus.IsMatch(dateString))
            {
                dateToReturn = DateTime.Today;

                int amountToAdd;
                string unitsToAdd;

                GetAmountAndUnitsToModifyDate(dateString, out amountToAdd, out unitsToAdd);

                switch (unitsToAdd)
                {
                    case "y":
                        {
                            dateToReturn = dateToReturn.AddYears(amountToAdd);
                            break;
                        }
                    case "m":
                        {
                            dateToReturn = dateToReturn.AddMonths(amountToAdd);
                            break;
                        }
                    case "w":
                        {
                            dateToReturn = dateToReturn.AddDays(7 * amountToAdd);
                            break;
                        }
                    default:
                        {
                            dateToReturn = dateToReturn.AddDays(amountToAdd);
                            break;
                        }
                }
            }
            else
            {
                if (dateWithoutSlashies.IsMatch(dateString))
                {
                    /*
                    * It was too hard to deal with 3, 4, 5, and 7 digit date strings without slashes,
                    * so I limited it to 6 (MMDDYY) or 8 (MMDDYYYY) to avoid ambiguity.
                    * For example, 12101 could be:
                    *       1/21/01 => Jan 21, 2001
                    *       12/1/01 => Dec 01, 2001
                    *       12/10/1 => Dec 10, 2001
                    * 
                    * Limiting it to 6 or 8 digits is much easier to deal with. Boo hoo if they have to
                    * enter leading zeroes.
                    */

                    // All should parse without problems, since we ensured it was a string of digits
                    dateString = dateString.Insert(4, "/").Insert(2, "/");
                }

                try
                {
                    dateToReturn = DateTime.Parse(dateString);
                }
                catch
                {
                    throw new FormatException(ERROR_USAGE);
                }
            }

            if (IsDateSQLValid(dateToReturn))
            {
                if (dateToReturn <= maxDate)
                {
                    return dateToReturn.ToString(DATE_FORMAT);
                }

                throw new ApplicationException(string.Format(ERROR_DATE_ABOVE_MAX_FORMAT, maxDate.ToString(DATE_FORMAT)));
            }

            throw new ApplicationException(String.Format(ERROR_INVALID_SQL_DATE_FORMAT, SqlMinDate.ToString(DATE_FORMAT), SqlMaxDate.ToString(DATE_FORMAT)));
        }

        /// <summary>
        /// Converts a string of the form:
        /// 
        /// "T [+-] \d{1,4}[dwmy]" (spaces optional, case insensitive)
        /// 
        /// to a number of days/weeks/months/years to add/subtract from the current date.
        /// </summary>
        /// <param name="dateString"></param>
        /// <param name="amountToAdd"></param>
        /// <param name="unitsToAdd"></param>
        private static void GetAmountAndUnitsToModifyDate(string dateString, out int amountToAdd, out string unitsToAdd)
        {
            GroupCollection groups = todayPlusOrMinus.Match(dateString).Groups;

            amountToAdd = 0;
            unitsToAdd = "d";

            string amountWithPossibleUnits = groups[1].Value;
            string possibleUnits = groups[2].Value;

            if (null == amountWithPossibleUnits ||
                0 == amountWithPossibleUnits.Trim().Length)
            {
                return;
            }

            // Strip out the whitespace
            string stripped = Regex.Replace(amountWithPossibleUnits, @"\s", "");

            if (null == possibleUnits ||
                0 == possibleUnits.Trim().Length)
            {
                amountToAdd = Int32.Parse(stripped);
                return;
            }

            // Should have a parseable integer followed by a units indicator (d/w/m/y)
            // Remove the units indicator from the end, so we have a parseable integer.
            stripped = stripped.Remove(stripped.LastIndexOf(possibleUnits));

            amountToAdd = Int32.Parse(stripped);
            unitsToAdd = possibleUnits;
        }

        public static bool IsDateSQLValid(string dt) { return IsDateSQLValid(DateTime.Parse(dt)); }

        /// <summary>
        /// Make sure the range of dates is valid for SQL Server
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static bool IsDateSQLValid(DateTime dt)
        {
            return (dt >= SqlMinDate && dt <= SqlMaxDate);
        }
    }
}

The only example in your list that might be difficult would be "Last June", but you could just calculate the string to pass in by figuring out how many months it's been since last June.

int monthDiff = (DateTime.Now.Month + 6) % 12;

if(monthDiff == 0) monthDiff = 12;
string lastJuneCode = string.Format("T - {0}m", monthDiff);

Of course, that'll depend on the accuracy of DateTime's AddMonths function, and I haven't really tested edge cases for that. It should give you a DateTime last June, and you could just use that to find the first and last of the month.

Everything else should be fairly easy to map or parse with regular expressions. For example:

  • Last week => "t - 1w"
  • Yesterday => "t - 1d"
  • Last year => "t - 1y"
  • Next week => "t + 1w"
  • Tomorrow => "t + 1d"
  • Next year => "t + 1y"
几度春秋 2024-07-28 06:28:02

我们有类似的控制。 我们只是添加一个组合框列表 - 控件来选择您的选择。

周期选择器:

  • 从 [datepicker] 直到 [datepicker]
  • [numericupdown] 几个月前
  • [numericupdown] 小时前
  • 上周
  • 昨天
  • 本周 [datepicker]
  • 天 [datepicker]
  • ...

只需选择对您的目的有意义的选择即可。

实现这个比解析文本要容易得多。 计算相当简单。

请务必注意您选择的是句号。 去年是指从2008年1月开始> 2008 年 12 月。两个小时前,从现在到现在 - 2 小时。 ETC。

We have a similar control. We just add a list of combo boxes - controls to pick your choice.

PeriodSelector:

  • From [datepicker] Until [datepicker]
  • [numericupdown] months ago
  • [numericupdown] hours ago
  • Last week
  • Yesterday
  • Week [datepicker]
  • Day [datepicker]
  • ...

And just take the choices that make sense for your purpose.

It's a lot easier to implement this then parsing the text. The calculations are rather straightforward.

It's important to see that you are picking a period. Last year means from january 2008 > december 2008. Two hours ago from now untill now - 2 hours. Etc.

久隐师 2024-07-28 06:28:02

Piotr Czapla 的答案中有一个错误:

new RegexDateTimePattern (
            @"([2-9]\d*) *h(ours)? +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(-val);
            }
       ),

使用 AddMonths 而不是 AddHours()。

PS:由于论坛积分低,我无法评论他的回答。 我已经浪费了时间来调试它,为什么当我尝试“5小时前”时它会删除5天。

There is a bug in Piotr Czapla's answer:

new RegexDateTimePattern (
            @"([2-9]\d*) *h(ours)? +ago",
            delegate (Match m) {
                var val = int.Parse(m.Groups[1].Value); 
                return DateTime.Now.AddMonths(-val);
            }
       ),

AddMonths is used instead of AddHours().

PS: I can't comment on his answer because of low forum points. I've already wasted time on debugging it of why it removes 5 days when I try with "5 hours ago".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文