{"id":24194372,"url":"https://github.com/kitimark/timeloc","last_synced_at":"2026-02-28T20:02:43.509Z","repository":{"id":272188451,"uuid":"915747615","full_name":"kitimark/timeloc","owner":"kitimark","description":"A linter to enforce explicit time.Location handling in Go to prevent timezone-related bugs","archived":false,"fork":false,"pushed_at":"2025-03-06T12:21:24.000Z","size":40,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T23:31:34.550Z","etag":null,"topics":["go","golangci-lint","linter","time","timezone"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kitimark.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-12T17:40:30.000Z","updated_at":"2025-01-20T04:33:48.000Z","dependencies_parsed_at":"2025-01-12T20:18:07.069Z","dependency_job_id":"f2cd39f7-a4a4-4b5c-a476-505e711612da","html_url":"https://github.com/kitimark/timeloc","commit_stats":null,"previous_names":["kitimark/timeloc"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/kitimark/timeloc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitimark%2Ftimeloc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitimark%2Ftimeloc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitimark%2Ftimeloc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitimark%2Ftimeloc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kitimark","download_url":"https://codeload.github.com/kitimark/timeloc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitimark%2Ftimeloc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29951075,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T18:42:55.706Z","status":"ssl_error","status_checked_at":"2026-02-28T18:42:48.811Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golangci-lint","linter","time","timezone"],"created_at":"2025-01-13T18:25:27.419Z","updated_at":"2026-02-28T20:02:43.492Z","avatar_url":"https://github.com/kitimark.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TimeLoc\n\nA golangci-lint analyzer to ensure proper `time.Location` handling in Go codebases. This analyzer helps prevent common timezone-related bugs by enforcing explicit time zone settings before using time-dependent methods and prevents the usage of system-dependent `time.Local`.\n\n## Problem\n\nWhen working with `time.Time` methods, many operations depend on the location (timezone) setting. If not explicitly set, these methods default to using the system's local timezone. This implicit behavior makes your code vulnerable to environment-specific bugs, as the same code can produce different results depending on the server's timezone configuration.\n\nThe analyzer also prevents the usage of `time.Local` as it relies on the system timezone, which can vary across different environments.\n\nExamples of potentially problematic code:\n```go\n// BAD: Using time methods without setting location\nt := time.Now()\nfmt.Println(t.Format(\"2006-01-02\"))  // Uses system timezone\n\n// BAD: Using time.Local which relies on system timezone\nt := time.Now().In(time.Local)       // Will trigger an error\nt, _ := time.ParseInLocation(\"2006-01-02\", \"2025-01-01\", time.Local)  // Will trigger an error\nt := time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local)  // Will trigger an error\n\n// GOOD: Explicitly set location before using time methods\nt := time.Now()\nt = t.In(time.UTC)\nfmt.Println(t.Format(\"2006-01-02\"))  // Consistently uses UTC\n```\n\n## Running TimeLoc\n\n### Standalone\n\nInstall the binary from source by running:\n```bash\ngo install github.com/kitimark/timeloc/cmd/timeloc@latest\n```\n\nThen, run the linter by:\n```bash\ntimeloc ./...\n```\n\n### golangci-lint (\u003e= v1.57.0)\n\nTimeLoc is available as a golangci-lint analyzer through the [Module Plugin System](https://golangci-lint.run/plugins/module-plugins/) (introduced in v1.57.0).\n\nFollow these steps to set up TimeLoc with golangci-lint:\n\n(1) Create a `.custom-gcl.yml` file at the root of the repository if you have not done so, add the following content:\n```yaml\n# This has to be \u003e= v1.57.0 for module plugin system support.\nversion: v1.57.0\nplugins:\n  - module: \"github.com/kitimark/timeloc\"\n    import: \"github.com/kitimark/timeloc/cmd/gclplugin\"\n    version: latest # Or a fixed version for reproducible builds.\n```\n\n(2) Add TimeLoc to the linter configuration file `.golangci.yaml`:\n```yaml\nlinters-settings:\n  custom:\n    timeloc:\n      type: \"module\"\n      description: A linter to enforce explicit timezone settings and prevent time.Local usage in Go code.\n# TimeLoc can be referred to as `timeloc` just like any other golangci-lint analyzers in other \n# parts of the configuration file.\n```\n\n(3) Build a custom `golangci-lint` binary with TimeLoc included:\n```bash\n# Note that your `golangci-lint` to bootstrap the custom binary must also be version \u003e= v1.57.0.\n$ golangci-lint custom\n```\nBy default, the custom binary will be built at `.` with the name `custom-gcl`, which can be further customized in `.custom-gcl.yml` file (see [Module Plugin System](https://golangci-lint.run/plugins/module-plugins/) for instructions).\n\n(4) Run the custom binary instead of `golangci-lint`:\n\n```bash\n# Arguments are the same as `golangci-lint`.\n$ ./custom-gcl run ./...\n```\n\n## What This Analyzer Checks\n\n### Time-dependent Methods\n\nThe analyzer checks for usage of the following time.Time methods without explicit location setting:\n- AppendFormat\n- Clock\n- Date\n- Day\n- Format\n- Hour\n- ISOWeek\n- Minute\n- Month\n- Second\n- Weekday\n- Year\n- YearDay\n\n### Function Parameters\n\nThe analyzer also tracks time.Time values passed to functions to ensure they have location set before being used with time-dependent methods.\n\n### time.Local Usage\n\nThe analyzer checks and prevents the use of `time.Local` in the following contexts:\n- `time.ParseInLocation(..., time.Local)`\n- `time.Date(..., time.Local)`\n- `someTime.In(time.Local)`\n\n### Import Aliases\n\nThis check works even when the time package is imported with an alias.\n\n### Supported Location Setting Methods\n\nThe analyzer recognizes location setting through:\n- Direct `In()` method calls: `t = t.In(time.UTC)`\n- `time.ParseInLocation()`: `t, _ := time.ParseInLocation(layout, value, loc)`\n- Functions that internally set location:\n  ```go\n  func setUtcTz(t time.Time) time.Time {\n      return t.In(time.UTC)\n  }\n  ```\n\n## Examples\n\n### Incorrect Code\n\n```go\nfunc badUsage() {\n    t := time.Now()\n    \n    // Direct method calls without location\n    fmt.Println(t.Format(\"2006-01-02\"))  // Analyzer reports error: (t time.Time).Format called before setting time.Location\n    \n    // Method chaining without location\n    fmt.Println(time.Now().Format(\"2006-01-02\"))  // Analyzer reports error: (t time.Time).Format called before setting time.Location\n    \n    // Passing to function without location\n    formatTime(t)  // Analyzer reports error: passing time.Time value without location set to function that may use location-dependent methods\n \n    // Using time.Local (will trigger errors)\n    t = t.In(time.Local)  // Analyzer reports error: time.Local usage is not allowed as it relies on system timezone\n}\n\nfunc formatTime(t time.Time) string {\n    return t.Format(\"2006-01-02\")\n}\n```\n\n### Correct Code\n\n```go\nfunc goodUsage() {\n    t := time.Now().In(time.UTC)\n    \n    // Location is set, so these are fine\n    fmt.Println(t.Format(\"2006-01-02\"))\n    formatTime(t)\n}\n\n// Using ParseInLocation with explicit timezone is good\nfunc parseExample() {\n    t, _ := time.ParseInLocation(\"2006-01-02\", \"2025-01-01\", time.UTC)\n    fmt.Println(t.Format(\"2006-01-02\"))  // Fine because location is set\n}\n```\n\n## Error Messages\n\nThe analyzer produces three types of error messages:\n\n1. For direct method calls on variables and chained calls:\n   ```\n   (t time.Time).Format called before setting time.Location\n   ```\n\n2. For function calls:\n   ```\n   passing time.Time value without location set to function that may use location-dependent methods\n   ```\n\n3. For time.Local usage:\n   ```\n   time.Local usage is not allowed as it relies on system timezone\n   ```\n\n## Why Not Trust Default Location?\n\nWhile time.Parse and the system timezone provide default location settings, relying on these can lead to issues:\n- System timezone can vary between development, testing, and production environments\n- Docker containers might have different timezone settings\n- Application deployments across different regions might behave differently\n- Using time.Local makes your code dependent on the host system's timezone configuration\n\nThis analyzer enforces explicit location setting and prevents time.Local usage to ensure consistent behavior across all environments.\n\n## Support\nPlease feel free to [open a GitHub issue](https://github.com/kitimark/timeloc/issues) if you have any questions, bug reports, and feature requests.\n\n## Contributions\n\nPull requests are welcome! Please ensure tests pass by running:\n\n```bash\ngo test ./...\n```\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkitimark%2Ftimeloc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkitimark%2Ftimeloc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkitimark%2Ftimeloc/lists"}