• Angular 学习笔记 ( timezone + moment + material date-picker + date pipe + asp.net core )


    更新: 2021-06-16

    Sql server convert utc to specify timezone

    有时候要方便看资料可以这样弄

    SELECT CONVERT(DATETIME, CreatedAt) AT TIME ZONE 'UTC' AT TIME ZONE 'Singapore Standard Time' FROM HangFire.Job;

    如果 convert datetimeoffset 的话, 参考这里, 以后才补上

    更新: 2021-06-11

    TimeZoneInfo

    之前有介绍过 c# nodatime, 但是毕竟是一个库吗, 如果没有要 timezone history 的话是不需要那么搞这么大动作的.

    如果我们只是单纯的想要弄 timezone 或者是 offset 不需要 case 这些 timezone 以前是否修改后, 那么我们可以用 asp.net core 自带的 TimeZoneInfo

    它会兼顾夏日时间哦. 只是没有 history 而已. 

    用法很简单

    var allTimeZones = TimeZoneInfo.GetSystemTimeZones();
    var USTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    var SGTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Singapore Standard Time");
    var now = DateTimeOffset.Now;
    var USNow = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, USTimeZone.Id);
    var SGNow = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, USTimeZone.Id);
    var USDateUtc = USNow.Date; // kind = Unspecified
    var value1 = USDateUtc.ToString("dd-MM-yyyy hh:mm:ss ttK"); // 11-06-2021 12:00:00 AM
    var value2 = USNow.ToString("dd-MM-yyyy hh:mm:ss ttK"); // 11-06-2021 07:33:26 AM-04:00

    聊聊 webapi, odata 和 js 的沟通. 

    js -> backend 是 utc time. 这个很好理解啦. 

    backend to js 就要看情况了.

    先说 web api 的情况. 

    datetimeoffset = 2021-06-12T23:56:58.3045267+08:00

    datetime local = 2021-06-12T00:00:00+08:00

    datetime utc = 2021-06-12T00:00:00Z

    datetime unspecified = 2021-06-12T00:00:00

    odata 的情况 

    datetimeoffset = 2021-06-12T23:56:58.3045267+08:00

    datetime 的话它不看 kind, 它看你的配置

    默认设置是返回 +08:00, 哪怕你是 datetime utc 它都会替你换掉时间变成 +08:00

    所以之前有说过, odata 一定要去调设置. 

    ef core 的情况. 

    sql date 的情况下, ef core 出来是 datetime unspecified 哦. 

    我个人的 best practice 是, js 和 c# 沟通要时间就用 datetimeoffset 只要日期就用用 utc

    ef core date 出来我会 convert to utc, 然后 odata 一定要设置 utc 

    更新 : 2019-06-17 

    常用 moment 

    1. Get start of day

    moment().startOf('day')
     
    2. Check same day
    if(date!.isSame(moment(), 'day'))
     
    3. Parse string by format
    moment('2019-01-02', 'ha')  

    4. to age

    const language = this.languageService.match({
        'en-US' : 'en-US',
        'zh' : 'zh-CN'
    });
    return moment(date).locale(language).fromNow();

    5. start of week 是星期天, isoWeek 是星期一

    moment().startOf('isoWeek')
     
     
     

    提醒 

    记得放 SetTimeZoneInfo, 不然 response Date value 会依据 local machine, 这是不对的. 

    app.UseMvc(config =>
    {
        // OData
        config.SetTimeZoneInfo(TimeZoneInfo.Utc); 
        config.Select().Expand().Filter().OrderBy().MaxTop(null).Count();
        config.EnableDependencyInjection();
        config.MapODataServiceRoute("WebApi", "api", ODataBuilder.GetEdmModel());
    });

    http://192.168.1.152:61547/api/orders?$filter=datetime eq 2019-05-18T09:29:27.019%2B08:00 

    +08.00 在 query 要记得 encode 成 %2B 哦

    参考 : 

    https://stackoverflow.com/questions/29979609/time-conversion-with-timezoneinfo-for-past-years

    https://nodatime.org/ 

    不常出国的人对 timezone 可能感到陌生. 

    这篇特地做了一些整理. 

    首先说说准备的资料 : 

    1. offset != timezone 

    offset 是说 +08:00, -03:00, 它表示某个时间和 UTC 的时差. 

    timezone 是说 Singapore Standard Time (SST), Greenwich Mean Time (GMT), 它表示某个时间规范.

    举个例子就明白了,Singapore Standard Time 这个 timezone 在过去 100 年里,修改了 offset 不下 5 次. 

    比如 1905 年, offset 是 +07:30 而如今却是 +08:00, 所以如果我问你, 1905-10-15 03:00:00 PM 英国是新加坡的几点... 你用现在的 offset 来算, 就会计算错误了. 

    2. javascript 自带了 timezone (历史所有 offset 调整计入都有) 

    当我们 new Date 时, 会依据当前的 timezone 去创建时间. 比如 new Date(1905,0,1) 和 new Date(2018,0,1) 在新加坡的话, 它们的 offset 是不一样的.

    3. 当我们想表达一个时间的时候,,我们一定要记得有 timezone 的概念,比如我说 18年3月16日 旁玩5点...这个就不对了,因为你说的是美国的 5 点还是中国呢 ?

    但也有一种例外, 比如我说圣诞节是在 12 月 15 日, 那么我想表达并不是任何一个 timezone, 而是每一个 timezone 都不一样的时间. 就好像我们倒数跨年一样.

    4. angular material datepicker moment 选择日期是 local 的, 可以通过设置调成 utc. 但是无法设置任意 timezone, 要嘛 local 要嘛 utc, 不能指定说我要选美国的 timezone. (目前不行, 可以通过覆盖 adapter + moment timezone 自己扩展)

    5. asp.net core 没有 timezone history, 只有某个 timezone 当今的 offset. 所以我们无法向 javascipt 那样任意的去创建一个准确 offset 很久以前的日子。 

    需要通过插件 nodatime 去实现. https://nodatime.org  (就是 java 的 jodatime 啦, 也支持 asp.net core 哦).

    6. 服务端是没有办法通过 request 获取到 user 的 timezone 的, 网上很多方法使用 js 拿 offset 其实是不准确的, 记得上面说的吗, offset != timezone. 

    最好的方式是要求用户选择一个 timezone. 

    7. javascript Date to Json 会转换成 utc 时间. 

    8. 前后端沟通 :

    utc 格式 : 2018-12-31T16:30:07.000z

    offset 格式: 2018-12-31T16:30:07.000+08:00

    unknown 格式 2018-12-31T16:30:07.000

    一般上前 -> 后, 我们都会做一次 to json, 那么 js 自带的逻辑是 to json 后就用 utc 格式了.

    但是上面 3 种格式都是可以传的, 直接传 string 不经过 json 的话, asp.net core 都可以处理. 

    一一举例,以后不要乱 : 前 -> 后 

    后是 datetimeoffset 

    utc = +00:00 

    offset = +xx:xx

    unknown = +08.00 <-- 依据后端的 timezone offset 

    后是 datetime 

    utc = 时间不变, kind = utc

    offset = 时间会调整到后端的 offset, kund = local

    unknown = 时间不变, kind = unknown

     一一举例,以后不要乱 : 后 -> 前

    DateTimeOffset 输出 offset 格式

    DateTime kind utc 输出 utc 格式

    DateTime kind local 输出 offset 格式

    DateTime kind unknown 输出 unknown 格式

    js new Date() 对不同格式有不同处理哦. 

    new Date(utc 格式) = 时间会转换成 local 

    new Date(offset 格式) = 时间会转换成 local

    new Date(unknow 格式) = 时间会保留, 但是它属于 local 时间了. 如果你要转来转去这个是会跑掉的哦,要注意.

        

    9. 日期的 locale 说的是语言展现, 并不是 timezone offset.

    10. sql = date, c# = datetime  

    c# -> sql 不过你什么 kind 只看年月日存 

    sql -> c# 出来肯定是 kind unknown 

    我的规范和对应场景 :

    如果是要计入 birthday 或者是圣诞节这种不管 timezone 的日期的话 

    sql 使用 date, c# 用 datetime, js 用 date 

    后端 -> 前端, new Date + substring 前一部分的日期, 然后设置 0点0分0秒. 让它变成 local 的当天. 

    前段 -> 后端 , 创建 Date.utc 只拿年月日.

    所以 js 是 local date, c# 和 sql 是 utc. 

    其余场景, date 也好,datetime 也好一律用 DateTimeOffset, sql server 也是 DateTimeOffset

    后端 -> 前端 就 parse json new Date 就好了

    前段 -> 后端 转换成 datetimeoffset 的格式, 而不要使用原本的 utc 格式. 

    搞清楚格式后, 剩下的就是沟通时的转换而已了, 比如我要 filter datetimeoffset 我可以很随意. 

    如果 filter date 就要小心, 记得 sql 是 utc date.

    下面是参考代码 

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    
    import { AppComponent } from './app.component';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';
    import { MatDatepickerModule } from '@angular/material/datepicker';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatMomentDateModule, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';
    import * as _moment from 'moment';
    import { MatInputModule, MAT_DATE_LOCALE } from '@angular/material';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        FormsModule,
        ReactiveFormsModule,
        MatDatepickerModule,
        MatFormFieldModule,
        MatMomentDateModule,
        MatInputModule
      ],
      providers: [
        // { provide: MAT_DATE_LOCALE, useValue: 'ja-JP' },
        { provide: MAT_DATE_LOCALE, useValue: 'en-US' },
        { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

    基本设置是这样. 

    这里有 2 个点要注意。

    第一个是 MAT_DATE_LOCALE, 这个和 timezone 一点关系也没有, 它就是表示了展现语言而已...千万不要被骗了 

    第二个是 MAT_MOMENT_DATE_ADAPTER_OPTIONS  useUtc, 下面是源码

      private _createMoment(...args: any[]): Moment {
        return (this.options && this.options.useUtc) ? moment.utc(...args) : moment(...args);
      }

    如果自己想要实现的话, 可以自己实现 MomentDateAdapter 配上 moment timezone 

    import * as _momentTimezone from 'moment-timezone';
    _momentTimezone().tz('America/Los_Angeles')

    nodatime 使用 

    1. 创建某个 timezone 的 某个时间, 这个类似于 js 的 new Date(1934,0,1) 更强大的是 js 只能设置 local timezone, 这个还能自己选 timezone.

    var timeZone = DateTimeZoneProviders.Tzdb["Asia/Kuala_Lumpur"];
    var standardDate = new DateTime(1934, 1, 1, 5, 42, 30);
    LocalDateTime nodaDate = LocalDateTime.FromDateTime(standardDate);
    ZonedDateTime dateWithZone = nodaDate.InZoneLeniently(timeZone);
    var datetimeoffset = dateWithZone.ToDateTimeOffset();

    2. 把 utc 或 datetimeoffset 转换成另一个 timezone 的时间. 

    var timeZone = DateTimeZoneProviders.Tzdb["Asia/Kuala_Lumpur"];
    var utc = new DateTime(1977, 12, 1, 0, 0, 0, DateTimeKind.Utc);
    var instant1 = Instant.FromDateTimeUtc(utc);
    var result1 = instant1.InZone(timeZone).ToOffsetDateTime();
    
    var offset = new DateTimeOffset(new DateTime(1956, 12, 1, 0, 0, 0), TimeSpan.FromHours(1));
    var instant2 = Instant.FromDateTimeOffset(offset);
    var result2 = instant2.InZone(timeZone).ToOffsetDateTime();

    3. 获取所有 timezone Id

     var allTimeZones = TzdbDateTimeZoneSource.Default.ZoneLocations.ToList();
  • 相关阅读:
    topshelf和quartz
    Dapper的使用
    多快好省的做个app开发
    端口扫描之王——nmap入门精讲(转)
    你对自己的定位是什么,就能成为什么样的人(转)
    2015工作总结及2016展望
    使用php+swoole对client数据实时更新(二) (转)
    解决一bug的流程复盘
    JSONObject与JSONArray的使用
    GDB十分钟教程
  • 原文地址:https://www.cnblogs.com/keatkeat/p/9737128.html
Copyright © 2020-2023  润新知