[{"data":1,"prerenderedAt":1400},["ShallowReactive",2],{"navigation_docs":3,"-api-zocam-point":101,"-api-zocam-point-surround":1395},[4,23,66],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":22},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,14,18],{"title":11,"path":12,"stem":13},"Introduction","\u002Fguide\u002Fintroduction","1.guide\u002F1.introduction",{"title":15,"path":16,"stem":17},"Getting started","\u002Fguide\u002Fgetting-started","1.guide\u002F2.getting-started",{"title":19,"path":20,"stem":21},"Pieces and sets","\u002Fguide\u002Fpieces-and-sets","1.guide\u002F3.pieces-and-sets",false,{"title":24,"icon":25,"path":26,"stem":27,"children":28},"Design (ADRs)","i-lucide-drafting-compass","\u002Fdesign","2.design",[29,32],{"title":30,"path":26,"stem":31},"Design decisions (ADRs)","2.design\u002Findex",{"title":33,"icon":34,"path":35,"stem":36,"children":37,"page":22},"Decision records","i-lucide-scroll-text","\u002Fdesign\u002Fadrs","2.design\u002Fadrs",[38,42,46,50,54,58,62],{"title":39,"path":40,"stem":41},"ADR-001: The refinement-chain model for calendar points","\u002Fdesign\u002Fadrs\u002F001-refinement-chain","2.design\u002Fadrs\u002F001-refinement-chain",{"title":43,"path":44,"stem":45},"ADR-002: Sets first — the Span algebra","\u002Fdesign\u002Fadrs\u002F002-set-primary-spans","2.design\u002Fadrs\u002F002-set-primary-spans",{"title":47,"path":48,"stem":49},"ADR-003: Overflow policy — clamp by default, skip on request","\u002Fdesign\u002Fadrs\u002F003-overflow-policy","2.design\u002Fadrs\u002F003-overflow-policy",{"title":51,"path":52,"stem":53},"ADR-004: Fortnights as every-k scopes","\u002Fdesign\u002Fadrs\u002F004-fortnight-scopes","2.design\u002Fadrs\u002F004-fortnight-scopes",{"title":55,"path":56,"stem":57},"ADR-005: One denotation function, and what DST does to it","\u002Fdesign\u002Fadrs\u002F005-shared-denotation","2.design\u002Fadrs\u002F005-shared-denotation",{"title":59,"path":60,"stem":61},"ADR-006: Zocam is a standalone, released library","\u002Fdesign\u002Fadrs\u002F006-standalone-library","2.design\u002Fadrs\u002F006-standalone-library",{"title":63,"path":64,"stem":65},"ADR-007: A set is one value — struct answers, enumerable shapes","\u002Fdesign\u002Fadrs\u002F007-one-value-sets","2.design\u002Fadrs\u002F007-one-value-sets",{"title":67,"path":68,"stem":69,"children":70,"icon":100},"API Reference","\u002Fapi","3.api\u002Findex",[71,72,76,80,84,88,92,96],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75},"Zocam","\u002Fapi\u002Fzocam","3.api\u002Fzocam",{"title":77,"path":78,"stem":79},"Zocam.Intervals","\u002Fapi\u002Fzocam-intervals","3.api\u002Fzocam-intervals",{"title":81,"path":82,"stem":83},"Zocam.ISO","\u002Fapi\u002Fzocam-iso","3.api\u002Fzocam-iso",{"title":85,"path":86,"stem":87},"Zocam.Point","\u002Fapi\u002Fzocam-point","3.api\u002Fzocam-point",{"title":89,"path":90,"stem":91},"Zocam.Point.ComposeError","\u002Fapi\u002Fzocam-point-composeerror","3.api\u002Fzocam-point-composeerror",{"title":93,"path":94,"stem":95},"Zocam.Span","\u002Fapi\u002Fzocam-span","3.api\u002Fzocam-span",{"title":97,"path":98,"stem":99},"Zocam.Span.Arc","\u002Fapi\u002Fzocam-span-arc","3.api\u002Fzocam-span-arc","i-lucide-braces",{"id":102,"title":85,"body":103,"description":140,"extension":1389,"links":1390,"meta":1391,"navigation":1392,"path":86,"seo":1393,"stem":87,"__hash__":1394},"docs\u002F3.api\u002Fzocam-point.md",{"type":104,"value":105,"toc":1351},"minimark",[106,129,138,141,153,183,188,193,196,206,209,215,219,229,245,249,256,262,266,306,310,315,328,333,342,347,356,361,370,375,457,462,471,476,485,489,498,503,543,548,557,560,565,574,579,594,598,603,612,622,633,639,650,656,659,665,668,674,681,721,724,735,741,750,759,765,768,774,779,788,797,812,818,829,835,846,857,863,868,877,894,897,903,909,915,921,927,932,941,951,957,962,971,978,984,989,998,1008,1014,1017,1023,1028,1037,1046,1052,1058,1067,1070,1076,1081,1088,1094,1097,1103,1119,1125,1130,1139,1142,1148,1153,1162,1169,1175,1180,1189,1196,1202,1212,1218,1223,1232,1239,1245,1250,1259,1269,1275,1280,1289,1297,1303,1308,1317,1328,1334,1337,1347],[107,108,109,115,116,120,121,128],"p",{},[110,111,114],"span",{"className":112},[113],"ai-slop","AI SLOP"," An AI agent wrote the docstrings; ",[117,118,119],"code",{},"mix docs"," generated this page. ",[122,123,127],"a",{"href":124,"rel":125},"https:\u002F\u002Fgithub.com\u002Fyuri4n",[126],"nofollow","yuri4n",", a senior engineer, gave the direction and did the review. The review is human, thus errors can stay.",[107,130,131],{},[122,132,137],{"href":133,"rel":134,"className":135},"https:\u002F\u002Fgithub.com\u002Fyuri4n\u002Fzocam\u002Fblob\u002Fmain\u002Flib\u002Fzocam\u002Fpoint.ex#L9",[126],[136],"source-link","Source on GitHub ↗",[107,139,140],{},"A calendar point: a concrete or abstract \"thing about time\".",[107,142,143,144,148,149,152],{},"A point is a pair of a ",[145,146,147],"strong",{},"scope"," and a ",[145,150,151],{},"chain",":",[154,155,156,176],"ul",{},[157,158,159,160,163,164,167,168,171,172,175],"li",{},"The scope is the cycle in which the point repeats. ",[117,161,162],{},"\"May\""," repeats\neach year, so its scope is ",[117,165,166],{},":year",". ",[117,169,170],{},"\"May 2026\""," does not repeat:\nits scope is ",[117,173,174],{},":absolute",".",[157,177,178,179,182],{},"The chain is a list of valued segments. It starts directly under\nthe scope and descends, one unit at a time, to a ",[145,180,181],{},"grain"," (the\nsize of what the point names).",[107,184,185,186,175],{},"Concrete and abstract are not two types. They are one field: a point\nis concrete exactly when its scope is ",[117,187,174],{},[189,190,192],"h2",{"id":191},"the-unit-graph","The unit graph",[107,194,195],{},"Chains must follow the edges of this graph. The week branch is a\nsibling of the month branch: weeks do not nest in months or years.",[197,198,203],"pre",{"className":199,"code":201,"language":202},[200],"language-text",":absolute ── year ──┬── month ── day ────┬── time\n                    │                    │\n                    └── week(1..53) ── weekday\n                                         │\n:week (cycle) ────────────── weekday ────┴── time\n","text",[117,204,201],{"__ignoreMap":205},"",[107,207,208],{},"Examples:",[197,210,213],{"className":211,"code":212,"language":202},[200],"\"May\"                  %Point{scope: :year,     chain: [month: :may]}\n\"May 2026\"             %Point{scope: :absolute, chain: [year: 2026, month: :may]}\n\"the 23rd\"             %Point{scope: :month,    chain: [day: 23]}\n\"15:00\"                %Point{scope: :day,      chain: [time: ~T[15:00:00]]}\n\"a Wednesday\"          %Point{scope: :week,     chain: [weekday: :wednesday]}\n\"first Wed of Jan\"     %Point{scope: :year,     chain: [month: :january, day: {:nth, 1, :wednesday}]}\n",[117,214,212],{"__ignoreMap":205},[189,216,218],{"id":217},"composition","Composition",[107,220,221,224,225,228],{},[117,222,223],{},"compose\u002F2"," concatenates two chains. It is defined exactly when the\ninner point repeats once per unit of what the outer point names:\nthe inner scope must be a plain cycle of the outer grain class (an\n",[117,226,227],{},"{:every, ...}"," inner repeats less often, so it is out). This is\ncomposition of arrows in a category: units are the objects, points\nare the arrows, and two arrows compose only where they meet.",[107,230,231,232,236,237,240,241,244],{},"When the guard fails, the meaning is usually a ",[233,234,235],"em",{},"set",", not a point.\n",[117,238,239],{},"compose(may, at_15_00)"," fails because the day is free between month\nand time; the error hint names the escape hatch:\n",[117,242,243],{},"Zocam.Span.intersection\u002F1"," (\"15:00 of every day of May\").",[189,246,248],{"id":247},"denotation","Denotation",[107,250,251,252,255],{},"A point denotes one grain-sized interval per instance of its scope\ncycle. ",[117,253,254],{},"Zocam.Span.of\u002F1"," lifts a point into the set algebra,\nwhere intervals, steps, and the set operators live.",[107,257,258,259,175],{},"This module is pure: no timezone, no Timex. The calendar meets the\nreal timeline only in ",[117,260,261],{},"Zocam.Span.ground\u002F3",[189,263,265],{"id":264},"one-calendar","One calendar",[107,267,268,269,272,273,276,277,280,281,284,285,288,289,292,293,296,297,299,300,303,304,175],{},"A point stores two standard-library values that carry a calendar:\nthe ",[117,270,271],{},"%Time{}"," of a ",[117,274,275],{},":time"," segment, and the ",[117,278,279],{},"%Date{}"," anchor of an\n",[117,282,283],{},"{:every, k, cycle, anchor}"," scope. Both must be on ",[117,286,287],{},"Calendar.ISO",",\nbecause ",[117,290,291],{},"month_number\u002F1",", ",[117,294,295],{},"weekday_number\u002F1",", and the cell\narithmetic in ",[117,298,93],{}," all read ISO numbers. A value on another\ncalendar is refused by ",[117,301,302],{},"new!\u002F1"," with an error that names the\ncalendar. See ",[117,305,81],{},[189,307,309],{"id":308},"types","Types",[311,312,313],"h3",{"id":151},[117,314,151],{},[197,316,320],{"className":317,"code":318,"language":319,"meta":205,"style":205},"language-elixir shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@type chain() :: [segment(), ...]\n","elixir",[117,321,322],{"__ignoreMap":205},[110,323,326],{"class":324,"line":325},"line",1,[110,327,318],{},[311,329,331],{"id":330},"cycle",[117,332,330],{},[197,334,336],{"className":317,"code":335,"language":319,"meta":205,"style":205},"@type cycle() :: :year | :month | :week | :day\n",[117,337,338],{"__ignoreMap":205},[110,339,340],{"class":324,"line":325},[110,341,335],{},[311,343,345],{"id":344},"day_index",[117,346,344],{},[197,348,350],{"className":317,"code":349,"language":319,"meta":205,"style":205},"@type day_index() :: 1..31 | -31..-1\n",[117,351,352],{"__ignoreMap":205},[110,353,354],{"class":324,"line":325},[110,355,349],{},[311,357,359],{"id":358},"grain_class",[117,360,358],{},[197,362,364],{"className":317,"code":363,"language":319,"meta":205,"style":205},"@type grain_class() :: :year | :month | :week | :day | :time\n",[117,365,366],{"__ignoreMap":205},[110,367,368],{"class":324,"line":325},[110,369,363],{},[311,371,373],{"id":372},"month",[117,374,372],{},[197,376,378],{"className":317,"code":377,"language":319,"meta":205,"style":205},"@type month() ::\n  :january\n  | :february\n  | :march\n  | :april\n  | :may\n  | :june\n  | :july\n  | :august\n  | :september\n  | :october\n  | :november\n  | :december\n",[117,379,380,385,391,397,403,409,415,421,427,433,439,445,451],{"__ignoreMap":205},[110,381,382],{"class":324,"line":325},[110,383,384],{},"@type month() ::\n",[110,386,388],{"class":324,"line":387},2,[110,389,390],{},"  :january\n",[110,392,394],{"class":324,"line":393},3,[110,395,396],{},"  | :february\n",[110,398,400],{"class":324,"line":399},4,[110,401,402],{},"  | :march\n",[110,404,406],{"class":324,"line":405},5,[110,407,408],{},"  | :april\n",[110,410,412],{"class":324,"line":411},6,[110,413,414],{},"  | :may\n",[110,416,418],{"class":324,"line":417},7,[110,419,420],{},"  | :june\n",[110,422,424],{"class":324,"line":423},8,[110,425,426],{},"  | :july\n",[110,428,430],{"class":324,"line":429},9,[110,431,432],{},"  | :august\n",[110,434,436],{"class":324,"line":435},10,[110,437,438],{},"  | :september\n",[110,440,442],{"class":324,"line":441},11,[110,443,444],{},"  | :october\n",[110,446,448],{"class":324,"line":447},12,[110,449,450],{},"  | :november\n",[110,452,454],{"class":324,"line":453},13,[110,455,456],{},"  | :december\n",[311,458,460],{"id":459},"nth_weekday",[117,461,459],{},[197,463,465],{"className":317,"code":464,"language":319,"meta":205,"style":205},"@type nth_weekday() :: {:nth, 1..5 | -1, weekday()}\n",[117,466,467],{"__ignoreMap":205},[110,468,469],{"class":324,"line":325},[110,470,464],{},[311,472,474],{"id":473},"overflow",[117,475,473],{},[197,477,479],{"className":317,"code":478,"language":319,"meta":205,"style":205},"@type overflow() :: :clamp | :skip\n",[117,480,481],{"__ignoreMap":205},[110,482,483],{"class":324,"line":325},[110,484,478],{},[311,486,487],{"id":147},[117,488,147],{},[197,490,492],{"className":317,"code":491,"language":319,"meta":205,"style":205},"@type scope() :: :absolute | cycle() | {:every, pos_integer(), cycle(), Date.t()}\n",[117,493,494],{"__ignoreMap":205},[110,495,496],{"class":324,"line":325},[110,497,491],{},[311,499,501],{"id":500},"segment",[117,502,500],{},[197,504,506],{"className":317,"code":505,"language":319,"meta":205,"style":205},"@type segment() ::\n  {:year, integer()}\n  | {:month, month()}\n  | {:week, 1..53}\n  | {:day, day_index() | nth_weekday()}\n  | {:weekday, weekday()}\n  | {:time, Time.t()}\n",[117,507,508,513,518,523,528,533,538],{"__ignoreMap":205},[110,509,510],{"class":324,"line":325},[110,511,512],{},"@type segment() ::\n",[110,514,515],{"class":324,"line":387},[110,516,517],{},"  {:year, integer()}\n",[110,519,520],{"class":324,"line":393},[110,521,522],{},"  | {:month, month()}\n",[110,524,525],{"class":324,"line":399},[110,526,527],{},"  | {:week, 1..53}\n",[110,529,530],{"class":324,"line":405},[110,531,532],{},"  | {:day, day_index() | nth_weekday()}\n",[110,534,535],{"class":324,"line":411},[110,536,537],{},"  | {:weekday, weekday()}\n",[110,539,540],{"class":324,"line":417},[110,541,542],{},"  | {:time, Time.t()}\n",[311,544,546],{"id":545},"t",[117,547,545],{},[197,549,551],{"className":317,"code":550,"language":319,"meta":205,"style":205},"@type t() :: %Zocam.Point{chain: chain(), overflow: overflow(), scope: scope()}\n",[117,552,553],{"__ignoreMap":205},[110,554,555],{"class":324,"line":325},[110,556,550],{},[107,558,559],{},"A calendar point: a scope and a contiguous chain.",[311,561,563],{"id":562},"unit",[117,564,562],{},[197,566,568],{"className":317,"code":567,"language":319,"meta":205,"style":205},"@type unit() :: :year | :month | :week | :day | :weekday | :time\n",[117,569,570],{"__ignoreMap":205},[110,571,572],{"class":324,"line":325},[110,573,567],{},[311,575,577],{"id":576},"weekday",[117,578,576],{},[197,580,582],{"className":317,"code":581,"language":319,"meta":205,"style":205},"@type weekday() ::\n  :monday | :tuesday | :wednesday | :thursday | :friday | :saturday | :sunday\n",[117,583,584,589],{"__ignoreMap":205},[110,585,586],{"class":324,"line":325},[110,587,588],{},"@type weekday() ::\n",[110,590,591],{"class":324,"line":387},[110,592,593],{},"  :monday | :tuesday | :wednesday | :thursday | :friday | :saturday | :sunday\n",[189,595,597],{"id":596},"functions","Functions",[311,599,601],{"id":600},"compose",[117,602,600],{},[197,604,606],{"className":317,"code":605,"language":319,"meta":205,"style":205},"@spec compose(t(), t()) :: {:ok, t()} | {:error, Zocam.Point.ComposeError.t()}\n",[117,607,608],{"__ignoreMap":205},[110,609,610],{"class":324,"line":325},[110,611,605],{},[107,613,614,615,618,619,175],{},"Compose two points: ",[117,616,617],{},"outer"," refined by ",[117,620,621],{},"inner",[107,623,624,625,628,629,632],{},"Defined exactly when the inner scope is a ",[233,626,627],{},"plain"," cycle whose class\nequals ",[117,630,631],{},"grain_class(outer)",". The result keeps the outer scope and\nappends the inner chain. \"2026\" refined by \"May\" is \"May 2026\":",[197,634,637],{"className":635,"code":636,"language":202},[200],"iex> Zocam.Point.compose(Zocam.Point.year(2026), Zocam.Point.month(:may))\n{:ok, %Zocam.Point{scope: :absolute, chain: [year: 2026, month: :may], overflow: :clamp}}\n",[117,638,636],{"__ignoreMap":205},[107,640,641,642,645,646,649],{},"Sibling units share a class, so \"a Wednesday\" takes a time of day\n(",[117,643,644],{},":weekday"," and ",[117,647,648],{},":day"," are different units of the same class):",[197,651,654],{"className":652,"code":653,"language":202},[200],"iex> {:ok, wed_15} =\n...>   Zocam.Point.compose(Zocam.Point.weekday(:wednesday), Zocam.Point.time(~T[15:00:00]))\niex> wed_15.chain\n[weekday: :wednesday, time: ~T[15:00:00]]\n",[117,655,653],{"__ignoreMap":205},[107,657,658],{},"The pitfall: \"May at 15:00\" looks like a point, but the day is free\nbetween month and time, so the meaning is a set. The error carries\nthe runnable repair:",[197,660,663],{"className":661,"code":662,"language":202},[200],"iex> {:error, error} =\n...>   Zocam.Point.compose(Zocam.Point.month(:may), Zocam.Point.time(~T[15:00:00]))\niex> error.reason\n:grain_gap\niex> error.hint\n\"Build the set with Zocam.Span.intersection([Span.of(outer), Span.of(inner)]).\"\n",[117,664,662],{"__ignoreMap":205},[107,666,667],{},"The edge: weeks nest in no month and in no year, so \"a Wednesday\nof 2026\" does not compose either. That meaning is also a set:",[197,669,672],{"className":670,"code":671,"language":202},[200],"iex> {:error, error} =\n...>   Zocam.Point.compose(Zocam.Point.year(2026), Zocam.Point.weekday(:wednesday))\niex> error.reason\n:cross_cycle\n",[117,673,671],{"__ignoreMap":205},[107,675,676,677,680],{},"When the pair does not compose, the failure names why (see\n",[117,678,679],{},"ComposeError","):",[154,682,683,689,695,701],{},[157,684,685,688],{},[117,686,687],{},":grain_gap"," — the inner nests inside the outer grain, but a unit\nlevel is free in the middle. The meaning is a set.",[157,690,691,694],{},[117,692,693],{},":cross_cycle"," — neither class nests in the other (weeks straddle\nmonth and year edges). The meaning is a set here too.",[157,696,697,700],{},[117,698,699],{},":invalid"," — the chain would run backward or repeat a unit.",[157,702,703,706,707,709,710,713,714,717,718,720],{},[117,704,705],{},":anchored"," — the inner scope is ",[117,708,283],{},". It\nrepeats once per ",[233,711,712],{},"k"," cycles, not once per cycle, and a chain has\nno place for that phase. Compose the plain points first, then\nre-scope the result with ",[117,715,716],{},"every\u002F3",". An every-scoped ",[233,719,617],{}," is\nfine: compose keeps the outer scope, phase included.",[107,722,723],{},"The composed point keeps the overflow policy of the operand whose\nchain holds the day segment: overflow only means something where a\nday number sits, and a composed chain holds at most one day\nsegment. When neither operand has one, the policy is inert and the\nouter's is kept.",[107,725,726,727,730,731,734],{},"The ",[117,728,729],{},"{:ok, _} | {:error, _}"," shape is deliberate: the type checker\npresses call sites to handle the failure. Use ",[117,732,733],{},"compose!\u002F2"," in\npipelines that must raise.",[311,736,738],{"id":737},"compose-1",[117,739,740],{},"compose!",[197,742,744],{"className":317,"code":743,"language":319,"meta":205,"style":205},"@spec compose!(t(), t()) :: t()\n",[117,745,746],{"__ignoreMap":205},[110,747,748],{"class":324,"line":325},[110,749,743],{},[107,751,752,753,755,756,758],{},"Like ",[117,754,223],{}," but raises the ",[117,757,679],{},". Use it in a\npipeline that must stop on a failed composition.",[197,760,763],{"className":761,"code":762,"language":202},[200],"iex> Zocam.Point.compose!(Zocam.Point.month(:january), Zocam.Point.day(23)).chain\n[month: :january, day: 23]\n",[117,764,762],{"__ignoreMap":205},[107,766,767],{},"\"May of June\" repeats a unit, so it raises:",[197,769,772],{"className":770,"code":771,"language":202},[200],"iex> Zocam.Point.compose!(Zocam.Point.month(:may), Zocam.Point.month(:june))\n** (Zocam.Point.ComposeError) cannot compose [month: :may] (grain :month) with [month: :june] (repeats per :year): the chain would run backward or repeat a unit. Swap the operands, or drop the repeated unit.\n",[117,773,771],{"__ignoreMap":205},[311,775,777],{"id":776},"day",[117,778,776],{},[197,780,782],{"className":317,"code":781,"language":319,"meta":205,"style":205},"@spec day(day_index() | nth_weekday()) :: t()\n",[117,783,784],{"__ignoreMap":205},[110,785,786],{"class":324,"line":325},[110,787,781],{},[107,789,790,791,794,795,175],{},"A day of the month: scope ",[117,792,793],{},":month",", grain class ",[117,796,648],{},[107,798,799,800,803,804,807,808,811],{},"Accepts a day number (",[117,801,802],{},"23","), a negative index from the month end\n(",[117,805,806],{},"-1"," is the last day), or an ordinal weekday selector\n(",[117,809,810],{},"{:nth, 1, :wednesday}"," is the first Wednesday).",[197,813,816],{"className":814,"code":815,"language":202},[200],"iex> Zocam.Point.day(23).chain\n[day: 23]\n\niex> Zocam.Point.day(-1).chain\n[day: -1]\n\niex> Zocam.Point.day({:nth, 1, :wednesday}).chain\n[day: {:nth, 1, :wednesday}]\n",[117,817,815],{"__ignoreMap":205},[107,819,820,821,824,825,828],{},"A day number ",[233,822,823],{},"measures"," into the month, so it clamps where the\nmonth is short (see ",[117,826,827],{},"t:overflow\u002F0","). February 2026 has 28 days,\nand \"the 31st\" still fires there:",[197,830,833],{"className":831,"code":832,"language":202},[200],"iex> Zocam.Span.member?(Zocam.Span.of(Zocam.Point.day(31)), ~U[2026-02-28 12:00:00Z])\ntrue\n",[117,834,832],{"__ignoreMap":205},[107,836,837,838,841,842,845],{},"An ordinal ",[233,839,840],{},"selects",", so a missing ordinal names nothing instead\n(see ",[117,843,844],{},"Zocam.Span.nth\u002F3"," for the same rule at the set level).",[107,847,848,849,852,853,856],{},"The pitfall: no month has a 32nd day, and the compile-time checker\ncannot flag a bad number (its types carry ",[117,850,851],{},"integer()",", not\n",[117,854,855],{},"1..31","). The constructor teaches at run time instead:",[197,858,861],{"className":859,"code":860,"language":202},[200],"iex> Zocam.Point.day(32)\n** (ArgumentError) day\u002F1 takes a day number in 1..31, or a negative index in -31..-1 (-1 is the last day of the month), got 32\n",[117,862,860],{"__ignoreMap":205},[311,864,866],{"id":865},"every",[117,867,865],{},[197,869,871],{"className":317,"code":870,"language":319,"meta":205,"style":205},"@spec every(t(), pos_integer(), Date.t()) :: t()\n",[117,872,873],{"__ignoreMap":205},[110,874,875],{"class":324,"line":325},[110,876,870],{},[107,878,879,880,882,883,886,887,890,891,175],{},"Re-scope a cyclic point to every ",[117,881,712],{},"-th instance of its cycle,\ncounted from the instance that contains ",[117,884,885],{},"anchor",". This is how a\nfortnight enters the library: \"every other Wednesday\" is a ",[117,888,889],{},":week","\npoint re-scoped with ",[117,892,893],{},"k = 2",[107,895,896],{},"Jan 7, 14, and 21 of 2026 are consecutive Wednesdays; only every\nsecond one is in the set, in phase with the anchor:",[197,898,901],{"className":899,"code":900,"language":202},[200],"iex> fortnightly = Zocam.Point.every(Zocam.Point.weekday(:wednesday), 2, ~D[2026-01-07])\niex> fortnightly.scope\n{:every, 2, :week, ~D[2026-01-07]}\niex> Zocam.Span.member?(Zocam.Span.of(fortnightly), ~U[2026-01-07 12:00:00Z])\ntrue\niex> Zocam.Span.member?(Zocam.Span.of(fortnightly), ~U[2026-01-14 12:00:00Z])\nfalse\n",[117,902,900],{"__ignoreMap":205},[107,904,905,906,908],{},"The point's scope must be a plain cycle. An ",[117,907,174],{}," point does\nnot repeat:",[197,910,913],{"className":911,"code":912,"language":202},[200],"iex> Zocam.Point.every(Zocam.Point.year(2026), 2, ~D[2026-01-07])\n** (ArgumentError) an :absolute point happens once, so it cannot repeat every 2 cycles. Give a cyclic point such as Point.weekday(:wednesday).\n",[117,914,912],{"__ignoreMap":205},[107,916,917,918,920],{},"And re-anchoring an ",[117,919,227],{}," scope must restate the rhythm\nfrom the base cycle:",[197,922,925],{"className":923,"code":924,"language":202},[200],"iex> fortnightly = Zocam.Point.every(Zocam.Point.weekday(:wednesday), 2, ~D[2026-01-07])\niex> Zocam.Point.every(fortnightly, 3, ~D[2026-02-04])\n** (ArgumentError) this point already repeats every 2 week(s) from ~D[2026-01-07]. Nested anchors would hide which phase wins: restate the rhythm from the base :week cycle.\n",[117,926,924],{"__ignoreMap":205},[311,928,930],{"id":929},"grain_class-1",[117,931,358],{},[197,933,935],{"className":317,"code":934,"language":319,"meta":205,"style":205},"@spec grain_class(t()) :: grain_class()\n",[117,936,937],{"__ignoreMap":205},[110,938,939],{"class":324,"line":325},[110,940,934],{},[107,942,943,944,947,948,950],{},"The class of the point's finest segment (what the point names).\nTogether with ",[117,945,946],{},"scope_class\u002F1"," it places a point on the two type\naxes; ",[117,949,223],{}," reads both to decide where two points meet.",[197,952,955],{"className":953,"code":954,"language":202},[200],"iex> Zocam.Point.grain_class(Zocam.Point.month(:may))\n:month\n\niex> wed_15 = Zocam.Point.compose!(Zocam.Point.weekday(:wednesday), Zocam.Point.time(~T[15:00:00]))\niex> Zocam.Point.grain_class(wed_15)\n:time\n",[117,956,954],{"__ignoreMap":205},[311,958,960],{"id":959},"month-1",[117,961,372],{},[197,963,965],{"className":317,"code":964,"language":319,"meta":205,"style":205},"@spec month(month()) :: t()\n",[117,966,967],{"__ignoreMap":205},[110,968,969],{"class":324,"line":325},[110,970,964],{},[107,972,973,974,794,976,175],{},"A month of the year: scope ",[117,975,166],{},[117,977,793],{},[197,979,982],{"className":980,"code":981,"language":202},[200],"iex> Zocam.Point.month(:may)\n%Zocam.Point{scope: :year, chain: [month: :may], overflow: :clamp}\n",[117,983,981],{"__ignoreMap":205},[311,985,987],{"id":986},"month_atom",[117,988,986],{},[197,990,992],{"className":317,"code":991,"language":319,"meta":205,"style":205},"@spec month_atom(1..12) :: month()\n",[117,993,994],{"__ignoreMap":205},[110,995,996],{"class":324,"line":325},[110,997,991],{},[107,999,1000,1001,1004,1005,175],{},"The month atom of a calendar number: 1 is ",[117,1002,1003],{},":january",", 12 is ",[117,1006,1007],{},":december",[197,1009,1012],{"className":1010,"code":1011,"language":202},[200],"iex> Zocam.Point.month_atom(12)\n:december\n",[117,1013,1011],{"__ignoreMap":205},[107,1015,1016],{},"A number outside 1..12 raises the taught error (the converters take\nrun-time values, so they keep a fallback clause):",[197,1018,1021],{"className":1019,"code":1020,"language":202},[200],"iex> Zocam.Point.month_atom(13)\n** (ArgumentError) not a valid value for :month: 13. Give a full-name month atom such as :may.\n",[117,1022,1020],{"__ignoreMap":205},[311,1024,1026],{"id":1025},"month_number",[117,1027,1025],{},[197,1029,1031],{"className":317,"code":1030,"language":319,"meta":205,"style":205},"@spec month_number(month()) :: 1..12\n",[117,1032,1033],{"__ignoreMap":205},[110,1034,1035],{"class":324,"line":325},[110,1036,1030],{},[107,1038,1039,1040,1042,1043,1045],{},"The calendar number of a month atom: ",[117,1041,1003],{}," is 1, ",[117,1044,1007],{}," is 12.",[197,1047,1050],{"className":1048,"code":1049,"language":202},[200],"iex> Zocam.Point.month_number(:may)\n5\n",[117,1051,1049],{"__ignoreMap":205},[311,1053,1055],{"id":1054},"new",[117,1056,1057],{},"new!",[197,1059,1061],{"className":317,"code":1060,"language":319,"meta":205,"style":205},"@spec new!(scope: scope(), chain: chain(), overflow: overflow()) :: t()\n",[117,1062,1063],{"__ignoreMap":205},[110,1064,1065],{"class":324,"line":325},[110,1066,1060],{},[107,1068,1069],{},"Build a point and validate every invariant the types cannot hold:\nthe chain is non-empty, starts directly under the scope, follows the\nunit graph edges without gaps, and every value is in range.",[107,1071,1072,1073,1075],{},"This is the single validation walk. All other functions trust it,\nso build points with ",[117,1074,302],{}," or the per-unit constructors, never\nwith raw structs.",[1077,1078,1080],"h4",{"id":1079},"examples","Examples",[107,1082,1083,1084,1087],{},"Build \"May\", the month that repeats each year (the constructor\n",[117,1085,1086],{},"month\u002F1"," is a shorthand for exactly this call):",[197,1089,1092],{"className":1090,"code":1091,"language":202},[200],"iex> Zocam.Point.new!(scope: :year, chain: [month: :may])\n%Zocam.Point{scope: :year, chain: [month: :may], overflow: :clamp}\n",[117,1093,1091],{"__ignoreMap":205},[107,1095,1096],{},"The pitfall: a chain that skips a level. \"The 23rd of 2026\" looks\nlike a point, but a month is free between year and day, so the\nmeaning is a set of days, not one point:",[197,1098,1101],{"className":1099,"code":1100,"language":202},[200],"iex> Zocam.Point.new!(scope: :year, chain: [day: 23])\n** (ArgumentError) unit :day cannot sit directly under :year: a chain descends the unit graph one level at a time, so the next unit must be one of [:month, :week]\n",[117,1102,1100],{"__ignoreMap":205},[107,1104,1105,1108,1109,1111,1112,1114,1115,1118],{},[117,1106,1107],{},"overflow:"," decides what a too-large day number means (see\n",[117,1110,827],{},"). The policy acts in ",[117,1113,93],{},", where the point\nis evaluated. February 2026 has 28 days, so under ",[117,1116,1117],{},":skip"," its\n\"31st\" names nothing:",[197,1120,1123],{"className":1121,"code":1122,"language":202},[200],"iex> the_31st = Zocam.Point.new!(scope: :month, chain: [day: 31], overflow: :skip)\niex> Zocam.Span.member?(Zocam.Span.of(the_31st), ~U[2026-02-28 12:00:00Z])\nfalse\n",[117,1124,1122],{"__ignoreMap":205},[311,1126,1128],{"id":1127},"scope_class",[117,1129,1127],{},[197,1131,1133],{"className":317,"code":1132,"language":319,"meta":205,"style":205},"@spec scope_class(t()) :: :absolute | grain_class()\n",[117,1134,1135],{"__ignoreMap":205},[110,1136,1137],{"class":324,"line":325},[110,1138,1132],{},[107,1140,1141],{},"The class of the point's scope unit (what the point repeats in).",[197,1143,1146],{"className":1144,"code":1145,"language":202},[200],"iex> Zocam.Point.scope_class(Zocam.Point.month(:may))\n:year\n\niex> Zocam.Point.scope_class(Zocam.Point.year(2026))\n:absolute\n",[117,1147,1145],{"__ignoreMap":205},[311,1149,1151],{"id":1150},"time",[117,1152,1150],{},[197,1154,1156],{"className":317,"code":1155,"language":319,"meta":205,"style":205},"@spec time(Time.t()) :: t()\n",[117,1157,1158],{"__ignoreMap":205},[110,1159,1160],{"class":324,"line":325},[110,1161,1155],{},[107,1163,1164,1165,794,1167,175],{},"A time of the day: scope ",[117,1166,648],{},[117,1168,275],{},[197,1170,1173],{"className":1171,"code":1172,"language":202},[200],"iex> Zocam.Point.time(~T[15:00:00])\n%Zocam.Point{scope: :day, chain: [time: ~T[15:00:00]], overflow: :clamp}\n",[117,1174,1172],{"__ignoreMap":205},[311,1176,1178],{"id":1177},"week",[117,1179,1177],{},[197,1181,1183],{"className":317,"code":1182,"language":319,"meta":205,"style":205},"@spec week(1..53) :: t()\n",[117,1184,1185],{"__ignoreMap":205},[110,1186,1187],{"class":324,"line":325},[110,1188,1182],{},[107,1190,1191,1192,794,1194,175],{},"An ISO week of the year (1..53): scope ",[117,1193,166],{},[117,1195,889],{},[197,1197,1200],{"className":1198,"code":1199,"language":202},[200],"iex> Zocam.Point.week(33)\n%Zocam.Point{scope: :year, chain: [week: 33], overflow: :clamp}\n",[117,1201,1199],{"__ignoreMap":205},[107,1203,1204,1205,1207,1208,1211],{},"The pitfall: a year holds at most 53 ISO weeks, and the\ncompile-time checker cannot flag a bad number (its types carry\n",[117,1206,851],{},", not ",[117,1209,1210],{},"1..53","). The constructor teaches at run time\ninstead:",[197,1213,1216],{"className":1214,"code":1215,"language":202},[200],"iex> Zocam.Point.week(54)\n** (ArgumentError) week\u002F1 takes an ISO week number in 1..53, got 54. A year holds at most 53 ISO weeks.\n",[117,1217,1215],{"__ignoreMap":205},[311,1219,1221],{"id":1220},"weekday-1",[117,1222,576],{},[197,1224,1226],{"className":317,"code":1225,"language":319,"meta":205,"style":205},"@spec weekday(weekday()) :: t()\n",[117,1227,1228],{"__ignoreMap":205},[110,1229,1230],{"class":324,"line":325},[110,1231,1225],{},[107,1233,1234,1235,794,1237,175],{},"A day of the week: scope ",[117,1236,889],{},[117,1238,648],{},[197,1240,1243],{"className":1241,"code":1242,"language":202},[200],"iex> Zocam.Point.weekday(:wednesday)\n%Zocam.Point{scope: :week, chain: [weekday: :wednesday], overflow: :clamp}\n",[117,1244,1242],{"__ignoreMap":205},[311,1246,1248],{"id":1247},"weekday_atom",[117,1249,1247],{},[197,1251,1253],{"className":317,"code":1252,"language":319,"meta":205,"style":205},"@spec weekday_atom(1..7) :: weekday()\n",[117,1254,1255],{"__ignoreMap":205},[110,1256,1257],{"class":324,"line":325},[110,1258,1252],{},[107,1260,1261,1262,1265,1266,175],{},"The weekday atom of an ISO number: 1 is ",[117,1263,1264],{},":monday",", 7 is ",[117,1267,1268],{},":sunday",[197,1270,1273],{"className":1271,"code":1272,"language":202},[200],"iex> Zocam.Point.weekday_atom(1)\n:monday\n",[117,1274,1272],{"__ignoreMap":205},[311,1276,1278],{"id":1277},"weekday_number",[117,1279,1277],{},[197,1281,1283],{"className":317,"code":1282,"language":319,"meta":205,"style":205},"@spec weekday_number(weekday()) :: 1..7\n",[117,1284,1285],{"__ignoreMap":205},[110,1286,1287],{"class":324,"line":325},[110,1288,1282],{},[107,1290,1291,1292,1042,1294,1296],{},"The ISO number of a weekday atom: ",[117,1293,1264],{},[117,1295,1268],{}," is 7.",[197,1298,1301],{"className":1299,"code":1300,"language":202},[200],"iex> Zocam.Point.weekday_number(:sunday)\n7\n",[117,1302,1300],{"__ignoreMap":205},[311,1304,1306],{"id":1305},"year",[117,1307,1305],{},[197,1309,1311],{"className":317,"code":1310,"language":319,"meta":205,"style":205},"@spec year(integer()) :: t()\n",[117,1312,1313],{"__ignoreMap":205},[110,1314,1315],{"class":324,"line":325},[110,1316,1310],{},[107,1318,1319,1320,1323,1324,794,1326,175],{},"The year ",[117,1321,1322],{},"n",": scope ",[117,1325,174],{},[117,1327,166],{},[197,1329,1332],{"className":1330,"code":1331,"language":202},[200],"iex> Zocam.Point.year(2026)\n%Zocam.Point{scope: :absolute, chain: [year: 2026], overflow: :clamp}\n",[117,1333,1331],{"__ignoreMap":205},[1335,1336],"hr",{},[107,1338,1339],{},[233,1340,1341,1342,1346],{},"Consult ",[122,1343,1345],{"href":1344},"api-reference","api-reference.md"," for complete listing",[1348,1349,1350],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":205,"searchDepth":387,"depth":387,"links":1352},[1353,1354,1355,1356,1357,1371],{"id":191,"depth":387,"text":192},{"id":217,"depth":387,"text":218},{"id":247,"depth":387,"text":248},{"id":264,"depth":387,"text":265},{"id":308,"depth":387,"text":309,"children":1358},[1359,1360,1361,1362,1363,1364,1365,1366,1367,1368,1369,1370],{"id":151,"depth":393,"text":151},{"id":330,"depth":393,"text":330},{"id":344,"depth":393,"text":344},{"id":358,"depth":393,"text":358},{"id":372,"depth":393,"text":372},{"id":459,"depth":393,"text":459},{"id":473,"depth":393,"text":473},{"id":147,"depth":393,"text":147},{"id":500,"depth":393,"text":500},{"id":545,"depth":393,"text":545},{"id":562,"depth":393,"text":562},{"id":576,"depth":393,"text":576},{"id":596,"depth":387,"text":597,"children":1372},[1373,1374,1375,1376,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,1387,1388],{"id":600,"depth":393,"text":600},{"id":737,"depth":393,"text":740},{"id":776,"depth":393,"text":776},{"id":865,"depth":393,"text":865},{"id":929,"depth":393,"text":358},{"id":959,"depth":393,"text":372},{"id":986,"depth":393,"text":986},{"id":1025,"depth":393,"text":1025},{"id":1054,"depth":393,"text":1057},{"id":1127,"depth":393,"text":1127},{"id":1150,"depth":393,"text":1150},{"id":1177,"depth":393,"text":1177},{"id":1220,"depth":393,"text":576},{"id":1247,"depth":393,"text":1247},{"id":1277,"depth":393,"text":1277},{"id":1305,"depth":393,"text":1305},"md",null,{},true,{"title":85,"description":140},"dZO5Waaot72IfVZuXXyFdlpaW_CBo8gjpSiGSHT96TI",[1396,1398],{"title":81,"path":82,"stem":83,"description":1397,"children":-1},"One calendar, checked at the door: Calendar.ISO.",{"title":89,"path":90,"stem":91,"description":1399,"children":-1},"Raised (or returned) when two points do not compose.",1786065168174]