【问题标题】:strtotime equivalent in .NET.NET 中的 strtotime 等效项
【发布时间】:2017-02-20 18:46:33
【问题描述】:

在 .NET Framework 上是否有与 PHP 的 strtotime() 函数等效的函数。我说的是它处理类似字符串的能力:

  • strtotime("现在")
  • strtotime("2000 年 9 月 10 日")
  • strtotime("+1 天")
  • strtotime("+1 周")
  • strtotime("+1 周 2 天 4 小时 2 秒")
  • strtotime("下周四")
  • strtotime("上周一")

显然DateTime.Parse()Convert.ToDateTime() 不这样做。

我发现最接近的是一个只处理其中一些的小类:http://refactormycode.com/codes/488-parse-relative-date

编辑:对 C# 编译时特性感兴趣。问题是在运行时将人类相对日期/时间字符串转换为DateTime(即“now”--> DateTime.Now 等)。

【问题讨论】:

  • 请注意,根据您的应用程序的目标受众或使用场景,您可能会在此处遇到严重的 I16N 问题。虽然“现在”之类的内容可能很容易翻译成许多不同的语言,但“+1 天”或“下周四”等可能不会。 (去过那里,尝试过,放弃(过于复杂的变化);-)
  • 我不确定 strtotime() 是否支持各种语言。只有英文就够了。
  • 显然没有(参见php.net/manual/en/datetime.formats.relative.php vs. php.net/manual/de/datetime.formats.relative.php)。但是,如果您只需要关心英语,那么对于 C#/.NET 版本当然也不是问题。

标签: .net string parsing datetime strtotime


【解决方案1】:

到目前为止还没有答案,我是根据给出的例子做的。它支持大多数情况,除了“上一个星期四”(或一周中的其他日子)之类的情况。

/// <summary>
/// Parse a date/time string.
/// 
/// Can handle relative English-written date times like:
///  - "-1 day": Yesterday
///  - "+12 weeks": Today twelve weeks later
///  - "1 seconds": One second later from now.
///  - "5 days 1 hour ago"
///  - "1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds"
///  - "today": This day at midnight.
///  - "now": Right now (date and time).
///  - "next week"
///  - "last month"
///  - "2010-12-31"
///  - "01/01/2010 1:59 PM"
///  - "23:59:58": Today at the given time.
/// 
/// If the relative time includes hours, minutes or seconds, it's relative to now,
/// else it's relative to today.
/// </summary>
internal class RelativeDateParser
{
    private const string ValidUnits = "year|month|week|day|hour|minute|second";

    /// <summary>
    /// Ex: "last year"
    /// </summary>
    private readonly Regex _basicRelativeRegex = new Regex(@"^(last|next) +(" + ValidUnits + ")$");

    /// <summary>
    /// Ex: "+1 week"
    /// Ex: " 1week"
    /// </summary>
    private readonly Regex _simpleRelativeRegex = new Regex(@"^([+-]?\d+) *(" + ValidUnits + ")s?$");

    /// <summary>
    /// Ex: "2 minutes"
    /// Ex: "3 months 5 days 1 hour ago"
    /// </summary>
    private readonly Regex _completeRelativeRegex = new Regex(@"^(?: *(\d) *(" + ValidUnits + ")s?)+( +ago)?$");

    public DateTime Parse(string input)
    {
        // Remove the case and trim spaces.
        input = input.Trim().ToLower();

        // Try common simple words like "yesterday".
        var result = TryParseCommonDateTime(input);
        if (result.HasValue)
            return result.Value;

        // Try common simple words like "last week".
        result = TryParseLastOrNextCommonDateTime(input);
        if (result.HasValue)
            return result.Value;

        // Try simple format like "+1 week".
        result = TryParseSimpleRelativeDateTime(input);
        if (result.HasValue)
            return result.Value;

        // Try first the full format like "1 day 2 hours 10 minutes ago".
        result = TryParseCompleteRelativeDateTime(input);
        if (result.HasValue)
            return result.Value;

        // Try parse fixed dates like "01/01/2000".
        return DateTime.Parse(input);
    }

    private static DateTime? TryParseCommonDateTime(string input)
    {
        switch (input)
        {
            case "now":
                return DateTime.Now;
            case "today":
                return DateTime.Today;
            case "tomorrow":
                return DateTime.Today.AddDays(1);
            case "yesterday":
                return DateTime.Today.AddDays(-1);
            default:
                return null;
        }
    }

    private DateTime? TryParseLastOrNextCommonDateTime(string input)
    {
        var match = _basicRelativeRegex.Match(input);
        if (!match.Success)
            return null;

        var unit = match.Groups[2].Value;
        var sign = string.Compare(match.Groups[1].Value, "next", true) == 0 ? 1 : -1;
        return AddOffset(unit, sign);
    }

    private DateTime? TryParseSimpleRelativeDateTime(string input)
    {
        var match = _simpleRelativeRegex.Match(input);
        if (!match.Success)
            return null;

        var delta = Convert.ToInt32(match.Groups[1].Value);
        var unit = match.Groups[2].Value;
        return AddOffset(unit, delta);
    }

