time-convertor-ts

Convert time across timezones (typescript)
git clone http://git.hanabi.in/repos/time-convertor-ts.git
Log | Files | Refs | README | LICENSE

commit 6c66dc8a7c14a41bc6ea3e841ca4dc09a0549041
parent 78612fe68242d0e9ae9348f14933faf91a7b4f11
Author: Agastya Chandrakant <acagastya@outlook.com>
Date:   Mon, 25 May 2020 21:36:16 +0530

Add Future; saving pending

Diffstat:
Asrc/App/comps/ErrorAlert.tsx | 22++++++++++++++++++++++
Msrc/App/index.tsx | 13+++++++++++++
Asrc/App/pages/FutureConversion.tsx | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/utils/index.ts | 14+++++++++++---
Msrc/utils/interfaces.tsx | 13+++++++++++++
5 files changed, 265 insertions(+), 3 deletions(-)

diff --git a/src/App/comps/ErrorAlert.tsx b/src/App/comps/ErrorAlert.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +function ErrorAlert({ msg }: { msg: string }) { + return ( + <div + className="alert alert-warning alert-dismissible fade show mt-5" + role="alert" + > + {msg} + <button + type="button" + className="close" + data-dismiss="alert" + aria-label="Close" + > + <span aria-hidden="true">&times;</span> + </button> + </div> + ); +} + +export default ErrorAlert; diff --git a/src/App/index.tsx b/src/App/index.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Route, BrowserRouter as Router, Switch } from 'react-router-dom'; import DualConvertor from './pages/DualConvertor'; +import FutureConversion from './pages/FutureConversion'; import Help from './pages/Help'; import SimpleConvertor from './pages/SimpleConvertor'; @@ -24,6 +25,18 @@ function App<never>(): JSX.Element { > <Header /> <Switch> + <Route + path="/future" + render={() => ( + <FutureConversion + time={now} + setTZ1={setTimezone1} + setTZ2={setTimezone2} + TZ1={timezone1} + TZ2={timezone2} + /> + )} + /> <Route path="/help" render={() => <Help time={now} />} /> <Route path="/from-to" diff --git a/src/App/pages/FutureConversion.tsx b/src/App/pages/FutureConversion.tsx @@ -0,0 +1,206 @@ +import React from 'react'; +import moment from 'moment-timezone'; + +import TimezoneInput from '../comps/TimezoneInput'; + +import { + displayTime, + friendlyStr, + getAbbr, + timezoneList, + HMMDY, + HMSDMY, +} from '../../utils'; +import { HM, MAX_DATE, YMD, localTimezone } from '../../utils'; +import { IFutureConversion, IFutureConverted } from '../../utils/interfaces'; +import ErrorAlert from '../comps/ErrorAlert'; + +function FutureConversion({ + time: now, + setTZ1, + setTZ2, + TZ1, + TZ2, +}: IFutureConversion): JSX.Element { + // + const [date, setDate] = React.useState( + displayTime({ fmtStr: YMD, time: now, timezone: localTimezone }) + ); + const [time, setTime] = React.useState( + displayTime({ fmtStr: HM, time: now, timezone: localTimezone }) + ); + + const [selectedTime, setSelectedTime] = React.useState< + moment.Moment | undefined + >(); + const [err, setErr] = React.useState<string>(''); + + function handleDateChange(e: React.ChangeEvent<HTMLInputElement>): void { + setDate(e.target.value); + setSelectedTime(undefined); + } + + function handleFormSubmit(e: React.FormEvent<HTMLFormElement>): void { + e.preventDefault(); + + // 0. Reset Error and selected time + setErr(''); + setSelectedTime(undefined); + // 1. Validate selected date + const selectedDate = moment(date); + if (!selectedDate.isValid()) { + setErr('Chosen date is not valid.'); + console.warn(err); + return; + } + // 2. Check limit of selected date + // 2.1 MAX allowed + const epoch = moment(MAX_DATE, YMD); + if (epoch.unix() - selectedDate.unix() < 0) { + setErr('Chosen date is outside the maximum permissible limit.'); + console.warn(err); + return; + } + // 2.2 MIN allowed + const todayMoment = moment(now.format(YMD), YMD); + const selectedMoment = moment(selectedDate.format(YMD), YMD); + if (selectedMoment.unix() - todayMoment.unix() < 0) { + setErr('Chosen date is in the past.'); + console.warn(err); + return; + } + // 3. Validate chosen time + const dateStr = selectedDate.format(YMD); + const timeStr = time; + const dateTimeStr = `${dateStr} ${timeStr}`; + const dateTime = moment.tz(dateTimeStr, TZ1); + if (!dateTime.isValid()) { + setErr('Error occurred while parsing time.'); + console.warn(err); + return; + } + // 4. validate chosen timezone + if (timezoneList.indexOf(TZ1) < 0) { + setErr('Selected timezone is invalid.'); + console.warn(err); + return; + } + // 5. validate set timezone + if (timezoneList.indexOf(TZ2) < 0) { + setErr('Selected timezone to convert is invalid.'); + console.warn(err); + return; + } + // 6. convert time + setSelectedTime(dateTime); + } + + function handleTimeChange(e: React.ChangeEvent<HTMLInputElement>): void { + setTime(e.target.value); + setSelectedTime(undefined); + } + + return ( + <div className="container"> + <form onSubmit={handleFormSubmit}> + <div className="form-group"> + <label htmlFor="choose-date">Choose date</label> + <input + aria-describedby="choose-date" + className="form-control" + id="chosen-date" + max={MAX_DATE} + min={displayTime({ + fmtStr: YMD, + time: now, + timezone: localTimezone, + })} + name="choose-date" + onChange={handleDateChange} + placeholder="Enter date" + type="date" + value={date} + /> + </div> + <div className="form-group"> + <label htmlFor="choose-time">Choose time</label> + <input + aria-describedby="choose-time" + className="form-control" + id="choose-time" + name="choose-time" + onChange={handleTimeChange} + placeholder="Enter time" + type="time" + value={time} + /> + </div> + <div className="form-group"> + <label htmlFor="choose-from-timezone">Set timezone</label> + <TimezoneInput + autofocus={false} + changeValue={setTZ1} + clearInput={setSelectedTime} + TZ={TZ1} + id="choose-from-timezone" + placeholder="Set timezone" + /> + </div> + <div className="form-group"> + <label htmlFor="convert-to-timezone">Convert to timezone</label> + <TimezoneInput + autofocus={false} + changeValue={setTZ2} + clearInput={setSelectedTime} + TZ={TZ2} + id="convert-to-timezone" + placeholder="Convert to timezone" + /> + </div> + <button type="submit" className="btn btn-success"> + Convert + </button> + {err ? <ErrorAlert msg={err} /> : null} + {selectedTime !== undefined ? ( + <FutureConverted selectedTime={selectedTime} TZ1={TZ1} TZ2={TZ2} /> + ) : null} + </form> + </div> + ); +} + +function FutureConverted({ + selectedTime, + TZ1, + TZ2, +}: IFutureConverted): JSX.Element { + // console.log(selectedTime); + const chosenTime = selectedTime; + const convertedTime = selectedTime.clone().tz(TZ2); + const fromLbl = getAbbr({ timezone: TZ1, time: moment(chosenTime, HMSDMY) }); + const toLbl = getAbbr({ timezone: TZ2, time: moment(convertedTime, HMSDMY) }); + return ( + <div className="mt-5"> + <div className="row"> + <div className="col"> + <h1>{friendlyStr(TZ1)}</h1> + </div> + <div className="col"> + <h1>{friendlyStr(TZ2)}</h1> + </div> + </div> + <div className="row"> + <div className="col"> + {displayTime({ fmtStr: HMMDY, time: chosenTime, timezone: TZ1 })} ( + {fromLbl}) + </div> + <div className="col"> + {displayTime({ fmtStr: HMMDY, time: convertedTime, timezone: TZ2 })} ( + {toLbl}) + </div> + </div> + </div> + ); +} + +export default FutureConversion; diff --git a/src/utils/index.ts b/src/utils/index.ts @@ -37,7 +37,7 @@ function friendlyStr(str: string): string { */ function getAbbr({ timezone, time }: { timezone: string, time: moment.Moment }): string { // @ts-ignore - return moment.tz.zone(timezone).abbr(time.unix()); + return moment.tz.zone(timezone).abbr(time); } /** @@ -103,15 +103,23 @@ export { }; const DEFAULT_TZ = 'UTC'; -const HMSDMY = 'HH:mm:ss MMMM, DD, YYYY'; +const HM = 'HH:mm'; +const HMMDY = 'HH:mm MMMM DD, YYYY'; +const HMSDMY = 'HH:mm:ss MMMM DD, YYYY'; const localTimezone = moment.tz.guess(); +const MAX_DATE = '2038-01-18'; const timezoneList = moment.tz.names().sort(); const YEAR = new Date().getFullYear(); +const YMD = 'YYYY-MM-DD' export { DEFAULT_TZ, + HM, + HMMDY, HMSDMY, localTimezone, + MAX_DATE, timezoneList, - YEAR + YEAR, + YMD }; \ No newline at end of file diff --git a/src/utils/interfaces.tsx b/src/utils/interfaces.tsx @@ -14,6 +14,19 @@ export interface IDisplayTime { timezone: string; } +export interface IFutureConversion { + time: moment.Moment; + setTZ1: React.Dispatch<React.SetStateAction<string>>; + setTZ2: React.Dispatch<React.SetStateAction<string>>; + TZ1: string; + TZ2: string; +} +export interface IFutureConverted { + selectedTime: moment.Moment; + TZ1: string; + TZ2: string; +} + export interface IMoment { time: moment.Moment; }