Compare commits
2 commits
f8c486132d
...
9340a16832
| Author | SHA1 | Date | |
|---|---|---|---|
| 9340a16832 | |||
| f47897f3fd |
7 changed files with 47 additions and 10 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
{"id":"quando-4bh","title":"Unit type and constants","description":"Define Unit type for time unit constants used in arithmetic operations.\n\n**Technical Details:**\n```go\ntype Unit int\n\nconst (\n Seconds Unit = iota\n Minutes\n Hours\n Days\n Weeks\n Months\n Quarters\n Years\n)\n```\n\n**Design:**\n- Type-safe constants (compile-time safety)\n- Use iota for clear ordering\n- Optional internal ParseUnit(string) for external inputs\n\n## Acceptance Criteria\n- [ ] Unit type defined as int\n- [ ] All 8 unit constants defined (Seconds through Years)\n- [ ] Units use iota for ordering\n- [ ] Godoc comments for Unit type and constants\n- [ ] Unit tests verifying constants","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:20:37.246514285+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:34:46.001417204+01:00","closed_at":"2026-02-11T16:34:46.001417204+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-4bh","depends_on_id":"quando-91w","type":"blocks","created_at":"2026-02-11T16:23:05.349519431+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":4,"issue_id":"quando-4bh","author":"Oliver Jakoubek","text":"Plan: 1) Create unit.go with Unit type as int, 2) Define all 8 unit constants (Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, Years) using iota, 3) Add String() method for better debugging, 4) Add comprehensive godoc comments, 5) Write unit tests verifying constants and ordering, 6) Add example tests","created_at":"2026-02-11T15:33:41Z"}]}
|
{"id":"quando-4bh","title":"Unit type and constants","description":"Define Unit type for time unit constants used in arithmetic operations.\n\n**Technical Details:**\n```go\ntype Unit int\n\nconst (\n Seconds Unit = iota\n Minutes\n Hours\n Days\n Weeks\n Months\n Quarters\n Years\n)\n```\n\n**Design:**\n- Type-safe constants (compile-time safety)\n- Use iota for clear ordering\n- Optional internal ParseUnit(string) for external inputs\n\n## Acceptance Criteria\n- [ ] Unit type defined as int\n- [ ] All 8 unit constants defined (Seconds through Years)\n- [ ] Units use iota for ordering\n- [ ] Godoc comments for Unit type and constants\n- [ ] Unit tests verifying constants","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:20:37.246514285+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:34:46.001417204+01:00","closed_at":"2026-02-11T16:34:46.001417204+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-4bh","depends_on_id":"quando-91w","type":"blocks","created_at":"2026-02-11T16:23:05.349519431+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":4,"issue_id":"quando-4bh","author":"Oliver Jakoubek","text":"Plan: 1) Create unit.go with Unit type as int, 2) Define all 8 unit constants (Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, Years) using iota, 3) Add String() method for better debugging, 4) Add comprehensive godoc comments, 5) Write unit tests verifying constants and ordering, 6) Add example tests","created_at":"2026-02-11T15:33:41Z"}]}
|
||||||
{"id":"quando-5ib","title":"Date inspection methods","description":"Implement date inspection methods for querying metadata about a date.\n\n**Individual Methods:**\n```go\nfunc (d Date) WeekNumber() int\nfunc (d Date) Quarter() int\nfunc (d Date) DayOfYear() int\nfunc (d Date) IsWeekend() bool\nfunc (d Date) IsLeapYear() bool\n```\n\n**Aggregated Method:**\n```go\ntype DateInfo struct {\n WeekNumber int\n Quarter int\n DayOfYear int\n IsWeekend bool\n IsLeapYear bool\n Unix int64\n}\n\nfunc (d Date) Info() DateInfo\n```\n\n**Specifications:**\n- **WeekNumber**: ISO 8601 (Monday first, Week 1 = first week containing Thursday)\n- **Quarter**: 1-4 (Q1=Jan-Mar, Q2=Apr-Jun, Q3=Jul-Sep, Q4=Oct-Dec)\n- **DayOfYear**: 1-366 (considering leap years)\n- **IsWeekend**: Saturday or Sunday (not configurable in Phase 1)\n- **IsLeapYear**: Divisible by 4, except centuries, except divisible by 400\n\n## Acceptance Criteria\n- [ ] WeekNumber() returns ISO 8601 week number\n- [ ] Quarter() returns 1-4\n- [ ] DayOfYear() returns 1-366\n- [ ] IsWeekend() returns true for Sat/Sun\n- [ ] IsLeapYear() returns true for leap years\n- [ ] Info() returns struct with all fields\n- [ ] ISO 8601 week compliance (Week 1 contains Thursday)\n- [ ] Leap year rules correct (4, 100, 400 rule)\n- [ ] Unit tests for all methods\n- [ ] Edge cases: year boundaries, week 53, leap years\n- [ ] Benchmarks meet \u003c1µs target\n- [ ] Godoc for each method with conventions documented","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:06.966079923+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:45:38.905860871+01:00","closed_at":"2026-02-11T20:45:38.905860871+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-5ib","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:13.310800388+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"quando-5ib","title":"Date inspection methods","description":"Implement date inspection methods for querying metadata about a date.\n\n**Individual Methods:**\n```go\nfunc (d Date) WeekNumber() int\nfunc (d Date) Quarter() int\nfunc (d Date) DayOfYear() int\nfunc (d Date) IsWeekend() bool\nfunc (d Date) IsLeapYear() bool\n```\n\n**Aggregated Method:**\n```go\ntype DateInfo struct {\n WeekNumber int\n Quarter int\n DayOfYear int\n IsWeekend bool\n IsLeapYear bool\n Unix int64\n}\n\nfunc (d Date) Info() DateInfo\n```\n\n**Specifications:**\n- **WeekNumber**: ISO 8601 (Monday first, Week 1 = first week containing Thursday)\n- **Quarter**: 1-4 (Q1=Jan-Mar, Q2=Apr-Jun, Q3=Jul-Sep, Q4=Oct-Dec)\n- **DayOfYear**: 1-366 (considering leap years)\n- **IsWeekend**: Saturday or Sunday (not configurable in Phase 1)\n- **IsLeapYear**: Divisible by 4, except centuries, except divisible by 400\n\n## Acceptance Criteria\n- [ ] WeekNumber() returns ISO 8601 week number\n- [ ] Quarter() returns 1-4\n- [ ] DayOfYear() returns 1-366\n- [ ] IsWeekend() returns true for Sat/Sun\n- [ ] IsLeapYear() returns true for leap years\n- [ ] Info() returns struct with all fields\n- [ ] ISO 8601 week compliance (Week 1 contains Thursday)\n- [ ] Leap year rules correct (4, 100, 400 rule)\n- [ ] Unit tests for all methods\n- [ ] Edge cases: year boundaries, week 53, leap years\n- [ ] Benchmarks meet \u003c1µs target\n- [ ] Godoc for each method with conventions documented","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:06.966079923+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:45:38.905860871+01:00","closed_at":"2026-02-11T20:45:38.905860871+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-5ib","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:13.310800388+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
{"id":"quando-5ol","title":"Format presets and constants","description":"Implement preset format constants and Format() method.\n\n**API:**\n```go\ntype Format int\n\nconst (\n ISO Format = iota // \"2026-02-09\"\n EU // \"09.02.2026\"\n US // \"02/09/2026\"\n Long // \"February 9, 2026\" (language-dependent)\n RFC2822 // \"Mon, 09 Feb 2026 00:00:00 +0000\"\n)\n\nfunc (d Date) Format(format Format) string\n```\n\n**Language Dependency:**\n- ISO, EU, US, RFC2822: Always language-independent\n- Long: Uses Lang setting\n - EN: \"February 9, 2026\"\n - DE: \"9. Februar 2026\"\n\n**Implementation:**\n- Map Format constants to Go layout strings\n- Use Lang() setting for Long format\n- Delegate to time.Format() internally\n\n## Acceptance Criteria\n- [ ] Format type and constants defined\n- [ ] Format() method implemented\n- [ ] ISO format outputs \"YYYY-MM-DD\"\n- [ ] EU format outputs \"DD.MM.YYYY\"\n- [ ] US format outputs \"MM/DD/YYYY\"\n- [ ] RFC2822 format correct\n- [ ] Long format respects Lang setting (EN and DE)\n- [ ] Non-Long formats ignore Lang setting\n- [ ] Unit tests for all formats\n- [ ] Unit tests for Long with both EN and DE\n- [ ] Benchmark meets \u003c5µs without i18n, \u003c10µs with i18n\n- [ ] Godoc for each format constant\n- [ ] Example tests showing each format","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:53.217816331+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:11:47.455205479+01:00","closed_at":"2026-02-11T20:11:47.455205479+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-5ol","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:12.164278602+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-5ol","depends_on_id":"quando-zbr","type":"blocks","created_at":"2026-02-11T16:23:12.20045666+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"quando-5ol","title":"Format presets and constants","description":"Implement preset format constants and Format() method.\n\n**API:**\n```go\ntype Format int\n\nconst (\n ISO Format = iota // \"2026-02-09\"\n EU // \"09.02.2026\"\n US // \"02/09/2026\"\n Long // \"February 9, 2026\" (language-dependent)\n RFC2822 // \"Mon, 09 Feb 2026 00:00:00 +0000\"\n)\n\nfunc (d Date) Format(format Format) string\n```\n\n**Language Dependency:**\n- ISO, EU, US, RFC2822: Always language-independent\n- Long: Uses Lang setting\n - EN: \"February 9, 2026\"\n - DE: \"9. Februar 2026\"\n\n**Implementation:**\n- Map Format constants to Go layout strings\n- Use Lang() setting for Long format\n- Delegate to time.Format() internally\n\n## Acceptance Criteria\n- [ ] Format type and constants defined\n- [ ] Format() method implemented\n- [ ] ISO format outputs \"YYYY-MM-DD\"\n- [ ] EU format outputs \"DD.MM.YYYY\"\n- [ ] US format outputs \"MM/DD/YYYY\"\n- [ ] RFC2822 format correct\n- [ ] Long format respects Lang setting (EN and DE)\n- [ ] Non-Long formats ignore Lang setting\n- [ ] Unit tests for all formats\n- [ ] Unit tests for Long with both EN and DE\n- [ ] Benchmark meets \u003c5µs without i18n, \u003c10µs with i18n\n- [ ] Godoc for each format constant\n- [ ] Example tests showing each format","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:53.217816331+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:11:47.455205479+01:00","closed_at":"2026-02-11T20:11:47.455205479+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-5ol","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:12.164278602+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-5ol","depends_on_id":"quando-zbr","type":"blocks","created_at":"2026-02-11T16:23:12.20045666+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
|
{"id":"quando-67n","title":"Change Now() and DefaultClock to use UTC instead of local time","description":"## Background\n`Now()` currently uses `time.Now()` which returns local server time. For a date library, UTC as default is the correct standard. Users who need local time can use `quando.Now().In(\"Europe/Berlin\")` or `quando.From(time.Now())`.\n\n## Changes\n\n### date.go\n- `Now()`: Change `time.Now()` → `time.Now().UTC()`\n\n### clock.go\n- `DefaultClock.Now()`: Change `time.Now()` → `time.Now().UTC()`\n\n### Tests\n- Update any tests that depend on local time behavior\n- Add explicit test: `Now().Time().Location() == time.UTC`\n\n## Rationale\n- UTC as default is industry standard for date/time libraries\n- Prevents server-timezone-dependent behavior (e.g. DatesAPI on German server vs. client in Tokyo)\n- No existing external users → no breaking change concern\n- Explicit timezone via `.In()` is cleaner than implicit server timezone\n\n## Acceptance Criteria\n- [ ] `quando.Now().Time().Location()` returns `time.UTC`\n- [ ] `DefaultClock.Now().Time().Location()` returns `time.UTC`\n- [ ] All existing tests pass (adjusted where needed)\n- [ ] `From(time.Now())` still works for local-time use case (documented)","status":"closed","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-12T15:42:17.745925554+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-12T16:38:35.582398347+01:00","closed_at":"2026-02-12T16:38:35.582398347+01:00","close_reason":"Closed"}
|
||||||
{"id":"quando-6c3","title":"CI/CD pipeline setup","description":"Set up GitHub Actions CI/CD pipeline for automated testing and quality checks.\n\n**CI Workflow:**\n- Run on push and pull requests\n- Test on multiple Go versions (1.22, 1.23, latest)\n- Test on multiple platforms (Linux, macOS, Windows)\n- Run go fmt check\n- Run go vet\n- Run tests with coverage\n- Run benchmarks (informational)\n- Optional: golangci-lint\n\n**Coverage Reporting:**\n- Generate coverage report\n- Fail if coverage \u003c95% for core calculation functions\n- Optional: Upload to codecov.io or similar\n\n**Performance Monitoring:**\n- Run benchmarks on each commit\n- Report benchmark results (informational, no fail)\n\n## Acceptance Criteria\n- [ ] .github/workflows/ci.yml created\n- [ ] Tests run on push and PR\n- [ ] Multi-version Go support (1.22+)\n- [ ] Multi-platform support (Linux, macOS, Windows)\n- [ ] go fmt check passes\n- [ ] go vet check passes\n- [ ] Tests run with coverage report\n- [ ] Coverage requirement enforced (95%+)\n- [ ] Benchmarks run (informational)\n- [ ] CI badge in README (if applicable)","status":"tombstone","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:51.928117055+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T19:21:16.246958875+01:00","dependencies":[{"issue_id":"quando-6c3","depends_on_id":"quando-r1o","type":"blocks","created_at":"2026-02-11T16:23:16.259739409+01:00","created_by":"Oliver Jakoubek"}],"deleted_at":"2026-02-11T19:21:16.246958875+01:00","deleted_by":"daemon","delete_reason":"delete","original_type":"task"}
|
{"id":"quando-6c3","title":"CI/CD pipeline setup","description":"Set up GitHub Actions CI/CD pipeline for automated testing and quality checks.\n\n**CI Workflow:**\n- Run on push and pull requests\n- Test on multiple Go versions (1.22, 1.23, latest)\n- Test on multiple platforms (Linux, macOS, Windows)\n- Run go fmt check\n- Run go vet\n- Run tests with coverage\n- Run benchmarks (informational)\n- Optional: golangci-lint\n\n**Coverage Reporting:**\n- Generate coverage report\n- Fail if coverage \u003c95% for core calculation functions\n- Optional: Upload to codecov.io or similar\n\n**Performance Monitoring:**\n- Run benchmarks on each commit\n- Report benchmark results (informational, no fail)\n\n## Acceptance Criteria\n- [ ] .github/workflows/ci.yml created\n- [ ] Tests run on push and PR\n- [ ] Multi-version Go support (1.22+)\n- [ ] Multi-platform support (Linux, macOS, Windows)\n- [ ] go fmt check passes\n- [ ] go vet check passes\n- [ ] Tests run with coverage report\n- [ ] Coverage requirement enforced (95%+)\n- [ ] Benchmarks run (informational)\n- [ ] CI badge in README (if applicable)","status":"tombstone","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:51.928117055+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T19:21:16.246958875+01:00","dependencies":[{"issue_id":"quando-6c3","depends_on_id":"quando-r1o","type":"blocks","created_at":"2026-02-11T16:23:16.259739409+01:00","created_by":"Oliver Jakoubek"}],"deleted_at":"2026-02-11T19:21:16.246958875+01:00","deleted_by":"daemon","delete_reason":"delete","original_type":"task"}
|
||||||
{"id":"quando-6x3","title":"Documentation and examples","description":"Complete documentation including README, godoc, and example code.\n\n**README.md Contents:**\n- Project overview and vision\n- Installation instructions (go get)\n- Quick start examples\n- Core features showcase\n- Performance characteristics\n- Comparison to time.Time\n- Contributing guidelines\n- License\n\n**Godoc Requirements:**\n- Every exported type/function documented\n- Godoc comments start with type/function name\n- Complex behaviors explained (month-end overflow, DST, etc.)\n- Edge cases noted where applicable\n\n**Example Tests:**\n- Example_basicArithmetic\n- Example_monthEndOverflow\n- Example_snapOperations\n- Example_diffCalculation\n- Example_humanFormat\n- Example_parsing\n- Example_formatting\n- Example_timezoneConversion\n- Example_chainedOperations\n\n**Code Examples in README:**\n- Complex chaining\n- Month-end handling\n- Human-readable diffs\n- Timezone-aware operations\n- Comparison with stdlib\n\n## Acceptance Criteria\n- [ ] README.md complete with all sections\n- [ ] Installation instructions clear\n- [ ] At least 5 code examples in README\n- [ ] All exported items have godoc comments\n- [ ] Godoc comments follow conventions (name first)\n- [ ] At least 8 example tests\n- [ ] Examples demonstrate key features\n- [ ] Edge cases documented in relevant godoc\n- [ ] Comparison table with time.Time\n- [ ] Contributing guidelines (if open source)","status":"closed","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:45.693695262+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T21:25:19.094926099+01:00","closed_at":"2026-02-11T21:25:19.094926099+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-6x3","depends_on_id":"quando-r1o","type":"blocks","created_at":"2026-02-11T16:23:16.222906911+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"quando-6x3","title":"Documentation and examples","description":"Complete documentation including README, godoc, and example code.\n\n**README.md Contents:**\n- Project overview and vision\n- Installation instructions (go get)\n- Quick start examples\n- Core features showcase\n- Performance characteristics\n- Comparison to time.Time\n- Contributing guidelines\n- License\n\n**Godoc Requirements:**\n- Every exported type/function documented\n- Godoc comments start with type/function name\n- Complex behaviors explained (month-end overflow, DST, etc.)\n- Edge cases noted where applicable\n\n**Example Tests:**\n- Example_basicArithmetic\n- Example_monthEndOverflow\n- Example_snapOperations\n- Example_diffCalculation\n- Example_humanFormat\n- Example_parsing\n- Example_formatting\n- Example_timezoneConversion\n- Example_chainedOperations\n\n**Code Examples in README:**\n- Complex chaining\n- Month-end handling\n- Human-readable diffs\n- Timezone-aware operations\n- Comparison with stdlib\n\n## Acceptance Criteria\n- [ ] README.md complete with all sections\n- [ ] Installation instructions clear\n- [ ] At least 5 code examples in README\n- [ ] All exported items have godoc comments\n- [ ] Godoc comments follow conventions (name first)\n- [ ] At least 8 example tests\n- [ ] Examples demonstrate key features\n- [ ] Edge cases documented in relevant godoc\n- [ ] Comparison table with time.Time\n- [ ] Contributing guidelines (if open source)","status":"closed","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:45.693695262+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T21:25:19.094926099+01:00","closed_at":"2026-02-11T21:25:19.094926099+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-6x3","depends_on_id":"quando-r1o","type":"blocks","created_at":"2026-02-11T16:23:16.222906911+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
{"id":"quando-7m5","title":"MustParse convenience function","description":"Implement MustParse() convenience function that panics on error (for tests/initialization).\n\n**API:**\n```go\nfunc MustParse(s string) Date\n```\n\n**Behavior:**\n- Calls Parse() internally\n- Returns Date on success\n- Panics on error (with clear panic message)\n\n**Use Cases:**\n- Test fixtures\n- Static initialization\n- Configuration files where values are known-good\n\n**Documentation:**\n- MUST clearly document that this function panics\n- MUST recommend using Parse() in production code\n- MUST show test usage examples\n\n## Acceptance Criteria\n- [ ] MustParse() implemented\n- [ ] Returns Date on successful parse\n- [ ] Panics with clear message on error\n- [ ] Godoc clearly warns about panic behavior\n- [ ] Godoc recommends Parse() for production\n- [ ] Example test showing test fixture usage\n- [ ] Unit tests verifying panic on invalid input","status":"closed","priority":3,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:46.007442996+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:54:19.638519056+01:00","closed_at":"2026-02-11T20:54:19.638519056+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-7m5","depends_on_id":"quando-gr5","type":"blocks","created_at":"2026-02-11T16:23:11.340600075+01:00","created_by":"Oliver Jakoubek"}]}
|
{"id":"quando-7m5","title":"MustParse convenience function","description":"Implement MustParse() convenience function that panics on error (for tests/initialization).\n\n**API:**\n```go\nfunc MustParse(s string) Date\n```\n\n**Behavior:**\n- Calls Parse() internally\n- Returns Date on success\n- Panics on error (with clear panic message)\n\n**Use Cases:**\n- Test fixtures\n- Static initialization\n- Configuration files where values are known-good\n\n**Documentation:**\n- MUST clearly document that this function panics\n- MUST recommend using Parse() in production code\n- MUST show test usage examples\n\n## Acceptance Criteria\n- [ ] MustParse() implemented\n- [ ] Returns Date on successful parse\n- [ ] Panics with clear message on error\n- [ ] Godoc clearly warns about panic behavior\n- [ ] Godoc recommends Parse() for production\n- [ ] Example test showing test fixture usage\n- [ ] Unit tests verifying panic on invalid input","status":"closed","priority":3,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:46.007442996+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:54:19.638519056+01:00","closed_at":"2026-02-11T20:54:19.638519056+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-7m5","depends_on_id":"quando-gr5","type":"blocks","created_at":"2026-02-11T16:23:11.340600075+01:00","created_by":"Oliver Jakoubek"}]}
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -39,3 +39,4 @@ build/
|
||||||
# Temporary files
|
# Temporary files
|
||||||
tmp/
|
tmp/
|
||||||
temp/
|
temp/
|
||||||
|
.beads/export-state/
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,19 @@ func TestNewClock(t *testing.T) {
|
||||||
func TestDefaultClock_Now(t *testing.T) {
|
func TestDefaultClock_Now(t *testing.T) {
|
||||||
clock := NewClock()
|
clock := NewClock()
|
||||||
|
|
||||||
before := time.Now()
|
before := time.Now().UTC()
|
||||||
date := clock.Now()
|
date := clock.Now()
|
||||||
after := time.Now()
|
after := time.Now().UTC()
|
||||||
|
|
||||||
// Verify that Now() returns a time between before and after
|
// Verify that Now() returns a time between before and after
|
||||||
if date.Time().Before(before) || date.Time().After(after) {
|
if date.Time().Before(before) || date.Time().After(after) {
|
||||||
t.Errorf("DefaultClock.Now() returned time outside expected range")
|
t.Errorf("DefaultClock.Now() returned time outside expected range")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify location is UTC
|
||||||
|
if date.Time().Location() != time.UTC {
|
||||||
|
t.Errorf("DefaultClock.Now().Time().Location() = %v, want UTC", date.Time().Location())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultClock_From(t *testing.T) {
|
func TestDefaultClock_From(t *testing.T) {
|
||||||
|
|
|
||||||
4
date.go
4
date.go
|
|
@ -70,14 +70,14 @@ type Date struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now returns a Date representing the current moment in time.
|
// Now returns a Date representing the current moment in time.
|
||||||
// The Date uses the local timezone by default.
|
// The Date uses the UTC timezone by default.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// now := quando.Now()
|
// now := quando.Now()
|
||||||
func Now() Date {
|
func Now() Date {
|
||||||
return Date{
|
return Date{
|
||||||
t: time.Now(),
|
t: time.Now().UTC(),
|
||||||
lang: EN, // Default language
|
lang: EN, // Default language
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNow(t *testing.T) {
|
func TestNow(t *testing.T) {
|
||||||
before := time.Now()
|
before := time.Now().UTC()
|
||||||
date := Now()
|
date := Now()
|
||||||
after := time.Now()
|
after := time.Now().UTC()
|
||||||
|
|
||||||
// Verify that Now() returns a time between before and after
|
// Verify that Now() returns a time between before and after
|
||||||
if date.Time().Before(before) || date.Time().After(after) {
|
if date.Time().Before(before) || date.Time().After(after) {
|
||||||
t.Errorf("Now() returned time outside expected range")
|
t.Errorf("Now() returned time outside expected range")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify location is UTC
|
||||||
|
if date.Time().Location() != time.UTC {
|
||||||
|
t.Errorf("Now().Time().Location() = %v, want UTC", date.Time().Location())
|
||||||
|
}
|
||||||
|
|
||||||
// Verify default language is EN
|
// Verify default language is EN
|
||||||
if date.lang != EN {
|
if date.lang != EN {
|
||||||
t.Errorf("Now() default lang = %v, want %v", date.lang, EN)
|
t.Errorf("Now() default lang = %v, want %v", date.lang, EN)
|
||||||
|
|
|
||||||
2
parse.go
2
parse.go
|
|
@ -294,7 +294,7 @@ func ParseRelativeWithClock(s string, clock Clock) (Date, error) {
|
||||||
return Date{}, fmt.Errorf("parsing relative date: empty input: %w", ErrInvalidFormat)
|
return Date{}, fmt.Errorf("parsing relative date: empty input: %w", ErrInvalidFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get base date (today at 00:00:00 in local timezone)
|
// Get base date (today at 00:00:00 in UTC timezone)
|
||||||
now := clock.Now()
|
now := clock.Now()
|
||||||
t := now.Time()
|
t := now.Time()
|
||||||
loc := t.Location()
|
loc := t.Location()
|
||||||
|
|
|
||||||
|
|
@ -738,8 +738,7 @@ func TestParseRelativeErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRelative(t *testing.T) {
|
func TestParseRelative(t *testing.T) {
|
||||||
// Test the production function (uses system clock)
|
// Test the production function (uses system clock with UTC)
|
||||||
// Just verify it doesn't error on valid inputs
|
|
||||||
validInputs := []string{
|
validInputs := []string{
|
||||||
"today",
|
"today",
|
||||||
"tomorrow",
|
"tomorrow",
|
||||||
|
|
@ -751,12 +750,38 @@ func TestParseRelative(t *testing.T) {
|
||||||
|
|
||||||
for _, input := range validInputs {
|
for _, input := range validInputs {
|
||||||
t.Run(input, func(t *testing.T) {
|
t.Run(input, func(t *testing.T) {
|
||||||
_, err := ParseRelative(input)
|
result, err := ParseRelative(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ParseRelative(%q) unexpected error: %v", input, err)
|
t.Errorf("ParseRelative(%q) unexpected error: %v", input, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify result is in UTC timezone
|
||||||
|
if result.Time().Location() != time.UTC {
|
||||||
|
t.Errorf("ParseRelative(%q) location = %v, want UTC", input, result.Time().Location())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specifically test "today" to verify UTC behavior
|
||||||
|
t.Run("today returns UTC date", func(t *testing.T) {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
result, err := ParseRelative("today")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseRelative(\"today\") error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect UTC location
|
||||||
|
if result.Time().Location() != time.UTC {
|
||||||
|
t.Errorf("ParseRelative(\"today\").Location() = %v, want UTC", result.Time().Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify it matches expected date
|
||||||
|
if !result.Time().Equal(today) {
|
||||||
|
t.Errorf("ParseRelative(\"today\") = %v, want %v", result.Time(), today)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRelativeImmutability(t *testing.T) {
|
func TestParseRelativeImmutability(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue