时间处理
处理时间与时区总是充满挑战,尤其是在需要构建一个供不同时区用户使用的全球系统时。
ABP提供了基础架构,使得时间处理变得简单并尽可能实现自动化。本文档涵盖了ABP中与时间和时区相关的服务和系统。
如果您正在创建一个在单一时区运行的本地应用,可能不需要所有这些系统。但即便如此,仍建议使用本文档介绍的
IClock服务。
IClock服务
DateTime.Now返回一个包含服务器本地日期和时间的DateTime对象。DateTime对象不存储时区信息,因此无法知晓该对象中存储的绝对日期和时间。您只能进行假设,比如假设它是在UTC+5时区创建的。当将此值保存到数据库并在之后读取,或将其发送到不同时区的客户端时,情况会变得尤为复杂。
解决此问题的一种方法是始终使用DateTime.UtcNow,并假设所有DateTime对象均为UTC时间。这样,您可以在需要时将其转换为目标客户端的时区。
IClock在获取当前时间时提供了抽象层,使您能够在应用的单一控制点管理日期时间的类型(UTC或本地)。
示例:获取当前时间
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
namespace AbpDemo
{
public class MyService : ITransientDependency
{
private readonly IClock _clock;
public MyService(IClock clock)
{
_clock = clock;
}
public void Foo()
{
//获取当前时间!
var now = _clock.Now;
}
}
}
- 当需要获取当前时间时,注入
IClock服务。常见的基类(如ApplicationService)已注入该服务并将其作为基础属性提供,因此您可以直接使用Clock。 - 使用
Now属性获取当前时间。
大多数情况下,
IClock是您应用中唯一需要了解和使用的服务。
时钟选项
AbpClockOptions是用于设置时钟类型的选项类。
示例:使用UTC时钟
Configure<AbpClockOptions>(options =>
{
options.Kind = DateTimeKind.Utc;
});
将此代码写入您模块的ConfigureServices方法中。
默认的
Kind是Unspecified,这实际上会禁用时钟功能。如果您希望利用时钟系统的优势,请将其设置为Utc或Local。
日期时间标准化
IClock的另一个重要功能是标准化DateTime对象。
使用示例:
DateTime dateTime = ...; //从某处获取
var normalizedDateTime = Clock.Normalize(dateTime)
Normalize方法的工作方式如下:
- 如果当前时钟为UTC且给定的
DateTime为本地时间,则将其转换为UTC(使用DateTime.ToUniversalTime()方法)。 - 如果当前时钟为本地时间且给定的
DateTime为UTC,则将其转换为本地时间(使用DateTime.ToLocalTime()方法)。 - 如果给定的
DateTime的Kind为Unspecified,则将其Kind设置为当前时钟的Kind(使用DateTime.SpecifyKind(...)方法)。
当ABP获取到非由IClock.Now创建且可能与当前时钟类型不兼容的DateTime时,会使用Normalize方法。例如:
- ASP.NET Core MVC模型绑定中的
DateTime类型绑定。 - 通过 Entity Framework Core 保存和读取数据。
- 在JSON反序列化中处理
DateTime对象。
禁用日期时间标准化属性
DisableDateTimeNormalization属性可用于禁用特定类或属性的标准化操作。
UTC与用户时区之间的日期时间转换
将给定的UTC转换为用户时区
DateTime ConvertToUserTime(DateTime utcDateTime)和DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset)方法将给定的UTC DateTime或DateTimeOffset转换为用户时区。
如果
SupportsMultipleTimezone为false,或dateTime.Kind不是Utc,或者没有时区设置,则返回给定的DateTime或DateTimeOffset,不做任何更改。
示例:
如果用户的时区设置为Europe/Istanbul
// 2025-03-01T05:30:00Z
var utcTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc);
var userTime = Clock.ConvertToUserTime(utcTime);
// Europe/Istanbul与UTC有3小时时差。因此,结果将晚3小时。
userTime.Kind.ShouldBe(DateTimeKind.Unspecified);
userTime.ToString("O").ShouldBe("2025-03-01T08:30:00");
// 2025-03-01T05:30:00Z
var utcTime = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero);
var userTime = Clock.ConvertToUserTime(utcTime);
// Europe/Istanbul与UTC有3小时时差。因此,结果将晚3小时。
userTime.Offset.ShouldBe(TimeSpan.FromHours(3));
userTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00");
将给定的用户日期时间转换为UTC
DateTime ConvertToUtc(DateTime dateTime)方法将给定的用户DateTime转换为UTC。
如果
SupportsMultipleTimezone为false,或dateTime.Kind为Utc,或者没有时区设置,则返回给定的DateTime,不做任何更改。
示例:
如果用户的时区设置为Europe/Istanbul
// 2025-03-01T05:30:00
var userTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); //与本地相同
var utcTime = Clock.ConvertToUtc(userTime);
// Europe/Istanbul与UTC有3小时时差。因此,结果将早3小时。
utcTime.Kind.ShouldBe(DateTimeKind.Utc);
utcTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z");
其他IClock属性
除了Now,IClock服务还具有以下属性:
Kind:返回当前使用的时钟类型的DateTimeKind(DateTimeKind.Utc、DateTimeKind.Local或DateTimeKind.Unspecified)。SupportsMultipleTimezone:如果当前使用的时钟是UTC,则返回true。
时区
本节涵盖ABP中与时区管理相关的基础架构。
时区设置
ABP定义了一个名为Abp.Timing.TimeZone的设置,可用于为用户、租户 或全局应用设置和获取时区。默认值为空,这意味着应用将使用服务器的时区。
您可以在 设置管理UI 中更改主机/租户的全局时区。
账户专业模块支持用户在账户设置页面设置自己的时区。
有关设置系统的更多信息,请参阅设置文档。
UseAbpTimeZone中间件
app.UseAbpTimeZone()中间件用于为当前请求设置时区。
* 它将从设置中获取时区,顺序为`用户` -> `租户` -> `应用/全局`。
* 如果当前请求是匿名的,它将从请求头/cookie/表单/查询字符串中获取时区,键为`__timezone`。
如果您想获取当前时区,可以注入
ICurrentTimezoneProvider服务。 请将此中间件添加在身份验证之后。
ITimezoneProvider
ITimezoneProvider是一个服务,用于简单地将Windows时区ID值转换为Iana时区名称值,反之亦然。它还提供方法获取这些时区的列表,并通过给定名称获取TimeZoneInfo。
该服务使用TimeZoneConverter库实现。
抠丁客


