chore: auto-commit (57 archivos)

- frontend/functions/core/format_datetime_short.md
- frontend/functions/core/format_datetime_short.test.ts
- frontend/functions/core/format_datetime_short.ts
- frontend/functions/core/format_duration.md
- frontend/functions/core/format_duration.test.ts
- frontend/functions/core/format_duration.ts
- frontend/functions/core/month_grid.md
- frontend/functions/core/month_grid.test.ts
- frontend/functions/core/month_grid.ts
- frontend/functions/core/string_hash_palette.md
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 03:41:58 +02:00
parent cd50e790ca
commit 03568c88e3
58 changed files with 2923 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
package core
import "time"
// ParseDateOrDefault parses s as a date/datetime and returns the result.
// Accepted formats: "2006-01-02" (YYYY-MM-DD) and time.RFC3339Nano.
// Returns dflt when s is empty or does not match either format.
// When endOfDay is true and the input matched YYYY-MM-DD, adds
// 24*time.Hour - time.Nanosecond so the result is the last nanosecond of
// that day (useful for inclusive end-of-range queries).
// RFC3339Nano inputs are never adjusted regardless of endOfDay.
func ParseDateOrDefault(s string, dflt time.Time, endOfDay bool) time.Time {
if s == "" {
return dflt
}
if t, err := time.Parse("2006-01-02", s); err == nil {
if endOfDay {
return t.Add(24*time.Hour - time.Nanosecond)
}
return t
}
if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
return t
}
return dflt
}
+64
View File
@@ -0,0 +1,64 @@
---
name: parse_date_or_default
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func ParseDateOrDefault(s string, dflt time.Time, endOfDay bool) time.Time"
description: "Parsea s como fecha (YYYY-MM-DD) o datetime (RFC3339Nano). Retorna dflt si s esta vacio o no encaja en ninguno de los dos formatos. Si endOfDay es true y el formato fue YYYY-MM-DD, ajusta al ultimo nanosegundo del dia (util para rangos de fechas inclusivos por el final)."
tags: [date, parse, time, default, range, YYYY-MM-DD, RFC3339Nano]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports:
- time
params:
- name: s
desc: "Cadena a parsear. Formatos aceptados: '2006-01-02' (YYYY-MM-DD) o RFC3339Nano. String vacio activa el valor por defecto."
- name: dflt
desc: "Valor time.Time a retornar cuando s esta vacio o no coincide con ningun formato soportado."
- name: endOfDay
desc: "Si es true y el input fue YYYY-MM-DD, suma 24h-1ns para apuntar al ultimo nanosegundo del dia. No tiene efecto sobre RFC3339Nano ni sobre el retorno de dflt."
output: "time.Time parseado desde s, ajustado si endOfDay=true y formato YYYY-MM-DD; o dflt si s esta vacio o es invalido."
tested: true
tests:
- "string vacio retorna dflt"
- "formato invalido retorna dflt"
- "YYYY-MM-DD sin endOfDay retorna inicio de dia"
- "YYYY-MM-DD con endOfDay retorna ultimo nanosegundo del dia"
- "RFC3339Nano sin endOfDay retorna timestamp exacto"
- "RFC3339Nano con endOfDay no se ajusta"
- "endOfDay false con dflt no lo modifica"
test_file_path: "functions/core/parse_date_or_default_test.go"
file_path: "functions/core/parse_date_or_default.go"
source_repo: "https://github.com/egutierrez/fn_registry/apps/kanban"
source_license: "private"
source_file: "apps/kanban/backend/metrics.go"
---
## Ejemplo
```go
dflt := time.Now().UTC()
// Inicio de rango (incluye el dia completo desde las 00:00)
from := ParseDateOrDefault("2024-01-01", dflt, false)
// Fin de rango (incluye hasta el ultimo nanosegundo del dia)
to := ParseDateOrDefault("2024-01-31", dflt, true)
// Timestamp exacto desde RFC3339Nano (no se ajusta aunque endOfDay=true)
ts := ParseDateOrDefault("2024-01-15T12:00:00Z", dflt, true)
// Valor invalido → dflt
fallback := ParseDateOrDefault("no-es-fecha", dflt, false)
```
## Notas
Unifica las dos funciones originales `parseDateOrDefault` y `parseEndDateOrDefault`
de `apps/kanban/backend/metrics.go:132-156` en una sola funcion parametrica.
`time.Parse` es deterministico, por lo que la funcion es pura aunque use el paquete `time`.
@@ -0,0 +1,65 @@
package core
import (
"testing"
"time"
)
func TestParseDateOrDefault(t *testing.T) {
dflt := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
t.Run("string vacio retorna dflt", func(t *testing.T) {
got := ParseDateOrDefault("", dflt, false)
if !got.Equal(dflt) {
t.Errorf("got %v, want %v", got, dflt)
}
})
t.Run("formato invalido retorna dflt", func(t *testing.T) {
got := ParseDateOrDefault("no-es-fecha", dflt, false)
if !got.Equal(dflt) {
t.Errorf("got %v, want %v", got, dflt)
}
})
t.Run("YYYY-MM-DD sin endOfDay retorna inicio de dia", func(t *testing.T) {
got := ParseDateOrDefault("2024-03-15", dflt, false)
want := time.Date(2024, 3, 15, 0, 0, 0, 0, time.UTC)
if !got.Equal(want) {
t.Errorf("got %v, want %v", got, want)
}
})
t.Run("YYYY-MM-DD con endOfDay retorna ultimo nanosegundo del dia", func(t *testing.T) {
got := ParseDateOrDefault("2024-03-15", dflt, true)
want := time.Date(2024, 3, 15, 23, 59, 59, 999999999, time.UTC)
if !got.Equal(want) {
t.Errorf("got %v, want %v", got, want)
}
})
t.Run("RFC3339Nano sin endOfDay retorna timestamp exacto", func(t *testing.T) {
input := "2024-03-15T14:30:00.123456789Z"
got := ParseDateOrDefault(input, dflt, false)
want, _ := time.Parse(time.RFC3339Nano, input)
if !got.Equal(want) {
t.Errorf("got %v, want %v", got, want)
}
})
t.Run("RFC3339Nano con endOfDay no se ajusta", func(t *testing.T) {
input := "2024-03-15T00:00:00Z"
got := ParseDateOrDefault(input, dflt, true)
want, _ := time.Parse(time.RFC3339Nano, input)
if !got.Equal(want) {
t.Errorf("RFC3339Nano should not be adjusted; got %v, want %v", got, want)
}
})
t.Run("endOfDay false con dflt no lo modifica", func(t *testing.T) {
got := ParseDateOrDefault("", dflt, true)
if !got.Equal(dflt) {
t.Errorf("got %v, want %v", got, dflt)
}
})
}