@react-native-community
謹製の DateTimePicker
を使うと、 onChange
経由で渡される時刻が UTC(協定世界時) である、という問題にぶち当たります。
具体的には、この記事を書いている現在、6月24日(土)の 1:06
なのですが、 DateTimePicker
から渡される日付は 2023-06-22T16:06:00.000Z
になってしまいます。日本からすると 9時間前になるわけです。
多言語、多国籍で使って貰う場合は、端末のタイムゾーンに合わせて時刻を変換したほうが都合が良いでしょう。
端末から動的にタイムゾーンを取得して、タイムゾーンに合わせた時刻に変換する処理を追加してみます。
moment-timezone をインストール
shell
npm i moment-timezone
2023年現在、 moment
は推奨されない状況ではありますが、ただでさえ不安定な React Native + Expo の関係であるのと、タイムゾーンの変換がメインとなるアプリではなく、ここで冒険するメリットはないため、事例が豊富な moment-timezone
を使います。
タイムゾーンに合わせて時刻を変換する
以下のようにタイムゾーンを取得します。
TypeScript.ts
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
timeUtil.ts
のようなファイルを作成して、以下の関数を作ってください。
timeUtil.ts
import moment from 'moment-timezone';
export function convertUTCToLocalISOString(isoDate: string, timezone: string): string {
return moment(isoDate).tz(timezone).format('YYYY-MM-DDTHH:mm:ss');
}
DateTimePicker
は以下です。
DateTimePicker.tsx
<DateTimePicker
testID="dateTimePickerStart"
value={registerDate}
mode="date"
display="default"
onChange={onRegisterDateChange}
/>
onChange={onRegisterDateChange}
で渡された event
を処理する関数は以下です。
sample.ts
const onRegisterDateChange = (event: DateTimePickerEvent, selectedDate?: Date) => {
const currentDate = selectedDate !== undefined ? selectedDate : registerDate;
const localDateString = convertUTCToLocalISOString(currentDate.toISOString(), timezone);
console.log(
`元の時刻 -> 変換した時刻(timezone) ${currentDate.toISOString()} -> ${localDateString}(${timezone})`
);
setRegisterDate(currentDate);
};
コンソールには以下のように変換された内容が表示されます。
shell
元の時刻 -> 変換した時刻(timezone) 2023-06-22T16:33:00.000Z -> 2023-06-23T01:33:00(Asia/Tokyo)
コンポーネント全体は以下のとおりです。あくまでサンプルですが、全体像が伝わればと思います。
Sample.tsx
import { convertUTCToLocalISOString } from '../../lib/utils/timeUtil';
import DateTimePicker, { type DateTimePickerEvent } from '@react-native-community/datetimepicker';
import React, { useState } from 'react';
import Dialog from 'react-native-dialog';
interface DocumentingDialogProps {
documentingDialogVisible: boolean;
setDocumentingDialogVisible: React.Dispatch<React.SetStateAction<boolean>>;
}
export default function DocumentingDialog({
documentingDialogVisible,
setDocumentingDialogVisible,
}: DocumentingDialogProps) {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const [registerDate, setRegisterDate] = useState<Date>(new Date());
const [sampleLocal, setSampleLocal] = useState<string>('');
const onRegisterDateChange = (event: DateTimePickerEvent, selectedDate?: Date) => {
const currentDate = selectedDate !== undefined ? selectedDate : registerDate;
const localDateString = convertUTCToLocalISOString(currentDate.toISOString(), timezone);
console.log(
`元の時刻 -> 変換した時刻(timezone) ${currentDate.toISOString()} -> ${localDateString}(${timezone})`
);
setSampleLocal(localDateString);
setRegisterDate(currentDate);
};
return (
<Dialog.Container visible={documentingDialogVisible}>
<Dialog.Title>登録</Dialog.Title>
<Dialog.Description>{sampleLocal}</Dialog.Description>
<DateTimePicker
testID="dateTimePickerStart"
value={registerDate}
mode="date"
display="default"
onChange={onRegisterDateChange}
/>
<Dialog.Button
label="Cancel"
onPress={() => {
setDocumentingDialogVisible(false);
}}
/>
<Dialog.Button
label="OK"
onPress={async () => {
// await documentResults(tasks);
setDocumentingDialogVisible(false);
}}
/>
</Dialog.Container>
);
}
iOSの端末にインストールして動作することも確認できました。