坑!为什么DateTime.Now获取时间不正确,而是过去的时间?
好久没发文章了,感觉确实该动动了。实际上已经准备了几篇内容,但是不整理到自己感觉清晰的程度,总是不想发布,因为网上零零碎碎的信息确实太多了,不必再多一份。
所以今天谈一个简单点的问题:DateTime.Now获取时间竟然不是当前时间!
原始场景使用FluentValidation框架做数据验证,顾客的出生日期与当前日期DateTime.Now.Date做对比,
对比结果就出现了标题所说的场景,Log记录大体意思是客户生日[2020-7-6]不能晚于当前日期。
而此时时间是2020-7-8。这不是怪了么!2020-7-6确实是前端传过来的没有错。
出现这个问题的时候,我的内心是崩溃的!这样说是DateTime.Now.Date获取时间不正确?What's the hell!
在说出这里面的鬼之前,先看一段代码。
1 class TimeValidation 2 { 3 /// <summary> 4 /// 最大时间值 5 /// </summary> 6 readonly DateTime _timeMax; 7 8 public TimeValidation(DateTime timeMax) 9 { 10 _timeMax = timeMax; 11 } 12 13 public bool IsTimeValid(DateTime time) 14 { 15 return time <= _timeMax; 16 } 17 }
对于这个验证类,执行以下代码返回值应该是什么呢?
1 var timeValidation = new TimeValidation(DateTime.Now); 2 bool result1 = timeValidation.IsTimeValid(now); 3 //概括目的就是:实例化验证类,用当前时间作为属性值的最大限定值,
4 //然后验证当前时间是不是有效的值,
5 //这时result1是true还是false?请先选出你的答案
最终结果当然不是true,否则就不会感觉见鬼了。在说明缘由之前,再看一段代码或许大家都能恍然大悟。
1 class TimeValidationEx 2 { 3 /// <summary> 4 /// 最大时间值表达式 5 /// </summary> 6 readonly Expression<Func<DateTime>> _timeMaxEx; 7 8 public TimeValidationEx(Expression<Func<DateTime>> timeMaxExpression) 9 { 10 _timeMaxEx = timeMaxExpression; 11 } 12 13 public bool IsTimeValid(DateTime time) 14 { 15 return time <= _timeMaxEx.Compile()(); 16 } 17 }
对于这个验证类,执行以下代码返回值应该是什么呢?
1 var timeValidation2 = new TimeValidationEx(() => DateTime.Now); 2 bool result2 = timeValidation2.IsTimeValid(now); 3 //这时result2是true还是false?
答案是true。
此时或许大家都能想到到底哪里有鬼了。
对于TimeValidation类,实例化时候传递参数DateTime.Now,本来意图是传递当前时间,但实际只是传递当前这一时刻的时间值,并赋值给了内部的_timeMax字段。
而赋值完成后,这个“当前时间”就已经是历史了(我提到的案例中就是停留在了昨天2020-7-6),而不是我们想要的当前时间了。
对于TimeValidationEx类,实例化时候传递参数是() => DateTime.Now,这是一个Expression<Func<DateTime>>类型的值(如果不了解Expression,可以单独去了解一下)。
而在验证方法里代码也是这样的:
return time <= _timeMaxEx.Compile()();
所有_timeMaxEx是一个Expression<Func<DateTime>>类型的实例,是一个Expression,而这个Expression内部是一个Func<DateTime>委托,
委托就是特殊的方法嘛,而这个方法的逻辑就是直接返回DateTime.Now的值。
相对TimeValidation类中的_timeMax字段,它在需要时候才会被调用来获取值并返回,它是“动态”的,它才是真正的获取当前时间!
总结一下:
- 在开发过程中,对于类似DateTime.Now这种“动态”的值一定要留神;
- 头脑要时刻明白,这个值只是代码执行的一瞬间获取到的,保存到任何变量以后,它就已经是固定的、过去的;
- 确定需要获取真正的当前值得时候,使用表达式或者委托(Expression<Func<T>>或Func<T>)。
努力工作 认真生活 持续学习 以勤补拙