Présentation d’une partie des PRD utilisés pour implémenter omnilude-tools
Introduction
Comme je l’ai mentionné dans le précédent article sur omnilude-tools, plusieurs outils réels de ce projet ont été construits à partir de documents PRD concrets. Le document publié ici en fait partie: le PRD de Unix Timestamp Converter.
Ce document a d’abord été rédigé en coréen pour construire l’outil réel timestamp-converter, et des traces de cette implémentation restent visibles dans src/app/[locale]/(tools)/developer/timestamp-converter/. Autrement dit, il ne s’agissait pas d’une simple note d’idée. C’était un document de travail qui regroupait déjà la structure d’écran, la gestion d’état, le SEO et la conception des tests.
Je veux insister sur trois points dans ce billet.
- Ce document est l’un des PRD utilisés pour créer de vrais outils dans
omnilude-tools, le projet présenté dans le billet précédent. - Il constitue aussi un bon cas d’usage concret du skill
prd-generator. - Le PRD original a été rédigé en coréen, et ce billet fournit une traduction complète afin que chaque lecteur puisse consulter l’intégralité du document dans sa propre langue.
L’outil est également accessible directement à l’adresse tools.omnilude.com/developer/timestamp-converter.
Si possible, j’aurais aussi aimé partager le prompt utilisé pour générer ce PRD, mais j’ai supprimé les données de session à cause des contraintes d’espace disque. Merci de votre compréhension sur le fait que je ne peux pas le fournir ici.
Développement
En résumé, ce document réduisait le périmètre jusqu’au niveau où l’implémenteur pouvait commencer immédiatement.
- Quoi construire: un outil de conversion bidirectionnelle entre Unix timestamp et date lisible par un humain
- Jusqu’où aller: MVP, fonctionnalités avancées, fuseaux horaires, heure actuelle en temps réel, extraits de code et calculatrice
- Comment le construire: structure des fichiers, gestion d’état avec Zustand, utilitaires de conversion, SEO, tests, accessibilité et exigences de performance
- Comment le valider: tests unitaires, tests E2E et checklist d’implémentation
Quand un document atteint ce niveau de précision, l’implémentation réelle devient beaucoup plus simple. En pratique, la page timestamp-converter a bien été construite à partir de ce type de guide structuré, et elle existe encore aujourd’hui comme outil du groupe developer dans omnilude-tools.
Ce document a été traduit depuis le coréen
Le texte de référence de ce billet est un PRD rédigé à l’origine en coréen. C’est pourquoi, pour chaque langue, j’ai privilégié une traduction complète du document plutôt qu’un simple résumé. Même s’il s’agit d’un PRD, la priorité était de conserver le contexte technique et l’arrière-plan réel de l’implémentation.
PRD original
Voici la version publique complète du PRD de Unix Timestamp Converter.
PRD: Unix Timestamp Converter
Informations de base
| Élément | Valeur |
|---|---|
| Tool ID | timestamp-converter |
| Groupe | developer |
| Chemin | /developer/timestamp-converter |
| Priorité | P0 (obligatoire) |
| Charge estimée | 1-2 jours |
| Statut | Draft |
1. Vue d’ensemble
1.1 Objectif
Outil fournissant une conversion bidirectionnelle entre Unix timestamp et formats de date lisibles par un humain.
1.2 Utilisateurs cibles
- Développeurs backend/frontend
- Analystes de données
- Administrateurs systèmes
1.3 Analyse concurrentielle
| Site | Points forts | Points faibles |
|---|---|---|
| epochconverter.com | Formats variés, extraits de code | Interface vieillissante |
| unixtimestamp.com | Interface simple | Fonctionnalités limitées |
| timestamp-converter.com | Design propre | Support limité des fuseaux horaires |
2. Exigences fonctionnelles
2.1 Fonctions principales (MVP)
F1: Conversion timestamp → date
Entrée: Unix timestamp (détection automatique des secondes/millisecondes/microsecondes)
Sortie: plusieurs formats de date
Logique de détection automatique:
function detectTimestampUnit(value: number): 'seconds' | 'milliseconds' | 'microseconds' {
if (value < 1e12) return 'seconds'; // < 10 chiffres
if (value < 1e15) return 'milliseconds'; // 13 chiffres
return 'microseconds'; // 16 chiffres
}
Formats de sortie:
- ISO 8601:
2026-01-29T13:45:30.000Z - RFC 2822:
Wed, 29 Jan 2026 13:45:30 +0000 - Format local:
29 janvier 2026 à 22:45:30 - Temps relatif:
il y a 5 minutes,dans 3 jours
F2: Conversion date → timestamp
Entrée: chaîne de date ou sélecteur de date
Sortie: Unix timestamp (secondes, millisecondes)
Formats d’entrée pris en charge:
- Chaîne ISO 8601
- Sélecteur de date/heure (DatePicker)
- Langage naturel (optionnel):
now,yesterday,next week
F3: Support des fuseaux horaires
Valeur par défaut: fuseau horaire local du navigateur
Options: UTC et sélection de fuseaux horaires pour les grandes villes
Liste principale des fuseaux horaires:
const TIMEZONES = [
{ value: 'UTC', label: 'UTC' },
{ value: 'Asia/Seoul', label: 'Séoul (KST, UTC+9)' },
{ value: 'Asia/Tokyo', label: 'Tokyo (JST, UTC+9)' },
{ value: 'America/New_York', label: 'New York (EST/EDT, UTC-5/-4)' },
{ value: 'America/Los_Angeles', label: 'Los Angeles (PST/PDT, UTC-8/-7)' },
{ value: 'Europe/London', label: 'Londres (GMT/BST, UTC+0/+1)' },
// ... en ajouter davantage
];
F4: Affichage en temps réel de l’heure actuelle
Afficher en haut de l’écran le Unix timestamp actuel, mis à jour toutes les 1 seconde
2.2 Fonctions avancées
F5: Génération d’extraits de code
Fournir le timestamp sélectionné sous forme d’extraits de code dans plusieurs langages.
const CODE_SNIPPETS = {
javascript: (ts: number) => `new Date(${ts * 1000})`,
python: (ts: number) => `datetime.fromtimestamp(${ts})`,
java: (ts: number) => `Instant.ofEpochSecond(${ts}L)`,
go: (ts: number) => `time.Unix(${ts}, 0)`,
php: (ts: number) => `date('Y-m-d H:i:s', ${ts})`,
ruby: (ts: number) => `Time.at(${ts})`,
csharp: (ts: number) => `DateTimeOffset.FromUnixTimeSeconds(${ts})`,
};
F6: Calculatrice de timestamp
Temps de référence + jours/heures/minutes/secondes = timestamp résultant
Exemple: maintenant + 7 jours = timestamp de la semaine prochaine
3. Conception UI/UX
3.1 Mise en page
┌─────────────────────────────────────────────────────────────┐
│ Unix timestamp actuel: 1738150800 (mise à jour en direct) [Copier] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ Entrée │ │ Résultat │ │
│ │ │ │ │ │
│ │ [Saisir timestamp] │ │ ISO 8601: ... [Copier] │ │
│ │ ou │ │ RFC 2822: ... [Copier] │ │
│ │ [Choisir date/heure]│ │ Local: ... [Copier] │ │
│ │ │ │ Relatif: ... [Copier] │ │
│ │ Fuseau: [▼] │ │ │ │
│ │ │ │ Timestamp (s): ... │ │
│ │ [Convertir] [Effacer]│ │ Timestamp (ms): ... │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────┤
│ Extraits de code │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [JS] [Python] [Java] [Go] [PHP] [Ruby] [C#] │ │
│ │ ───────────────────────────────────────────────── │ │
│ │ new Date(1738150800000) [Copier] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 Design responsive
Desktop (lg+):
- Mise en page à 2 colonnes (entrée | résultat)
Tablet/Mobile (< lg):
- Mise en page à 1 colonne (entrée au-dessus, résultat en dessous)
3.3 Interactions
- Conversion en temps réel: conversion automatique à la saisie (debounce de 300 ms)
- Retour après copie: affichage d’un toast après clic sur le bouton de copie
- Raccourcis clavier: Entrée pour convertir, Ctrl+C pour copier le résultat
4. Spécification technique
4.1 Structure des fichiers
src/app/[locale]/(tools)/developer/timestamp-converter/
├── page.tsx # Entrée de page + SEO
├── _store/
│ └── timestamp-store.ts # Gestion d’état avec Zustand
└── _components/
├── timestamp-page.tsx # Composant client principal
├── current-time-display.tsx # Affichage de l’heure actuelle
├── timestamp-input.tsx # Section de saisie
├── conversion-result.tsx # Section des résultats
└── code-snippets.tsx # Section des extraits de code
4.2 Gestion d’état (Zustand)
// _store/timestamp-store.ts
import { create } from 'zustand';
type InputMode = 'timestamp' | 'datetime';
interface TimestampState {
// Entrée
inputMode: InputMode;
timestampInput: string;
dateInput: Date | null;
timezone: string;
// Résultat
result: ConversionResult | null;
// Extraits de code
selectedLanguage: string;
// Actions
setInputMode: (mode: InputMode) => void;
setTimestampInput: (value: string) => void;
setDateInput: (date: Date | null) => void;
setTimezone: (tz: string) => void;
setSelectedLanguage: (lang: string) => void;
convert: () => void;
clear: () => void;
}
interface ConversionResult {
timestampSeconds: number;
timestampMillis: number;
iso8601: string;
rfc2822: string;
localFormat: string;
relative: string;
}
export const useTimestampStore = create<TimestampState>((set, get) => ({
inputMode: 'timestamp',
timestampInput: '',
dateInput: null,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
result: null,
selectedLanguage: 'javascript',
setInputMode: (mode) => set({ inputMode: mode }),
setTimestampInput: (value) => set({ timestampInput: value }),
setDateInput: (date) => set({ dateInput: date }),
setTimezone: (tz) => set({ timezone: tz }),
setSelectedLanguage: (lang) => set({ selectedLanguage: lang }),
convert: () => {
const { inputMode, timestampInput, dateInput, timezone } = get();
// Logique de conversion
},
clear: () => set({
timestampInput: '',
dateInput: null,
result: null,
}),
}));
4.3 Logique principale de conversion
// lib/timestamp-utils.ts
import { format, formatDistanceToNow } from 'date-fns';
import { formatInTimeZone } from 'date-fns-tz';
import { ko, en, ja } from 'date-fns/locale';
export function convertTimestamp(
timestamp: number,
timezone: string,
locale: string
): ConversionResult {
// Détection automatique de l’unité
const unit = detectTimestampUnit(timestamp);
const timestampMs = unit === 'seconds'
? timestamp * 1000
: unit === 'microseconds'
? Math.floor(timestamp / 1000)
: timestamp;
const date = new Date(timestampMs);
const localeObj = { ko, en, ja }[locale] || ko;
return {
timestampSeconds: Math.floor(timestampMs / 1000),
timestampMillis: timestampMs,
iso8601: date.toISOString(),
rfc2822: format(date, 'EEE, dd MMM yyyy HH:mm:ss xx', { locale: localeObj }),
localFormat: formatInTimeZone(date, timezone, 'PPpp', { locale: localeObj }),
relative: formatDistanceToNow(date, { addSuffix: true, locale: localeObj }),
};
}
export function dateToTimestamp(date: Date): { seconds: number; millis: number } {
const millis = date.getTime();
return {
seconds: Math.floor(millis / 1000),
millis,
};
}
4.4 Dépendances
{
"dependencies": {
"date-fns": "^3.x",
"date-fns-tz": "^3.x"
}
}
Déjà installées dans le projet.
5. Support multilingue
5.1 Clés de traduction
// messages/fr/tools/developer/timestamp-converter.json
{
"tools": {
"developer": {
"timestampConverter": "Convertisseur de timestamp",
"timestampConverterDesc": "Convertit dans les deux sens entre Unix timestamp et date",
"timestamp": {
"currentTime": "Unix timestamp actuel",
"inputTimestamp": "Saisir un timestamp",
"inputDatetime": "Saisir une date/heure",
"timezone": "Fuseau horaire",
"convert": "Convertir",
"clear": "Effacer",
"copy": "Copier",
"copied": "Copié",
"results": "Résultats de conversion",
"iso8601": "ISO 8601",
"rfc2822": "RFC 2822",
"localFormat": "Format local",
"relative": "Temps relatif",
"timestampSeconds": "Timestamp (secondes)",
"timestampMillis": "Timestamp (millisecondes)",
"codeSnippets": "Extraits de code",
"invalidTimestamp": "Timestamp non valide",
"autoDetected": "Détection automatique: {unit}"
}
}
}
}
6. Métadonnées SEO
// page.tsx
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale });
return generateSeoMetadata({
locale,
title: t('tools.developer.timestampConverter'),
description: t('tools.developer.timestampConverterDesc'),
path: '/developer/timestamp-converter',
keywords: [
'unix timestamp',
'epoch converter',
'conversion de timestamp',
'conversion de date',
'timestamp to date',
'date to timestamp',
],
});
}
7. Cas de test
7.1 Tests unitaires
describe('detectTimestampUnit', () => {
it('should detect seconds', () => {
expect(detectTimestampUnit(1738150800)).toBe('seconds');
});
it('should detect milliseconds', () => {
expect(detectTimestampUnit(1738150800000)).toBe('milliseconds');
});
it('should detect microseconds', () => {
expect(detectTimestampUnit(1738150800000000)).toBe('microseconds');
});
});
describe('convertTimestamp', () => {
it('should convert timestamp to ISO 8601', () => {
const result = convertTimestamp(1738150800, 'UTC', 'en');
expect(result.iso8601).toBe('2025-01-29T09:00:00.000Z');
});
});
7.2 Tests E2E
test('flux de conversion de timestamp', async ({ page }) => {
await page.goto('/fr/developer/timestamp-converter');
// Saisir le timestamp
await page.fill('[data-testid="timestamp-input"]', '1738150800');
// Vérifier le résultat
await expect(page.locator('[data-testid="iso8601-result"]'))
.toContainText('2025-01-29');
// Cliquer sur le bouton de copie
await page.click('[data-testid="copy-iso8601"]');
await expect(page.locator('.toast')).toContainText('Copié');
});
8. Exigences d’accessibilité
- Label approprié pour tous les champs de saisie
-
aria-labelsur le bouton de copie - Support de la navigation clavier
- Compatibilité avec les lecteurs d’écran
9. Exigences de performance
- Affichage du résultat après saisie: < 100ms
- Mise à jour de l’heure actuelle: précise chaque seconde
- Augmentation de la taille du bundle: < 5KB (gzip)
10. Checklist d’implémentation
- Créer la structure des fichiers
- Implémenter le store Zustand
- Implémenter les fonctions utilitaires de conversion
- Implémenter le composant principal de page
- Implémenter le composant d’affichage de l’heure actuelle
- Implémenter la section de saisie
- Implémenter la section de résultats
- Implémenter la section des extraits de code
- Ajouter les clés de traduction multilingues (7 langues)
- Enregistrer l’outil dans
tools.ts - Appliquer un style responsive
- Vérifier l’accessibilité
- Écrire les tests