    private DateTime? TryParseCompleteRelativeDateTime(string input)
    {
        var match = _completeRelativeRegex.Match(input);
        if (!match.Success)
            return null;

        var values = match.Groups[1].Captures;
        var units = match.Groups[2].Captures;
        var sign = match.Groups[3].Success ? -1 : 1;
        Debug.Assert(values.Count == units.Count);

        var dateTime = UnitIncludeTime(units) ? DateTime.Now : DateTime.Today;

        for (int i = 0; i < values.Count; ++i)
        {
            var value = sign*Convert.ToInt32(values[i].Value);
            var unit = units[i].Value;

            dateTime = AddOffset(unit, value, dateTime);
        }

        return dateTime;
    }

    /// <summary>
    /// Add/Remove years/days/hours... to a datetime.
    /// </summary>
    /// <param name="unit">Must be one of ValidUnits</param>
    /// <param name="value">Value in given unit to add to the datetime</param>
    /// <param name="dateTime">Relative datetime</param>
    /// <returns>Relative datetime</returns>
    private static DateTime AddOffset(string unit, int value, DateTime dateTime)
    {
        switch (unit)
        {
            case "year":
                return dateTime.AddYears(value);
            case "month":
                return dateTime.AddMonths(value);
            case "week":
                return dateTime.AddDays(value * 7);
            case "day":
                return dateTime.AddDays(value);
            case "hour":
                return dateTime.AddHours(value);
            case "minute":
                return dateTime.AddMinutes(value);
            case "second":
                return dateTime.AddSeconds(value);
            default:
                throw new Exception("Internal error: Unhandled relative date/time case.");
        }
    }

    /// <summary>
    /// Add/Remove years/days/hours... relative to today or now.
    /// </summary>
    /// <param name="unit">Must be one of ValidUnits</param>
    /// <param name="value">Value in given unit to add to the datetime</param>
    /// <returns>Relative datetime</returns>
    private static DateTime AddOffset(string unit, int value)
    {
        var now = UnitIncludesTime(unit) ? DateTime.Now : DateTime.Today;
        return AddOffset(unit, value, now);
    }

    private static bool UnitIncludeTime(CaptureCollection units)
    {
        foreach (Capture unit in units)
            if (UnitIncludesTime(unit.Value))
                return true;
        return false;
    }

    private static bool UnitIncludesTime(string unit)
    {
        switch (unit)
        {
            case "hour":
            case "minute":
            case "second":
                return true;

            default:
                return false;
        }
    }
}

我确信有改进的可能,但它应该可以处理大多数英语情况。如果您发现改进(如语言环境错误等),请发表评论。

编辑:如果相对时间包括时间,则固定为相对于现在。

【讨论】:

  • 正如我在回答中所说,您可能想尝试一系列日期格式 - 请参阅此处的示例 - msdn.microsoft.com/en-us/library/ey1cdcx8.aspx
  • 接受我自己的答案听起来不公平,但除非有人找到任何其他解决方案来解决问题,否则我将不得不这样做。
【解决方案2】:

DateTime 结构有几个方法和属性来获得你需要的东西:

DateTime.Now;
DateTime.Parse("10 September 2000");
DateTime.Now.AddDays(1);
DateTime.Now.AddDays(7);
DateTime.Now.AddDays(9).AddHours(4).AddSeconds(2);
// And so on

如果 DateTime 结构提供的功能还不够,我建议查看 the noda-time project(作者 Jon Skeet)。

【讨论】:

  • 对不起,但这不是回答问题:Relative DateTime String --> DataTime object
  • @Wernight - 你是对的。我没有看到关于在运行时尝试执行此操作的编辑。我还在看看还有什么。
【解决方案3】:

我认为这里的“正确”答案是使用 https://github.com/robertwilczynski/nChronic 之类的东西。它是 Ruby 等效项的一个端口,可以解析各种日期时间格式,如下所示:“示例”部分中的http://chronic.rubyforge.org/(向下滚动)。

【讨论】:

    【解决方案4】:

    我认为您必须编写自己的方法。

    “现在”只是DateTime.Now

    “+1 天”例如是 DateTime.Now.AddDays(1)

    因此,您需要解析字符串以查找此类输入,然后调用适当的DateTime 方法。失败的情况是通过 DateTime.Parse(String, IFormatProvider, DateTimeStyles) 使用不同的 DateTimeStyles 传递字符串。

    【讨论】:

      【解决方案5】:
      DateTime date = new DateTime(10,10,2010)
      Response.Write(date.ToShortDateTimeString());
      Response.Write(date.Year);
      date = DateTime.Now;
      

      等等等等

      【讨论】:

      • 他在询问 .NET 中的日期/时间操作 - 他如何问这个问题?
      • 我不是在问日期/时间操作,而是从相对日期的字符串转换。
      • 明白。然后只需使用 date.AddDays(1).ToShortDateTimeString();等
      【解决方案6】:

      我认为这是为 DateTime 编写扩展方法来满足您的需求的最佳方式。

      也许,它可以是 OSS,所以社区可以帮助你实现它。

      【讨论】:

        猜你喜欢
        • 2010-09-20
        • 2010-10-19
        • 2011-12-22
        • 2013-12-25
        • 1970-01-01
        • 1970-01-01
        • 2011-02-20
        • 2011-05-18
        • 1970-01-01
        相关资源
        最近更新 更多