[{"data":1,"prerenderedAt":2627},["ShallowReactive",2],{"highlight:import \"github.com/zoobz-io/clockz\"\n\nfunc TestRetryBackoff(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC))\n    service := NewService(clock)\n\n    go service.RetryWithBackoff()\n\n    clock.Advance(1 * time.Second)   // First retry\n    clock.Advance(2 * time.Second)   // Second retry\n    clock.Advance(4 * time.Second)   // Third retry\n    // Test completes in milliseconds, not seconds\n}\n\n// Production: zero overhead\nservice := NewService(clockz.RealClock)\n\n// Testing: full control\nclock := clockz.NewFakeClockAt(time.Now())\nservice := NewService(clock)\n\nctx, cancel := clock.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\nclock.Advance(6 * time.Second) // Context cancelled instantly":3,"footer-resources":4,"search-sections-clockz":2204,"nav-clockz":2562,"content-tree-clockz":2595,"content-table-nav-clockz":2611},"\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-keyword)\">import\u003C/span>\u003Cspan style=\"color:var(--shiki-string)\"> \"github.com/zoobz-io/clockz\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-keyword)\">func\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\"> TestRetryBackoff\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-parameter)\">t\u003C/span>\u003Cspan style=\"color:var(--shiki-operator)\"> *\u003C/span>\u003Cspan style=\"color:var(--shiki-type)\">testing\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-type)\">T\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">    clock\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> :=\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> clockz\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">NewFakeClockAt\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Date\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\">2024\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 1\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 1\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 12\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 0\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 0\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 0\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">UTC\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">))\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">    service\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> :=\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\"> NewService\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-operator)\">    go\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> service\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">RetryWithBackoff\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">    clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Advance\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\">1\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> *\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">Second\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003Cspan style=\"color:var(--shiki-comment)\">   // First retry\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">    clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Advance\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\">2\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> *\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">Second\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003Cspan style=\"color:var(--shiki-comment)\">   // Second retry\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">    clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Advance\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\">4\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> *\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">Second\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003Cspan style=\"color:var(--shiki-comment)\">   // Third retry\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-comment)\">    // Test completes in milliseconds, not seconds\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-punctuation)\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-comment)\">// Production: zero overhead\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">service\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> :=\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\"> NewService\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">clockz\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">RealClock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-comment)\">// Testing: full control\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">clock\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> :=\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> clockz\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">NewFakeClockAt\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Now\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">())\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">service\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> :=\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\"> NewService\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">ctx\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">,\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> cancel\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> :=\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">WithTimeout\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">context\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Background\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(),\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\"> 5\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">*time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">Second\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-operator)\">defer\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\"> cancel\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">()\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:var(--shiki-text)\">clock\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-function)\">Advance\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">(\u003C/span>\u003Cspan style=\"color:var(--shiki-number)\">6\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> *\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\"> time\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">.\u003C/span>\u003Cspan style=\"color:var(--shiki-text)\">Second\u003C/span>\u003Cspan style=\"color:var(--shiki-punctuation)\">)\u003C/span>\u003Cspan style=\"color:var(--shiki-comment)\"> // Context cancelled instantly\u003C/span>\u003C/span>",[5,1187,1613],{"id":6,"title":7,"body":8,"description":100,"extension":1180,"icon":1181,"meta":1182,"navigation":251,"path":1183,"seo":1184,"stem":1185,"__hash__":1186},"resources/readme.md","README",{"type":9,"value":10,"toc":1170},"minimark",[11,15,83,86,89,94,381,384,388,406,409,413,905,909,1016,1020,1058,1062,1067,1081,1086,1094,1099,1108,1113,1120,1124,1132,1158,1161,1166],[12,13,14],"h1",{"id":14},"clockz",[16,17,18,29,37,45,53,61,68,75],"p",{},[19,20,24],"a",{"href":21,"rel":22},"https://github.com/zoobz-io/clockz/actions/workflows/ci.yml",[23],"nofollow",[25,26],"img",{"alt":27,"src":28},"CI Status","https://github.com/zoobz-io/clockz/workflows/CI/badge.svg",[19,30,33],{"href":31,"rel":32},"https://codecov.io/gh/zoobz-io/clockz",[23],[25,34],{"alt":35,"src":36},"codecov","https://codecov.io/gh/zoobz-io/clockz/graph/badge.svg?branch=main",[19,38,41],{"href":39,"rel":40},"https://goreportcard.com/report/github.com/zoobz-io/clockz",[23],[25,42],{"alt":43,"src":44},"Go Report Card","https://goreportcard.com/badge/github.com/zoobz-io/clockz",[19,46,49],{"href":47,"rel":48},"https://github.com/zoobz-io/clockz/security/code-scanning",[23],[25,50],{"alt":51,"src":52},"CodeQL","https://github.com/zoobz-io/clockz/workflows/CodeQL/badge.svg",[19,54,57],{"href":55,"rel":56},"https://pkg.go.dev/github.com/zoobz-io/clockz",[23],[25,58],{"alt":59,"src":60},"Go Reference","https://pkg.go.dev/badge/github.com/zoobz-io/clockz.svg",[19,62,64],{"href":63},"LICENSE",[25,65],{"alt":66,"src":67},"License","https://img.shields.io/github/license/zoobz-io/clockz",[19,69,71],{"href":70},"go.mod",[25,72],{"alt":73,"src":74},"Go Version","https://img.shields.io/github/go-mod/go-version/zoobz-io/clockz",[19,76,79],{"href":77,"rel":78},"https://github.com/zoobz-io/clockz/releases",[23],[25,80],{"alt":81,"src":82},"Release","https://img.shields.io/github/v/release/zoobz-io/clockz",[16,84,85],{},"Type-safe clock abstractions for Go with zero dependencies.",[16,87,88],{},"Build time-dependent code that's deterministic to test and simple to reason about.",[90,91,93],"h2",{"id":92},"deterministic-time","Deterministic Time",[95,96,101],"pre",{"className":97,"code":98,"language":99,"meta":100,"style":100},"language-go shiki shiki-themes","func TestRetryBackoff(t *testing.T) {\n    // Create a clock frozen at a specific moment\n    clock := clockz.NewFakeClockAt(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC))\n    service := NewService(clock)\n\n    go service.RetryWithBackoff()\n\n    // Advance through retry delays instantly\n    clock.Advance(1 * time.Second)  // First retry\n    clock.Advance(2 * time.Second)  // Second retry\n    clock.Advance(4 * time.Second)  // Third retry\n\n    // Test completes in milliseconds, not seconds\n}\n","go","",[102,103,104,145,152,227,246,253,270,275,281,310,337,364,369,375],"code",{"__ignoreMap":100},[105,106,109,113,117,121,125,129,133,136,139,142],"span",{"class":107,"line":108},"line",1,[105,110,112],{"class":111},"sUt3r","func",[105,114,116],{"class":115},"s5klm"," TestRetryBackoff",[105,118,120],{"class":119},"sq5bi","(",[105,122,124],{"class":123},"sSYET","t",[105,126,128],{"class":127},"sW3Qg"," *",[105,130,132],{"class":131},"sYBwO","testing",[105,134,135],{"class":119},".",[105,137,138],{"class":131},"T",[105,140,141],{"class":119},")",[105,143,144],{"class":119}," {\n",[105,146,148],{"class":107,"line":147},2,[105,149,151],{"class":150},"sLkEo","    // Create a clock frozen at a specific moment\n",[105,153,155,159,162,165,167,170,172,175,177,180,182,186,189,192,194,196,198,201,203,206,208,210,212,214,216,219,221,224],{"class":107,"line":154},3,[105,156,158],{"class":157},"sh8_p","    clock",[105,160,161],{"class":157}," :=",[105,163,164],{"class":157}," clockz",[105,166,135],{"class":119},[105,168,169],{"class":115},"NewFakeClockAt",[105,171,120],{"class":119},[105,173,174],{"class":157},"time",[105,176,135],{"class":119},[105,178,179],{"class":115},"Date",[105,181,120],{"class":119},[105,183,185],{"class":184},"sMAmT","2024",[105,187,188],{"class":119},",",[105,190,191],{"class":184}," 1",[105,193,188],{"class":119},[105,195,191],{"class":184},[105,197,188],{"class":119},[105,199,200],{"class":184}," 12",[105,202,188],{"class":119},[105,204,205],{"class":184}," 0",[105,207,188],{"class":119},[105,209,205],{"class":184},[105,211,188],{"class":119},[105,213,205],{"class":184},[105,215,188],{"class":119},[105,217,218],{"class":157}," time",[105,220,135],{"class":119},[105,222,223],{"class":157},"UTC",[105,225,226],{"class":119},"))\n",[105,228,230,233,235,238,240,243],{"class":107,"line":229},4,[105,231,232],{"class":157},"    service",[105,234,161],{"class":157},[105,236,237],{"class":115}," NewService",[105,239,120],{"class":119},[105,241,242],{"class":157},"clock",[105,244,245],{"class":119},")\n",[105,247,249],{"class":107,"line":248},5,[105,250,252],{"emptyLinePlaceholder":251},true,"\n",[105,254,256,259,262,264,267],{"class":107,"line":255},6,[105,257,258],{"class":127},"    go",[105,260,261],{"class":157}," service",[105,263,135],{"class":119},[105,265,266],{"class":115},"RetryWithBackoff",[105,268,269],{"class":119},"()\n",[105,271,273],{"class":107,"line":272},7,[105,274,252],{"emptyLinePlaceholder":251},[105,276,278],{"class":107,"line":277},8,[105,279,280],{"class":150},"    // Advance through retry delays instantly\n",[105,282,284,286,288,291,293,296,298,300,302,305,307],{"class":107,"line":283},9,[105,285,158],{"class":157},[105,287,135],{"class":119},[105,289,290],{"class":115},"Advance",[105,292,120],{"class":119},[105,294,295],{"class":184},"1",[105,297,128],{"class":157},[105,299,218],{"class":157},[105,301,135],{"class":119},[105,303,304],{"class":157},"Second",[105,306,141],{"class":119},[105,308,309],{"class":150},"  // First retry\n",[105,311,313,315,317,319,321,324,326,328,330,332,334],{"class":107,"line":312},10,[105,314,158],{"class":157},[105,316,135],{"class":119},[105,318,290],{"class":115},[105,320,120],{"class":119},[105,322,323],{"class":184},"2",[105,325,128],{"class":157},[105,327,218],{"class":157},[105,329,135],{"class":119},[105,331,304],{"class":157},[105,333,141],{"class":119},[105,335,336],{"class":150},"  // Second retry\n",[105,338,340,342,344,346,348,351,353,355,357,359,361],{"class":107,"line":339},11,[105,341,158],{"class":157},[105,343,135],{"class":119},[105,345,290],{"class":115},[105,347,120],{"class":119},[105,349,350],{"class":184},"4",[105,352,128],{"class":157},[105,354,218],{"class":157},[105,356,135],{"class":119},[105,358,304],{"class":157},[105,360,141],{"class":119},[105,362,363],{"class":150},"  // Third retry\n",[105,365,367],{"class":107,"line":366},12,[105,368,252],{"emptyLinePlaceholder":251},[105,370,372],{"class":107,"line":371},13,[105,373,374],{"class":150},"    // Test completes in milliseconds, not seconds\n",[105,376,378],{"class":107,"line":377},14,[105,379,380],{"class":119},"}\n",[16,382,383],{},"No sleeps. No flaky timeouts. Time moves when you say so.",[90,385,387],{"id":386},"installation","Installation",[95,389,393],{"className":390,"code":391,"language":392,"meta":100,"style":100},"language-bash shiki shiki-themes","go get github.com/zoobz-io/clockz\n","bash",[102,394,395],{"__ignoreMap":100},[105,396,397,399,403],{"class":107,"line":108},[105,398,99],{"class":115},[105,400,402],{"class":401},"sxAnc"," get",[105,404,405],{"class":401}," github.com/zoobz-io/clockz\n",[16,407,408],{},"Requires Go 1.24+",[90,410,412],{"id":411},"quick-start","Quick Start",[95,414,416],{"className":97,"code":415,"language":99,"meta":100,"style":100},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"time\"\n\n    \"github.com/zoobz-io/clockz\"\n)\n\ntype Service struct {\n    clock clockz.Clock\n}\n\nfunc (s *Service) ProcessWithTimeout(ctx context.Context) error {\n    ctx, cancel := s.clock.WithTimeout(ctx, 30*time.Second)\n    defer cancel()\n\n    timer := s.clock.NewTimer(5 * time.Second)\n    defer timer.Stop()\n\n    select {\n    case t := \u003C-timer.C():\n        fmt.Printf(\"Processing at %v\\n\", t)\n        return nil\n    case \u003C-ctx.Done():\n        return ctx.Err()\n    }\n}\n\nfunc main() {\n    // Production: real time\n    service := &Service{clock: clockz.RealClock}\n    if err := service.ProcessWithTimeout(context.Background()); err != nil {\n        fmt.Printf(\"Error: %v\\n\", err)\n    }\n}\n",[102,417,418,426,430,439,444,449,454,458,463,467,471,484,496,500,504,546,589,599,604,638,653,658,666,691,724,733,749,764,770,775,780,793,799,828,869,895,900],{"__ignoreMap":100},[105,419,420,423],{"class":107,"line":108},[105,421,422],{"class":111},"package",[105,424,425],{"class":131}," main\n",[105,427,428],{"class":107,"line":147},[105,429,252],{"emptyLinePlaceholder":251},[105,431,432,435],{"class":107,"line":154},[105,433,434],{"class":111},"import",[105,436,438],{"class":437},"soy-K"," (\n",[105,440,441],{"class":107,"line":229},[105,442,443],{"class":401},"    \"context\"\n",[105,445,446],{"class":107,"line":248},[105,447,448],{"class":401},"    \"fmt\"\n",[105,450,451],{"class":107,"line":255},[105,452,453],{"class":401},"    \"time\"\n",[105,455,456],{"class":107,"line":272},[105,457,252],{"emptyLinePlaceholder":251},[105,459,460],{"class":107,"line":277},[105,461,462],{"class":401},"    \"github.com/zoobz-io/clockz\"\n",[105,464,465],{"class":107,"line":283},[105,466,245],{"class":437},[105,468,469],{"class":107,"line":312},[105,470,252],{"emptyLinePlaceholder":251},[105,472,473,476,479,482],{"class":107,"line":339},[105,474,475],{"class":111},"type",[105,477,478],{"class":131}," Service",[105,480,481],{"class":111}," struct",[105,483,144],{"class":119},[105,485,486,489,491,493],{"class":107,"line":366},[105,487,158],{"class":488},"sBGCq",[105,490,164],{"class":131},[105,492,135],{"class":119},[105,494,495],{"class":131},"Clock\n",[105,497,498],{"class":107,"line":371},[105,499,380],{"class":119},[105,501,502],{"class":107,"line":377},[105,503,252],{"emptyLinePlaceholder":251},[105,505,507,509,512,515,518,521,523,526,528,531,534,536,539,541,544],{"class":107,"line":506},15,[105,508,112],{"class":111},[105,510,511],{"class":119}," (",[105,513,514],{"class":123},"s ",[105,516,517],{"class":127},"*",[105,519,520],{"class":131},"Service",[105,522,141],{"class":119},[105,524,525],{"class":115}," ProcessWithTimeout",[105,527,120],{"class":119},[105,529,530],{"class":123},"ctx",[105,532,533],{"class":131}," context",[105,535,135],{"class":119},[105,537,538],{"class":131},"Context",[105,540,141],{"class":119},[105,542,543],{"class":131}," error",[105,545,144],{"class":119},[105,547,549,552,554,557,559,562,564,566,568,571,573,575,577,580,583,585,587],{"class":107,"line":548},16,[105,550,551],{"class":157},"    ctx",[105,553,188],{"class":119},[105,555,556],{"class":157}," cancel",[105,558,161],{"class":157},[105,560,561],{"class":157}," s",[105,563,135],{"class":119},[105,565,242],{"class":157},[105,567,135],{"class":119},[105,569,570],{"class":115},"WithTimeout",[105,572,120],{"class":119},[105,574,530],{"class":157},[105,576,188],{"class":119},[105,578,579],{"class":184}," 30",[105,581,582],{"class":157},"*time",[105,584,135],{"class":119},[105,586,304],{"class":157},[105,588,245],{"class":119},[105,590,592,595,597],{"class":107,"line":591},17,[105,593,594],{"class":127},"    defer",[105,596,556],{"class":115},[105,598,269],{"class":119},[105,600,602],{"class":107,"line":601},18,[105,603,252],{"emptyLinePlaceholder":251},[105,605,607,610,612,614,616,618,620,623,625,628,630,632,634,636],{"class":107,"line":606},19,[105,608,609],{"class":157},"    timer",[105,611,161],{"class":157},[105,613,561],{"class":157},[105,615,135],{"class":119},[105,617,242],{"class":157},[105,619,135],{"class":119},[105,621,622],{"class":115},"NewTimer",[105,624,120],{"class":119},[105,626,627],{"class":184},"5",[105,629,128],{"class":157},[105,631,218],{"class":157},[105,633,135],{"class":119},[105,635,304],{"class":157},[105,637,245],{"class":119},[105,639,641,643,646,648,651],{"class":107,"line":640},20,[105,642,594],{"class":127},[105,644,645],{"class":157}," timer",[105,647,135],{"class":119},[105,649,650],{"class":115},"Stop",[105,652,269],{"class":119},[105,654,656],{"class":107,"line":655},21,[105,657,252],{"emptyLinePlaceholder":251},[105,659,661,664],{"class":107,"line":660},22,[105,662,663],{"class":127},"    select",[105,665,144],{"class":119},[105,667,669,672,675,677,680,683,685,688],{"class":107,"line":668},23,[105,670,671],{"class":127},"    case",[105,673,674],{"class":157}," t",[105,676,161],{"class":157},[105,678,679],{"class":127}," \u003C-",[105,681,682],{"class":157},"timer",[105,684,135],{"class":119},[105,686,687],{"class":115},"C",[105,689,690],{"class":119},"():\n",[105,692,694,697,699,702,704,707,711,715,718,720,722],{"class":107,"line":693},24,[105,695,696],{"class":157},"        fmt",[105,698,135],{"class":119},[105,700,701],{"class":115},"Printf",[105,703,120],{"class":119},[105,705,706],{"class":401},"\"Processing at ",[105,708,710],{"class":709},"scyPU","%v",[105,712,714],{"class":713},"suWN2","\\n",[105,716,717],{"class":401},"\"",[105,719,188],{"class":119},[105,721,674],{"class":157},[105,723,245],{"class":119},[105,725,727,730],{"class":107,"line":726},25,[105,728,729],{"class":127},"        return",[105,731,732],{"class":111}," nil\n",[105,734,736,738,740,742,744,747],{"class":107,"line":735},26,[105,737,671],{"class":127},[105,739,679],{"class":127},[105,741,530],{"class":157},[105,743,135],{"class":119},[105,745,746],{"class":115},"Done",[105,748,690],{"class":119},[105,750,752,754,757,759,762],{"class":107,"line":751},27,[105,753,729],{"class":127},[105,755,756],{"class":157}," ctx",[105,758,135],{"class":119},[105,760,761],{"class":115},"Err",[105,763,269],{"class":119},[105,765,767],{"class":107,"line":766},28,[105,768,769],{"class":119},"    }\n",[105,771,773],{"class":107,"line":772},29,[105,774,380],{"class":119},[105,776,778],{"class":107,"line":777},30,[105,779,252],{"emptyLinePlaceholder":251},[105,781,783,785,788,791],{"class":107,"line":782},31,[105,784,112],{"class":111},[105,786,787],{"class":115}," main",[105,789,790],{"class":119},"()",[105,792,144],{"class":119},[105,794,796],{"class":107,"line":795},32,[105,797,798],{"class":150},"    // Production: real time\n",[105,800,802,804,806,809,811,814,816,819,821,823,826],{"class":107,"line":801},33,[105,803,232],{"class":157},[105,805,161],{"class":157},[105,807,808],{"class":127}," &",[105,810,520],{"class":131},[105,812,813],{"class":119},"{",[105,815,242],{"class":488},[105,817,818],{"class":119},":",[105,820,164],{"class":157},[105,822,135],{"class":119},[105,824,825],{"class":157},"RealClock",[105,827,380],{"class":119},[105,829,831,834,837,839,841,843,846,848,851,853,856,859,861,864,867],{"class":107,"line":830},34,[105,832,833],{"class":127},"    if",[105,835,836],{"class":157}," err",[105,838,161],{"class":157},[105,840,261],{"class":157},[105,842,135],{"class":119},[105,844,845],{"class":115},"ProcessWithTimeout",[105,847,120],{"class":119},[105,849,850],{"class":157},"context",[105,852,135],{"class":119},[105,854,855],{"class":115},"Background",[105,857,858],{"class":119},"());",[105,860,836],{"class":157},[105,862,863],{"class":127}," !=",[105,865,866],{"class":111}," nil",[105,868,144],{"class":119},[105,870,872,874,876,878,880,883,885,887,889,891,893],{"class":107,"line":871},35,[105,873,696],{"class":157},[105,875,135],{"class":119},[105,877,701],{"class":115},[105,879,120],{"class":119},[105,881,882],{"class":401},"\"Error: ",[105,884,710],{"class":709},[105,886,714],{"class":713},[105,888,717],{"class":401},[105,890,188],{"class":119},[105,892,836],{"class":157},[105,894,245],{"class":119},[105,896,898],{"class":107,"line":897},36,[105,899,769],{"class":119},[105,901,903],{"class":107,"line":902},37,[105,904,380],{"class":119},[90,906,908],{"id":907},"capabilities","Capabilities",[910,911,912,928],"table",{},[913,914,915],"thead",{},[916,917,918,922,925],"tr",{},[919,920,921],"th",{},"Feature",[919,923,924],{},"Description",[919,926,927],{},"Docs",[929,930,931,950,964,978,996],"tbody",{},[916,932,933,938,944],{},[934,935,936],"td",{},[102,937,825],{},[934,939,940,941,943],{},"Production clock wrapping ",[102,942,174],{}," package",[934,945,946],{},[19,947,949],{"href":948},"docs/reference/api","API Reference",[916,951,952,957,960],{},[934,953,954],{},[102,955,956],{},"FakeClock",[934,958,959],{},"Test clock with manual time control",[934,961,962],{},[19,963,949],{"href":948},[916,965,966,969,972],{},[934,967,968],{},"Timers & Tickers",[934,970,971],{},"Create, stop, reset timing primitives",[934,973,974],{},[19,975,977],{"href":976},"docs/learn/concepts","Concepts",[916,979,980,983,992],{},[934,981,982],{},"Context Integration",[934,984,985,987,988,991],{},[102,986,570],{}," and ",[102,989,990],{},"WithDeadline"," support",[934,993,994],{},[19,995,977],{"href":976},[916,997,998,1001,1010],{},[934,999,1000],{},"Time Advancement",[934,1002,1003,987,1006,1009],{},[102,1004,1005],{},"Advance()",[102,1007,1008],{},"SetTime()"," for tests",[934,1011,1012],{},[19,1013,1015],{"href":1014},"docs/guides/testing","Testing Guide",[90,1017,1019],{"id":1018},"why-clockz","Why clockz?",[1021,1022,1023,1031,1037,1043,1052],"ul",{},[1024,1025,1026,1030],"li",{},[1027,1028,1029],"strong",{},"Deterministic tests"," — Eliminate time-based flakiness entirely",[1024,1032,1033,1036],{},[1027,1034,1035],{},"Zero dependencies"," — Only the standard library",[1024,1038,1039,1042],{},[1027,1040,1041],{},"Thread-safe"," — All operations safe for concurrent use",[1024,1044,1045,1048,1049,1051],{},[1027,1046,1047],{},"Familiar API"," — Mirrors the ",[102,1050,174],{}," package interface",[1024,1053,1054,1057],{},[1027,1055,1056],{},"Context-aware"," — Built-in timeout and deadline support",[90,1059,1061],{"id":1060},"documentation","Documentation",[16,1063,1064],{},[1027,1065,1066],{},"Learn",[1021,1068,1069,1075],{},[1024,1070,1071,1074],{},[19,1072,412],{"href":1073},"docs/learn/quickstart"," — Get up and running",[1024,1076,1077,1080],{},[19,1078,1079],{"href":976},"Core Concepts"," — Clock selection, dependency injection",[16,1082,1083],{},[1027,1084,1085],{},"Guides",[1021,1087,1088],{},[1024,1089,1090,1093],{},[19,1091,1092],{"href":1014},"Testing Patterns"," — Strategies for time-dependent tests",[16,1095,1096],{},[1027,1097,1098],{},"Cookbook",[1021,1100,1101],{},[1024,1102,1103,1107],{},[19,1104,1106],{"href":1105},"docs/cookbook/patterns","Common Patterns"," — Retry, rate limiting, deadlines",[16,1109,1110],{},[1027,1111,1112],{},"Reference",[1021,1114,1115],{},[1024,1116,1117,1119],{},[19,1118,949],{"href":948}," — Complete interface documentation",[90,1121,1123],{"id":1122},"contributing","Contributing",[16,1125,1126,1127,1131],{},"Contributions welcome. See ",[19,1128,1130],{"href":1129},"CONTRIBUTING","CONTRIBUTING.md"," for guidelines.",[95,1133,1135],{"className":390,"code":1134,"language":392,"meta":100,"style":100},"make help      # Available commands\nmake check     # Run tests and lint\n",[102,1136,1137,1148],{"__ignoreMap":100},[105,1138,1139,1142,1145],{"class":107,"line":108},[105,1140,1141],{"class":115},"make",[105,1143,1144],{"class":401}," help",[105,1146,1147],{"class":150},"      # Available commands\n",[105,1149,1150,1152,1155],{"class":107,"line":147},[105,1151,1141],{"class":115},[105,1153,1154],{"class":401}," check",[105,1156,1157],{"class":150},"     # Run tests and lint\n",[90,1159,66],{"id":1160},"license",[16,1162,1163,1164],{},"MIT — see ",[19,1165,63],{"href":63},[1167,1168,1169],"style",{},"html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}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 pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"title":100,"searchDepth":147,"depth":147,"links":1171},[1172,1173,1174,1175,1176,1177,1178,1179],{"id":92,"depth":147,"text":93},{"id":386,"depth":147,"text":387},{"id":411,"depth":147,"text":412},{"id":907,"depth":147,"text":908},{"id":1018,"depth":147,"text":1019},{"id":1060,"depth":147,"text":1061},{"id":1122,"depth":147,"text":1123},{"id":1160,"depth":147,"text":66},"md","book-open",{},"/readme",{"title":7,"description":100},"readme","tHPKoMVuTuLF_Yc5RpGOzzmpWvFwPyLJps_a1_pZmJs",{"id":1188,"title":1189,"body":1190,"description":100,"extension":1180,"icon":1607,"meta":1608,"navigation":251,"path":1609,"seo":1610,"stem":1611,"__hash__":1612},"resources/security.md","Security",{"type":9,"value":1191,"toc":1593},[1192,1196,1200,1203,1242,1246,1249,1254,1259,1262,1301,1305,1308,1357,1361,1387,1391,1394,1398,1401,1479,1483,1486,1518,1522,1525,1550,1554,1568,1572,1575,1581,1584,1590],[12,1193,1195],{"id":1194},"security-policy","Security Policy",[90,1197,1199],{"id":1198},"supported-versions","Supported Versions",[16,1201,1202],{},"We release patches for security vulnerabilities. Which versions are eligible for receiving such patches depends on the CVSS v3.0 Rating:",[910,1204,1205,1218],{},[913,1206,1207],{},[916,1208,1209,1212,1215],{},[919,1210,1211],{},"Version",[919,1213,1214],{},"Supported",[919,1216,1217],{},"Status",[929,1219,1220,1231],{},[916,1221,1222,1225,1228],{},[934,1223,1224],{},"latest",[934,1226,1227],{},"✅",[934,1229,1230],{},"Active development",[916,1232,1233,1236,1239],{},[934,1234,1235],{},"\u003C latest",[934,1237,1238],{},"❌",[934,1240,1241],{},"Security fixes only for critical issues",[90,1243,1245],{"id":1244},"reporting-a-vulnerability","Reporting a Vulnerability",[16,1247,1248],{},"We take the security of clockz seriously. If you have discovered a security vulnerability in this project, please report it responsibly.",[1250,1251,1253],"h3",{"id":1252},"how-to-report","How to Report",[16,1255,1256],{},[1027,1257,1258],{},"Please DO NOT report security vulnerabilities through public GitHub issues.",[16,1260,1261],{},"Instead, please report them via one of the following methods:",[1263,1264,1265,1288],"ol",{},[1024,1266,1267,1270,1271],{},[1027,1268,1269],{},"GitHub Security Advisories"," (Preferred)",[1021,1272,1273,1282,1285],{},[1024,1274,1275,1276,1281],{},"Go to the ",[19,1277,1280],{"href":1278,"rel":1279},"https://github.com/zoobz-io/clockz/security",[23],"Security tab"," of this repository",[1024,1283,1284],{},"Click \"Report a vulnerability\"",[1024,1286,1287],{},"Fill out the form with details about the vulnerability",[1024,1289,1290,1293],{},[1027,1291,1292],{},"Email",[1021,1294,1295,1298],{},[1024,1296,1297],{},"Send details to the repository maintainer through GitHub profile contact information",[1024,1299,1300],{},"Use PGP encryption if possible for sensitive details",[1250,1302,1304],{"id":1303},"what-to-include","What to Include",[16,1306,1307],{},"Please include the following information (as much as you can provide) to help us better understand the nature and scope of the possible issue:",[1021,1309,1310,1316,1322,1328,1334,1339,1345,1351],{},[1024,1311,1312,1315],{},[1027,1313,1314],{},"Type of issue"," (e.g., race condition, resource leak, timing attack, etc.)",[1024,1317,1318,1321],{},[1027,1319,1320],{},"Full paths of source file(s)"," related to the manifestation of the issue",[1024,1323,1324,1327],{},[1027,1325,1326],{},"The location of the affected source code"," (tag/branch/commit or direct URL)",[1024,1329,1330,1333],{},[1027,1331,1332],{},"Any special configuration required"," to reproduce the issue",[1024,1335,1336,1333],{},[1027,1337,1338],{},"Step-by-step instructions",[1024,1340,1341,1344],{},[1027,1342,1343],{},"Proof-of-concept or exploit code"," (if possible)",[1024,1346,1347,1350],{},[1027,1348,1349],{},"Impact of the issue",", including how an attacker might exploit the issue",[1024,1352,1353,1356],{},[1027,1354,1355],{},"Your name and affiliation"," (optional)",[1250,1358,1360],{"id":1359},"what-to-expect","What to Expect",[1021,1362,1363,1369,1375,1381],{},[1024,1364,1365,1368],{},[1027,1366,1367],{},"Acknowledgment",": We will acknowledge receipt of your vulnerability report within 48 hours",[1024,1370,1371,1374],{},[1027,1372,1373],{},"Initial Assessment",": Within 7 days, we will provide an initial assessment of the report",[1024,1376,1377,1380],{},[1027,1378,1379],{},"Resolution Timeline",": We aim to resolve critical issues within 30 days",[1024,1382,1383,1386],{},[1027,1384,1385],{},"Disclosure",": We will coordinate with you on the disclosure timeline",[1250,1388,1390],{"id":1389},"preferred-languages","Preferred Languages",[16,1392,1393],{},"We prefer all communications to be in English.",[90,1395,1397],{"id":1396},"security-best-practices","Security Best Practices",[16,1399,1400],{},"When using clockz in your applications, we recommend:",[1263,1402,1403,1424,1437,1450,1463],{},[1024,1404,1405,1408],{},[1027,1406,1407],{},"Keep Dependencies Updated",[95,1409,1411],{"className":390,"code":1410,"language":392,"meta":100,"style":100},"go get -u github.com/zoobz-io/clockz\n",[102,1412,1413],{"__ignoreMap":100},[105,1414,1415,1417,1419,1422],{"class":107,"line":108},[105,1416,99],{"class":115},[105,1418,402],{"class":401},[105,1420,1421],{"class":111}," -u",[105,1423,405],{"class":401},[1024,1425,1426,1429],{},[1027,1427,1428],{},"Use Context Properly",[1021,1430,1431,1434],{},[1024,1432,1433],{},"Always pass contexts with appropriate timeouts",[1024,1435,1436],{},"Handle context cancellation in clock operations",[1024,1438,1439,1442],{},[1027,1440,1441],{},"Error Handling",[1021,1443,1444,1447],{},[1024,1445,1446],{},"Never ignore errors returned by clock operations",[1024,1448,1449],{},"Implement proper fallback mechanisms",[1024,1451,1452,1455],{},[1027,1453,1454],{},"Testing with Real Clocks",[1021,1456,1457,1460],{},[1024,1458,1459],{},"Use fake clocks only in testing environments",[1024,1461,1462],{},"Ensure production code uses real clock implementations",[1024,1464,1465,1468],{},[1027,1466,1467],{},"Resource Management",[1021,1469,1470,1473,1476],{},[1024,1471,1472],{},"Set appropriate timeouts for all operations",[1024,1474,1475],{},"Handle goroutine cleanup properly",[1024,1477,1478],{},"Avoid clock-based infinite loops",[90,1480,1482],{"id":1481},"security-features","Security Features",[16,1484,1485],{},"clockz includes several built-in security features:",[1021,1487,1488,1494,1500,1506,1512],{},[1024,1489,1490,1493],{},[1027,1491,1492],{},"Type Safety",": Generic types prevent type confusion attacks",[1024,1495,1496,1499],{},[1027,1497,1498],{},"Context Support",": Built-in cancellation and timeout support",[1024,1501,1502,1505],{},[1027,1503,1504],{},"Error Isolation",": Errors are properly wrapped and traced",[1024,1507,1508,1511],{},[1027,1509,1510],{},"Thread Safety",": All operations are safe for concurrent use",[1024,1513,1514,1517],{},[1027,1515,1516],{},"No Dependencies",": Zero external dependencies reduce attack surface",[90,1519,1521],{"id":1520},"automated-security-scanning","Automated Security Scanning",[16,1523,1524],{},"This project uses:",[1021,1526,1527,1532,1538,1544],{},[1024,1528,1529,1531],{},[1027,1530,51],{},": GitHub's semantic code analysis for security vulnerabilities",[1024,1533,1534,1537],{},[1027,1535,1536],{},"Dependabot",": Automated dependency updates (for dev dependencies)",[1024,1539,1540,1543],{},[1027,1541,1542],{},"golangci-lint",": Static analysis including security linters",[1024,1545,1546,1549],{},[1027,1547,1548],{},"Codecov",": Coverage tracking to ensure security-critical code is tested",[90,1551,1553],{"id":1552},"vulnerability-disclosure-policy","Vulnerability Disclosure Policy",[1021,1555,1556,1559,1562,1565],{},[1024,1557,1558],{},"Security vulnerabilities will be disclosed via GitHub Security Advisories",[1024,1560,1561],{},"We follow a 90-day disclosure timeline for non-critical issues",[1024,1563,1564],{},"Critical vulnerabilities may be disclosed sooner after patches are available",[1024,1566,1567],{},"We will credit reporters who follow responsible disclosure practices",[90,1569,1571],{"id":1570},"credits","Credits",[16,1573,1574],{},"We thank the following individuals for responsibly disclosing security issues:",[16,1576,1577],{},[1578,1579,1580],"em",{},"This list is currently empty. Be the first to help improve our security!",[1582,1583],"hr",{},[16,1585,1586,1589],{},[1027,1587,1588],{},"Last Updated",": 2025-09-14",[1167,1591,1592],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}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);}",{"title":100,"searchDepth":147,"depth":147,"links":1594},[1595,1596,1602,1603,1604,1605,1606],{"id":1198,"depth":147,"text":1199},{"id":1244,"depth":147,"text":1245,"children":1597},[1598,1599,1600,1601],{"id":1252,"depth":154,"text":1253},{"id":1303,"depth":154,"text":1304},{"id":1359,"depth":154,"text":1360},{"id":1389,"depth":154,"text":1390},{"id":1396,"depth":147,"text":1397},{"id":1481,"depth":147,"text":1482},{"id":1520,"depth":147,"text":1521},{"id":1552,"depth":147,"text":1553},{"id":1570,"depth":147,"text":1571},"shield",{},"/security",{"title":1189,"description":100},"security","J9HUhpjmoes7Uzsqt7m_Zm-X0iiK2LKqtvJkz8HUkN4",{"id":1614,"title":1123,"body":1615,"description":1623,"extension":1180,"icon":102,"meta":2200,"navigation":251,"path":2201,"seo":2202,"stem":1122,"__hash__":2203},"resources/contributing.md",{"type":9,"value":1616,"toc":2174},[1617,1621,1624,1628,1631,1635,1673,1677,1681,1699,1702,1718,1720,1731,1735,1739,1753,1757,1768,1772,1777,1780,1798,1802,1805,1819,1823,1826,1840,1844,1872,1875,1878,1893,1896,1912,1915,1931,1935,1943,1947,1950,1994,1998,2002,2005,2016,2019,2033,2037,2040,2068,2072,2098,2102,2129,2135,2139,2142,2153,2157,2168,2171],[12,1618,1620],{"id":1619},"contributing-to-clockz","Contributing to clockz",[16,1622,1623],{},"Thank you for your interest in contributing to clockz! This guide will help you get started.",[90,1625,1627],{"id":1626},"code-of-conduct","Code of Conduct",[16,1629,1630],{},"By participating in this project, you agree to maintain a respectful and inclusive environment for all contributors.",[90,1632,1634],{"id":1633},"getting-started","Getting Started",[1263,1636,1637,1640,1646,1652,1655,1661,1664,1670],{},[1024,1638,1639],{},"Fork the repository",[1024,1641,1642,1643],{},"Clone your fork: ",[102,1644,1645],{},"git clone https://github.com/yourusername/clockz.git",[1024,1647,1648,1649],{},"Create a feature branch: ",[102,1650,1651],{},"git checkout -b feature/your-feature-name",[1024,1653,1654],{},"Make your changes",[1024,1656,1657,1658],{},"Run tests: ",[102,1659,1660],{},"go test ./...",[1024,1662,1663],{},"Commit your changes with a descriptive message",[1024,1665,1666,1667],{},"Push to your fork: ",[102,1668,1669],{},"git push origin feature/your-feature-name",[1024,1671,1672],{},"Create a Pull Request",[90,1674,1676],{"id":1675},"development-guidelines","Development Guidelines",[1250,1678,1680],{"id":1679},"code-style","Code Style",[1021,1682,1683,1686,1693,1696],{},[1024,1684,1685],{},"Follow standard Go conventions",[1024,1687,1688,1689,1692],{},"Run ",[102,1690,1691],{},"go fmt"," before committing",[1024,1694,1695],{},"Add comments for exported functions and types",[1024,1697,1698],{},"Keep functions small and focused",[1250,1700,1701],{"id":132},"Testing",[1021,1703,1704,1707,1712,1715],{},[1024,1705,1706],{},"Write tests for new functionality",[1024,1708,1709,1710],{},"Ensure all tests pass: ",[102,1711,1660],{},[1024,1713,1714],{},"Include benchmarks for performance-critical code",[1024,1716,1717],{},"Aim for good test coverage",[1250,1719,1061],{"id":1060},[1021,1721,1722,1725,1728],{},[1024,1723,1724],{},"Update documentation for API changes",[1024,1726,1727],{},"Add examples for new features",[1024,1729,1730],{},"Keep doc comments clear and concise",[90,1732,1734],{"id":1733},"types-of-contributions","Types of Contributions",[1250,1736,1738],{"id":1737},"bug-reports","Bug Reports",[1021,1740,1741,1744,1747,1750],{},[1024,1742,1743],{},"Use GitHub Issues",[1024,1745,1746],{},"Include minimal reproduction code",[1024,1748,1749],{},"Describe expected vs actual behavior",[1024,1751,1752],{},"Include Go version and OS",[1250,1754,1756],{"id":1755},"feature-requests","Feature Requests",[1021,1758,1759,1762,1765],{},[1024,1760,1761],{},"Open an issue for discussion first",[1024,1763,1764],{},"Explain the use case",[1024,1766,1767],{},"Consider backwards compatibility",[1250,1769,1771],{"id":1770},"code-contributions","Code Contributions",[1773,1774,1776],"h4",{"id":1775},"adding-clock-implementations","Adding Clock Implementations",[16,1778,1779],{},"New clock implementations should:",[1021,1781,1782,1789,1792,1795],{},[1024,1783,1784,1785,1788],{},"Implement the ",[102,1786,1787],{},"Clock"," interface",[1024,1790,1791],{},"Handle context cancellation properly",[1024,1793,1794],{},"Include comprehensive tests",[1024,1796,1797],{},"Add documentation with examples",[1773,1799,1801],{"id":1800},"adding-testing-utilities","Adding Testing Utilities",[16,1803,1804],{},"New testing utilities should:",[1021,1806,1807,1810,1813,1816],{},[1024,1808,1809],{},"Follow the existing pattern",[1024,1811,1812],{},"Support deterministic testing",[1024,1814,1815],{},"Include tests for error cases",[1024,1817,1818],{},"Document behavior clearly",[1773,1820,1822],{"id":1821},"examples","Examples",[16,1824,1825],{},"New examples should:",[1021,1827,1828,1831,1834,1837],{},[1024,1829,1830],{},"Solve a real-world problem",[1024,1832,1833],{},"Include tests and benchmarks",[1024,1835,1836],{},"Have a descriptive README",[1024,1838,1839],{},"Follow the existing structure",[90,1841,1843],{"id":1842},"pull-request-process","Pull Request Process",[1263,1845,1846,1852,1857,1862,1867],{},[1024,1847,1848,1851],{},[1027,1849,1850],{},"Keep PRs focused"," - One feature/fix per PR",[1024,1853,1854],{},[1027,1855,1856],{},"Write descriptive commit messages",[1024,1858,1859],{},[1027,1860,1861],{},"Update tests and documentation",[1024,1863,1864],{},[1027,1865,1866],{},"Ensure CI passes",[1024,1868,1869],{},[1027,1870,1871],{},"Respond to review feedback",[90,1873,1701],{"id":1874},"testing-1",[16,1876,1877],{},"Run the full test suite:",[95,1879,1881],{"className":390,"code":1880,"language":392,"meta":100,"style":100},"go test ./...\n",[102,1882,1883],{"__ignoreMap":100},[105,1884,1885,1887,1890],{"class":107,"line":108},[105,1886,99],{"class":115},[105,1888,1889],{"class":401}," test",[105,1891,1892],{"class":401}," ./...\n",[16,1894,1895],{},"Run with race detection:",[95,1897,1899],{"className":390,"code":1898,"language":392,"meta":100,"style":100},"go test -race ./...\n",[102,1900,1901],{"__ignoreMap":100},[105,1902,1903,1905,1907,1910],{"class":107,"line":108},[105,1904,99],{"class":115},[105,1906,1889],{"class":401},[105,1908,1909],{"class":111}," -race",[105,1911,1892],{"class":401},[16,1913,1914],{},"Run benchmarks:",[95,1916,1918],{"className":390,"code":1917,"language":392,"meta":100,"style":100},"go test -bench=. ./...\n",[102,1919,1920],{"__ignoreMap":100},[105,1921,1922,1924,1926,1929],{"class":107,"line":108},[105,1923,99],{"class":115},[105,1925,1889],{"class":401},[105,1927,1928],{"class":111}," -bench=.",[105,1930,1892],{"class":401},[90,1932,1934],{"id":1933},"project-structure","Project Structure",[95,1936,1941],{"className":1937,"code":1939,"language":1940},[1938],"language-text","clockz/\n├── *.go              # Core library files\n├── *_test.go         # Tests\n├── testing/          # Testing utilities\n│   └── integration/  # Integration tests\n└── docs/            # Documentation\n","text",[102,1942,1939],{"__ignoreMap":100},[90,1944,1946],{"id":1945},"commit-messages","Commit Messages",[16,1948,1949],{},"Follow conventional commits:",[1021,1951,1952,1958,1964,1970,1976,1982,1988],{},[1024,1953,1954,1957],{},[102,1955,1956],{},"feat:"," New feature",[1024,1959,1960,1963],{},[102,1961,1962],{},"fix:"," Bug fix",[1024,1965,1966,1969],{},[102,1967,1968],{},"docs:"," Documentation changes",[1024,1971,1972,1975],{},[102,1973,1974],{},"test:"," Test additions/changes",[1024,1977,1978,1981],{},[102,1979,1980],{},"refactor:"," Code refactoring",[1024,1983,1984,1987],{},[102,1985,1986],{},"perf:"," Performance improvements",[1024,1989,1990,1993],{},[102,1991,1992],{},"chore:"," Maintenance tasks",[90,1995,1997],{"id":1996},"release-process","Release Process",[1250,1999,2001],{"id":2000},"automated-releases","Automated Releases",[16,2003,2004],{},"This project uses automated release versioning. To create a release:",[1263,2006,2007,2010,2013],{},[1024,2008,2009],{},"Go to Actions → Release → Run workflow",[1024,2011,2012],{},"Leave \"Version override\" empty for automatic version inference",[1024,2014,2015],{},"Click \"Run workflow\"",[16,2017,2018],{},"The system will:",[1021,2020,2021,2024,2027,2030],{},[1024,2022,2023],{},"Automatically determine the next version from conventional commits",[1024,2025,2026],{},"Create a git tag",[1024,2028,2029],{},"Generate release notes via GoReleaser",[1024,2031,2032],{},"Publish the release to GitHub",[1250,2034,2036],{"id":2035},"manual-release-legacy","Manual Release (Legacy)",[16,2038,2039],{},"You can still create releases manually:",[95,2041,2043],{"className":390,"code":2042,"language":392,"meta":100,"style":100},"git tag v1.2.3\ngit push origin v1.2.3\n",[102,2044,2045,2056],{"__ignoreMap":100},[105,2046,2047,2050,2053],{"class":107,"line":108},[105,2048,2049],{"class":115},"git",[105,2051,2052],{"class":401}," tag",[105,2054,2055],{"class":401}," v1.2.3\n",[105,2057,2058,2060,2063,2066],{"class":107,"line":147},[105,2059,2049],{"class":115},[105,2061,2062],{"class":401}," push",[105,2064,2065],{"class":401}," origin",[105,2067,2055],{"class":401},[1250,2069,2071],{"id":2070},"known-limitations","Known Limitations",[1021,2073,2074,2080,2086],{},[1024,2075,2076,2079],{},[1027,2077,2078],{},"Protected branches",": The automated release cannot bypass branch protection rules. This is by design for security.",[1024,2081,2082,2085],{},[1027,2083,2084],{},"Concurrent releases",": Rapid successive releases may fail. Simply retry after a moment.",[1024,2087,2088,2091,2092,2094,2095,2097],{},[1027,2089,2090],{},"Conventional commits required",": Version inference requires conventional commit format (",[102,2093,1956],{},", ",[102,2096,1962],{},", etc.)",[1250,2099,2101],{"id":2100},"commit-conventions-for-versioning","Commit Conventions for Versioning",[1021,2103,2104,2109,2114,2120],{},[1024,2105,2106,2108],{},[102,2107,1956],{}," new features (minor version: 1.2.0 → 1.3.0)",[1024,2110,2111,2113],{},[102,2112,1962],{}," bug fixes (patch version: 1.2.0 → 1.2.1)",[1024,2115,2116,2119],{},[102,2117,2118],{},"feat!:"," breaking changes (major version: 1.2.0 → 2.0.0)",[1024,2121,2122,2094,2124,2094,2126,2128],{},[102,2123,1968],{},[102,2125,1974],{},[102,2127,1992],{}," no version change",[16,2130,2131,2132],{},"Example: ",[102,2133,2134],{},"feat(clock): add timeout support for clock operations",[1250,2136,2138],{"id":2137},"version-preview-on-pull-requests","Version Preview on Pull Requests",[16,2140,2141],{},"Every PR automatically shows the next version that will be created:",[1021,2143,2144,2147,2150],{},[1024,2145,2146],{},"Check PR comments for \"Version Preview\"",[1024,2148,2149],{},"Updates automatically as you add commits",[1024,2151,2152],{},"Helps verify your commits have the intended effect",[90,2154,2156],{"id":2155},"questions","Questions?",[1021,2158,2159,2162,2165],{},[1024,2160,2161],{},"Open an issue for questions",[1024,2163,2164],{},"Check existing issues first",[1024,2166,2167],{},"Be patient and respectful",[16,2169,2170],{},"Thank you for contributing to clockz!",[1167,2172,2173],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}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 pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}",{"title":100,"searchDepth":147,"depth":147,"links":2175},[2176,2177,2178,2183,2188,2189,2190,2191,2192,2199],{"id":1626,"depth":147,"text":1627},{"id":1633,"depth":147,"text":1634},{"id":1675,"depth":147,"text":1676,"children":2179},[2180,2181,2182],{"id":1679,"depth":154,"text":1680},{"id":132,"depth":154,"text":1701},{"id":1060,"depth":154,"text":1061},{"id":1733,"depth":147,"text":1734,"children":2184},[2185,2186,2187],{"id":1737,"depth":154,"text":1738},{"id":1755,"depth":154,"text":1756},{"id":1770,"depth":154,"text":1771},{"id":1842,"depth":147,"text":1843},{"id":1874,"depth":147,"text":1701},{"id":1933,"depth":147,"text":1934},{"id":1945,"depth":147,"text":1946},{"id":1996,"depth":147,"text":1997,"children":2193},[2194,2195,2196,2197,2198],{"id":2000,"depth":154,"text":2001},{"id":2035,"depth":154,"text":2036},{"id":2070,"depth":154,"text":2071},{"id":2100,"depth":154,"text":2101},{"id":2137,"depth":154,"text":2138},{"id":2155,"depth":147,"text":2156},{},"/contributing",{"title":1123,"description":1623},"dRMHa7ytf-UzRc2mlS6xZVDVJI2xC1tFZ5RtPHPEEcg",[2205,2209,2213,2217,2222,2227,2232,2237,2242,2246,2249,2254,2258,2262,2267,2272,2277,2281,2286,2291,2295,2299,2303,2307,2312,2317,2322,2327,2332,2337,2341,2346,2351,2356,2360,2364,2369,2374,2379,2384,2389,2393,2398,2403,2408,2413,2418,2423,2428,2432,2436,2441,2446,2451,2456,2461,2466,2471,2476,2481,2486,2491,2496,2501,2506,2511,2516,2521,2525,2528,2533,2538,2543,2548,2553,2557],{"id":2206,"title":412,"titles":2207,"content":2208,"level":108},"/v1.0.2/learn/quickstart",[],"Get up and running with clockz in minutes",{"id":2210,"title":412,"titles":2211,"content":2212,"level":108},"/v1.0.2/learn/quickstart#quick-start",[],"This guide gets you from zero to deterministic time testing in minutes.",{"id":2214,"title":387,"titles":2215,"content":2216,"level":147},"/v1.0.2/learn/quickstart#installation",[412],"go get github.com/zoobz-io/clockz Requires Go 1.24 or higher.",{"id":2218,"title":2219,"titles":2220,"content":2221,"level":147},"/v1.0.2/learn/quickstart#the-pattern","The Pattern",[412],"clockz works through dependency injection. Instead of calling time.Now() directly, your code accepts a Clock interface: type Service struct {\n    clock clockz.Clock\n}\n\nfunc NewService(clock clockz.Clock) *Service {\n    return &Service{clock: clock}\n} In production, inject RealClock. In tests, inject FakeClock.",{"id":2223,"title":2224,"titles":2225,"content":2226,"level":147},"/v1.0.2/learn/quickstart#production-usage","Production Usage",[412],"package main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/zoobz-io/clockz\"\n)\n\ntype Scheduler struct {\n    clock clockz.Clock\n}\n\nfunc (s *Scheduler) NextRun() time.Time {\n    return s.clock.Now().Add(24 * time.Hour)\n}\n\nfunc (s *Scheduler) WaitUntil(target time.Time) {\n    duration := target.Sub(s.clock.Now())\n    if duration > 0 {\n        s.clock.Sleep(duration)\n    }\n}\n\nfunc main() {\n    scheduler := &Scheduler{clock: clockz.RealClock}\n\n    next := scheduler.NextRun()\n    fmt.Printf(\"Next run: %v\\n\", next)\n}",{"id":2228,"title":2229,"titles":2230,"content":2231,"level":147},"/v1.0.2/learn/quickstart#test-usage","Test Usage",[412],"func TestScheduler(t *testing.T) {\n    // Start at a known time\n    start := time.Date(2024, 6, 15, 10, 0, 0, 0, time.UTC)\n    clock := clockz.NewFakeClockAt(start)\n\n    scheduler := &Scheduler{clock: clock}\n\n    // Verify next run is 24 hours later\n    next := scheduler.NextRun()\n    expected := start.Add(24 * time.Hour)\n\n    if !next.Equal(expected) {\n        t.Errorf(\"expected %v, got %v\", expected, next)\n    }\n\n    // Test waiting (completes instantly)\n    done := make(chan bool)\n    go func() {\n        scheduler.WaitUntil(next)\n        done \u003C- true\n    }()\n\n    // Advance time to trigger completion\n    clock.Advance(24 * time.Hour)\n\n    select {\n    case \u003C-done:\n        // Success\n    case \u003C-time.After(100 * time.Millisecond):\n        t.Fatal(\"WaitUntil did not complete\")\n    }\n}",{"id":2233,"title":2234,"titles":2235,"content":2236,"level":147},"/v1.0.2/learn/quickstart#key-points","Key Points",[412],"One interface, two implementations — Clock is implemented by both RealClock and FakeClockInject at construction — Pass the clock when creating your typesAdvance explicitly — FakeClock time only moves when you call Advance() or SetTime()Thread-safe — Both implementations are safe for concurrent use",{"id":2238,"title":2239,"titles":2240,"content":2241,"level":147},"/v1.0.2/learn/quickstart#next-steps","Next Steps",[412],"Core Concepts — Understand clock selection and the dependency injection patternTesting Patterns — Common testing strategies html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}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 pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":2243,"title":1079,"titles":2244,"content":2245,"level":108},"/v1.0.2/learn/concepts",[],"Understanding clock selection and dependency injection in clockz",{"id":2247,"title":1079,"titles":2248,"content":100,"level":108},"/v1.0.2/learn/concepts#core-concepts",[],{"id":2250,"title":2251,"titles":2252,"content":2253,"level":147},"/v1.0.2/learn/concepts#clock-selection","Clock Selection",[1079],"clockz provides two clock implementations. Choosing between them is straightforward.",{"id":2255,"title":825,"titles":2256,"content":2257,"level":154},"/v1.0.2/learn/concepts#realclock",[1079,2251],"Use RealClock when: Running in productionIntegrating with external systems that expect real timeMeasuring actual wall-clock performanceAny code path that faces the outside world service := NewService(clockz.RealClock) RealClock is a package-level variable—there's no constructor. It delegates directly to the time package with zero overhead.",{"id":2259,"title":956,"titles":2260,"content":2261,"level":154},"/v1.0.2/learn/concepts#fakeclock",[1079,2251],"Use FakeClock when: Writing unit testsTesting timeout behaviorSimulating time-dependent scenariosEliminating test flakiness from timing // Start at Unix epoch\nclock := clockz.NewFakeClock()\n\n// Start at specific time\nclock := clockz.NewFakeClockAt(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)) Time in FakeClock never advances automatically. You control it explicitly.",{"id":2263,"title":2264,"titles":2265,"content":2266,"level":147},"/v1.0.2/learn/concepts#dependency-injection-pattern","Dependency Injection Pattern",[1079],"The core pattern is simple: accept a Clock interface instead of calling time functions directly.",{"id":2268,"title":2269,"titles":2270,"content":2271,"level":154},"/v1.0.2/learn/concepts#before-hard-to-test","Before (Hard to Test)",[1079,2264],"type Cache struct {\n    data    map[string]entry\n}\n\ntype entry struct {\n    value   interface{}\n    expires time.Time\n}\n\nfunc (c *Cache) Get(key string) (interface{}, bool) {\n    e, ok := c.data[key]\n    if !ok || time.Now().After(e.expires) {  // Direct time dependency\n        return nil, false\n    }\n    return e.value, true\n} Testing this requires either: Waiting for real time to pass (slow)Complex mocking of the time package (fragile)Accepting flaky tests (unreliable)",{"id":2273,"title":2274,"titles":2275,"content":2276,"level":154},"/v1.0.2/learn/concepts#after-testable","After (Testable)",[1079,2264],"type Cache struct {\n    clock clockz.Clock\n    data  map[string]entry\n}\n\nfunc NewCache(clock clockz.Clock) *Cache {\n    return &Cache{\n        clock: clock,\n        data:  make(map[string]entry),\n    }\n}\n\nfunc (c *Cache) Set(key string, value interface{}, ttl time.Duration) {\n    c.data[key] = entry{\n        value:   value,\n        expires: c.clock.Now().Add(ttl),\n    }\n}\n\nfunc (c *Cache) Get(key string) (interface{}, bool) {\n    e, ok := c.data[key]\n    if !ok || c.clock.Now().After(e.expires) {  // Injected clock\n        return nil, false\n    }\n    return e.value, true\n} Now testing is trivial: func TestCacheExpiration(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n    cache := NewCache(clock)\n\n    cache.Set(\"key\", \"value\", 5*time.Minute)\n\n    // Before expiration\n    if _, ok := cache.Get(\"key\"); !ok {\n        t.Fatal(\"expected value before expiration\")\n    }\n\n    // After expiration\n    clock.Advance(6 * time.Minute)\n\n    if _, ok := cache.Get(\"key\"); ok {\n        t.Fatal(\"expected no value after expiration\")\n    }\n}",{"id":2278,"title":2279,"titles":2280,"content":100,"level":147},"/v1.0.2/learn/concepts#timers-and-tickers","Timers and Tickers",[1079],{"id":2282,"title":2283,"titles":2284,"content":2285,"level":154},"/v1.0.2/learn/concepts#timers","Timers",[1079,2279],"Timers fire once after a duration: timer := clock.NewTimer(5 * time.Second)\ndefer timer.Stop()\n\nselect {\ncase \u003C-timer.C():\n    fmt.Println(\"Timer fired\")\ncase \u003C-ctx.Done():\n    fmt.Println(\"Cancelled\")\n} Key timer operations: Stop() — Prevents the timer from firing, returns true if it was activeReset(d) — Restarts the timer for duration d, returns true if it was activeC() — Returns the channel that receives the firing time",{"id":2287,"title":2288,"titles":2289,"content":2290,"level":154},"/v1.0.2/learn/concepts#tickers","Tickers",[1079,2279],"Tickers fire repeatedly at intervals: ticker := clock.NewTicker(time.Second)\ndefer ticker.Stop()  // Always stop tickers\n\nfor i := 0; i \u003C 5; i++ {\n    \u003C-ticker.C()\n    fmt.Printf(\"Tick %d\\n\", i+1)\n} Important: Always call Stop() on tickers to release resources.",{"id":2292,"title":982,"titles":2293,"content":2294,"level":147},"/v1.0.2/learn/concepts#context-integration",[1079],"clockz provides clock-aware context timeouts and deadlines: // Timeout after duration\nctx, cancel := clock.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\n\n// Deadline at specific time\ndeadline := clock.Now().Add(1 * time.Hour)\nctx, cancel := clock.WithDeadline(ctx, deadline)\ndefer cancel() With FakeClock, contexts cancel when fake time reaches the deadline—no real waiting required. func TestTimeout(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n\n    ctx, cancel := clock.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n\n    // Context is not cancelled yet\n    if ctx.Err() != nil {\n        t.Fatal(\"context cancelled too early\")\n    }\n\n    // Advance past deadline\n    clock.Advance(6 * time.Second)\n\n    // Now it's cancelled\n    if ctx.Err() != context.DeadlineExceeded {\n        t.Fatalf(\"expected DeadlineExceeded, got %v\", ctx.Err())\n    }\n}",{"id":2296,"title":1510,"titles":2297,"content":2298,"level":147},"/v1.0.2/learn/concepts#thread-safety",[1079],"Both RealClock and FakeClock are fully thread-safe: Multiple goroutines can call any clock method concurrentlyFakeClock.Advance() and SetTime() are safe to call while other goroutines wait on timersTimer and ticker operations are safe for concurrent use This means your production code and tests can use clocks freely in concurrent scenarios without additional synchronization. html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}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 pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"id":2300,"title":1092,"titles":2301,"content":2302,"level":108},"/v1.0.2/guides/testing",[],"Strategies for testing time-dependent code with clockz",{"id":2304,"title":1092,"titles":2305,"content":2306,"level":108},"/v1.0.2/guides/testing#testing-patterns",[],"This guide covers common testing patterns for time-dependent code.",{"id":2308,"title":2309,"titles":2310,"content":2311,"level":147},"/v1.0.2/guides/testing#basic-time-control","Basic Time Control",[1092],"The fundamental pattern: create a FakeClock, advance time, verify behavior. func TestTimeoutBehavior(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n\n    done := make(chan bool)\n    go func() {\n        \u003C-clock.After(5 * time.Minute)\n        done \u003C- true\n    }()\n\n    // Verify nothing happens before timeout\n    clock.Advance(4 * time.Minute)\n    select {\n    case \u003C-done:\n        t.Fatal(\"Should not timeout yet\")\n    default:\n        // Expected\n    }\n\n    // Trigger timeout\n    clock.Advance(1 * time.Minute)\n    select {\n    case \u003C-done:\n        // Success\n    case \u003C-time.After(100 * time.Millisecond):\n        t.Fatal(\"Should have timed out\")\n    }\n}",{"id":2313,"title":2314,"titles":2315,"content":2316,"level":147},"/v1.0.2/guides/testing#testing-concurrent-timers","Testing Concurrent Timers",[1092],"When testing multiple timers, they fire in chronological order as time advances: func TestConcurrentTimers(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n    results := make(chan int, 3)\n\n    // Start timers with different durations\n    go func() {\n        \u003C-clock.After(1 * time.Second)\n        results \u003C- 1\n    }()\n\n    go func() {\n        \u003C-clock.After(2 * time.Second)\n        results \u003C- 2\n    }()\n\n    go func() {\n        \u003C-clock.After(3 * time.Second)\n        results \u003C- 3\n    }()\n\n    // Allow goroutines to register their timers\n    time.Sleep(10 * time.Millisecond)\n\n    // Advance past first two timers\n    clock.Advance(2 * time.Second)\n\n    if val := \u003C-results; val != 1 {\n        t.Errorf(\"Expected 1, got %d\", val)\n    }\n    if val := \u003C-results; val != 2 {\n        t.Errorf(\"Expected 2, got %d\", val)\n    }\n\n    // Advance past third timer\n    clock.Advance(1 * time.Second)\n    if val := \u003C-results; val != 3 {\n        t.Errorf(\"Expected 3, got %d\", val)\n    }\n}",{"id":2318,"title":2319,"titles":2320,"content":2321,"level":147},"/v1.0.2/guides/testing#context-timeout-testing","Context Timeout Testing",[1092],"Test context deadlines without real waits: func TestContextDeadline(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n\n    ctx, cancel := clock.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n\n    done := make(chan error)\n    go func() {\n        \u003C-ctx.Done()\n        done \u003C- ctx.Err()\n    }()\n\n    // Advance to trigger timeout\n    clock.Advance(5 * time.Second)\n\n    err := \u003C-done\n    if err != context.DeadlineExceeded {\n        t.Errorf(\"Expected DeadlineExceeded, got %v\", err)\n    }\n}",{"id":2323,"title":2324,"titles":2325,"content":2326,"level":147},"/v1.0.2/guides/testing#testing-periodic-tasks","Testing Periodic Tasks",[1092],"Test ticker-based periodic work: func TestPeriodicTask(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n    executions := 0\n\n    ticker := clock.NewTicker(time.Minute)\n    defer ticker.Stop()\n\n    // Simulate 5 ticks\n    go func() {\n        for i := 0; i \u003C 5; i++ {\n            \u003C-ticker.C()\n            executions++\n        }\n    }()\n\n    // Advance through 5 minutes\n    for i := 0; i \u003C 5; i++ {\n        clock.Advance(time.Minute)\n        time.Sleep(time.Millisecond) // Let goroutine process\n    }\n\n    if executions != 5 {\n        t.Errorf(\"Expected 5 executions, got %d\", executions)\n    }\n}",{"id":2328,"title":2329,"titles":2330,"content":2331,"level":147},"/v1.0.2/guides/testing#synchronization-with-blockuntilready","Synchronization with BlockUntilReady",[1092],"When goroutines need time to set up their timers, use BlockUntilReady(): func TestWithSynchronization(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n\n    done := make(chan bool)\n    go func() {\n        \u003C-clock.After(5 * time.Second)\n        done \u003C- true\n    }()\n\n    // Wait for timer to be registered\n    clock.BlockUntilReady()\n\n    // Now safe to advance\n    clock.Advance(5 * time.Second)\n\n    select {\n    case \u003C-done:\n        // Success\n    case \u003C-time.After(100 * time.Millisecond):\n        t.Fatal(\"Timer did not fire\")\n    }\n}",{"id":2333,"title":2334,"titles":2335,"content":2336,"level":147},"/v1.0.2/guides/testing#testing-timer-reset","Testing Timer Reset",[1092],"Verify timer reset behavior: func TestTimerReset(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n\n    timer := clock.NewTimer(5 * time.Second)\n    defer timer.Stop()\n\n    // Advance partway\n    clock.Advance(3 * time.Second)\n\n    // Reset extends from current time\n    timer.Reset(5 * time.Second)\n\n    // Original deadline (5s) has passed, but reset deadline (8s total) hasn't\n    clock.Advance(3 * time.Second) // Now at 6s total\n\n    select {\n    case \u003C-timer.C():\n        t.Fatal(\"Timer should not have fired yet\")\n    default:\n        // Expected - 2 more seconds until reset deadline\n    }\n\n    // Advance past reset deadline\n    clock.Advance(2 * time.Second) // Now at 8s total\n\n    select {\n    case \u003C-timer.C():\n        // Success\n    case \u003C-time.After(100 * time.Millisecond):\n        t.Fatal(\"Timer should have fired\")\n    }\n}",{"id":2338,"title":2339,"titles":2340,"content":100,"level":147},"/v1.0.2/guides/testing#common-pitfalls","Common Pitfalls",[1092],{"id":2342,"title":2343,"titles":2344,"content":2345,"level":154},"/v1.0.2/guides/testing#pitfall-forgetting-to-stop-timers","Pitfall: Forgetting to Stop Timers",[1092,2339],"// Bad - timer leaks\ntimer := clock.NewTimer(5 * time.Second)\n// ... use timer\n\n// Good - always stop\ntimer := clock.NewTimer(5 * time.Second)\ndefer timer.Stop()",{"id":2347,"title":2348,"titles":2349,"content":2350,"level":154},"/v1.0.2/guides/testing#pitfall-not-waiting-for-goroutines","Pitfall: Not Waiting for Goroutines",[1092,2339],"// Bad - race between goroutine setup and Advance\ngo func() {\n    \u003C-clock.After(time.Second)\n}()\nclock.Advance(time.Second) // May advance before timer is registered\n\n// Good - allow setup time or use BlockUntilReady\ngo func() {\n    \u003C-clock.After(time.Second)\n}()\nclock.BlockUntilReady()\nclock.Advance(time.Second)",{"id":2352,"title":2353,"titles":2354,"content":2355,"level":154},"/v1.0.2/guides/testing#pitfall-not-calling-context-cancel","Pitfall: Not Calling Context Cancel",[1092,2339],"// Bad - resources leak\nctx, _ := clock.WithTimeout(ctx, time.Second)\n\n// Good - always call cancel\nctx, cancel := clock.WithTimeout(ctx, time.Second)\ndefer cancel() html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}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 pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",{"id":2357,"title":1106,"titles":2358,"content":2359,"level":108},"/v1.0.2/cookbook/patterns",[],"Recipes for retry logic, rate limiting, and deadline management",{"id":2361,"title":1106,"titles":2362,"content":2363,"level":108},"/v1.0.2/cookbook/patterns#common-patterns",[],"Production-ready patterns for time-dependent operations.",{"id":2365,"title":2366,"titles":2367,"content":2368,"level":147},"/v1.0.2/cookbook/patterns#retry-with-exponential-backoff","Retry with Exponential Backoff",[1106],"func RetryWithBackoff(clock clockz.Clock, operation func() error) error {\n    backoff := 100 * time.Millisecond\n    maxBackoff := 30 * time.Second\n\n    for attempt := 0; attempt \u003C 5; attempt++ {\n        if err := operation(); err == nil {\n            return nil\n        }\n\n        if attempt \u003C 4 { // Don't sleep after last attempt\n            clock.Sleep(backoff)\n            backoff *= 2\n            if backoff > maxBackoff {\n                backoff = maxBackoff\n            }\n        }\n    }\n\n    return errors.New(\"operation failed after 5 attempts\")\n} Testing: func TestRetryWithBackoff(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n\n    attempts := 0\n    operation := func() error {\n        attempts++\n        if attempts \u003C 3 {\n            return errors.New(\"temporary failure\")\n        }\n        return nil\n    }\n\n    done := make(chan error)\n    go func() {\n        done \u003C- RetryWithBackoff(clock, operation)\n    }()\n\n    // Advance through backoff delays: 100ms, 200ms\n    clock.Advance(100 * time.Millisecond)\n    clock.Advance(200 * time.Millisecond)\n\n    if err := \u003C-done; err != nil {\n        t.Fatalf(\"unexpected error: %v\", err)\n    }\n\n    if attempts != 3 {\n        t.Errorf(\"expected 3 attempts, got %d\", attempts)\n    }\n}",{"id":2370,"title":2371,"titles":2372,"content":2373,"level":147},"/v1.0.2/cookbook/patterns#rate-limiter","Rate Limiter",[1106],"Token bucket rate limiter with clock injection: type RateLimiter struct {\n    clock  clockz.Clock\n    ticker clockz.Ticker\n    tokens chan struct{}\n}\n\nfunc NewRateLimiter(clock clockz.Clock, rps int) *RateLimiter {\n    rl := &RateLimiter{\n        clock:  clock,\n        ticker: clock.NewTicker(time.Second / time.Duration(rps)),\n        tokens: make(chan struct{}, rps),\n    }\n\n    // Fill initial tokens\n    for i := 0; i \u003C rps; i++ {\n        rl.tokens \u003C- struct{}{}\n    }\n\n    // Refill tokens periodically\n    go func() {\n        for range rl.ticker.C() {\n            select {\n            case rl.tokens \u003C- struct{}{}:\n            default: // Bucket full\n            }\n        }\n    }()\n\n    return rl\n}\n\nfunc (rl *RateLimiter) Wait() {\n    \u003C-rl.tokens\n}\n\nfunc (rl *RateLimiter) Stop() {\n    rl.ticker.Stop()\n} Testing: func TestRateLimiter(t *testing.T) {\n    clock := clockz.NewFakeClockAt(time.Now())\n    limiter := NewRateLimiter(clock, 10) // 10 requests per second\n    defer limiter.Stop()\n\n    // Consume all initial tokens\n    for i := 0; i \u003C 10; i++ {\n        limiter.Wait()\n    }\n\n    // Next request should block until token refills\n    done := make(chan bool)\n    go func() {\n        limiter.Wait()\n        done \u003C- true\n    }()\n\n    // Advance to refill one token\n    clock.Advance(100 * time.Millisecond)\n\n    select {\n    case \u003C-done:\n        // Success\n    case \u003C-time.After(100 * time.Millisecond):\n        t.Fatal(\"Wait should have completed after token refill\")\n    }\n}",{"id":2375,"title":2376,"titles":2377,"content":2378,"level":147},"/v1.0.2/cookbook/patterns#deadline-management","Deadline Management",[1106],"Process items with a deadline, abandoning work if time runs out: func ProcessBatch(clock clockz.Clock, items []Item, deadline time.Time) error {\n    for i, item := range items {\n        if clock.Now().After(deadline) {\n            return fmt.Errorf(\"deadline exceeded, processed %d/%d items\", i, len(items))\n        }\n\n        remaining := deadline.Sub(clock.Now())\n        ctx, cancel := clock.WithTimeout(context.Background(), remaining)\n\n        err := processItem(ctx, item)\n        cancel()\n\n        if err != nil {\n            return fmt.Errorf(\"failed to process item %d: %w\", i, err)\n        }\n    }\n\n    return nil\n}",{"id":2380,"title":2381,"titles":2382,"content":2383,"level":147},"/v1.0.2/cookbook/patterns#scheduled-tasks","Scheduled Tasks",[1106],"Run a function at a specific time: type Scheduler struct {\n    clock clockz.Clock\n}\n\nfunc (s *Scheduler) RunAt(target time.Time, fn func()) {\n    duration := target.Sub(s.clock.Now())\n    if duration \u003C= 0 {\n        fn()\n        return\n    }\n\n    timer := s.clock.NewTimer(duration)\n    \u003C-timer.C()\n    fn()\n}\n\nfunc (s *Scheduler) RunEvery(interval time.Duration, fn func()) (stop func()) {\n    ticker := s.clock.NewTicker(interval)\n\n    go func() {\n        for range ticker.C() {\n            fn()\n        }\n    }()\n\n    return ticker.Stop\n}",{"id":2385,"title":2386,"titles":2387,"content":2388,"level":147},"/v1.0.2/cookbook/patterns#measuring-elapsed-time","Measuring Elapsed Time",[1106],"func MeasureOperation(clock clockz.Clock, operation func()) time.Duration {\n    start := clock.Now()\n    operation()\n    return clock.Since(start)\n}",{"id":2390,"title":2391,"titles":2392,"content":100,"level":147},"/v1.0.2/cookbook/patterns#migration-from-time-package","Migration from time Package",[1106],{"id":2394,"title":2395,"titles":2396,"content":2397,"level":154},"/v1.0.2/cookbook/patterns#timeafter-clockafter","time.After → clock.After",[1106,2391],"// Before\nselect {\ncase \u003C-time.After(5 * time.Second):\n    handleTimeout()\n}\n\n// After\nselect {\ncase \u003C-clock.After(5 * time.Second):\n    handleTimeout()\n}",{"id":2399,"title":2400,"titles":2401,"content":2402,"level":154},"/v1.0.2/cookbook/patterns#timenewtimer-clocknewtimer","time.NewTimer → clock.NewTimer",[1106,2391],"// Before\ntimer := time.NewTimer(duration)\n\u003C-timer.C\n\n// After\ntimer := clock.NewTimer(duration)\n\u003C-timer.C() // Note: C is a method, not a field",{"id":2404,"title":2405,"titles":2406,"content":2407,"level":154},"/v1.0.2/cookbook/patterns#timenewticker-clocknewticker","time.NewTicker → clock.NewTicker",[1106,2391],"// Before\nticker := time.NewTicker(interval)\ndefer ticker.Stop()\n\u003C-ticker.C\n\n// After\nticker := clock.NewTicker(interval)\ndefer ticker.Stop()\n\u003C-ticker.C() // Note: C is a method, not a field",{"id":2409,"title":2410,"titles":2411,"content":2412,"level":154},"/v1.0.2/cookbook/patterns#contextwithtimeout-clockwithtimeout","context.WithTimeout → clock.WithTimeout",[1106,2391],"// Before\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\n\n// After\nctx, cancel := clock.WithTimeout(ctx, 30*time.Second)",{"id":2414,"title":2415,"titles":2416,"content":2417,"level":154},"/v1.0.2/cookbook/patterns#timesleep-clocksleep","time.Sleep → clock.Sleep",[1106,2391],"// Before\ntime.Sleep(duration)\n\n// After\nclock.Sleep(duration)",{"id":2419,"title":2420,"titles":2421,"content":2422,"level":154},"/v1.0.2/cookbook/patterns#timenow-clocknow","time.Now → clock.Now",[1106,2391],"// Before\nnow := time.Now()\n\n// After\nnow := clock.Now()",{"id":2424,"title":2425,"titles":2426,"content":2427,"level":154},"/v1.0.2/cookbook/patterns#timesince-clocksince","time.Since → clock.Since",[1106,2391],"// Before\nelapsed := time.Since(start)\n\n// After\nelapsed := clock.Since(start) html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}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 pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}",{"id":2429,"title":949,"titles":2430,"content":2431,"level":108},"/v1.0.2/reference/api",[],"Complete interface documentation for clockz",{"id":2433,"title":949,"titles":2434,"content":2435,"level":108},"/v1.0.2/reference/api#api-reference",[],"Complete documentation of clockz interfaces and implementations.",{"id":2437,"title":2438,"titles":2439,"content":2440,"level":147},"/v1.0.2/reference/api#clock-interface","Clock Interface",[949],"The core interface implemented by both RealClock and FakeClock: type Clock interface {\n    Now() time.Time\n    After(d time.Duration) \u003C-chan time.Time\n    AfterFunc(d time.Duration, f func()) Timer\n    NewTimer(d time.Duration) Timer\n    NewTicker(d time.Duration) Ticker\n    Sleep(d time.Duration)\n    Since(t time.Time) time.Duration\n    WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc)\n    WithDeadline(ctx context.Context, deadline time.Time) (context.Context, context.CancelFunc)\n}",{"id":2442,"title":2443,"titles":2444,"content":2445,"level":154},"/v1.0.2/reference/api#clocknow","Clock.Now",[949,2438],"Now() time.Time Returns the current time according to the clock. RealClock: Returns time.Now()FakeClock: Returns the fake clock's current time",{"id":2447,"title":2448,"titles":2449,"content":2450,"level":154},"/v1.0.2/reference/api#clockafter","Clock.After",[949,2438],"After(d time.Duration) \u003C-chan time.Time Returns a channel that sends the current time after duration d. RealClock: Wraps time.After(d)FakeClock: Fires when fake time advances past targetChannel has buffer of 1 to prevent blocking",{"id":2452,"title":2453,"titles":2454,"content":2455,"level":154},"/v1.0.2/reference/api#clockafterfunc","Clock.AfterFunc",[949,2438],"AfterFunc(d time.Duration, f func()) Timer Executes function f after duration d. RealClock: Wraps time.AfterFunc(d, f), function runs in its own goroutineFakeClock: Executes synchronously when time advancesReturns Timer that can be stopped or reset",{"id":2457,"title":2458,"titles":2459,"content":2460,"level":154},"/v1.0.2/reference/api#clocknewtimer","Clock.NewTimer",[949,2438],"NewTimer(d time.Duration) Timer Creates a new Timer that fires after duration d. RealClock: Wraps time.NewTimer(d)FakeClock: Creates timer that respects fake timeTimer can be stopped or resetUse timer.C() to access the channel",{"id":2462,"title":2463,"titles":2464,"content":2465,"level":154},"/v1.0.2/reference/api#clocknewticker","Clock.NewTicker",[949,2438],"NewTicker(d time.Duration) Ticker Creates a new Ticker that fires repeatedly every duration d. RealClock: Wraps time.NewTicker(d)FakeClock: Creates ticker that respects fake timeMust call ticker.Stop() to release resourcesPanics if d \u003C= 0 (both implementations)",{"id":2467,"title":2468,"titles":2469,"content":2470,"level":154},"/v1.0.2/reference/api#clocksleep","Clock.Sleep",[949,2438],"Sleep(d time.Duration) Blocks the calling goroutine for duration d. RealClock: Wraps time.Sleep(d)FakeClock: Blocks until fake time advances by d",{"id":2472,"title":2473,"titles":2474,"content":2475,"level":154},"/v1.0.2/reference/api#clocksince","Clock.Since",[949,2438],"Since(t time.Time) time.Duration Returns time elapsed since t. Equivalent to clock.Now().Sub(t)",{"id":2477,"title":2478,"titles":2479,"content":2480,"level":154},"/v1.0.2/reference/api#clockwithtimeout","Clock.WithTimeout",[949,2438],"WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) Creates a context that cancels after timeout duration. RealClock: Wraps context.WithTimeout(ctx, timeout)FakeClock: Cancels when fake time advances past deadlineCancel function must be called to release resources",{"id":2482,"title":2483,"titles":2484,"content":2485,"level":154},"/v1.0.2/reference/api#clockwithdeadline","Clock.WithDeadline",[949,2438],"WithDeadline(ctx context.Context, deadline time.Time) (context.Context, context.CancelFunc) Creates a context that cancels at deadline time. RealClock: Wraps context.WithDeadline(ctx, deadline)FakeClock: Cancels when fake time reaches deadlineReturns immediately cancelled context if deadline already passedCancel function must be called to release resources",{"id":2487,"title":2488,"titles":2489,"content":2490,"level":147},"/v1.0.2/reference/api#timer-interface","Timer Interface",[949],"type Timer interface {\n    Stop() bool\n    Reset(d time.Duration) bool\n    C() \u003C-chan time.Time\n}",{"id":2492,"title":2493,"titles":2494,"content":2495,"level":154},"/v1.0.2/reference/api#timerstop","Timer.Stop",[949,2488],"Stop() bool Stops the timer from firing. Returns true if timer was stopped before firingReturns false if timer already fired or was stoppedSafe to call multiple times",{"id":2497,"title":2498,"titles":2499,"content":2500,"level":154},"/v1.0.2/reference/api#timerreset","Timer.Reset",[949,2488],"Reset(d time.Duration) bool Resets the timer to fire after duration d. Returns true if timer was active before resetReturns false if timer had expired or was stoppedResets from current clock time, not original start time",{"id":2502,"title":2503,"titles":2504,"content":2505,"level":154},"/v1.0.2/reference/api#timerc","Timer.C",[949,2488],"C() \u003C-chan time.Time Returns the channel on which the timer sends its time. Channel has buffer of 1Receives exactly once when timer firesChannel is not closed after firing",{"id":2507,"title":2508,"titles":2509,"content":2510,"level":147},"/v1.0.2/reference/api#ticker-interface","Ticker Interface",[949],"type Ticker interface {\n    Stop()\n    C() \u003C-chan time.Time\n}",{"id":2512,"title":2513,"titles":2514,"content":2515,"level":154},"/v1.0.2/reference/api#tickerstop","Ticker.Stop",[949,2508],"Stop() Stops the ticker. No more ticks sent after StopDoes not close the channelRequired to release ticker resources",{"id":2517,"title":2518,"titles":2519,"content":2520,"level":154},"/v1.0.2/reference/api#tickerc","Ticker.C",[949,2508],"C() \u003C-chan time.Time Returns the channel on which ticks are delivered. Channel has buffer of 1Sends time at regular intervalsMay drop ticks if receiver is slow",{"id":2522,"title":825,"titles":2523,"content":2524,"level":147},"/v1.0.2/reference/api#realclock",[949],"var RealClock Clock = &realClock{} Package-level variable that delegates to the standard time package. Usage: now := clockz.RealClock.Now()\ntimer := clockz.RealClock.NewTimer(5 * time.Second) Characteristics: Thread-safeZero overhead (direct delegation)No additional methods beyond Clock interface",{"id":2526,"title":956,"titles":2527,"content":100,"level":147},"/v1.0.2/reference/api#fakeclock",[949],{"id":2529,"title":2530,"titles":2531,"content":2532,"level":154},"/v1.0.2/reference/api#constructors","Constructors",[949,956],"func NewFakeClock() *FakeClock Creates a fake clock initialized to time.Now(). func NewFakeClockAt(t time.Time) *FakeClock Creates a fake clock initialized to specific time t.",{"id":2534,"title":2535,"titles":2536,"content":2537,"level":154},"/v1.0.2/reference/api#fakeclockadvance","FakeClock.Advance",[949,956],"func (f *FakeClock) Advance(d time.Duration) Advances the fake clock's time by duration d. Triggers all timers scheduled before new timeExecutes AfterFunc callbacks synchronouslyUpdates all tickersTriggers context timeouts/deadlinesThread-safe",{"id":2539,"title":2540,"titles":2541,"content":2542,"level":154},"/v1.0.2/reference/api#fakeclocksettime","FakeClock.SetTime",[949,956],"func (f *FakeClock) SetTime(t time.Time) Sets the fake clock to specific time t. Can only move time forward (panics if t is before current time)Triggers all timers and contexts with deadlines before tThread-safe",{"id":2544,"title":2545,"titles":2546,"content":2547,"level":154},"/v1.0.2/reference/api#fakeclockhaswaiters","FakeClock.HasWaiters",[949,956],"func (f *FakeClock) HasWaiters() bool Returns true if there are active timers, tickers, or contexts. Useful for test assertionsIncludes: timers, tickers, AfterFunc callbacks, contextsThread-safe",{"id":2549,"title":2550,"titles":2551,"content":2552,"level":154},"/v1.0.2/reference/api#fakeclockblockuntilready","FakeClock.BlockUntilReady",[949,956],"func (f *FakeClock) BlockUntilReady() Blocks until all pending timer operations are delivered. Ensures timer channel sends are processedUseful for test synchronizationThread-safe",{"id":2554,"title":1510,"titles":2555,"content":2556,"level":147},"/v1.0.2/reference/api#thread-safety",[949],"All clockz operations are thread-safe. RealClock: Delegates to thread-safe time package functionsNo additional synchronization needed FakeClock: Uses sync.RWMutex for time accessSeparate mutex for context operationsSafe for concurrent timer/ticker creationSafe for concurrent time advancement",{"id":2558,"title":2559,"titles":2560,"content":2561,"level":147},"/v1.0.2/reference/api#performance","Performance",[949],"RealClock: Zero overhead: direct delegation to time packageNo allocations beyond time package needs FakeClock: O(n) time advancement where n = number of waitersTimers sorted by target timeSynchronous AfterFunc executionMinimal allocations for timer tracking html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}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 pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",[2563],{"title":2564,"path":2565,"stem":2566,"children":2567,"page":2576},"V102","/v1.0.2","v1.0.2",[2568,2577,2583,2589],{"title":1066,"path":2569,"stem":2570,"children":2571,"page":2576},"/v1.0.2/learn","v1.0.2/2.learn",[2572,2574],{"title":412,"path":2206,"stem":2573,"description":2208},"v1.0.2/2.learn/1.quickstart",{"title":1079,"path":2243,"stem":2575,"description":2245},"v1.0.2/2.learn/2.concepts",false,{"title":1085,"path":2578,"stem":2579,"children":2580,"page":2576},"/v1.0.2/guides","v1.0.2/3.guides",[2581],{"title":1092,"path":2300,"stem":2582,"description":2302},"v1.0.2/3.guides/1.testing",{"title":1098,"path":2584,"stem":2585,"children":2586,"page":2576},"/v1.0.2/cookbook","v1.0.2/4.cookbook",[2587],{"title":1106,"path":2357,"stem":2588,"description":2359},"v1.0.2/4.cookbook/1.patterns",{"title":1112,"path":2590,"stem":2591,"children":2592,"page":2576},"/v1.0.2/reference","v1.0.2/5.reference",[2593],{"title":949,"path":2429,"stem":2594,"description":2431},"v1.0.2/5.reference/1.api",[2596],{"title":2564,"path":2565,"stem":2566,"children":2597,"page":2576},[2598,2602,2605,2608],{"title":1066,"path":2569,"stem":2570,"children":2599,"page":2576},[2600,2601],{"title":412,"path":2206,"stem":2573},{"title":1079,"path":2243,"stem":2575},{"title":1085,"path":2578,"stem":2579,"children":2603,"page":2576},[2604],{"title":1092,"path":2300,"stem":2582},{"title":1098,"path":2584,"stem":2585,"children":2606,"page":2576},[2607],{"title":1106,"path":2357,"stem":2588},{"title":1112,"path":2590,"stem":2591,"children":2609,"page":2576},[2610],{"title":949,"path":2429,"stem":2594},[2612],{"title":2564,"path":2565,"stem":2566,"children":2613,"page":2576},[2614,2618,2621,2624],{"title":1066,"path":2569,"stem":2570,"children":2615,"page":2576},[2616,2617],{"title":412,"path":2206,"stem":2573,"description":2208},{"title":1079,"path":2243,"stem":2575,"description":2245},{"title":1085,"path":2578,"stem":2579,"children":2619,"page":2576},[2620],{"title":1092,"path":2300,"stem":2582,"description":2302},{"title":1098,"path":2584,"stem":2585,"children":2622,"page":2576},[2623],{"title":1106,"path":2357,"stem":2588,"description":2359},{"title":1112,"path":2590,"stem":2591,"children":2625,"page":2576},[2626],{"title":949,"path":2429,"stem":2594,"description":2431},1776189578607]