我们都写过这样的代码:
if (DateTime.Now > token.Expiry)
{
return Unauthorized();
}
它看似能用——直到彻底崩溃。 在生产环境中,这行代码会因时钟漂移、时区切换或测试模拟问题引发灾难性故障。
DateTime.Now
如同埋在应用里的定时炸弹,尤其在令牌验证等关键场景:
DateTime.Now
返回服务器本地时间。全球应用中将引发混乱:伦敦签发令牌 GMT纽约服务器 EST时区未处理授权失败/安全漏洞改用 DateTime.UtcNow
解决时区问题,但仍有缺陷:
// 仍存在硬编码依赖
public void CheckExpiry()
{
if (DateTime.UtcNow > expiry) { ... }
}
未解决问题:
public interface ITimeProvider
{
DateTime UtcNow { get; }
}
public class SystemTimeProvider : ITimeProvider
{
public DateTime UtcNow => DateTime.UtcNow;
}
builder.Services.AddSingleton<ITimeProvider, SystemTimeProvider>();
public class TokenService
{
private readonly ITimeProvider _clock;
public TokenService(ITimeProvider clock) => _clock = clock;
public bool IsExpired(DateTime expiry) => _clock.UtcNow > expiry;
}
public classFakeTimeProvider : ITimeProvider
{
public DateTime UtcNow { get; set; } = DateTime.UtcNow;
}
// 测试用例
[Test]
public void Token_Expired_Correctly()
{
// 模拟特定时间点
var clock = new FakeTimeProvider { UtcNow = new DateTime(, , ) };
var service = new TokenService(clock);
Assert.True(service.IsExpired(new DateTime(, , )));
}
优势:
public static class Clock
{
public static ITimeProvider Current { get; set; } = new SystemTimeProvider();
public static DateTime Now => Current.UtcNow;
}
// 安全调用
if (Clock.Now > expiry) { ... }
某定时任务使用 DateTime.Now
,夏令时切换时提前执行,误删核心数据
DateTime.Now
导致各服务器缓存失效时间不一致,用户看到过期内容
多个测试同时调用 DateTime.UtcNow
引发竞态条件,CI/CD 持续失败
1. 🚫 立即停止使用 DateTime.Now 尤其在云端和全球化场景中
2. ✅ 改用 UTC 但需封装 永远通过接口获取时间
3. ➡️ 依赖注入时间提供器
services.AddScoped<ITimeProvider, SystemTimeProvider>();
4. 🧪 单元测试必用模拟时钟
[Test]
public void Test_NewYear_Eve()
{
var fakeTime = new FakeTimeProvider { UtcNow = new DateTime(,,,,,) };
// 验证临界时间逻辑
}
5. 🏠 遗留代码用静态包装器过渡
// 旧代码改造
public class LegacyService
{
public void Check()
{
if (Clock.Now > deadline) { ... }
}
}
6. 👀 持续警惕时区和时钟漂移 即使使用正确模式,仍需监控:
最后:
DateTime.Now
的破坏性往往在深夜爆发。遵循本文方案,今晚你定能安睡无忧。
点击下方卡片关注DotNet NB