Skip to content

Instantly share code, notes, and snippets.

@beatrizsmerino
Created April 12, 2026 22:04
Show Gist options
  • Select an option

  • Save beatrizsmerino/bcaee8778710c7d3154290f1ba0b0b7a to your computer and use it in GitHub Desktop.

Select an option

Save beatrizsmerino/bcaee8778710c7d3154290f1ba0b0b7a to your computer and use it in GitHub Desktop.
Calcula horas reales de jornada con Claude Code (detecta sueño con pausas >=4h)
#!/usr/bin/env bash
# Uso: horas-claude.sh [YYYY-MM-DD] (por defecto: hoy)
DAY="${1:-$(date +%Y-%m-%d)}"
python3 - "$DAY" <<'PY'
import json, glob, sys
from datetime import datetime, timezone, timedelta
day = datetime.strptime(sys.argv[1], "%Y-%m-%d").date()
local = datetime.now().astimezone().tzinfo
files = glob.glob('/Users/beatrizsmerino/.claude/projects/*/*.jsonl')
all_ts = []
for f in files:
try:
with open(f) as fh:
for line in fh:
try:
d = json.loads(line)
t = d.get('timestamp')
if not t: continue
dt = datetime.fromisoformat(t.replace('Z','+00:00')).astimezone(local)
# ventana amplia: día anterior + día + siguiente
if abs((dt.date() - day).days) <= 1:
all_ts.append(dt)
except: pass
except: pass
all_ts.sort()
# Inicio real de la jornada: primera actividad del día tras una pausa >=4h (sueño).
# Si la madrugada es continuación de la noche anterior, no cuenta como inicio.
SLEEP = timedelta(hours=4)
start_idx = None
for i, t in enumerate(all_ts):
if t.date() != day: continue
if i == 0 or (t - all_ts[i-1]) >= SLEEP:
start_idx = i
break
# mismo día pero sin pausa de sueño previa → buscamos la siguiente pausa
# Si no hay pausa >=4h dentro del día, usar primera actividad del día
if start_idx is None:
for i, t in enumerate(all_ts):
if t.date() == day:
start_idx = i
break
if start_idx is None:
print(f"Sin actividad el {day}")
sys.exit(0)
# Fin: última actividad antes de la siguiente pausa de sueño (o fin del array)
end_idx = len(all_ts) - 1
for i in range(start_idx + 1, len(all_ts)):
if (all_ts[i] - all_ts[i-1]) >= SLEEP:
end_idx = i - 1
break
ts = all_ts[start_idx:end_idx+1]
GAP = timedelta(minutes=15)
active = timedelta()
breaks = []
for i in range(1, len(ts)):
g = ts[i] - ts[i-1]
if g < GAP:
active += g
else:
breaks.append((ts[i-1], ts[i], g))
def hm(td):
s = int(td.total_seconds())
return f"{s//3600}h {(s%3600)//60}min"
print(f"📅 {day}")
print(f" Inicio: {ts[0].strftime('%H:%M')}")
print(f" Fin: {ts[-1].strftime('%H:%M')}")
print(f" Span total: {hm(ts[-1]-ts[0])}")
print(f" Tiempo activo: {hm(active)} (huecos <15min)")
print(f" Mensajes: {len(ts)}")
print(f" Pausas largas: {len(breaks)}")
for a,b,g in breaks:
print(f" {a.strftime('%H:%M')} → {b.strftime('%H:%M')} ({hm(g)})")
# Sugerencia descanso: regla 50/10
ideal_breaks = int(active.total_seconds() // 3000) # 50min
done = sum(1 for _,_,g in breaks if g >= timedelta(minutes=10))
print(f"\n💡 Para {hm(active)} activo: ideal ~{ideal_breaks} pausas de 10min. Hechas: {done}")
PY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment