用于远程时区的material-ui LocalizationProvider

Sco*_*amb 7 javascript momentjs material-ui date-fns moment-timezone

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

我正在使用material-ui v5 alphas。文档说您为时间库指定了一个@date-io适配器。看起来有四个明显的选择:

  • @date-io/date-fns(基于date-fnsdate-fns-tz 的远程时区设计有问题。它使用 Javascript Dates 来表示远程时区的日期和时间,但如果本地时区有“春天向前”小时,有时候你无法代表问题
  • @date-io/dayjs(基于dayjs)无法正确处理夏令时。问题
  • @date-io/luxon(基于luxon)看起来很有前途
  • @date-io/moment(基于时刻时刻时区)看起来很有希望

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

这两个库都支持设置全局默认时区(luxonmoment),但我更愿意在构建特定日期适配器时设置时区。根据服务器响应来搞乱全局状态是很恶心的。

我发现了一个date-io 问题,上面写着:

您可以将时刻时区直接传递给 ,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...
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

Dan*_*zik 6

问题是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"
Run Code Online (Sandbox Code Playgroud)

工作演示

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"));
Run Code Online (Sandbox Code Playgroud)