如何在 C# 中拒绝来自客户端的非 UTC/GMT 日期?

Posted

技术标签:

【中文标题】如何在 C# 中拒绝来自客户端的非 UTC/GMT 日期?【英文标题】:How to reject non UTC/GMT dates from client in C#? 【发布时间】:2021-12-26 08:56:25 【问题描述】:

我想强制客户端(web、androidios)仅在 UTC/GMT 时间发送 API。

这意味着他们获取用户的本地时间(使用任何方法),将其转换为 UTC/GMT,然后将其发送到 API。

我想拒绝任何不在 UTC/GMT 中的日期时间参数。

javascript 中,我可以通过这种方式获取 UTC:

new Date().toUTCString() 给出了这个结果:

'格林威治标准时间 2021 年 11 月 15 日星期一 04:26:38'

然后我将此字符串发送到 API:

[HttpGet]
public object Parse(string clientDateTime)

    var date = DateTime.Parse(clientDateTime);
    return new 
    
        ParsedDate = date,
        Kind = date.Kind.ToString()
    ;

但是,我看到 .NET 将此日期时间解析为 Local 而不是 Utc。尽管字符串包含 GMT。

如何检查传入的日期时间并确保它是 UTC/GMT?

【问题讨论】:

您可以使用 DateTime.Parse(clientDateTime).ToUniversalTime() 将本地转换为 utc。 不,那应该没有必要。这可能会根据偏移量和服务器的本地时间更改日期时间。我错了吗? 是的,你是对的,如果这个 DateTime 是非本地的,那么你应该使用 TimeZoneInfo.ConvertTimeToUtc(DateTime, TimeZoneInfo) docs.microsoft.com/en-us/dotnet/api/… 看看:***.com/questions/10659523/…和***.com/questions/1091372/… 【参考方案1】:

您可以通过使用传统的日期时间序列化格式来大大简化您的问题。此问题的常见选择是使用 ISO 8601 日期时间格式。

Here你可以找到这个格式的深入解释,但是作为一个例子,这是一个ISO 8601格式的日期时间:2021-11-15T06:40:48.204Z(最后的Z表示这个字符串表示的日期时间是@ 987654334@)

固定日期和时间格式的主要优点是您会提前知道格式,并且您可以更好地解析服务器上的日期时间字符串。 p>

使用 ISO 8601 格式是一个不错的选择,因为它是一种众所周知的格式,并且它是日期时间序列化的事实上的标准:这基本上意味着为您的应用程序编写客户端的任何人都将能够遵守所需的格式。 当然,您需要清楚地记录此约定,以便您的客户(或您的开发人员同行)了解它。

另一个技巧是使用DateTimeOffset 结构而不是DateTimeDateTimeOffset 基本上用于表示特定时间点,而这正是您想要的:您的客户将向您发送代表时间点的 ISO 8601 字符串,而您想知道应用程序中的那个时间点。

您的客户将能够使用任何时区来表达他们想要发送到您的应用程序的时间点。使用 UTC 日期时间或任何其他时区执行此操作只是一个实现细节。将日期时间字符串解析为 DateTimeOffset 实例后,如果您确实需要,可以通过检查 Offset 属性来检查它是否为 UTC 时间:如果 @987654341 将为零 TimeSpan 值@instance 表示 UTC 日期和时间。

为了在 Javascript 客户端应用程序中操作日期,我强烈建议使用 Moment.js 库。检查this docs 以了解如何使用 Moment.js 获取 ISO 8601 字符串

您可以使用 this helper method 将 ISO 8601 字符串解析为 DateTimeOffset 实例。此实现允许客户端向您发送范围广泛的符合 ISO 8601 的字符串格式,如果您愿意,可以通过减少允许的格式数量来更严格(参见代码中的 Iso8601Formats 静态字段)。

总结一下:

要求您的客户仅以符合 ISO8601 规范的格式向您发送日期时间字符串。 清楚地记录这个选择 对于 Javascript 客户端,请使用 Moment.js 之类的库来操作日期和时间。这将比使用普通的旧 javascript Date 对象简单得多。 如果您要处理表示特定时间点的日期时间字符串,请使用DateTimeOffset 结构而不是DateTime 结构。 DateTimeOffset 表示以某个时区表示的特定时间点。 Offset 属性表示由 DateTimeOffset 实例表示的时间点与 UTC 之间的差异:如果 DateTimeOffset 实例表示 UTC 日期时间,则其值将为零 TimeSpan。请注意,无论它指的是哪个时区,时间点始终是相同的,因此使用 UTC 并没有任何真正的区别(此时它只是一个实现细节)。 使用this one 之类的代码从字符串中解析DateTimeOffset 实例。此代码尝试尽可能多的符合 ISO 8601 的格式(这样做是为了接受尽可能多的有效格式)。如果您愿意,您可以决定更严格:为此,只需减少 Iso8601Formats 数组中的格式数量即可。

关于您的代码的最后说明。您从DateTime.Parse 观察到的行为正是预期的行为。检查documentation 是否为DateTime.Parse

将日期和时间的字符串表示形式转换为其 DateTime 使用当前线程文化的约定等效。

DateTime.Parse 基本上是为使用运行代码的机器的区域设置而设计的。

如果您想详细了解DateTimeDateTimeOffset 之间的区别,可以查看此*** question。

【讨论】:

以上是关于如何在 C# 中拒绝来自客户端的非 UTC/GMT 日期?的主要内容,如果未能解决你的问题,请参考以下文章

将 UTC/GMT 时间转换为本地时间

获取当前 GMT 时间

C# - 格式化当前时间

来自 C# 客户端的多部分表单

在 PHP 中获取 UTC 时间

如何防止外部实体拒绝来自测试服务器的电子邮件?