qcal/frcal/src/TimeOfDay.tsx
2023-04-21 02:34:54 -04:00

125 lines
3.8 KiB
TypeScript

import React from 'react';
import './TimeOfDay.scss';
import {dateJDN, gregorianJDN} from '@common/gregorian';
type TimeStamp = {
hour: number,
minute: number,
second: number,
};
type DecimalTimeStamp = TimeStamp;
type NormalTimeStamp = TimeStamp;
function currentDayMs(): number {
const now = new Date();
return now.getHours() * 3_600_000 + now.getMinutes() * 60_000 + now.getSeconds() * 1000 + now.getMilliseconds();
}
export function dayMsToDecimal(dayMs: number): DecimalTimeStamp {
const second = Math.floor(dayMs / 864 % 100);
const minute = Math.floor(dayMs / 86_400 % 100);
const hour = Math.floor(dayMs / 8_640_000);
return {hour, minute, second};
}
export function decimalToDayMs({hour, minute, second}: DecimalTimeStamp): number {
return 864 * (hour * 10_000 + minute * 100 + second);
}
function timePad(value: number): string {
return value.toString().padStart(2, '0');
}
type TimeOfDayProps = {
onDateChange?: (jdn: number) => void,
};
type TimeOfDayState = {
decimal: DecimalTimeStamp,
decimalTimer: number,
normal: NormalTimeStamp,
normalTimer: number,
jdn: number,
};
export class TimeOfDay extends React.Component<TimeOfDayProps, TimeOfDayState> {
constructor(props: TimeOfDayProps) {
super(props);
const zero = {hour: 0, minute: 0, second: 0};
this.state = {
decimal: zero,
decimalTimer: 0,
normal: zero,
normalTimer: 0,
jdn: dateJDN(new Date()),
};
}
componentDidMount() {
this.updateDecimalTime();
this.updateNormalTime();
}
componentWillUnmount() {
if (this.state.decimalTimer) {
clearTimeout(this.state.decimalTimer);
}
if (this.state.normalTimer) {
clearTimeout(this.state.normalTimer);
}
this.setState({decimalTimer: 0, normalTimer: 0});
}
updateDecimalTime = () => {
const now = currentDayMs();
const {hour, minute, second} = dayMsToDecimal(now);
const nextTick = decimalToDayMs({hour, minute, second: second + 1});
const decimalTimer = window.setTimeout(this.updateDecimalTime, nextTick - now);
this.setState({decimal: {hour, minute, second}, decimalTimer});
}
updateNormalTime = () => {
const now = new Date();
const jdn = gregorianJDN(now.getFullYear(), now.getMonth() + 1, now.getDate());
if (this.props.onDateChange && jdn !== this.state.jdn) {
this.props.onDateChange(jdn);
}
const normalTimer = window.setTimeout(this.updateNormalTime, 1000 - now.getMilliseconds());
this.setState({
normal: {
hour: now.getHours(),
minute: now.getMinutes(),
second: now.getSeconds()
},
normalTimer,
jdn,
});
}
render() {
return <div className="TimeOfDay">
<h4>Time of Day</h4>
<div className="TimeOfDay-Rows">
<div className="TimeOfDay-Style">
<strong>Decimal time:</strong>
<span className="TimeOfDay-Value">
{this.state.decimal.hour}:
{timePad(this.state.decimal.minute)}:
{timePad(this.state.decimal.second)}
</span>
</div>
<div className="TimeOfDay-Style">
<strong>24-hour time:</strong>
<span className="TimeOfDay-Value">
{timePad(this.state.normal.hour)}:
{timePad(this.state.normal.minute)}:
{timePad(this.state.normal.second)}
</span>
</div>
</div>
</div>;
}
}