所以,我有一个非常大的项目,里面有大量的DI。该解决方案过于架构化且非常复杂。该解决方案是使用 EF Code 优先方法(不存在模型)开发的,对于大多数表对象,它包含一个或多个 DateTime 属性。
它已经到了(这个过程的后期!)到我需要将日期时间转换为UTC格式的地步。对于数据库中的现有数据,我可以运行 SQL 脚本并作为一次性作业轻松进行转换。
但是对于未来和正在运行的代码,从 UTC 格式转换(在插入、更新和选择时)从数据库到和从数据库转换(即 -2 小时)并应用偏移量(即 -2 小时)以向 UI 显示正确的日期时间的最佳方法是什么?(即世界协调时到太平洋标准时间)
要记住的一件事是,有很多代码,很多嵌套和深层埋藏的代码,我希望能够找到最简单的方法,而无需接触所有对象,接口等......在EF中发出SQL命令时将其全部转换为UTC。我想使用 SQL Server 方法在存储时转换为 UTC,在检索时使用 UTC 方法转换,而不是在 .NET 级别内转换。
任何想法或见解将不胜感激。
此处没有要显示的代码,因为这不是真正的编码问题,而是关于如何让 EF 执行(在调用 SQL 之前构造查询时)SQL Server 函数的见解讨论,以与 UTC 进行转换,并使用正确的日期返回结果集。
谢谢。
几件事:
- 是的,
您的数据库应该存储 UTC,是的,您可以在 UI 中显示之前转换为特定时区。 但是,数据访问层不是执行此操作的正确位置。 数据模型应准确反映数据库中的内容。
如果您只是转换用于输入或显示的时间戳,那么最佳做法是在进入时尽早执行此操作,并在离开时尽可能晚地执行此操作。 对于某些人来说,这意味着转换是在控制器或视图模型中完成的,而对于其他人来说,它被一直推送到客户端并在JavaScript中完成。 在哪里执行此操作完全取决于您,但在数据访问层中通常不适合这样做。
有时(但并非总是如此)在域层中进行转换是有意义的,尤其是在域涉及基于本地日期或时间做出的决策时。 如果您的网域能够准确地对不同时区的时间进行建模,这将很有帮助。 Noda Time在这方面真的很好,因为它包含专用类型,例如
Instant
,LocalDate
和ZonedDateTime
。你希望在一个地方做到这一点,这样你就不必触及代码的所有部分是可以理解的 - 但我可以从经验中告诉你,采取一刀切的方法最终会回来困扰你。 事实是,"始终使用UTC"的常见建议是短视的。 最终,你将拥有需要本地时间值的方案。 并非所有
DateTime
的使用都是出于相同的目的。 通常 UTC 是合适的,但有时不是。 如果您找到一种方法在您的架构中全局应用 UTC 转换,那么您就放弃了控制它的能力。特别是,时间戳是 UTC(或
DateTimeOffset
)的好方案,但调度不是。 日程安排通常基于当地时间,例如每天早上 8:00(隐含的当地时间)响起的闹钟,或每周三东部时间上午 10:00 举行的会议。另外,请考虑某些日期时间方案实际上只是没有时间的固定日期。 生日和工作纪念日就是一个很好的例子。 不要试图将这些东西转换为 UTC,否则您最终可能会成为 +/- 1 天。
另外,您说您正在尝试转换为固定偏移量,但您还说您想从 UTC 转换为 PST。 您应该了解 PST 仅在一年中的部分时间有效。 夏令时生效时,太平洋时间将切换到 PDT。 要正确转换时区,不能只使用固定偏移量。 转换需要确定 -08:00 还是 -07:00 是否适合要转换的时间戳。 另请参阅时区标签维基中的"时区!=偏移量"。
希望对您有所帮助,并祝您的项目好运!
您可以重写 EF SaveChanges() 方法以检查是否有任何修改的实体具有日期并将其转换为 UTC 日期。下面是要放入数据库上下文的示例代码。
public override int SaveChanges()
{
var selectedEntityList = ChangeTracker.Entries()
.Where(x => x.Entity is EntityInformation &&
(x.State == EntityState.Added || x.State == EntityState.Modified));
foreach (var entity in selectedEntityList)
{
((EntityInformation)entity.Entity).Date = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now.ToUniversalTime());
}
return base.SaveChanges();
}
因此,这将检查是否有任何修改/添加的实体具有名为 Date 的属性,并将其设置为 UTC 日期。这可能不是您需要的,但这是您应该处理解决方案的方式,以便有一个地方将日期转换为 UTC。
从 EF 6 开始,您可以使用拦截器并在 DbCommand 命中数据库之前对其进行修改。https://msdn.microsoft.com/en-us/data/dn469464(v=vs.113).aspx#BuildBlocks
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
for (int i = 0; i < command.Parameters.Count; i++) {
var param = command.Parameters[i];
if (param.DbType == DbType.DateTime) {
// Change param.Value here
}
}
}