• 理解.Net中的时间


    前言

    如果在你的项目中只使用Datetime 足以满足一切需求,那你可能不需要点亮关于时间的技能点;
    一旦你需要参与到一些国际化项目或者与定时调度相关的项目,则有必要对.Net中的时间处理方式进行一些系统的了解。

    背景知识

    • 时区:由于世界各国家与地区经度不同,地方时也有所不同,因此会划分为不同的时区。正式的时区划分,其中包括24个时区,每一时区由一个英文字母表示。每隔经度15°划分一个时区。1884年在华盛顿召开的一次国际经度会议(又称国际子午线会议)上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1-12区,西1-12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。

    • 时区计算:计算的区时=已知区时-(已知区时的时区-要计算区时的时区),(注:东时区为正,西时区为负)。例如:
    例1:已知东京(东九区)时间为5月1日12:00,求北京(东八区)的区时?
    答:北京时间=5/1 12:00时 -(9-8)时=5/1 11:00 时。
    例2:已知北京时间为5月1日12:00,求伦敦(中时区)的区时?
    答:伦敦时间=5/1 12:00时-(8-0)时=5/1 4:00时。
    例3:已知北京时间为5月1日12:00,求纽约(西五区)的区时。
    答:纽约时间=5/1 12:00-(8-(-5))=4/30 23:00时。
    例4:已知纽约时间(西五区)为5月1日12:00,求东京(东九区)的区时。
    答:东京时间=5/1 12:00-(-5-9)=5/1 12+14 超过24时加一天 ,即5/2 2:00时。
    判断新旧两天,要看两条线,一是人为日界线-180度国际日期变更线,二是自然分界线-当地时间为0点的地区经线。自西向东越过国际日期变更线,日期应减1天,比如你在国际日期变更线西侧,当地时间是20日的00:30,当你自西向东越过国际日期变更线后,你所在位置的当地时间是19日的00:30。如果是自东向西越过国际日期变更线,则应该加1天。

    .Net 中的时间相关概念

    TimeZone

    TimeZone是一个Class,主要用来得到本地时区信息,并将时间转换到协调世界时(UTC)。注意:这个API已过时,关于时区的操作都使用TimeZoneInfo类。
    参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.timezone?view=netcore-3.0

    TimeZoneInfo

    TimeZoneInfo 是一个class,一个TimeZoneInfo对象可以表示任何时区,可用于将一个时区的时间转换为其他时区中对应的事件。并且TimeZoneInfo的实例时不可变的,一旦已实例化一个对象(不能通过new实例化),不能修改其值。

        class Program
        {
            static void Main(string[] args)
            {           
                TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
                // 属性 本机所在时区
                Console.WriteLine("本地时区名称:{0}",timeZoneInfo.StandardName);
                Console.WriteLine("是否支持夏令时规则:{0}", timeZoneInfo.SupportsDaylightSavingTime);
                Console.WriteLine("与国际标准时(零时区)的时差:{0}", timeZoneInfo.BaseUtcOffset);
                Console.WriteLine("夏令时(中国1992年后已不再实施):{0}", timeZoneInfo.DaylightName);
                Console.WriteLine("本地时区显示全名:{0}", timeZoneInfo.DisplayName);
                // 时间转换 从本地时区到目标时区
                DateTime dateTime = DateTime.Now;
                TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
                TimeZoneInfo hwt = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
                var estTime= TimeZoneInfo.ConvertTime(dateTime, est);
                Console.WriteLine("本地时间:{0}", dateTime.ToString());
                Console.WriteLine("美国东部时间:{0}", estTime.ToString());
                /* 
                 * 重载 表示把一个时间(来自本地的时间)从美国东部时间转换成夏威夷时间
                 * 注意执行此转换时第一个DateTime参数的DaTimeKind必须和源DatetimeKind一致,否则会抛出异常。
                 * 所以下面的转换不会成功。
                 * https://docs.microsoft.com/zh-cn/dotnet/api/system.timezoneinfo.converttime?view=netcore-3.0#System_TimeZoneInfo_ConvertTime_System_DateTime_System_TimeZoneInfo_
                */
                var tartetTime =TimeZoneInfo.ConvertTime(dateTime, est, hwt);
                Console.WriteLine("本地时间:{0}", tartetTime.ToString());
            }
        }
    

    参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.timezoneinfo?view=netcore-3.0

    DateTime

    DateTime是一个Struct,是最常用用于跟时间相关的操作的对象。主要关注的功能点:
    • 使用构造函数初始化一个时间

    // new DateTime()默认等于DateTime.MinValue  01/01/0001 00:00:00
    // 可以直接静态方法得到系统时间 DateTime.Now/DateTime.UtcNow/DateTime.Today ……
    // 构造函数创建时间的重载方法
    public DateTime(long ticks); // ticks 是以100纳秒为单位的数值,初始化时以0001/01/01 00:00:00 为起点
    public DateTime(long ticks, DateTimeKind kind);
    public DateTime(int year, int month, int day);
    public DateTime(int year, int month, int day, Calendar calendar);// Calendar表示系统使用的日历,System.Globalization有其实现
    public DateTime(int year, int month, int day, int hour, int minute, int second);
    public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind);
    public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind);
    

    • 将一定格式的字符串转换成时间对象

    // 可以使用Parse()  TryParse()方法将字符串转换成DateTime。
    // 支持的格式通过如下代码查看:
                List<string> badFormats = new List<String>();
                System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CurrentCulture;
                foreach (var dateString in DateTime.Now.GetDateTimeFormats())
                {
                    DateTime parsedDate;
                    if (DateTime.TryParse(dateString, out parsedDate))
                        Console.WriteLine($"{dateString,-37} {DateTime.Parse(dateString),-19}");
                    else
                        badFormats.Add(dateString);
                }
                if (badFormats.Count > 0)
                {
                    Console.WriteLine("
    Strings that could not be parsed: ");
                    foreach (var badFormat in badFormats)
                        Console.WriteLine($"   {badFormat}");
                }
                Console.ReadKey();
    2019/6/28                             2019/6/28 0:00:00
    2019-6-28                             2019/6/28 0:00:00
    2019.6.28                             2019/6/28 0:00:00
    2019/06/28                            2019/6/28 0:00:00
    2019-06-28                            2019/6/28 0:00:00
    2019.06.28                            2019/6/28 0:00:00
    19/6/28                               2019/6/28 0:00:00
    19-6-28                               2019/6/28 0:00:00
    19.6.28                               2019/6/28 0:00:00
    19/06/28                              2019/6/28 0:00:00
    2019年6月28日                            2019/6/28 0:00:00
    2019年6月28日, 星期五                       2019/6/28 0:00:00
    星期五, 2019年6月28日                       2019/6/28 0:00:00
    2019年6月28日                            2019/6/28 0:00:00
    2019年6月28日, 星期五                       2019/6/28 0:00:00
    2019年6月28日 14:35                      2019/6/28 14:35:00
    2019年6月28日 14:35                      2019/6/28 14:35:00
    2019年6月28日 下午 2:35                    2019/6/28 14:35:00
    2019年6月28日 下午 02:35                   2019/6/28 14:35:00
    2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
    2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
    2019年6月28日, 星期五 下午 2:35               2019/6/28 14:35:00
    2019年6月28日, 星期五 下午 02:35              2019/6/28 14:35:00
    星期五, 2019年6月28日 14:35                 2019/6/28 14:35:00
    星期五, 2019年6月28日 14:35                 2019/6/28 14:35:00
    星期五, 2019年6月28日 下午 2:35               2019/6/28 14:35:00
    星期五, 2019年6月28日 下午 02:35              2019/6/28 14:35:00
    2019年6月28日 14:35                      2019/6/28 14:35:00
    2019年6月28日 14:35                      2019/6/28 14:35:00
    2019年6月28日 下午 2:35                    2019/6/28 14:35:00
    2019年6月28日 下午 02:35                   2019/6/28 14:35:00
    2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
    2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
    2019年6月28日, 星期五 下午 2:35               2019/6/28 14:35:00
    2019年6月28日, 星期五 下午 02:35              2019/6/28 14:35:00
    2019年6月28日 14:35:13                   2019/6/28 14:35:13
    2019年6月28日 14:35:13                   2019/6/28 14:35:13
    2019年6月28日 下午 2:35:13                 2019/6/28 14:35:13
    2019年6月28日 下午 02:35:13                2019/6/28 14:35:13
    2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
    2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
    2019年6月28日, 星期五 下午 2:35:13            2019/6/28 14:35:13
    2019年6月28日, 星期五 下午 02:35:13           2019/6/28 14:35:13
    星期五, 2019年6月28日 14:35:13              2019/6/28 14:35:13
    星期五, 2019年6月28日 14:35:13              2019/6/28 14:35:13
    星期五, 2019年6月28日 下午 2:35:13            2019/6/28 14:35:13
    星期五, 2019年6月28日 下午 02:35:13           2019/6/28 14:35:13
    2019年6月28日 14:35:13                   2019/6/28 14:35:13
    2019年6月28日 14:35:13                   2019/6/28 14:35:13
    2019年6月28日 下午 2:35:13                 2019/6/28 14:35:13
    2019年6月28日 下午 02:35:13                2019/6/28 14:35:13
    2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
    2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
    2019年6月28日, 星期五 下午 2:35:13            2019/6/28 14:35:13
    2019年6月28日, 星期五 下午 02:35:13           2019/6/28 14:35:13
    2019/6/28 14:35                       2019/6/28 14:35:00
    2019/6/28 14:35                       2019/6/28 14:35:00
    2019/6/28 下午 2:35                     2019/6/28 14:35:00
    2019/6/28 下午 02:35                    2019/6/28 14:35:00
    2019-6-28 14:35                       2019/6/28 14:35:00
    2019-6-28 14:35                       2019/6/28 14:35:00
    2019-6-28 下午 2:35                     2019/6/28 14:35:00
    2019-6-28 下午 02:35                    2019/6/28 14:35:00
    2019.6.28 14:35                       2019/6/28 14:35:00
    2019.6.28 14:35                       2019/6/28 14:35:00
    2019.6.28 下午 2:35                     2019/6/28 14:35:00
    2019.6.28 下午 02:35                    2019/6/28 14:35:00
    2019/06/28 14:35                      2019/6/28 14:35:00
    2019/06/28 14:35                      2019/6/28 14:35:00
    2019/06/28 下午 2:35                    2019/6/28 14:35:00
    2019/06/28 下午 02:35                   2019/6/28 14:35:00
    2019-06-28 14:35                      2019/6/28 14:35:00
    2019-06-28 14:35                      2019/6/28 14:35:00
    2019-06-28 下午 2:35                    2019/6/28 14:35:00
    2019-06-28 下午 02:35                   2019/6/28 14:35:00
    2019.06.28 14:35                      2019/6/28 14:35:00
    2019.06.28 14:35                      2019/6/28 14:35:00
    2019.06.28 下午 2:35                    2019/6/28 14:35:00
    2019.06.28 下午 02:35                   2019/6/28 14:35:00
    19/6/28 14:35                         2019/6/28 14:35:00
    19/6/28 14:35                         2019/6/28 14:35:00
    19/6/28 下午 2:35                       2019/6/28 14:35:00
    19/6/28 下午 02:35                      2019/6/28 14:35:00
    19-6-28 14:35                         2019/6/28 14:35:00
    19-6-28 14:35                         2019/6/28 14:35:00
    19-6-28 下午 2:35                       2019/6/28 14:35:00
    19-6-28 下午 02:35                      2019/6/28 14:35:00
    19.6.28 14:35                         2019/6/28 14:35:00
    19.6.28 14:35                         2019/6/28 14:35:00
    19/06/28 14:35                        2019/6/28 14:35:00
    19/06/28 14:35                        2019/6/28 14:35:00
    19/06/28 下午 2:35                      2019/6/28 14:35:00
    19/06/28 下午 02:35                     2019/6/28 14:35:00
    2019/6/28 14:35:13                    2019/6/28 14:35:13
    2019/6/28 14:35:13                    2019/6/28 14:35:13
    2019/6/28 下午 2:35:13                  2019/6/28 14:35:13
    2019/6/28 下午 02:35:13                 2019/6/28 14:35:13
    2019-6-28 14:35:13                    2019/6/28 14:35:13
    2019-6-28 14:35:13                    2019/6/28 14:35:13
    2019-6-28 下午 2:35:13                  2019/6/28 14:35:13
    2019-6-28 下午 02:35:13                 2019/6/28 14:35:13
    2019.6.28 14:35:13                    2019/6/28 14:35:13
    2019.6.28 14:35:13                    2019/6/28 14:35:13
    2019.6.28 下午 2:35:13                  2019/6/28 14:35:13
    2019.6.28 下午 02:35:13                 2019/6/28 14:35:13
    2019/06/28 14:35:13                   2019/6/28 14:35:13
    2019/06/28 14:35:13                   2019/6/28 14:35:13
    2019/06/28 下午 2:35:13                 2019/6/28 14:35:13
    2019/06/28 下午 02:35:13                2019/6/28 14:35:13
    2019-06-28 14:35:13                   2019/6/28 14:35:13
    2019-06-28 14:35:13                   2019/6/28 14:35:13
    2019-06-28 下午 2:35:13                 2019/6/28 14:35:13
    2019-06-28 下午 02:35:13                2019/6/28 14:35:13
    2019.06.28 14:35:13                   2019/6/28 14:35:13
    2019.06.28 14:35:13                   2019/6/28 14:35:13
    2019.06.28 下午 2:35:13                 2019/6/28 14:35:13
    2019.06.28 下午 02:35:13                2019/6/28 14:35:13
    19/6/28 14:35:13                      2019/6/28 14:35:13
    19/6/28 14:35:13                      2019/6/28 14:35:13
    19/6/28 下午 2:35:13                    2019/6/28 14:35:13
    19/6/28 下午 02:35:13                   2019/6/28 14:35:13
    19-6-28 14:35:13                      2019/6/28 14:35:13
    19-6-28 14:35:13                      2019/6/28 14:35:13
    19-6-28 下午 2:35:13                    2019/6/28 14:35:13
    19-6-28 下午 02:35:13                   2019/6/28 14:35:13
    19.6.28 14:35:13                      2019/6/28 14:35:13
    19.6.28 14:35:13                      2019/6/28 14:35:13
    19/06/28 14:35:13                     2019/6/28 14:35:13
    19/06/28 14:35:13                     2019/6/28 14:35:13
    19/06/28 下午 2:35:13                   2019/6/28 14:35:13
    19/06/28 下午 02:35:13                  2019/6/28 14:35:13
    6月28日                                 2019/6/28 0:00:00
    6月28日                                 2019/6/28 0:00:00
    2019-06-28T14:35:13.8497646+08:00     2019/6/28 14:35:13
    2019-06-28T14:35:13.8497646+08:00     2019/6/28 14:35:13
    Fri, 28 Jun 2019 14:35:13 GMT         2019/6/28 22:35:13
    Fri, 28 Jun 2019 14:35:13 GMT         2019/6/28 22:35:13
    2019-06-28T14:35:13                   2019/6/28 14:35:13
    14:35                                 2019/6/28 14:35:00
    14:35                                 2019/6/28 14:35:00
    下午 2:35                               2019/6/28 14:35:00
    下午 02:35                              2019/6/28 14:35:00
    14:35:13                              2019/6/28 14:35:13
    14:35:13                              2019/6/28 14:35:13
    下午 2:35:13                            2019/6/28 14:35:13
    下午 02:35:13                           2019/6/28 14:35:13
    2019-06-28 14:35:13Z                  2019/6/28 22:35:13
    2019年6月28日 6:35:13                    2019/6/28 6:35:13
    2019年6月28日 06:35:13                   2019/6/28 6:35:13
    2019年6月28日 上午 6:35:13                 2019/6/28 6:35:13
    2019年6月28日 上午 06:35:13                2019/6/28 6:35:13
    2019年6月28日, 星期五 6:35:13               2019/6/28 6:35:13
    2019年6月28日, 星期五 06:35:13              2019/6/28 6:35:13
    2019年6月28日, 星期五 上午 6:35:13            2019/6/28 6:35:13
    2019年6月28日, 星期五 上午 06:35:13           2019/6/28 6:35:13
    星期五, 2019年6月28日 6:35:13               2019/6/28 6:35:13
    星期五, 2019年6月28日 06:35:13              2019/6/28 6:35:13
    星期五, 2019年6月28日 上午 6:35:13            2019/6/28 6:35:13
    星期五, 2019年6月28日 上午 06:35:13           2019/6/28 6:35:13
    2019年6月28日 6:35:13                    2019/6/28 6:35:13
    2019年6月28日 06:35:13                   2019/6/28 6:35:13
    2019年6月28日 上午 6:35:13                 2019/6/28 6:35:13
    2019年6月28日 上午 06:35:13                2019/6/28 6:35:13
    2019年6月28日, 星期五 6:35:13               2019/6/28 6:35:13
    2019年6月28日, 星期五 06:35:13              2019/6/28 6:35:13
    2019年6月28日, 星期五 上午 6:35:13            2019/6/28 6:35:13
    2019年6月28日, 星期五 上午 06:35:13           2019/6/28 6:35:13
    2019年6月                               2019/6/1 0:00:00
    2019年6月                               2019/6/1 0:00:00
    2019.6                                2019/6/1 0:00:00
    2019年6月                               2019/6/1 0:00:00
    2019年6月                               2019/6/1 0:00:00
    2019.6                                2019/6/1 0:00:00
    Strings that could not be parsed:
       19.6.28 下午 2:35
       19.6.28 下午 02:35
       19.6.28 下午 2:35:13
       19.6.28 下午 02:35:13
       2019年六月
       2019年六月
    

    • 计算时间之间的差值
    DateTime 是一个可以计算的结构体,可以对其年月日时分秒进行数学计算,一般结合系统日历输出。如果计算时间差值一般使用TimeSpan对象。

    DateTimeKind

    DateTimeKind是一个枚举,是DateTime的一个属性,表示得到时间是Utc时间还是本地时间,或者是Unspecified。在做时间计算时需要保持一致。

                var dt = DateTime.Now;
                Console.WriteLine("时间:{0},DateTimeKind:{1}", dt, dt.Kind);
                var dt2 = DateTime.UtcNow;
                Console.WriteLine("时间:{0},DateTimeKind:{1}", dt, dt2.Kind);
                Console.ReadKey();
    时间:2019/6/28 14:47:58,DateTimeKind:Local
    时间:2019/6/28 14:47:58,DateTimeKind:Utc
    

    TimeSpan

    TimeSpan是一个结构体,表示一个时间间隔。如果需要进行比较精确的计算可以通过ticks(100纳秒为单位)来计算。

    // 构造方式 显然由于年和月不是一个固定的时间刻度,所以不能按年和月构造时间刻度。
    public TimeSpan(long ticks);
    public TimeSpan(int hours, int minutes, int seconds);
    public TimeSpan(int days, int hours, int minutes, int seconds);
    public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds);
    

    DateTimeOffset

    DateTimeOffset是一个结构体,包含了Datetime值和Offset值,用于定义当前的时间和相对于零时区的偏移量。
    它提供了以下几个方面的功能:
    • 日期和时间的计算
    • 类型转换 和DateTime类型互换
    • 比较:两个DateTimeOffset值的比较会通过转换成UTC时间进行。

                var dt = DateTime.Now;
                var dtoffset = DateTimeOffset.Now;
                Console.WriteLine("当前系统时间:{0},DateTimeOffset:{1}",dt,dtoffset);
    //当前系统时间:2019/6/28 15:12:45,DateTimeOffset:2019/6/28 15:12:45 +08:00
                var dateOffset1 = DateTimeOffset.Now;
                var dateOffset2 = DateTimeOffset.UtcNow;
                var difference = dateOffset1 - dateOffset2;
                Console.WriteLine("{0} - {1} = {2}",
                                  dateOffset1, dateOffset2, difference);
    //2019/6/28 15:19:26 +08:00 - 2019/6/28 7:19:26 +00:00 = -00:00:00.0068201
    //构造方式:
    public DateTimeOffset(DateTime dateTime);
    public DateTimeOffset(long ticks, TimeSpan offset);
    public DateTimeOffset(DateTime dateTime, TimeSpan offset);
    public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, TimeSpan offset);
    public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset);
    public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset);
    

    在SQL Server2008 以上版本提供了datetimeoffset(7)的字段类型,可以进行一系列排序或比较操作。
    参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.datetimeoffset?view=netframework-4.8

    选择DateTime还是DateTimeOffset?

    DateTime 结构适用于执行以下操作的应用程序:
    • 仅使用日期。
    • 只使用时间。
    • 使用抽象的日期和时间。
    • 使用缺少时区信息的日期和时间。
    • 只使用 UTC 日期和时间。
    • 从.NET 中,外部的源,如 SQL 数据库中检索日期和时间信息。 通常,这些源按与 DateTime 结构兼容的简单格式存储日期和时间信息。
    • 执行日期和时间算法,但不关注常规结果。 例如,在向特定日期和时间添加六个月的加法运算中,是否将结果调整为夏令时通常并不重要。
    DateTimeOffset 结构表示日期和时间值,以及指示此值与 UTC 的差异程度的偏移量。 因此,此值始终明确地标识单个时间点。它适合于应用程序执行以下操作:
    • 唯一、明确地标识单个时间点。 DateTimeOffset 类型可用于明确定义“现在”的含义、记录事务时间、记录系统或应用程序事件时间以及记录创建和修改时间。
    • 执行常规日期和时间算法。
    • 保留多个相关时间,只要这些时间存储为两个单独的值或结构中的两个成员。

    参考:https://docs.microsoft.com/zh-cn/dotnet/standard/datetime/choosing-between-datetime?view=netframework-4.8

    DateTimeOffset 的使用范围比DateTime更加广泛,应该优先考虑使用。

  • 相关阅读:
    stack, deque 和 queue的对比
    Android 修改圆形progressBar颜色
    java.lang.OutOfMemoryError: Failed to allocate a 3110419 byte allocation with 741152 free bytes and
    Android GreenDAO 3.0 不修改版本号的情况下增加、删除表、添加字段
    Android监听安装卸载
    Android实现异步的几种方法
    Android GreenDao清空数据库的方法
    Android 6.0 动态权限申请
    Android无需权限显示悬浮窗
    Android 极光推送JPush---自定义提示音
  • 原文地址:https://www.cnblogs.com/aimigi/p/13884146.html
Copyright © 2020-2023  润新知