/ src / components / relative-time.jsx
relative-time.jsx
 1  // Twitter-style relative time component
 2  // Seconds = 1s
 3  // Minutes = 1m
 4  // Hours = 1h
 5  // Days = 1d
 6  // After 7 days, use DD/MM/YYYY or MM/DD/YYYY
 7  import dayjs from 'dayjs';
 8  import dayjsTwitter from 'dayjs-twitter';
 9  import localizedFormat from 'dayjs/plugin/localizedFormat';
10  import relativeTime from 'dayjs/plugin/relativeTime';
11  import { useEffect, useState } from 'preact/hooks';
12  
13  dayjs.extend(dayjsTwitter);
14  dayjs.extend(localizedFormat);
15  dayjs.extend(relativeTime);
16  
17  const dtf = new Intl.DateTimeFormat();
18  
19  export default function RelativeTime({ datetime, format }) {
20    if (!datetime) return null;
21    const date = dayjs(datetime);
22    const [dateStr, setDateStr] = useState('');
23  
24    useEffect(() => {
25      let timer, raf;
26      const update = () => {
27        raf = requestAnimationFrame(() => {
28          let str;
29          if (format === 'micro') {
30            // If date <= 1 day ago or day is within this year
31            const now = dayjs();
32            const dayDiff = now.diff(date, 'day');
33            if (dayDiff <= 1 || now.year() === date.year()) {
34              str = date.twitter();
35            } else {
36              str = dtf.format(date.toDate());
37            }
38          } else {
39            str = date.fromNow();
40          }
41          setDateStr(str);
42  
43          timer = setTimeout(update, 30_000);
44        });
45      };
46      raf = requestAnimationFrame(update);
47      return () => {
48        clearTimeout(timer);
49        cancelAnimationFrame(raf);
50      };
51    }, [date]);
52  
53    return (
54      <time datetime={date.toISOString()} title={date.format('LLLL')}>
55        {dateStr}
56      </time>
57    );
58  }