webtools

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }