page.tsx (3407B)
1 "use client"; 2 import React, { useState, useEffect, useCallback } from "react"; 3 4 type TimeValues = { 5 ms: string; 6 iso: string; 7 utc: string; 8 jst: string; 9 local: string; 10 }; 11 12 export default function TimeTranslate() { 13 const [values, setValues] = useState<TimeValues>({ 14 ms: "", 15 iso: "", 16 utc: "", 17 jst: "", 18 local: "", 19 }); 20 21 const updateAllFromDate = useCallback( 22 (d: Date, excludeKey?: keyof TimeValues) => { 23 if (isNaN(d.getTime())) return; 24 25 const newMs = d.getTime().toString(); 26 const newValues: TimeValues = { 27 ms: newMs, 28 iso: d.toISOString(), 29 utc: d.toUTCString(), 30 jst: d.toLocaleString("ja-JP", { timeZone: "Asia/Tokyo" }), 31 local: d.toLocaleString(), 32 }; 33 34 setValues((prev) => { 35 const next = { ...newValues }; 36 if (excludeKey) { 37 next[excludeKey] = prev[excludeKey]; 38 } 39 return next; 40 }); 41 }, 42 [], 43 ); 44 45 useEffect(() => { 46 updateAllFromDate(new Date()); 47 }, [updateAllFromDate]); 48 49 const handleChange = (key: keyof TimeValues, val: string) => { 50 setValues((prev) => ({ ...prev, [key]: val })); 51 52 let d: Date | null = null; 53 if (key === "ms") { 54 d = new Date(Number.parseInt(val)); 55 } else if (key === "jst") { 56 let parseable = val; 57 // If it looks like a date and lacks timezone info, assume JST (+0900) 58 if (/^\d{4}/.test(val) && !/[Z\+\-]/.test(val)) { 59 parseable = `${val} GMT+0900`; 60 } 61 d = new Date(parseable); 62 } else { 63 d = new Date(val); 64 } 65 66 if (d && !isNaN(d.getTime())) { 67 updateAllFromDate(d, key); 68 } 69 }; 70 71 const setNow = () => { 72 updateAllFromDate(new Date()); 73 }; 74 75 const copyToClip = async (text: string) => { 76 try { 77 await navigator.clipboard.writeText(text); 78 alert("success copying to clipboard"); 79 } catch (error) { 80 alert(`fail to copying to clipboard: ${error}`); 81 } 82 }; 83 84 const fields: { label: string; key: keyof TimeValues }[] = [ 85 { label: "Milliseconds (Unix Timestamp ms)", key: "ms" }, 86 { label: "ISO 8601", key: "iso" }, 87 { label: "UTC", key: "utc" }, 88 { label: "JST (Japan Standard Time)", key: "jst" }, 89 { label: "Local Time", key: "local" }, 90 ]; 91 92 return ( 93 <main> 94 <h2>Time Translate</h2> 95 <button type="button" onClick={setNow} style={{ marginBottom: 16 }}> 96 Set to Current Time 97 </button> 98 99 {fields.map((f) => ( 100 <div key={f.key} style={{ marginTop: 12 }}> 101 <strong>{f.label}:</strong> 102 <div 103 style={{ 104 display: "flex", 105 alignItems: "center", 106 gap: 8, 107 marginTop: 4, 108 }} 109 > 110 <input 111 type="text" 112 value={values[f.key]} 113 onChange={(e) => handleChange(f.key, e.target.value)} 114 style={{ 115 flex: 1, 116 fontFamily: "monospace", 117 padding: 8, 118 border: "1px solid #ddd", 119 }} 120 /> 121 <button type="button" onClick={() => copyToClip(values[f.key])}> 122 Copy 123 </button> 124 </div> 125 </div> 126 ))} 127 128 {values.ms && isNaN(new Date(Number.parseInt(values.ms)).getTime()) && ( 129 <div style={{ color: "red", marginTop: 12 }}>Invalid Input</div> 130 )} 131 </main> 132 ); 133 }