Refactor time utility functions for improved type safety and error handling

- Removed deprecated formatDate function.
- Enhanced formatWithZone to support TypeScript types for parameters.
- Updated getTimeZoneOffset to use TypeScript types.
- Improved formatWithDate for better error messages and type safety.
This commit is contained in:
雨霖铃
2026-01-28 22:39:44 +08:00
parent 710bffd2f8
commit 92026ee7fa
6 changed files with 813 additions and 843 deletions
+12 -12
View File
@@ -6,6 +6,7 @@ const CopyButton = ({
className = 'action-btn', className = 'action-btn',
successMessage = '复制成功!', successMessage = '复制成功!',
errorMessage = '复制失败,请手动复制。', errorMessage = '复制失败,请手动复制。',
copyingMessage = '复制中...',
}) => { }) => {
const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error' const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error'
@@ -53,25 +54,24 @@ const CopyButton = ({
// 根据状态计算当前显示的文本 // 根据状态计算当前显示的文本
const currentText = const currentText =
status === 'success' ? successMessage : status === 'error' ? errorMessage : buttonText; status === 'success'
? successMessage
: status === 'error'
? errorMessage
: status === 'copying'
? copyingMessage
: buttonText;
// 动态样式:只在非默认状态下覆盖颜色,平时让 className 控制 // 动态样式:只在非默认状态下覆盖颜色,平时让 className 控制
const getStyle = () => { const getStyle = () => {
const baseStyle = {
transition: 'all 0.3s ease',
cursor: 'pointer', // 确保有手型光标
// 这里去掉了 padding/border/radius 的硬编码,建议在 CSS 类中定义
// 除非你想强制覆盖
};
if (status === 'success') { if (status === 'success') {
return { ...baseStyle, backgroundColor: '#4CAF50', color: 'white' }; return { backgroundColor: '#4CAF50', color: 'white' };
} }
if (status === 'error') { if (status === 'error') {
return { ...baseStyle, backgroundColor: '#f44336', color: 'white' }; return { backgroundColor: '#f44336', color: 'white' };
} }
return baseStyle; return {};
}; };
return ( return (
@@ -79,7 +79,7 @@ const CopyButton = ({
onClick={handleClick} onClick={handleClick}
className={`${className} ${status} copy-button`} className={`${className} ${status} copy-button`}
style={getStyle()} style={getStyle()}
disabled={status === 'copying'} disabled={status === 'success' || status === 'copying'}
> >
{currentText} {currentText}
</button> </button>
+27 -25
View File
@@ -1,5 +1,5 @@
import {formatWithDate, formatWithZone, TimezoneOptions} from "../utils/timeUtils"; import { formatWithDate, formatWithZone } from '../utils/timeUtils';
import {useState, useCallback} from "react"; import { useState, useCallback } from 'react';
/** /**
* 日期时间转时间戳组件 * 日期时间转时间戳组件
@@ -44,13 +44,13 @@ const TIME_ZONE_LIST = [
// 时间戳单位选项 // 时间戳单位选项
const TIMESTAMP_UNITS = [ const TIMESTAMP_UNITS = [
{value: 'milliseconds', label: '毫秒(ms)'}, { value: 'milliseconds', label: '毫秒(ms)' },
{value: 'seconds', label: '秒(s)'}, { value: 'seconds', label: '秒(s)' },
]; ];
export function DatetimeToTimestamp() { export function DatetimeToTimestamp() {
/** @type {[string, function]} 输入的日期时间字符串 */ /** @type {[string, function]} 输入的日期时间字符串 */
const [dateValue, setDateValue] = useState(() => formatWithZone(Date.now())); const [dateValue, setDateValue] = useState(() => formatWithZone(Date.now(), 'Asia/Shanghai'));
/** @type {[string, function]} 选择的时区 */ /** @type {[string, function]} 选择的时区 */
const [selectedZone, setSelectedZone] = useState('Asia/Shanghai'); const [selectedZone, setSelectedZone] = useState('Asia/Shanghai');
@@ -79,9 +79,7 @@ export function DatetimeToTimestamp() {
return; return;
} }
const finalResult = unit === 'milliseconds' const finalResult = unit === 'milliseconds' ? timestamp : Math.floor(timestamp / 1000);
? timestamp
: Math.floor(timestamp / 1000);
setResult(finalResult.toString()); setResult(finalResult.toString());
} catch (err) { } catch (err) {
@@ -111,21 +109,23 @@ export function DatetimeToTimestamp() {
* 处理时间戳单位变化 * 处理时间戳单位变化
* @type {function(React.ChangeEvent<HTMLSelectElement>): void} * @type {function(React.ChangeEvent<HTMLSelectElement>): void}
*/ */
const handleUnitChange = useCallback((e) => { const handleUnitChange = useCallback(
const newUnit = e.target.value; (e) => {
setUnit(newUnit); const newUnit = e.target.value;
setUnit(newUnit);
// 如果已有结果,重新计算 // 如果已有结果,重新计算
if (result) { if (result) {
const currentResult = parseInt(result, 10); const currentResult = parseInt(result, 10);
if (!isNaN(currentResult)) { if (!isNaN(currentResult)) {
const newResult = newUnit === 'milliseconds' const newResult =
? currentResult * 1000 newUnit === 'milliseconds' ? currentResult * 1000 : Math.floor(currentResult / 1000);
: Math.floor(currentResult / 1000); setResult(newResult.toString());
setResult(newResult.toString()); }
} }
} },
}, [result]); [result],
);
return ( return (
<div className="datetime-converter"> <div className="datetime-converter">
@@ -148,7 +148,11 @@ export function DatetimeToTimestamp() {
onChange={handleZoneChange} onChange={handleZoneChange}
aria-label="选择时区" aria-label="选择时区"
> >
<TimezoneOptions zones={TIME_ZONE_LIST}/> {TIME_ZONE_LIST.map((zone) => (
<option key={zone} value={zone}>
{zone}
</option>
))}
</select> </select>
</div> </div>
@@ -183,16 +187,14 @@ export function DatetimeToTimestamp() {
onChange={handleUnitChange} onChange={handleUnitChange}
aria-label="选择时间戳单位" aria-label="选择时间戳单位"
> >
{TIMESTAMP_UNITS.map(({value, label}) => ( {TIMESTAMP_UNITS.map(({ value, label }) => (
<option key={value} value={value}> <option key={value} value={value}>
{label} {label}
</option> </option>
))} ))}
</select> </select>
</div> </div>
</div> </div>
</div> </div>
); );
} }
+6 -6
View File
@@ -1,5 +1,5 @@
import {useEffect, useState, useRef, useCallback} from "react"; import { useEffect, useState, useRef, useCallback } from 'react';
import CopyButton from "./CopyButton"; import CopyButton from './CopyButton';
/** /**
* 时间戳显示和执行组件 * 时间戳显示和执行组件
@@ -79,7 +79,7 @@ export function TimestampExecution() {
* @type {function(): void} * @type {function(): void}
*/ */
const toggleUnit = useCallback(() => { const toggleUnit = useCallback(() => {
setShowMilliseconds(prev => !prev); setShowMilliseconds((prev) => !prev);
}, []); }, []);
/** /**
@@ -87,7 +87,7 @@ export function TimestampExecution() {
* @type {function(): void} * @type {function(): void}
*/ */
const toggleTimestamp = useCallback(() => { const toggleTimestamp = useCallback(() => {
setIsRunningTimestamp(prev => !prev); setIsRunningTimestamp((prev) => !prev);
}, []); }, []);
/** /**
@@ -118,7 +118,7 @@ export function TimestampExecution() {
<div className="timestamp-controls"> <div className="timestamp-controls">
<button <button
type="button" type="button"
className="timestamp-btn action-btn" className="action-btn"
onClick={toggleUnit} onClick={toggleUnit}
aria-label={unitButtonLabel} aria-label={unitButtonLabel}
title={unitButtonLabel} title={unitButtonLabel}
@@ -134,7 +134,7 @@ export function TimestampExecution() {
<button <button
type="button" type="button"
className={`timestamp-btn ${isRunningTimestamp ? 'stop-btn' : 'action-btn'}`} className={`${isRunningTimestamp ? 'stop-btn' : 'action-btn'}`}
onClick={toggleTimestamp} onClick={toggleTimestamp}
aria-label={toggleButtonLabel} aria-label={toggleButtonLabel}
title={toggleButtonLabel} title={toggleButtonLabel}
+11 -7
View File
@@ -1,5 +1,5 @@
import {useState, useCallback} from "react"; import { useState, useCallback } from 'react';
import {formatWithZone, TimezoneOptions} from "../utils/timeUtils"; import { formatWithZone } from '../utils/timeUtils';
/** /**
* 时间戳转日期时间组件 * 时间戳转日期时间组件
@@ -43,8 +43,8 @@ const TIME_ZONE_LIST = [
// 时间戳单位选项 // 时间戳单位选项
const TIMESTAMP_UNITS = [ const TIMESTAMP_UNITS = [
{value: 'milliseconds', label: '毫秒(ms)'}, { value: 'milliseconds', label: '毫秒(ms)' },
{value: 'seconds', label: '秒(s)'}, { value: 'seconds', label: '秒(s)' },
]; ];
export function TimestampToDatetime() { export function TimestampToDatetime() {
@@ -138,7 +138,7 @@ export function TimestampToDatetime() {
onChange={handleUnitChange} onChange={handleUnitChange}
aria-label="选择时间戳单位" aria-label="选择时间戳单位"
> >
{TIMESTAMP_UNITS.map(({value, label}) => ( {TIMESTAMP_UNITS.map(({ value, label }) => (
<option key={value} value={value}> <option key={value} value={value}>
{label} {label}
</option> </option>
@@ -177,10 +177,14 @@ export function TimestampToDatetime() {
onChange={handleZoneChange} onChange={handleZoneChange}
aria-label="选择时区" aria-label="选择时区"
> >
<TimezoneOptions zones={TIME_ZONE_LIST}/> {TIME_ZONE_LIST.map((zone) => (
<option key={zone} value={zone}>
{zone}
</option>
))}
</select> </select>
</div> </div>
</div> </div>
</div> </div>
) );
} }
+686 -710
View File
File diff suppressed because it is too large Load Diff
+14 -26
View File
@@ -1,20 +1,8 @@
export function formatDate(value, timezone = 'Asia/Shanghai') { export const formatWithZone = (
return (new Intl.DateTimeFormat('zh-CN', { timestamp: number | string,
year: 'numeric', zone = 'Asia/Shanghai',
month: 'numeric', unit = 'milliseconds',
day: 'numeric', ) => {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZone: timezone,
})).format(value);
}
export const TimezoneOptions = ({zones}) => {
return (zones.map((zone) => (<option key={zone} value={zone}>{zone}</option>)));
}
export const formatWithZone = (timestamp, zone = 'Asia/Shanghai', unit = 'milliseconds') => {
try { try {
const ms = unit === 'milliseconds' ? Number(timestamp) : Number(timestamp) * 1000; const ms = unit === 'milliseconds' ? Number(timestamp) : Number(timestamp) * 1000;
return new Intl.DateTimeFormat('zh-CN', { return new Intl.DateTimeFormat('zh-CN', {
@@ -29,18 +17,18 @@ export const formatWithZone = (timestamp, zone = 'Asia/Shanghai', unit = 'millis
} catch (e) { } catch (e) {
return '格式错误'; return '格式错误';
} }
} };
export const getTimeZoneOffset = (timeZone) => { export const getTimeZoneOffset = (timeZone: string) => {
const now = new Date(); const now = new Date();
const utc = new Date(now.toLocaleString('en-US', {timeZone: 'UTC'})); const utc = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' }));
const target = new Date(now.toLocaleString('en-US', {timeZone: timeZone})); const target = new Date(now.toLocaleString('en-US', { timeZone: timeZone }));
return target.getTime() - utc.getTime(); return target.getTime() - utc.getTime();
} };
export const formatWithDate = ( export const formatWithDate = (
date, date: string,
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone,
) => { ) => {
try { try {
// 创建日期对象 // 创建日期对象
@@ -64,6 +52,6 @@ export const formatWithDate = (
return dateObj.getTime(); return dateObj.getTime();
} }
} catch (error) { } catch (error) {
return '日期转换错误: ' + error.message; return '日期转换错误: ' + error;
} }
} };