远程时区的material-ui LocalizationProvider

Posted

技术标签:

【中文标题】远程时区的material-ui LocalizationProvider【英文标题】:material-ui LocalizationProvider for a remote time zone 【发布时间】:2021-06-16 05:39:39 【问题描述】:

我的应用需要 material-ui 日期和时间选择器来在服务器指定的远程时区上运行。我希望日期选择器上的今天圆圈实际指示远程时区的今天,并且我想将远程时区中的日期时间转换为自 1970-01-01T00:00:00Z 以来的秒数。

我正在使用 material-ui v5 alphas。 docs 表示您为时间库指定了 @date-io adapter。看起来有四个明显的选择:

@date-io/date-fns(基于 date-fns 和 date-fns-tz 有一个损坏的远程时区设计。它使用 javascript 日期来表示远程时区中的日期和时间,但如果本地时区有一个“春天”小时,有些时候你无法代表。issue。 @date-io/dayjs(基于dayjs)不能正确处理夏令时。 issue @date-io/luxon(基于 luxon)看起来很有希望 @date-io/moment(基于 moment 和 moment-timezone)看起来很有希望

所以我想为 luxon 或 moment 指定一个在特定区域中构造日期的适配器。

这两个库都支持设置全局默认时区(luxon、moment),但我更喜欢在构建特定日期适配器时设置时区。根据服务器响应混淆全局状态是很严重的。

我找到了一个date-io issue,上面写着:

您可以将时刻时区直接传递给libInstance,在这种情况下,它将使用时刻实例或全局时区集

这就是我想要的!但我对这个实例到底应该是什么感到困惑。我对 Javascript 还很陌生,这对我没有帮助。

@date-io/luxon 构造函数现在似乎不允许像这样覆盖实例。

试图让这一刻开始工作:

$ mkdir tztest
$ cd tztest
$ npm init -y
$ npm install --save moment moment-timezone '@date-io/moment'
$ node
> let MomentUtils = require('@date-io/moment');
undefined
> let moment = require('moment');
undefined
> let _ = require('moment-timezone');
undefined

> // Operations including the following should all work similarly to when using the default instance:
> (new MomentUtils()).date();
Moment<2021-03-18T11:57:30-07:00>
> (new MomentUtils()).date('2021-01-01T00:00:00');
Moment<2021-01-01T00:00:00-08:00>
> (new MomentUtils()).getCurrentLocaleCode();
'en'

> // Here's some garbage I tried
> (new MomentUtils(instance: moment().tz('America/New_York'))).date();
Uncaught TypeError: _this.moment is not a function
    at MomentUtils.date (/Users/slamb/git/tztest/node_modules/@date-io/moment/build/index.js:78:32)
> (new MomentUtils(instance: moment.tz('America/New_York'))).date();
Uncaught TypeError: _this.moment is not a function
    at MomentUtils.date (/Users/slamb/git/tztest/node_modules/@date-io/moment/build/index.js:78:32)
> (new MomentUtils(instance: () => moment.tz('America/New_York'))).date();
Moment<2021-03-18T14:44:07-04:00>
> (new MomentUtils(instance: () => moment.tz('America/New_York'))).date('2021-01-01T00:00:00');
Moment<2021-03-18T14:44:19-04:00>
> (new MomentUtils(instance: (arg1, arg2, arg3, arg4) => moment.tz(arg1, arg2, arg3, arg4, 'America/New_York'))).date('2021-01-01T00:00:00');
Moment<2021-01-01T00:00:00-05:00>
> (new MomentUtils(instance: (arg1, arg2, arg3, arg4) => moment.tz(arg1, arg2, arg3, arg4, 'America/New_York'))).getCurrentLocaleCode();
Uncaught TypeError: _this.moment.locale is not a function
    at MomentUtils.getCurrentLocaleCode (/private/tmp/tztest/node_modules/@date-io/moment/build/index.js:63:49)
> (new MomentUtils(instance: (arg1, arg2, arg3, arg4) => moment.tz(arg1, arg2, arg3, arg4, 'America/New_York'))).date();
Moment<2021-03-18T14:44:36-04:00>
> (new MomentUtils(instance: function()  return moment(arguments).tz('America/New_York');  )).date()
...here the interpreter started making fun of me...

来自@date-io/moment source,如下所述,我看到它以几种不同的方式使用它。当然,我希望所有这些都能正常工作。

export default class MomentUtils implements IUtils<defaultMoment.Moment> 
  ...
  constructor( locale, formats, instance : Opts = ) 
    this.moment = instance || defaultMoment;
  ...
    return /A|a/.test(this.moment().localeData().longDateFormat("LT"));
  ...
          return this.moment.localeData().longDateFormat(token as LongDateFormatKey);
  ...
    return this.locale || this.moment.locale();
  ...
      return this.moment(value, format, this.locale, true);
  ...
    return this.moment(value, format, true);
  ...
    const moment = this.moment(value);
  ...
    return this.moment.weekdaysShort(true);

【问题讨论】:

【参考方案1】:

问题是 material-ui 有不同的文档和日期适配器集成 api 用于不同的主要版本。

我得到moment-timezone@material-ui/pickers@4.0.0-alpha.11 合作

依赖

"@material-ui/core": "4.11.3",
"@material-ui/pickers": "4.0.0-alpha.11",
"moment": "2.29.1",
"moment-timezone": "0.5.33",
"react": "17.0.2",
"react-dom": "17.0.2"

工作演示

https://codesandbox.io/s/material-ui-starter-template-forked-pvpmc

代码示例

import React from "react";
import  render  from "react-dom";
import momentTimezone from "moment-timezone";
import  TextField  from "@material-ui/core";
import  DatePicker, LocalizationProvider  from "@material-ui/pickers";
import MomentAdapter from "@material-ui/pickers/adapter/moment";

const App = () => 
  const timeZoneFromServer = "Asia/Tokyo";
  const  moment  = new MomentAdapter( instance: momentTimezone );
  const dateWithTimeZone = moment().tz(timeZoneFromServer);

  const handleDateChange = () => ;

  return (
    <LocalizationProvider dateAdapter=MomentAdapter>
      <DatePicker
        renderInput=(props) => <TextField ...props />
        value=dateWithTimeZone
        onChange=handleDateChange
      />
    </LocalizationProvider>
  );
;

render(<App />, document.getElementById("root"));

【讨论】:

差不多了!但我希望今天的指标是正确的,即使没有选择任何内容/valuenull。所以我需要通过dateAdapter 以某种方式传递时区,而不是通过value。 (另外,我用的是v5而不是v4,但我觉得差不多:your demo link adapted to v5) 如果您执行moment().tz('Some/Place') 不向moment() 调用传递任何参数,则立即将其设置为当前日期。这是你的预期输出吗?我编辑了我的示例以删除 .add(5, 'days') 调用...我不记得为什么要添加它。 你也可以moment.tz.setDefault('America/New_York')。 momentjs.com/timezone/docs/#/using-timezones/default-timezone

以上是关于远程时区的material-ui LocalizationProvider的主要内容,如果未能解决你的问题,请参考以下文章

GPS位置到时区

如何将 datagrip 设置为与主机数据库相同的时区

Oracle RAC集群本地时间和远程时间不一致?

Django:如何让 datetime 对象知道创建它的时区?

LINUX时间同步

material-ui 'createSvgIcon' 不是从 '@material-ui/core/utils' 导出的