Handling time-zone between client and server for date-only DateTime
我的ASP.NET Web API应用程序中有一个自定义验证属性,用于检查日期是否在未来(不允许日期)。这是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| protected override ValidationResult IsValid (object value, ValidationContext validationContext )
{
if (value != null)
{
var date = (DateTime )value;
if (date != null)
{
if(date .Date > DateTime .UtcNow.Date)
{
return new ValidationResult (FormatErrorMessage (validationContext .DisplayName), new[] { validationContext .MemberName });
}
}
}
return ValidationResult .Success;
} |
我无法弄清楚如何进行比较,因此它适用于所有时区。使用DateTime.UtcNow不是解决方案,因为如果客户端和服务器位于同一时区,则在接近午夜的几小时内,日期将是后一天。当然,DateTime.Now不适用于其他时区。那么,解决方案是什么?
更新:
在我的WebApiConfig.cs文件中,我有这段代码将DateTimeZoneHandling设置为Utc:
1 2 3 4 5 6
| jsonFormatter .SerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver (),
DateFormatHandling = DateFormatHandling .IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling .Utc
}; |
这将在JSON响应中格式化日期,如下所示:"2018-03-02T00:00:00Z"。
并且,来自客户端的所有DateTime值都将其Kind属性设置为Utc。然后,我可以将日期与UtcNow进行比较,但现在问题是日期"2018-03-02T00:00:00Z"在我的浏览器中显示为2018年3月1日,因为它被转换为本地时间(UTC -5)。
-
我不确定这个,但你可能想尝试:DateTime now = DateTime.Now; if(date.ToUniversalTime()。Date> now.ToUniversalTime()。Date)
-
@EdSF,现在,例如,在我的时区,它是晚上11:45。如果我在浏览器中选择2018年3月2日并将其发布到服务器,则服务器上的今天UTC日期也将是2018年3月2日。因此,验证将通过。但是,它不应该,因为今天的日期实际上仍然是2018年3月1日。
-
@RavirajPalvankar明天我会试试,让你知道。
-
您应该重新阅读发布内容,并找出您认为比较两个UTC仅限日期值的问题的原因。 (显然你需要在客户端和服务器上首先将本地时间转换为UTC,但这听起来并不像你那部分有问题)
-
in the hours close to midnight不,他们仍然会有相同的时间,相同的日期和当前的日期。
-
在任何情况下,由于这是一个Web API,只是不接受DateTime。需要时区时间并使用DateTimeOffset而不是DateTime。 ISO8601格式无论如何都包含时区元素。
-
@PanagiotisKanavos,我试过了。所以,不,在接近午夜的几小时内,服务器上的UTC日期会有所不同。另外,您是否能够提供一些代码来向我展示如何在这种情况下使用DateTimeOffset?
-
@ataravati no,除非服务器的时钟设置为错误的时间,否则UTC日期不应该不同。根据定义,UTC没有偏移量。如果一台机器说12:00 + 00而另一台机器说13:00 + 00,则其中一台机器出错。哪个,顺便说一下,为什么你不能相信服务器时钟来验证未来的日期。服务器设置为错误的时间。或夏令时设置错误。即使偏移也没问题,时钟偏差意味着服务器和客户端可能会报告略有不同的时间
-
@PanagiotisKanavos,他们是不同的,因为来自客户的日期不是UTC日期。看看下面的答案。在将它与UtcNow进行比较之前,我需要将日期转换为UTC。
-
@ataravati,这不是你在问题或评论中写的。是的,你必须转换。转换后,除非配置错误,否则不应该有所不同。
-
@PanagiotisKanavos,在问题和之前的评论中我写了同样的事情。请记住,我只需要来自客户端的日期时间的日期部分。这使它更复杂。问题是,在接近午夜的几个小时内,当我在浏览器中选择明天的日期并仅将日期部分发送到服务器时,服务器上的UtcNow.Date也将在明天。因此,验证将通过,但它不应该因为明天是将来。我希望我能正确解释自己。
只有两种可行的方法。
在将值发送到服务器以进行比较之前,在客户端上转换为UTC。
尝试确定客户端的时区ID(或仅针对单个时间点的偏移),并将其从客户端传递到服务器。然后,服务器可以在比较之前使用该信息进行UTC转换。
如果您不同时使用这两个选项,只将客户端本地时间值传递给服务器而没有任何偏移或时区信息,那么就无法实现您的目标。服务器没有关于客户端时区的魔力。
此外,如果您要比较整个日期(ex 2018-03-02,而不是2018-03-02T00:00:00),那么您需要确定哪个时区与您的用例相关。例如,如果您与UTC日进行比较,那么除非他们将业务流程与UTC对齐,否则这对于洛杉矶的企业来说效果不佳。
我们的聊天更新:
由于您只需要处理整个日期,因此不要进行任何时区转换。确保从datepicker中选择的日期与发送的完全相同 - 而不是通过Date对象。如果您的日期选择器不直接提供此值,则从Date对象创建它 - 手动或使用像moment.js这样的库。不要调用.toISOString(),因为除了ISO格式之外,它还会假设您指的是午夜当地时间并进行UTC转换。
也不要设置任何JSON.Net的日期设置 - 只需使用默认值。
-
谢谢你,马特!问题出在客户端,我只有一个仅限日期的日期时间。日期从日期选择器中选择。我甚至不关心时间。但是,我确实希望确保日期不会在将来。
-
能否解释为什么这对洛杉矶的企业不起作用?
-
洛杉矶的一家企业可能会根据美国太平洋时间运营。他们的"日子"并没有停止,而是在UTC日的同一时刻开始。它在标准时间提前8小时开始和结束,在夏令时提前7小时开始和结束。
-
在发送到服务器之前,我可以在客户端将日期转换为UTC。这没有问题,但为了做到这一点,我必须将DateTimeZoneHandling设置为Utc,然后从服务器到客户端的日期将被视为UTC,并且将无法正确显示在浏览器。
-
如果您的意思是在JSON设置中,则无需进行设置。只需确保使用带有尾部Z的ISO 8601格式,例如JS date对象上的.toISOString()函数给出的格式。另一方面,确保在.net DateTime对象上设置DateTimeKind.Utc,结果Z将正确显示 - 当您加载回JS date对象时,它将全部正常工作。
-
好的,这有点令人困惑。我将用更多代码更新我的问题,因此您可以帮助我编写代码。谢谢!
-
此外,由于您只询问客户整个日期,因此您可能应该从该日期的开始到下一个日期的开始之前 - 在当地时间进行解释。由于您只关心日期不在将来,只需传递日期的开始即可。例如,如果您的用户在太平洋时间,并且他们选择2018-03-02,您会将其解释为2018-03-02T00:00:00-08:00 to 2018-03-03T00:00:00-08:00,转换为UTC的是2018-03-02T08:00:00Z to 2018-03-03T08:00:00Z。将2018-03-02T08:00:00Z发送到服务器以与DateTime.UtcNow进行比较。
-
那么,你要我发两个日期?
-
请参阅我的问题中的更新。
-
让我们在聊天中继续讨论。
试试这个:
1 2
| var date = (DateTime)value;
var utcDate = date.ToUniversalTime(); |
并且比这样主持:
1
| if(utcDate .Date > DateTime.UtcNow.Date) |
-
谢谢!这有效,但如何测试它以确保它在客户端位于不同的时区时也能正常工作?
-
如果在服务器上运行,这不是一个好方法,因为它假定date是服务器的本地时区。
-
@MattJohnson,这也是我的想法。我怀疑这会在接近午夜时解决问题。你有解决方案吗?另请阅读我对OP的最新评论。谢谢!