Presentamos parte de los PRD usados para implementar omnilude-tools
Introducción
Como mencioné en la publicación anterior sobre omnilude-tools, varias herramientas reales de ese proyecto nacieron a partir de documentos PRD concretos. El documento que publico aquí es uno de ellos: el PRD de Unix Timestamp Converter.
Este documento se escribió primero en coreano para construir la herramienta real timestamp-converter, y todavía quedan rastros de esa implementación en src/app/[locale]/(tools)/developer/timestamp-converter/. Es decir, no era una nota suelta de ideas. Era un documento de trabajo que ya incluía estructura de pantalla, manejo de estado, SEO y diseño de pruebas.
Hay tres puntos que quiero dejar especialmente claros en esta publicación.
- Este documento es uno de los PRD usados para crear herramientas reales dentro de
omnilude-tools, el proyecto que presenté en la publicación anterior. - También es un buen caso real de uso del skill
prd-generator. - El PRD original se escribió en coreano, y en esta publicación incluyo la traducción completa para que cada lector pueda leer el documento entero en su propio idioma.
La herramienta también se puede consultar directamente en tools.omnilude.com/developer/timestamp-converter.
Si fuera posible, también me habría gustado compartir el prompt que se usó para generar este PRD, pero eliminé los datos de la sesión por limitaciones de espacio en disco, así que agradezco su comprensión por no poder incluirlo aquí.
Desarrollo
En pocas palabras, este documento reducía el alcance hasta un nivel en el que la persona encargada de implementarlo podía empezar a trabajar de inmediato.
- Qué construir: una herramienta que convierta en ambos sentidos entre Unix timestamp y fechas legibles por humanos
- Hasta dónde construir: MVP, funciones avanzadas, zonas horarias, hora actual en tiempo real, snippets de código y calculadora
- Cómo construirlo: estructura de archivos, manejo de estado con Zustand, utilidades de conversión, SEO, pruebas, accesibilidad y requisitos de rendimiento
- Cómo validarlo: pruebas unitarias, pruebas E2E y checklist de implementación
Cuando un documento llega a este nivel de detalle, la implementación real se vuelve mucho más simple. De hecho, la página timestamp-converter se implementó siguiendo esta guía estructural y todavía existe como una herramienta del grupo developer dentro de omnilude-tools.
Este documento fue traducido desde coreano
El texto base de esta publicación es un PRD escrito originalmente en coreano. Por eso, en cada idioma prioricé trasladar el documento completo en lugar de resumirlo. Aunque se trate de un PRD, la prioridad fue conservar el contexto técnico y el trasfondo real de implementación, no recortar el contenido.
PRD original
A continuación comparto la versión pública completa del PRD de Unix Timestamp Converter.
PRD: Unix Timestamp Converter
Información básica
| Campo | Valor |
|---|---|
| Tool ID | timestamp-converter |
| Grupo | developer |
| Ruta | /developer/timestamp-converter |
| Prioridad | P0 (obligatorio) |
| Esfuerzo estimado | 1-2 días |
| Estado | Draft |
1. Resumen
1.1 Objetivo
Herramienta que ofrece conversión bidireccional entre Unix timestamps y formatos de fecha legibles por personas.
1.2 Usuarios objetivo
- Desarrolladores backend/frontend
- Analistas de datos
- Administradores de sistemas
1.3 Análisis de competidores
| Sitio | Ventajas | Desventajas |
|---|---|---|
| epochconverter.com | Muchos formatos, snippets de código | UI anticuada |
| unixtimestamp.com | UI simple | Funciones limitadas |
| timestamp-converter.com | Diseño limpio | Soporte de zona horaria limitado |
2. Requisitos funcionales
2.1 Funciones principales (MVP)
F1: Conversión de timestamp → fecha
Entrada: Unix timestamp (detección automática de segundos/milisegundos/microsegundos)
Salida: varios formatos de fecha
Lógica de detección automática:
function detectTimestampUnit(value: number): 'seconds' | 'milliseconds' | 'microseconds' {
if (value < 1e12) return 'seconds'; // < 10 dígitos
if (value < 1e15) return 'milliseconds'; // 13 dígitos
return 'microseconds'; // 16 dígitos
}
Formatos de salida:
- ISO 8601:
2026-01-29T13:45:30.000Z - RFC 2822:
Wed, 29 Jan 2026 13:45:30 +0000 - Formato local:
29 de enero de 2026, 10:45:30 p. m. - Tiempo relativo:
hace 5 minutos,dentro de 3 días
F2: Conversión de fecha → timestamp
Entrada: cadena de fecha o selector de fecha
Salida: Unix timestamp (segundos, milisegundos)
Formatos de entrada compatibles:
- Cadena ISO 8601
- Selector de fecha/hora (DatePicker)
- Lenguaje natural (opcional):
now,yesterday,next week
F3: Soporte de zona horaria
Valor predeterminado: zona horaria local del navegador
Opciones: UTC y selección de zonas horarias de ciudades principales
Lista principal de zonas horarias:
const TIMEZONES = [
{ value: 'UTC', label: 'UTC' },
{ value: 'Asia/Seoul', label: 'Seúl (KST, UTC+9)' },
{ value: 'Asia/Tokyo', label: 'Tokio (JST, UTC+9)' },
{ value: 'America/New_York', label: 'Nueva York (EST/EDT, UTC-5/-4)' },
{ value: 'America/Los_Angeles', label: 'Los Ángeles (PST/PDT, UTC-8/-7)' },
{ value: 'Europe/London', label: 'Londres (GMT/BST, UTC+0/+1)' },
// ... agregar más
];
F4: Mostrar la hora actual en tiempo real
Mostrar en la parte superior de la pantalla el Unix timestamp actual, actualizándolo cada 1 segundo
2.2 Funciones avanzadas
F5: Generación de snippets de código
Proporcionar el timestamp seleccionado como código en varios lenguajes de programación.
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: Calculadora de timestamps
Tiempo base + días/horas/minutos/segundos = timestamp resultante
Ejemplo: ahora + 7 días = timestamp de la próxima semana
3. Diseño UI/UX
3.1 Layout
┌─────────────────────────────────────────────────────────────┐
│ Unix timestamp actual: 1738150800 (actualización en vivo) [Copiar] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ Entrada │ │ Resultado │ │
│ │ │ │ │ │
│ │ [Ingresar timestamp]│ │ ISO 8601: ... [Copiar] │ │
│ │ o │ │ RFC 2822: ... [Copiar] │ │
│ │ [Elegir fecha/hora]│ │ Local: ... [Copiar] │ │
│ │ │ │ Relativo: ... [Copiar] │ │
│ │ Zona horaria: [▼] │ │ │ │
│ │ │ │ Timestamp (s): ... │ │
│ │ [Convertir] [Limpiar]│ │ Timestamp (ms): ... │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────┤
│ Snippets de código │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [JS] [Python] [Java] [Go] [PHP] [Ruby] [C#] │ │
│ │ ───────────────────────────────────────────────── │ │
│ │ new Date(1738150800000) [Copiar] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 Diseño responsive
Desktop (lg+):
- Layout de 2 columnas (entrada | resultado)
Tablet/Mobile (< lg):
- Layout de 1 columna (entrada arriba, resultado abajo)
3.3 Interacciones
- Conversión en tiempo real: convertir automáticamente al ingresar datos (debounce de 300 ms)
- Feedback de copiado: mostrar toast al hacer clic en copiar
- Atajos de teclado: Enter para convertir, Ctrl+C para copiar el resultado
4. Especificación técnica
4.1 Estructura de archivos
src/app/[locale]/(tools)/developer/timestamp-converter/
├── page.tsx # Entrada de página + SEO
├── _store/
│ └── timestamp-store.ts # Manejo de estado con Zustand
└── _components/
├── timestamp-page.tsx # Componente cliente principal
├── current-time-display.tsx # Visualización de hora actual
├── timestamp-input.tsx # Sección de entrada
├── conversion-result.tsx # Sección de resultados
└── code-snippets.tsx # Sección de snippets de código
4.2 Manejo de estado (Zustand)
// _store/timestamp-store.ts
import { create } from 'zustand';
type InputMode = 'timestamp' | 'datetime';
interface TimestampState {
// Entrada
inputMode: InputMode;
timestampInput: string;
dateInput: Date | null;
timezone: string;
// Resultado
result: ConversionResult | null;
// Snippets de código
selectedLanguage: string;
// Acciones
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();
// Lógica de conversión
},
clear: () => set({
timestampInput: '',
dateInput: null,
result: null,
}),
}));
4.3 Lógica principal de conversión
// 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 {
// Detectar la unidad automáticamente
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 Dependencias
{
"dependencies": {
"date-fns": "^3.x",
"date-fns-tz": "^3.x"
}
}
Ya están instaladas en el proyecto.
5. Soporte multilingüe
5.1 Claves de traducción
// messages/es/tools/developer/timestamp-converter.json
{
"tools": {
"developer": {
"timestampConverter": "Convertidor de timestamps",
"timestampConverterDesc": "Convierte entre Unix timestamp y fecha en ambos sentidos",
"timestamp": {
"currentTime": "Unix timestamp actual",
"inputTimestamp": "Ingresar timestamp",
"inputDatetime": "Ingresar fecha/hora",
"timezone": "Zona horaria",
"convert": "Convertir",
"clear": "Limpiar",
"copy": "Copiar",
"copied": "Copiado",
"results": "Resultados de conversión",
"iso8601": "ISO 8601",
"rfc2822": "RFC 2822",
"localFormat": "Formato local",
"relative": "Tiempo relativo",
"timestampSeconds": "Timestamp (segundos)",
"timestampMillis": "Timestamp (milisegundos)",
"codeSnippets": "Snippets de código",
"invalidTimestamp": "Timestamp no válido",
"autoDetected": "Detección automática: {unit}"
}
}
}
}
6. Metadatos 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',
'conversión de timestamp',
'conversión de fecha',
'timestamp to date',
'date to timestamp',
],
});
}
7. Casos de prueba
7.1 Pruebas unitarias
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 Pruebas E2E
test('flujo de conversión de timestamp', async ({ page }) => {
await page.goto('/es/developer/timestamp-converter');
// Ingresar timestamp
await page.fill('[data-testid="timestamp-input"]', '1738150800');
// Verificar resultado
await expect(page.locator('[data-testid="iso8601-result"]'))
.toContainText('2025-01-29');
// Hacer clic en copiar
await page.click('[data-testid="copy-iso8601"]');
await expect(page.locator('.toast')).toContainText('Copiado');
});
8. Requisitos de accesibilidad
- Etiqueta adecuada para todos los campos de entrada
-
aria-labelen el botón de copiar - Soporte para navegación por teclado
- Compatibilidad con lectores de pantalla
9. Requisitos de rendimiento
- Mostrar resultado tras la entrada: < 100ms
- Actualización de la hora actual: exacta cada segundo
- Incremento del tamaño del bundle: < 5KB (gzip)
10. Checklist de implementación
- Crear estructura de archivos
- Implementar store con Zustand
- Implementar funciones utilitarias de conversión
- Implementar el componente principal de página
- Implementar componente de hora actual
- Implementar sección de entrada
- Implementar sección de resultados
- Implementar sección de snippets de código
- Agregar claves de traducción multilingüe (7 idiomas)
- Registrar la herramienta en
tools.ts - Aplicar estilos responsive
- Revisar accesibilidad
- Escribir pruebas