Biblioteca estándar
Tiempo y random
-- Doc example: dates as types (date / datetime / duration, IANA zones, DST), formatting and parsing,
-- and randomness: seeded generators (pure, numpy's numbers) and OS randomness (`require random`).
intent: "doc example: time and random"
require random -- only for random() / random_int(lo, hi) without a generator
let g be rng(42)
print("rng(42)() = " + text(g()) + ", Madrid 01:30 + 1h = " + text(datetime("2026-03-29T01:30:00", "Europe/Madrid") + duration(hours = 1)))
test "format_time / parse_time: unix timestamps, pure (no clock), date-only = midnight UTC"
assert_eq(format_time(0), "1970-01-01T00:00:00Z")
assert_eq(format_time(0, "%Y-%m-%d %H:%M"), "1970-01-01 00:00")
assert_eq(parse_time("1970-01-01T00:00:00Z"), 0)
assert_eq(parse_time("2026-01-03", "%Y-%m-%d"), 1767398400.0)
assert_eq(date_parts(0).year, 1970)
assert(now() > 1000000000) -- now() reads the clock: `time`, auto-granted under run/test
test "three types: date, datetime, duration"
assert_eq(type_of(date(2026, 1, 31)), "date")
assert_eq(date("2026-01-31"), date(2026, 1, 31))
assert_eq(type_of(datetime("2026-01-03T10:00:00Z")), "datetime")
assert_eq(type_of(duration(hours = 1)), "duration")
assert_eq(text(date(2026, 1, 31)), "2026-01-31")
assert_eq(text(datetime("2026-01-03T10:00:00Z")), "2026-01-03T10:00:00Z")
assert_eq(text(datetime(2026, 1, 3, 10, 0, 0, "UTC")), "2026-01-03T10:00:00Z")
assert_eq(text(datetime("2026-01-03")), "2026-01-03T00:00:00Z") -- a bare date is midnight UTC
assert_eq(text(datetime(0)), "1970-01-01T00:00:00Z") -- from unix seconds
assert_eq(text(datetime(1767434400)), "2026-01-03T10:00:00Z")
assert_eq(text(duration(hours = 1, minutes = 30)), "PT1H30M")
assert_eq(text(duration(hours = 1.5)), "PT1H30M")
assert_eq(text(duration(weeks = 1)), "P7D")
assert_eq(text(duration(days = 1)), "P1D")
assert_eq(text(duration(seconds = 90)), "PT1M30S")
assert_eq(text(duration(milliseconds = 5)), "PT0.005S")
assert_error(() => date(2026, 2, 30)) -- not a calendar date
assert_error(() => date("31/01/2026")) -- date(text) is ISO only: parse_date for others
test "zones: IANA names follow DST, fixed offsets are kept"
let madrid be datetime("2026-03-29T01:30:00", "Europe/Madrid") -- local time + zone
assert_eq(text(madrid), "2026-03-29T01:30:00+01:00[Europe/Madrid]")
assert_eq(text(datetime("2026-01-03T10:00:00-03:00")), "2026-01-03T10:00:00-03:00")
assert_eq(text(datetime("2026-09-24T02:00:00+0530")), "2026-09-24T02:00:00+05:30")
assert_eq(date_parts(datetime("2026-09-24T02:00:00+05:30")).hour, 2)
assert_eq(date_parts(datetime("2026-09-24T02:00:00+05:30")).zone, "+05:30")
assert_eq(text(datetime("2026-01-03T10:00:00[Europe/Madrid]")), "2026-01-03T10:00:00+01:00[Europe/Madrid]")
assert_error(() => datetime("2026-01-03T10:00:00+05:30[Europe/Madrid]")) -- offset and zone disagree
assert_eq(text(datetime("2026-01-03T10:00:00Z", "America/Sao_Paulo")), "2026-01-03T07:00:00-03:00[America/Sao_Paulo]")
assert_eq(text(datetime(date(2026, 1, 31), "Europe/Madrid")), "2026-01-31T00:00:00+01:00[Europe/Madrid]")
assert_eq(text(datetime(0, "Asia/Kolkata")), "1970-01-01T05:30:00+05:30[Asia/Kolkata]")
assert_eq(date(datetime("2026-01-03T23:30:00-03:00")), date(2026, 1, 3)) -- the day in its own zone
assert_error(() => datetime("2026-03-29T02:30:00", "Europe/Madrid")) -- falls in the DST gap
assert_eq(text(datetime("2026-10-25T02:30:00", "Europe/Madrid")), "2026-10-25T02:30:00+02:00[Europe/Madrid]") -- repeated hour: the first
assert_error(() => datetime("2026-01-03T10:00:00Z", "Mars/Olympus")) -- unknown zone
assert_eq(text(to_timezone(datetime("2026-01-03T10:00:00Z"), "-0300")), "2026-01-03T07:00:00-03:00")
assert_eq(text(to_timezone(datetime("2026-01-03T10:00:00Z"), "+05")), "2026-01-03T15:00:00+05:00")
let parts be date_parts(datetime("2026-03-29T01:30:00", "Europe/Madrid"))
assert_eq([parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second, parts.weekday, parts.yearday, parts.zone], [2026, 3, 29, 1, 30, 0, 7, 88, "Europe/Madrid"])
assert_eq(text(to_timezone(datetime("2026-01-03T10:00:00Z"), "Asia/Kolkata")), "2026-01-03T15:30:00+05:30[Asia/Kolkata]")
assert_eq(text(to_timezone(datetime("2026-01-03T10:00:00Z"), "-03:00")), "2026-01-03T07:00:00-03:00")
test "arithmetic: elapsed time vs calendar steps"
assert_eq(text(datetime("2026-03-29T01:30:00", "Europe/Madrid") + duration(hours = 1)), "2026-03-29T03:30:00+02:00[Europe/Madrid]")
assert_eq(date("2026-03-01") - date(2026, 1, 31), duration(days = 29))
assert_eq(datetime("2026-01-01T12:00:00Z") - datetime("2026-01-01T09:00:00Z"), duration(hours = 3))
assert_eq(date(2026, 1, 31) + duration(days = 1), date(2026, 2, 1))
assert_error(() => date(2026, 1, 1) + duration(hours = 1)) -- a date moves by whole days
assert_eq(duration(hours = 3) / duration(minutes = 30), 6.0)
assert_eq(duration(hours = 1) * 2, duration(hours = 2))
assert_eq(duration(hours = 1) / 4, duration(minutes = 15))
assert_eq(text(duration(hours = 1) - duration(hours = 2)), "-PT1H")
let noon be datetime("2026-03-28T12:00:00", "Europe/Madrid")
assert_eq(text(add_days(noon, 1)), "2026-03-29T12:00:00+02:00[Europe/Madrid]") -- same local time
assert_eq(text(noon + duration(days = 1)), "2026-03-29T13:00:00+02:00[Europe/Madrid]") -- exactly 24 h
assert_eq(text(add_days(datetime("2026-03-28T02:30:00", "Europe/Madrid"), 1)), "2026-03-29T03:30:00+02:00[Europe/Madrid]") -- lands in the gap: moved forward
assert_eq(add_days(date(2026, 1, 31), 1), date(2026, 2, 1))
assert_eq(add_months(date(2026, 1, 31), 1), date(2026, 2, 28)) -- clamps to the month's end
assert_eq(add_months(date(2024, 1, 31), 1), date(2024, 2, 29))
assert_eq(add_months(date(2026, 3, 31), -1), date(2026, 2, 28))
test "comparison is by instant; a date is not a datetime"
assert(datetime("2026-01-01T12:00:00Z") == datetime("2026-01-01T09:00:00-03:00"))
assert(date(2026, 1, 1) < date(2026, 1, 2))
assert_error(() => date(2026, 1, 1) < datetime("2026-01-01T00:00:00Z"))
assert_eq(min([date(2026, 2, 1), date(2026, 1, 1)]), date(2026, 1, 1))
assert_eq(sort([duration(hours = 2), duration(minutes = 1)]), [duration(minutes = 1), duration(hours = 2)])
assert_eq(max([datetime("2026-01-01T12:00:00Z"), datetime("2026-01-01T10:00:00-03:00")]), datetime("2026-01-01T13:00:00Z"))
assert_eq(sort_by([{"d": date(2026, 2, 1)}, {"d": date(2026, 1, 1)}], (r) => r.d)[0].d, date(2026, 1, 1))
test "truncate: the start of a period (group by month, week...)"
let t be datetime("2026-05-14T15:42:10", "Europe/Madrid")
assert_eq(text(truncate(t, "month")), "2026-05-01T00:00:00+02:00[Europe/Madrid]")
assert_eq(truncate(date(2026, 5, 14), "year"), date(2026, 1, 1))
assert_eq(truncate(date(2026, 5, 14), "quarter"), date(2026, 4, 1))
assert_eq(truncate(date(2026, 5, 14), "week"), date(2026, 5, 11)) -- Monday
assert_eq(truncate(date(2026, 5, 14), "day"), date(2026, 5, 14))
let u be datetime("2026-05-14T15:42:10Z")
assert_eq(text(truncate(u, "hour")), "2026-05-14T15:00:00Z")
assert_eq(text(truncate(u, "minute")), "2026-05-14T15:42:00Z")
assert_eq(text(truncate(datetime("2026-05-14T15:42:10.5Z"), "second")), "2026-05-14T15:42:10Z")
-- a day without midnight (DST jump at 00:00) starts when it starts
assert_eq(text(truncate(datetime("2024-09-08T15:00:00", "America/Santiago"), "day")), "2024-09-08T01:00:00-03:00[America/Santiago]")
test "date_range: inclusive, by calendar unit or by elapsed time"
assert_eq(date_range(date(2026, 1, 1), date(2026, 1, 3)), [date(2026, 1, 1), date(2026, 1, 2), date(2026, 1, 3)])
assert_eq(date_range(date(2026, 1, 1), date(2026, 3, 1), "month"), [date(2026, 1, 1), date(2026, 2, 1), date(2026, 3, 1)])
assert_eq(length(date_range(date(2026, 1, 1), date(2026, 1, 15), "week")), 3)
assert_eq(length(date_range(date(2026, 1, 1), date(2027, 1, 1), "quarter")), 5)
assert_eq(length(date_range(date(2020, 1, 1), date(2026, 1, 1), "year")), 7)
let hours be date_range(datetime("2026-01-01T00:00:00Z"), datetime("2026-01-01T02:00:00Z"), "hour")
assert_eq(length(hours), 3)
assert_eq(length(date_range(datetime("2026-01-01T00:00:00Z"), datetime("2026-01-01T00:02:00Z"), "minute")), 3)
assert_eq(length(date_range(datetime("2026-01-01T00:00:00Z"), datetime("2026-01-01T00:00:02Z"), "second")), 3)
assert_eq(length(date_range(datetime("2026-01-01T00:00:00Z"), datetime("2026-01-01T01:00:00Z"), duration(minutes = 20))), 4)
let days be date_range(datetime("2026-03-28T12:00:00", "Europe/Madrid"), datetime("2026-03-30T12:00:00", "Europe/Madrid"))
assert_eq(text(days[1]), "2026-03-29T12:00:00+02:00[Europe/Madrid]") -- the local time survives DST
assert_error(() => date_range(date(2026, 1, 1), date(2026, 1, 2), "hour")) -- a date has no hours
assert_error(() => date_range(date(2026, 1, 1), datetime("2026-01-02T00:00:00Z"))) -- one type for both ends
test "timestamp and in_units"
assert_eq(timestamp(datetime("1970-01-02T00:00:00Z")), 86400.0)
assert_error(() => timestamp(date(2026, 1, 1))) -- a datetime only
assert_eq(in_units(duration(hours = 1, minutes = 30), "minutes"), 90.0)
assert_eq(in_units(duration(days = 14), "weeks"), 2.0)
assert_eq(in_units(duration(days = 1), "days"), 1.0)
assert_eq(in_units(duration(hours = 1), "hours"), 1.0)
assert_eq(in_units(duration(minutes = 1), "seconds"), 60.0)
assert_eq(in_units(duration(seconds = 1), "milliseconds"), 1000.0)
test "parse_date / parse_datetime / format_time"
assert_eq(parse_date("31/01/2026", "%d/%m/%Y"), date(2026, 1, 31))
assert_eq(parse_date("2026-01-31"), date(2026, 1, 31))
assert_error(() => parse_date("2026/01/31", "%d/%m/%Y"))
assert_eq(text(parse_datetime("2026-01-03T10:00:00Z")), "2026-01-03T10:00:00Z")
assert_eq(text(parse_datetime("24/09/2026 02:00 +0530", "%d/%m/%Y %H:%M %z")), "2026-09-24T02:00:00+05:30")
assert_eq(text(parse_datetime("24/09/2026 02:00 +0530", "%d/%m/%Y %H:%M %z", "UTC")), "2026-09-23T20:30:00Z")
assert_eq(text(parse_datetime("24/09/2026 02:00", "%d/%m/%Y %H:%M", "Europe/Madrid")), "2026-09-24T02:00:00+02:00[Europe/Madrid]")
assert_eq(text(parse_datetime("03/01/2026", "%d/%m/%Y")), "2026-01-03T00:00:00Z") -- date-only: midnight
assert_eq(text(parse_datetime("03/01/2026", "%d/%m/%Y", "Europe/Madrid")), "2026-01-03T00:00:00+01:00[Europe/Madrid]")
-- %Z: only UTC and GMT identify a zone; any other abbreviation is an error
assert_eq(text(parse_datetime("Thu, 24 Sep 2026 10:00:00 GMT", "%a, %d %b %Y %H:%M:%S %Z")), "2026-09-24T10:00:00Z")
assert_eq(text(parse_datetime("24/09/2026 10:00 utc", "%d/%m/%Y %H:%M %Z")), "2026-09-24T10:00:00Z")
assert_error(() => parse_datetime("24/09/2026 10:00 IST", "%d/%m/%Y %H:%M %Z"))
assert_eq(text(parse_datetime("24/09/2026 10:00 +0530 IST", "%d/%m/%Y %H:%M %z %Z")), "2026-09-24T10:00:00+05:30") -- the offset decides
assert_eq(text(parse_datetime("24/09/2026 10:00", "%d/%m/%Y %H:%M", "Asia/Kolkata")), "2026-09-24T10:00:00+05:30[Asia/Kolkata]")
assert_eq(format_time(date(2026, 1, 31), "%d/%m/%Y"), "31/01/2026")
assert_eq(format_time(datetime("2026-03-29T01:30:00", "Europe/Madrid"), "%H:%M %z"), "01:30 +0100") -- in its own zone
assert_eq(format_time(datetime("2026-01-03T10:00:00Z")), "2026-01-03T10:00:00Z")
assert_error(() => format_time(date(2026, 1, 31), "%H")) -- a date has no hours
assert_error(() => format_time(0, "%Q")) -- not a strftime specifier
assert_eq(date_parts(date(2026, 1, 31)), {"year": 2026, "month": 1, "day": 31, "weekday": 6, "yearday": 31})
test "rng(seed): numpy's default_rng, bit for bit — pure, no capability"
assert_eq(rng(42)(), 0.7739560485559633) -- np.random.default_rng(42).random()
let g be rng(42)
assert_eq(random(g), 0.7739560485559633) -- random(g) is g()
assert_eq(random(g), 0.4388784397520523)
let d be random_int(g, 1, 6) -- inclusive: integers(1, 7)
assert(d >= 1 and d <= 6)
assert_eq(is_integer(d), true)
let x be random_normal(g, mean = 10, std = 2)
assert_eq(type_of(x), "number")
assert_eq(type_of(random_normal(g)), "number") -- mean 0, std 1
assert_error(() => random_normal(g, std = -1))
assert_error(() => random_int(g, 1.5, 6)) -- bounds are integers
assert_error(() => rng(-1))
test "same seed, same sequence; shuffle, choice, sample, rng_spawn"
assert_eq(shuffle(rng(0), ["a", "b", "c"]), shuffle(rng(0), ["a", "b", "c"]))
assert_eq(sort(shuffle(rng(1), [3, 1, 2])), [1, 2, 3]) -- a NEW list, same items
assert(choice(rng(1), ["a", "b", "c"]) in ["a", "b", "c"])
let picked be sample(rng(1), range(0, 100), 5)
assert_eq(length(unique(picked)), 5) -- distinct
assert_error(() => sample(rng(1), [1, 2], 3))
assert_error(() => choice(rng(1), []))
let kids be rng_spawn(rng(42), 2)
assert_eq(length(kids), 2)
assert_eq(kids[0](), 0.9167441575549085) -- np default_rng(42).spawn(2)[0].random()
assert(kids[0]() != kids[1]())
test "a generator is a process: an alias shares the sequence"
let a be rng(7)
let b be a
let first be a()
assert(b() != first) -- b continued a's sequence
assert_eq(rng(7)(), first)
test "parallel_map: one child per item with rng_spawn; the same generator twice is an error"
let draws be parallel_map((c) => c(), rng_spawn(rng(42), 3))
assert_eq(draws[0], 0.9167441575549085)
let c be rng(3)
assert_error(() => parallel_map((x) => x(), [c, c]))
assert_error(() => parallel_map((x) => g(), [1, 2])) -- g was created at the top level
assert_error(() => json_encode({"g": rng(1)})) -- a generator is not data
test "random() / random_int(lo, hi) without a generator: OS entropy, `require random`"
let r be random()
assert(r >= 0 and r < 1)
let d be random_int(1, 6)
assert(d >= 1 and d <= 6)El reloj§
Solo dos builtins leen o esperan al reloj: now() (timestamp unix, un número) y sleep(segundos). Necesitan la capacidad time, que se auto-otorga bajo run/test (declarás require time bajo serve). Todo lo demás de esta página — construir, parsear, formatear, comparar y calcular fechas — es puro: sin capacidad, la misma respuesta en cada corrida.
now() -- unix timestamp (number) — needs time
sleep(0.5) -- pause — needs time
format_time(0) -- "1970-01-01T00:00:00Z" — ISO 8601 by default
format_time(0, "%Y-%m-%d %H:%M") -- strftime pattern
parse_time("2026-06-30T12:00:00Z") -- inverse of format_time: unix seconds (float)
parse_time("2026-01-03", "%Y-%m-%d") -- a date-only pattern is midnight UTC
date_parts(0) -- {year, month, day, hour, minute, second, ...}
format_time, parse_time y date_parts aceptan un timestamp unix pelado. Para trabajar de verdad con fechas, usá los tres tipos de abajo.
Fechas como tipos: date, datetime, duration§
| Tipo | Qué es | Cómo se construye | Se muestra como |
|---|---|---|---|
date | un día civil — sin hora ni zona | date(2026, 1, 31), date("2026-01-31"), date(dt) (el día en la zona propia de dt) | 2026-01-31 |
datetime | un instante + su zona | ver abajo | 2026-01-03T10:00:00Z, 2026-01-03T10:00:00-03:00, 2026-03-29T01:30:00+01:00[Europe/Madrid] |
duration | una cantidad exacta de tiempo | duration(days =, hours =, minutes =, seconds =, milliseconds =, weeks =) — todo por nombre, todo opcional, se admiten fracciones | ISO 8601: PT1H30M, P29D, -PT1H |
type_of devuelve "date", "datetime" o "duration". date(texto) lee solo YYYY-MM-DD (para otros formatos, parse_date); un día imposible (date(2026, 2, 30)) es un error. Un día dentro de una duration son exactamente 24 horas.
Todas las formas de construir un datetime:
datetime("2026-01-03T10:00:00Z") -- UTC
datetime("2026-01-03T10:00:00-03:00") -- keeps its offset (+0530 without the colon reads too)
datetime("2026-03-29T01:30:00", "Europe/Madrid") -- local time in an IANA zone
datetime("2026-01-03T10:00:00[Europe/Madrid]") -- RFC 9557: local time in that zone
datetime("2026-01-03T10:00:00Z", "America/Sao_Paulo") -- a text with an offset + a zone: the same instant, converted
datetime("2026-01-03") -- midnight UTC
datetime(2026, 1, 3, 10, 0, 0, "UTC") -- hour/minute/second optional, zone last (default UTC)
datetime(1767434400) -- from unix seconds (a zone may follow)
datetime(date(2026, 1, 31), "Europe/Madrid") -- midnight of that day in that zone
Zonas§
Una zona es un nombre IANA ("Europe/Madrid", "America/Buenos_Aires"), "UTC", o un offset fijo ("+05:30", "-0300", "+05") — en cualquier lugar donde va una zona (datetime, to_timezone, parse_datetime). Una zona desconocida es un error que sugiere las dos formas.
- Una zona IANA sigue sus reglas de horario de verano. Un offset fijo no tiene: para una ciudad, usá su zona IANA.
- Un datetime escrito con offset lo conserva, como en Python, java.time, Temporal y pandas:
datetime("2026-09-24T02:00:00+05:30")se imprime así, ydate_partsda hora 2 y zona"+05:30". Las operaciones de calendario (date,truncate,add_days,add_months,date_range,format_time) trabajan en esa hora local. …T10:00:00[Asia/Kolkata]son las 10:00 hora local de ahí; un offset que no coincide con la zona entre corchetes (…+05:30[Europe/Madrid]) es un error.- Una hora local dentro de un salto de horario de verano (Madrid, 2026-03-29 02:30) es un error:
… does not exist in Europe/Madrid (it falls in a daylight-saving gap). Una hora local en la hora que se repite cuando se atrasan los relojes toma la primera de las dos. to_timezone(dt, zona)→ el mismo instante visto en otra zona (IANA u offset).date(dt)→ el día civil en la zona propia dedt.
Aritmética y comparación§
datetime ± durationes tiempo transcurrido, así que es correcto ante el horario de verano: Madrid2026-03-29T01:30+ 1 h =03:30+02:00(las 02:00–03:00 no existieron esa noche).date ± durationse mueve solo por días enteros;date(2026, 1, 1) + duration(hours = 1)es un error (usá un datetime).datetime − datetimeydate − date→ una duration (date("2026-03-01") - date(2026, 1, 31)→P29D).duration ± duration,duration * número,duration / número→ duration;duration / duration→ float (duration(hours = 3) / duration(minutes = 30)→6.0).- Los pasos de calendario no son durations.
add_days(t, n)conserva la hora local a través de un cambio de horario (add_daysde Madrid 03-28 12:00 → 03-29 12:00+02:00, mientras que+ duration(days = 1)da 13:00); un paso que cae en un salto de horario avanza lo que dura el salto.add_months(t, n)recorta el día al fin de mes (add_months(date(2026, 1, 31), 1)→2026-02-28); unnnegativo va hacia atrás; un año esadd_months(t, 12). <,==,sort,sort_by,min/maxfuncionan dentro de un mismo tipo. Los datetimes se comparan por instante, sean cuales sean sus zonas (12:00Z == 09:00-03:00). Un date y un datetime no se comparan (cannot order a date and a datetime): convertí condatetime(d, tz)odate(dt).
Períodos: truncate y date_range§
truncate(t, unidad) → el inicio del período: "year", "quarter", "month", "week" (lunes), "day", y en un datetime "hour", "minute", "second" — en la zona propia del valor. En un día sin medianoche (un salto de horario a las 00:00, como en Santiago), "day" da el primer instante que existe (01:00). Es la herramienta para agrupar por período: summarize(rows, (r) => truncate(r.when, "month"), {…}) — ver Análisis de datos.
date_range(inicio, fin, paso?) → una lista de inicio a fin, inclusive. paso es "day" (default), "week", "month", "quarter", "year", en datetimes también "hour", "minute", "second", o cualquier duration positiva. Los pasos de calendario conservan la hora local a través del horario de verano; horas, minutos, segundos y durations son tiempo transcurrido. Inicio y fin tienen que ser del mismo tipo; pedirle "hour" a un date es un error.
Parseo y formato§
parse_date("31/01/2026", "%d/%m/%Y") -- 2026-01-31
parse_datetime("24/09/2026 02:00 +0530", "%d/%m/%Y %H:%M %z") -- keeps +05:30
parse_datetime("24/09/2026 02:00", "%d/%m/%Y %H:%M", "Europe/Madrid") -- no offset: local time in that zone
parse_datetime("03/01/2026", "%d/%m/%Y") -- date-only: 2026-01-03T00:00:00Z
format_time(date(2026, 1, 31), "%d/%m/%Y") -- "31/01/2026"
parse_date(texto, formato?)yparse_datetime(texto, formato?, tz?)usan especificadores strftime; sin formato leen ISO 8601. Un formato sin offset lee hora local entz(default UTC). Con%zse conserva el offset del texto — o se convierte atzsi pasás una. Un formato solo de fecha es medianoche (entz, si la das).%Zacepta soloUTCyGMT(en cualquier caja, como en Python — una fecha HTTP se parsea). Cualquier otra abreviatura es un error, porque no identifica una zona (ISTes India, Irlanda o Israel). Escribí%zpara un offset, o sacá la abreviatura del texto y pasá la zona:parse_datetime(texto, formato, "Asia/Kolkata"). Cuando el formato también tiene%z("%z %Z"sobre"+0530 IST"), decide el offset y la abreviatura es solo texto.- Un texto que no coincide con el formato es un error que nombra a los dos.
format_time(valor, patrón?)toma un timestamp, undateo undatetime; un datetime se formatea en su propia zona. Un especificador desconocido (%Q) o uno que el valor no tiene (%Hde un date) es un error atrapable que lo nombra.timestamp(dt)→ segundos unix como float (solo un datetime).in_units(d, unidad)→ float, con unidad"weeks","days","hours","minutes","seconds"o"milliseconds".date_parts(x)→ un mapa: un datetime dayear,month,day,hour,minute,second,weekday(1 = lunes … 7 = domingo),yeardayyzone, en su propia zona; un date dayear,month,day,weekday,yearday.json_encodeycsv_encodeescriben el texto ISO. JSON no tiene tipo fecha, así quejson_decodedevuelve texto: re-tipalo condate(x)/datetime(x). CSV y Parquet leen columnas tipadas: ver Archivos e I/O.
Aleatoriedad con semilla — rng(seed), pura§
rng(seed) devuelve un generador: bit a bit numpy.random.default_rng(seed) (SeedSequence + PCG64), así que la misma semilla da los números de numpy en cualquier plataforma. Es puro — sin capacidad, permitido bajo --deterministic, en sandbox y en tests — lo que lo vuelve la herramienta para splits train/test, simulaciones y muestras reproducibles.
let g be rng(42)
g() -- 0.7739560485559633, like np.random.default_rng(42).random()
random(g) -- the same as g()
random_int(g, 1, 6) -- inclusive: g.integers(1, 7)
random_normal(g, mean = 0, std = 1) -- g.normal(0, 1); mean and std by name, optional
shuffle(g, [1, 2, 3]) -- a NEW list: g.permutation(xs)
choice(g, ["a", "b", "c"]) -- one item: g.choice(xs)
sample(g, range(0, 100), 5) -- 5 distinct items: g.choice(xs, 5, replace=False)
rng_spawn(g, 4) -- 4 independent generators: g.spawn(4)
seedes un entero ≥ 0 de cualquier tamaño. Los límites derandom_inttienen que ser enteros;stdtiene que ser ≥ 0;choicede una lista vacía ysamplede más ítems de los que tiene la lista son errores.- Un generador es un proceso, no un valor, como en numpy:
let h be go pasarga una task da el MISMO generador, así que se turnan sobre una sola secuencia. Para flujos independientes, usárng_spawn(g, n)o semillas separadas. - Con
parallel_map, pasá un hijo por ítem:parallel_map(f, rng_spawn(g, n))da exactamente el[c.random() for c in g.spawn(n)]de numpy, y los generadores de quien llama avanzan lo que sacaron los workers. El mismo generador en dos ítems es un error (cada worker sacaría los mismos números). Un generador creado en el nivel superior no se puede usar dentro de un worker deparallel_mapni de un request deserve— reiniciaría la misma secuencia cada vez; crealo ahí, a partir de algo propio del request (rng(int(params.seed))). - Un generador no es un dato:
json_encodelo rechaza.
Aleatoriedad del SO — deny-by-default§
Sin generador, la aleatoriedad viene del sistema operativo y necesita require random — incluso bajo run. La aleatoriedad es de lo que están hechos los tokens y los nonces, así que es una capacidad, no algo gratis.
require random
random() -- float in [0, 1)
random_int(1, 6) -- integer in [min, max], integer bounds
random_bytes(16) -- n bytes from the OS CSPRNG
token() -- 32 CSPRNG bytes as base64url — session ids, CSRF tokens
Ni random() ni rng sirven para secretos: tokens, nonces y salts pasan por random_bytes/token, que leen el CSPRNG del SO. Más en Login y sesiones.