URL 2014-W43中的iso 8601转换周

时间:2014-10-23 14:12:26

标签: asp.net url asp.net-web-api iso8601 nodatime

我正在编写一个ASP.Net Web Api(2.2)。它适用于报告系统(漂亮的图表和事物)

我想用prev / next按钮提供数据的日/周/月视图。

我的URI路径如下所示:

config.Routes.MapHttpRoute(
   name: "Api UriPathExtension Report DateCategory ISO 8601",
    routeTemplate: "api/{controller}/{dateCategory}/{iso8601date}.{ext}",
    defaults: new
    {
        ext = RouteParameter.Optional
    }
);

我想接受我的iso8601日期并根据dateCategory参数中的值(日/周/月)处理它。

/// <param name="dateCategory">string {Day/Week/Month}</param>
/// <param name="iso8601date">The iso8601 compliant 
/// date(2014-10-22),
/// week(2014-W43),
/// or month(2014-10)</param>

DateTime date = DateTime.Parse(iso8601date,null,DateTimeStyles.RoundtripKind);

// 2014-10-22 works fine: 2014-10-22T00:00
// 2014-10 works fine: 2014-10-01T00:00
// 2014-W43 throws error: 

An exception of type 'System.FormatException' occurred in mscorlib.dll but was not handled in user code

Additional information: The string was not recognized as a valid DateTime. There is an unknown word starting at index 5.

有没有办法将这个2014-W43解析为我可以在JSON.net序列化中使用的时间。也许与NodaTime?我似乎找不到任何东西。

我可以构建它,但似乎应该有一个解决方案。根据维基百科,它是part of the iso8601 spec

1 个答案:

答案 0 :(得分:3)

虽然它是ISO-8601的一部分,但是没有内置于.NET或Noda Time来解析像"2014-W43"这样的值,并且有一个很好的理由:

该值代表整个周。解析它的结果数据必须是值的范围。在Noda Time中,Interval类型表示一系列Instant值,但没有其他通用范围类型。

更具体地说,整周应该被建模为完全包含范围的时区无知的仅限日期值。使用Noda Time,您将获得一对LocalDate值。

如果没有Noda Time,您将遇到.NET没有仅限日期数据类型的问题。如果您在午夜时间使用一对DateTime值,那么您需要在前一天调整结束日期,并使用半开区间逻辑,以便开始日期包含但是结束日期是独家的。

解析本身相当简单,但您无法使用任何现有的解析器。在这里,这应该让你开始。我确定您可以完成您可能需要的任何剩余功能:

public struct LocalDateRange
{
    public LocalDate StartDate { get; private set; }
    public LocalDate EndDate { get; private set; }

    public int LengthInDays
    {
        get
        {
            var period = Period.Between(StartDate, EndDate, PeriodUnits.Days);
            return (int) period.Days + 1;
        }
    }

    public LocalDateRange(LocalDate startDate, LocalDate endDate)
        : this()
    {
        StartDate = startDate;
        EndDate = endDate;
    }

    public static LocalDateRange ParseFromIsoWeek(string s)
    {
        var m = Regex.Match(s, @"(\d{4})-W(\d{1,2})");
        if (!m.Success)
            throw new FormatException();

        var year = int.Parse(m.Groups[1].Value);
        var weekNumber = int.Parse(m.Groups[2].Value);

        // Note: ISO8601 weeks run from Monday through Sunday, per spec
        var startDate = LocalDate.FromWeekYearWeekAndDay(
            year, weekNumber, IsoDayOfWeek.Monday);
        var endDate = LocalDate.FromWeekYearWeekAndDay(
            year, weekNumber, IsoDayOfWeek.Sunday);

        return new LocalDateRange(startDate, endDate);
    }

    public override string ToString()
    {
        return string.Format("[{0} - {1}]", StartDate, EndDate);
    }
}

调用它很简单,就像这样:

var week = LocalDateRange.ParseFromIsoWeek("2014-W43");

注意,在这个例子中我没有实现相等或比较功能。