feat(quando-r1o): consolidate benchmarks and achieve 99.5% test coverage

Reorganized test suite with consolidated benchmarks and comprehensive
edge case coverage, exceeding 95% minimum coverage requirement.

Changes:
- Created benchmark_test.go with all 52 benchmarks organized by category
  (arithmetic, clock, date, diff, format, inspection, parse, snap, unit)
- Removed benchmarks from 9 individual test files for better organization
- Added 6 edge case tests to improve coverage:
  * Format() unknown type fallback
  * formatLong() empty lang default
  * Format.String() unknown value
  * isYearPrefix() edge cases
  * StartOf()/EndOf() unsupported units
- Added 3 DST transition tests:
  * Spring forward (23-hour day)
  * Fall back (25-hour day)
  * Multiple timezone preservation

Results:
- Test coverage: 97.7% → 99.5% (exceeds 95% target)
- All 52 benchmarks consolidated and verified
- All benchmarks meet performance targets
- No test regressions

Files modified:
- Created: benchmark_test.go (580 lines)
- Modified: 9 test files (removed benchmarks, added tests)
This commit is contained in:
Oliver Jakoubek 2026-02-11 21:13:09 +01:00
commit 889e78da90
11 changed files with 710 additions and 460 deletions

View file

@ -15,7 +15,7 @@
{"id":"quando-gr5","title":"Automatic date parsing","description":"Implement automatic parsing that detects common date formats without explicit layout.\n\n**API:**\n```go\nfunc Parse(s string) (Date, error)\n```\n\n**Supported Formats:**\n- ISO: \"2026-02-09\"\n- ISO with slash: \"2026/02/09\"\n- EU (dot separator): \"09.02.2026\"\n- RFC2822: \"Mon, 09 Feb 2026 00:00:00 +0000\"\n\n**Ambiguity Rules:**\nSlash formats without year prefix are AMBIGUOUS and must error:\n\n| Input | Recognition | Reason |\n|-------|-------------|--------|\n| 2026-02-01 | ✅ ISO | Standard format |\n| 01.02.2026 | ✅ EU | Dot = EU convention |\n| 2026/02/09 | ✅ ISO | Year prefix unambiguous |\n| 01/02/2026 | ❌ ERROR | Ambiguous (US vs EU) |\n\n**Error Handling:**\n- Return clear error for ambiguous formats\n- Return clear error for invalid dates\n- Never panic\n\n## Acceptance Criteria\n- [ ] Parse() implemented\n- [ ] ISO format recognized (YYYY-MM-DD)\n- [ ] ISO slash format recognized (YYYY/MM/DD)\n- [ ] EU format recognized (DD.MM.YYYY)\n- [ ] RFC2822 format recognized\n- [ ] Ambiguous slash formats return error\n- [ ] Invalid dates return error\n- [ ] Never panics on any input\n- [ ] Unit tests for all supported formats\n- [ ] Unit tests for ambiguous/invalid inputs\n- [ ] Benchmark meets \u003c10µs target\n- [ ] Godoc with ambiguity rules documented\n- [ ] Example tests showing supported formats","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:28.074836359+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T18:53:00.296984877+01:00","closed_at":"2026-02-11T18:53:00.296984877+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-gr5","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:11.106618119+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-gr5","depends_on_id":"quando-36t","type":"blocks","created_at":"2026-02-11T16:23:11.142801721+01:00","created_by":"Oliver Jakoubek"}]} {"id":"quando-gr5","title":"Automatic date parsing","description":"Implement automatic parsing that detects common date formats without explicit layout.\n\n**API:**\n```go\nfunc Parse(s string) (Date, error)\n```\n\n**Supported Formats:**\n- ISO: \"2026-02-09\"\n- ISO with slash: \"2026/02/09\"\n- EU (dot separator): \"09.02.2026\"\n- RFC2822: \"Mon, 09 Feb 2026 00:00:00 +0000\"\n\n**Ambiguity Rules:**\nSlash formats without year prefix are AMBIGUOUS and must error:\n\n| Input | Recognition | Reason |\n|-------|-------------|--------|\n| 2026-02-01 | ✅ ISO | Standard format |\n| 01.02.2026 | ✅ EU | Dot = EU convention |\n| 2026/02/09 | ✅ ISO | Year prefix unambiguous |\n| 01/02/2026 | ❌ ERROR | Ambiguous (US vs EU) |\n\n**Error Handling:**\n- Return clear error for ambiguous formats\n- Return clear error for invalid dates\n- Never panic\n\n## Acceptance Criteria\n- [ ] Parse() implemented\n- [ ] ISO format recognized (YYYY-MM-DD)\n- [ ] ISO slash format recognized (YYYY/MM/DD)\n- [ ] EU format recognized (DD.MM.YYYY)\n- [ ] RFC2822 format recognized\n- [ ] Ambiguous slash formats return error\n- [ ] Invalid dates return error\n- [ ] Never panics on any input\n- [ ] Unit tests for all supported formats\n- [ ] Unit tests for ambiguous/invalid inputs\n- [ ] Benchmark meets \u003c10µs target\n- [ ] Godoc with ambiguity rules documented\n- [ ] Example tests showing supported formats","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:28.074836359+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T18:53:00.296984877+01:00","closed_at":"2026-02-11T18:53:00.296984877+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-gr5","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:11.106618119+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-gr5","depends_on_id":"quando-36t","type":"blocks","created_at":"2026-02-11T16:23:11.142801721+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"quando-j2s","title":"Core infrastructure: Date type and conversions","description":"Implement the core Date type that wraps time.Time and provides the foundation for the fluent API.\n\n**Technical Details:**\n- `Date` struct with private `time.Time` field and optional `Lang` field\n- Package-level constructors: `Now()`, `From(time.Time)`\n- Conversion methods: `Time()` returns underlying time.Time\n- Unix timestamp support: `Unix()` and `FromUnix(int64)`\n\n**Implementation Notes:**\n- Date must wrap time.Time, not reimplement it\n- All operations return new Date instances (immutability)\n- Support full Go time.Time range (year 0-9999+)\n- Support negative Unix timestamps (before 1970)\n\n## Acceptance Criteria\n- [ ] Date struct defined with time.Time and Lang fields\n- [ ] Now() returns current date\n- [ ] From(time.Time) converts to Date\n- [ ] Time() extracts underlying time.Time\n- [ ] Unix() returns Unix timestamp (int64)\n- [ ] FromUnix(int64) creates Date from timestamp\n- [ ] Unit tests with 95%+ coverage\n- [ ] Godoc comments for all exported types/functions\n- [ ] Example tests in example_test.go","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:20:29.134906992+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:31:25.561098309+01:00","closed_at":"2026-02-11T16:31:25.561098309+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-j2s","depends_on_id":"quando-91w","type":"blocks","created_at":"2026-02-11T16:23:05.272420642+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":2,"issue_id":"quando-j2s","author":"Oliver Jakoubek","text":"Plan: 1) Create date.go with Date struct (t time.Time, lang Lang), 2) Implement package-level constructors Now() and From(time.Time), 3) Add conversion methods Time(), Unix(), FromUnix(), 4) Create quando.go for package-level exports, 5) Write comprehensive unit tests covering edge cases (negative Unix timestamps, full time.Time range), 6) Add example tests in example_test.go, 7) Ensure all exports have godoc comments","created_at":"2026-02-11T15:28:39Z"}]} {"id":"quando-j2s","title":"Core infrastructure: Date type and conversions","description":"Implement the core Date type that wraps time.Time and provides the foundation for the fluent API.\n\n**Technical Details:**\n- `Date` struct with private `time.Time` field and optional `Lang` field\n- Package-level constructors: `Now()`, `From(time.Time)`\n- Conversion methods: `Time()` returns underlying time.Time\n- Unix timestamp support: `Unix()` and `FromUnix(int64)`\n\n**Implementation Notes:**\n- Date must wrap time.Time, not reimplement it\n- All operations return new Date instances (immutability)\n- Support full Go time.Time range (year 0-9999+)\n- Support negative Unix timestamps (before 1970)\n\n## Acceptance Criteria\n- [ ] Date struct defined with time.Time and Lang fields\n- [ ] Now() returns current date\n- [ ] From(time.Time) converts to Date\n- [ ] Time() extracts underlying time.Time\n- [ ] Unix() returns Unix timestamp (int64)\n- [ ] FromUnix(int64) creates Date from timestamp\n- [ ] Unit tests with 95%+ coverage\n- [ ] Godoc comments for all exported types/functions\n- [ ] Example tests in example_test.go","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:20:29.134906992+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:31:25.561098309+01:00","closed_at":"2026-02-11T16:31:25.561098309+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-j2s","depends_on_id":"quando-91w","type":"blocks","created_at":"2026-02-11T16:23:05.272420642+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":2,"issue_id":"quando-j2s","author":"Oliver Jakoubek","text":"Plan: 1) Create date.go with Date struct (t time.Time, lang Lang), 2) Implement package-level constructors Now() and From(time.Time), 3) Add conversion methods Time(), Unix(), FromUnix(), 4) Create quando.go for package-level exports, 5) Write comprehensive unit tests covering edge cases (negative Unix timestamps, full time.Time range), 6) Add example tests in example_test.go, 7) Ensure all exports have godoc comments","created_at":"2026-02-11T15:28:39Z"}]}
{"id":"quando-ljj","title":"Duration type and Diff calculation","description":"Implement Duration type and Diff function for calculating differences between dates.\n\n**API:**\n```go\nfunc Diff(a, b time.Time) Duration\n\ntype Duration struct {\n // private fields\n}\n\n// Integer methods (rounded down)\nfunc (dur Duration) Seconds() int64\nfunc (dur Duration) Minutes() int64\nfunc (dur Duration) Hours() int64\nfunc (dur Duration) Days() int\nfunc (dur Duration) Weeks() int\nfunc (dur Duration) Months() int\nfunc (dur Duration) Years() int\n\n// Float methods (precise)\nfunc (dur Duration) MonthsFloat() float64\nfunc (dur Duration) YearsFloat() float64\n```\n\n**Precision:**\n- Integer variants return rounded-down values\n- Float variants for precise calculations\n- Handle negative differences (date1 \u003c date2)\n- Cross year boundaries correctly\n- Handle leap years correctly\n\n## Acceptance Criteria\n- [ ] Duration type defined\n- [ ] Diff(a, b) returns Duration\n- [ ] All integer methods implemented (Seconds through Years)\n- [ ] Float methods for Months and Years implemented\n- [ ] Negative differences handled correctly\n- [ ] Calculations correct across year boundaries\n- [ ] Leap year handling correct\n- [ ] Unit tests with 95%+ coverage\n- [ ] Table-driven tests for various date ranges\n- [ ] Benchmarks meet \u003c1µs (int) and \u003c2µs (float) targets\n- [ ] Godoc comments with precision explanation","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:06.159742785+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T17:38:26.443877375+01:00","closed_at":"2026-02-11T17:38:26.443877375+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-ljj","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:08.127266864+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":7,"issue_id":"quando-ljj","author":"Oliver Jakoubek","text":"Plan: 1) Create diff.go with Duration type (private fields: start, end time.Time), 2) Implement Diff(a, b) package function, 3) Implement integer methods (Seconds through Years) with rounded-down values, 4) Implement float methods (MonthsFloat, YearsFloat) for precise calculations, 5) Handle negative differences (when a \u003c b), 6) Correctly handle year boundaries and leap years in month/year calculations, 7) Write comprehensive table-driven tests covering edge cases, 8) Add benchmarks, 9) Godoc comments with precision explanation","created_at":"2026-02-11T16:34:17Z"}]} {"id":"quando-ljj","title":"Duration type and Diff calculation","description":"Implement Duration type and Diff function for calculating differences between dates.\n\n**API:**\n```go\nfunc Diff(a, b time.Time) Duration\n\ntype Duration struct {\n // private fields\n}\n\n// Integer methods (rounded down)\nfunc (dur Duration) Seconds() int64\nfunc (dur Duration) Minutes() int64\nfunc (dur Duration) Hours() int64\nfunc (dur Duration) Days() int\nfunc (dur Duration) Weeks() int\nfunc (dur Duration) Months() int\nfunc (dur Duration) Years() int\n\n// Float methods (precise)\nfunc (dur Duration) MonthsFloat() float64\nfunc (dur Duration) YearsFloat() float64\n```\n\n**Precision:**\n- Integer variants return rounded-down values\n- Float variants for precise calculations\n- Handle negative differences (date1 \u003c date2)\n- Cross year boundaries correctly\n- Handle leap years correctly\n\n## Acceptance Criteria\n- [ ] Duration type defined\n- [ ] Diff(a, b) returns Duration\n- [ ] All integer methods implemented (Seconds through Years)\n- [ ] Float methods for Months and Years implemented\n- [ ] Negative differences handled correctly\n- [ ] Calculations correct across year boundaries\n- [ ] Leap year handling correct\n- [ ] Unit tests with 95%+ coverage\n- [ ] Table-driven tests for various date ranges\n- [ ] Benchmarks meet \u003c1µs (int) and \u003c2µs (float) targets\n- [ ] Godoc comments with precision explanation","status":"closed","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:06.159742785+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T17:38:26.443877375+01:00","closed_at":"2026-02-11T17:38:26.443877375+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-ljj","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:08.127266864+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":7,"issue_id":"quando-ljj","author":"Oliver Jakoubek","text":"Plan: 1) Create diff.go with Duration type (private fields: start, end time.Time), 2) Implement Diff(a, b) package function, 3) Implement integer methods (Seconds through Years) with rounded-down values, 4) Implement float methods (MonthsFloat, YearsFloat) for precise calculations, 5) Handle negative differences (when a \u003c b), 6) Correctly handle year boundaries and leap years in month/year calculations, 7) Write comprehensive table-driven tests covering edge cases, 8) Add benchmarks, 9) Godoc comments with precision explanation","created_at":"2026-02-11T16:34:17Z"}]}
{"id":"quando-r1o","title":"Comprehensive test suite and benchmarks","description":"Ensure comprehensive test coverage (95%+) and performance benchmarks for all features.\n\n**Test Requirements:**\n- Minimum 95% coverage for all calculation functions\n- Table-driven tests for edge cases\n- Example tests for godoc\n- Separate benchmark file\n\n**Critical Test Scenarios:**\n1. Month arithmetic: Overflow, leap years, negative\n2. Snap operations: All units, edge cases\n3. Next/Prev: Same weekday edge case\n4. Diff calculations: Year boundaries, leap years, negative\n5. DST handling: Add across DST transitions\n6. Parsing: All formats, ambiguous, invalid\n7. WeekNumber: ISO 8601 compliance\n8. Formatting: All formats, i18n\n\n**Benchmarks:**\nTarget performance:\n- Add/Sub: \u003c1µs\n- Diff: \u003c1µs (int), \u003c2µs (float)\n- Format: \u003c5µs (no i18n), \u003c10µs (with i18n)\n- Parse: \u003c10µs (auto), \u003c20µs (relative)\n\n## Acceptance Criteria\n- [ ] Test coverage ≥95% for all calculation functions\n- [ ] Table-driven tests for all edge cases\n- [ ] Example tests in example_test.go\n- [ ] Benchmark file with all critical operations\n- [ ] All benchmarks meet performance targets\n- [ ] CI/CD runs tests and reports coverage\n- [ ] Tests use FixedClock for determinism\n- [ ] Edge cases documented in test names/comments","status":"open","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:38.100200304+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:22:38.100200304+01:00","dependencies":[{"issue_id":"quando-r1o","depends_on_id":"quando-b4r","type":"blocks","created_at":"2026-02-11T16:23:15.418358186+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-dsx","type":"blocks","created_at":"2026-02-11T16:23:15.474799177+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-9sf","type":"blocks","created_at":"2026-02-11T16:23:15.513811922+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-10t","type":"blocks","created_at":"2026-02-11T16:23:15.545561332+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-gr5","type":"blocks","created_at":"2026-02-11T16:23:15.578273993+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-5ol","type":"blocks","created_at":"2026-02-11T16:23:15.609031891+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-5ib","type":"blocks","created_at":"2026-02-11T16:23:15.640381484+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-41g","type":"blocks","created_at":"2026-02-11T16:23:15.672161997+01:00","created_by":"Oliver Jakoubek"}]} {"id":"quando-r1o","title":"Comprehensive test suite and benchmarks","description":"Ensure comprehensive test coverage (95%+) and performance benchmarks for all features.\n\n**Test Requirements:**\n- Minimum 95% coverage for all calculation functions\n- Table-driven tests for edge cases\n- Example tests for godoc\n- Separate benchmark file\n\n**Critical Test Scenarios:**\n1. Month arithmetic: Overflow, leap years, negative\n2. Snap operations: All units, edge cases\n3. Next/Prev: Same weekday edge case\n4. Diff calculations: Year boundaries, leap years, negative\n5. DST handling: Add across DST transitions\n6. Parsing: All formats, ambiguous, invalid\n7. WeekNumber: ISO 8601 compliance\n8. Formatting: All formats, i18n\n\n**Benchmarks:**\nTarget performance:\n- Add/Sub: \u003c1µs\n- Diff: \u003c1µs (int), \u003c2µs (float)\n- Format: \u003c5µs (no i18n), \u003c10µs (with i18n)\n- Parse: \u003c10µs (auto), \u003c20µs (relative)\n\n## Acceptance Criteria\n- [ ] Test coverage ≥95% for all calculation functions\n- [ ] Table-driven tests for all edge cases\n- [ ] Example tests in example_test.go\n- [ ] Benchmark file with all critical operations\n- [ ] All benchmarks meet performance targets\n- [ ] CI/CD runs tests and reports coverage\n- [ ] Tests use FixedClock for determinism\n- [ ] Edge cases documented in test names/comments","status":"closed","priority":3,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:22:38.100200304+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T21:12:53.961602877+01:00","closed_at":"2026-02-11T21:12:53.961602877+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-r1o","depends_on_id":"quando-b4r","type":"blocks","created_at":"2026-02-11T16:23:15.418358186+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-dsx","type":"blocks","created_at":"2026-02-11T16:23:15.474799177+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-9sf","type":"blocks","created_at":"2026-02-11T16:23:15.513811922+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-10t","type":"blocks","created_at":"2026-02-11T16:23:15.545561332+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-gr5","type":"blocks","created_at":"2026-02-11T16:23:15.578273993+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-5ol","type":"blocks","created_at":"2026-02-11T16:23:15.609031891+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-5ib","type":"blocks","created_at":"2026-02-11T16:23:15.640381484+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-r1o","depends_on_id":"quando-41g","type":"blocks","created_at":"2026-02-11T16:23:15.672161997+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"quando-tn3","title":"Relative date parsing","description":"Implement ParseRelative for parsing relative date expressions.\n\n**API:**\n```go\nfunc ParseRelative(s string) (Date, error)\n```\n\n**Supported Expressions (Phase 1):**\n- \"today\" → Today 00:00:00\n- \"tomorrow\" → Tomorrow 00:00:00\n- \"yesterday\" → Yesterday 00:00:00\n- \"+2 days\" → Today + 2 days\n- \"-1 week\" → Today - 1 week\n- \"+3 months\" → Today + 3 months\n\n**Format:**\n- Relative offsets: [+|-]\u003cnumber\u003e \u003cunit\u003e\n- Units: day(s), week(s), month(s), quarter(s), year(s)\n- Singular and plural forms supported\n\n**Out of Scope (Phase 1):**\n- Complex expressions (\"next monday\", \"start of month\")\n- These are nice-to-have for later versions\n\n## Acceptance Criteria\n- [ ] ParseRelative() implemented\n- [ ] \"today\" returns today 00:00:00\n- [ ] \"tomorrow\" returns tomorrow 00:00:00\n- [ ] \"yesterday\" returns yesterday 00:00:00\n- [ ] \"+N \u003cunit\u003e\" adds N units to today\n- [ ] \"-N \u003cunit\u003e\" subtracts N units from today\n- [ ] Singular and plural units both work\n- [ ] All Phase 1 units supported (days, weeks, months, quarters, years)\n- [ ] Invalid expressions return clear errors\n- [ ] Never panics\n- [ ] Unit tests for all supported expressions\n- [ ] Unit tests for invalid inputs\n- [ ] Benchmark meets \u003c20µs target\n- [ ] Godoc with supported expressions listed\n- [ ] Note about future complex expressions","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:40.790156181+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:01:22.266890625+01:00","closed_at":"2026-02-11T20:01:22.266890625+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-tn3","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:11.247985003+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-tn3","depends_on_id":"quando-36t","type":"blocks","created_at":"2026-02-11T16:23:11.278394998+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-tn3","depends_on_id":"quando-b4r","type":"blocks","created_at":"2026-02-11T16:23:11.309549914+01:00","created_by":"Oliver Jakoubek"}]} {"id":"quando-tn3","title":"Relative date parsing","description":"Implement ParseRelative for parsing relative date expressions.\n\n**API:**\n```go\nfunc ParseRelative(s string) (Date, error)\n```\n\n**Supported Expressions (Phase 1):**\n- \"today\" → Today 00:00:00\n- \"tomorrow\" → Tomorrow 00:00:00\n- \"yesterday\" → Yesterday 00:00:00\n- \"+2 days\" → Today + 2 days\n- \"-1 week\" → Today - 1 week\n- \"+3 months\" → Today + 3 months\n\n**Format:**\n- Relative offsets: [+|-]\u003cnumber\u003e \u003cunit\u003e\n- Units: day(s), week(s), month(s), quarter(s), year(s)\n- Singular and plural forms supported\n\n**Out of Scope (Phase 1):**\n- Complex expressions (\"next monday\", \"start of month\")\n- These are nice-to-have for later versions\n\n## Acceptance Criteria\n- [ ] ParseRelative() implemented\n- [ ] \"today\" returns today 00:00:00\n- [ ] \"tomorrow\" returns tomorrow 00:00:00\n- [ ] \"yesterday\" returns yesterday 00:00:00\n- [ ] \"+N \u003cunit\u003e\" adds N units to today\n- [ ] \"-N \u003cunit\u003e\" subtracts N units from today\n- [ ] Singular and plural units both work\n- [ ] All Phase 1 units supported (days, weeks, months, quarters, years)\n- [ ] Invalid expressions return clear errors\n- [ ] Never panics\n- [ ] Unit tests for all supported expressions\n- [ ] Unit tests for invalid inputs\n- [ ] Benchmark meets \u003c20µs target\n- [ ] Godoc with supported expressions listed\n- [ ] Note about future complex expressions","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:40.790156181+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T20:01:22.266890625+01:00","closed_at":"2026-02-11T20:01:22.266890625+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-tn3","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:11.247985003+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-tn3","depends_on_id":"quando-36t","type":"blocks","created_at":"2026-02-11T16:23:11.278394998+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-tn3","depends_on_id":"quando-b4r","type":"blocks","created_at":"2026-02-11T16:23:11.309549914+01:00","created_by":"Oliver Jakoubek"}]}
{"id":"quando-vih","title":"Clock abstraction for testability","description":"Implement Clock interface to enable dependency injection and deterministic testing.\n\n**Technical Details:**\n```go\ntype Clock interface {\n Now() Date\n From(t time.Time) Date\n}\n```\n\n**Implementations:**\n- DefaultClock: Uses time.Now()\n- FixedClock: Returns fixed time for tests\n\n**API:**\n- `NewClock()` - returns DefaultClock\n- `NewFixedClock(time.Time)` - returns FixedClock for tests\n\n## Acceptance Criteria\n- [ ] Clock interface defined\n- [ ] DefaultClock implementation using time.Now()\n- [ ] FixedClock implementation with fixed time\n- [ ] NewClock() factory function\n- [ ] NewFixedClock(time.Time) factory function\n- [ ] Unit tests demonstrating deterministic test patterns\n- [ ] Godoc comments\n- [ ] Example test showing test usage pattern","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:20:33.357927572+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:33:26.936900547+01:00","closed_at":"2026-02-11T16:33:26.936900547+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-vih","depends_on_id":"quando-91w","type":"blocks","created_at":"2026-02-11T16:23:05.308383809+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":3,"issue_id":"quando-vih","author":"Oliver Jakoubek","text":"Plan: 1) Create clock.go with Clock interface (Now, From methods), 2) Implement DefaultClock using time.Now(), 3) Implement FixedClock with fixed time for deterministic tests, 4) Add factory functions NewClock() and NewFixedClock(time.Time), 5) Write comprehensive unit tests demonstrating deterministic test patterns, 6) Add example tests showing test usage, 7) Ensure all exports have godoc comments","created_at":"2026-02-11T15:31:45Z"}]} {"id":"quando-vih","title":"Clock abstraction for testability","description":"Implement Clock interface to enable dependency injection and deterministic testing.\n\n**Technical Details:**\n```go\ntype Clock interface {\n Now() Date\n From(t time.Time) Date\n}\n```\n\n**Implementations:**\n- DefaultClock: Uses time.Now()\n- FixedClock: Returns fixed time for tests\n\n**API:**\n- `NewClock()` - returns DefaultClock\n- `NewFixedClock(time.Time)` - returns FixedClock for tests\n\n## Acceptance Criteria\n- [ ] Clock interface defined\n- [ ] DefaultClock implementation using time.Now()\n- [ ] FixedClock implementation with fixed time\n- [ ] NewClock() factory function\n- [ ] NewFixedClock(time.Time) factory function\n- [ ] Unit tests demonstrating deterministic test patterns\n- [ ] Godoc comments\n- [ ] Example test showing test usage pattern","status":"closed","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:20:33.357927572+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T16:33:26.936900547+01:00","closed_at":"2026-02-11T16:33:26.936900547+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-vih","depends_on_id":"quando-91w","type":"blocks","created_at":"2026-02-11T16:23:05.308383809+01:00","created_by":"Oliver Jakoubek"}],"comments":[{"id":3,"issue_id":"quando-vih","author":"Oliver Jakoubek","text":"Plan: 1) Create clock.go with Clock interface (Now, From methods), 2) Implement DefaultClock using time.Now(), 3) Implement FixedClock with fixed time for deterministic tests, 4) Add factory functions NewClock() and NewFixedClock(time.Time), 5) Write comprehensive unit tests demonstrating deterministic test patterns, 6) Add example tests showing test usage, 7) Ensure all exports have godoc comments","created_at":"2026-02-11T15:31:45Z"}]}
{"id":"quando-wny","title":"Explicit parsing with layout","description":"Implement explicit parsing using Go's standard layout format.\n\n**API:**\n```go\nfunc ParseWithLayout(s, layout string) (Date, error)\n```\n\n**Purpose:**\nHandle ambiguous or custom formats by providing explicit layout\n\n**Examples:**\n```go\nParseWithLayout(\"01/02/2026\", \"02/01/2006\") // EU format\nParseWithLayout(\"01/02/2026\", \"01/02/2006\") // US format\nParseWithLayout(\"9. Februar 2026\", \"2. January 2006\") // German custom\n```\n\n**Implementation:**\n- Delegate to time.Parse() with layout\n- Wrap result in quando.Date\n- Return clear errors for invalid inputs\n\n## Acceptance Criteria\n- [ ] ParseWithLayout() implemented\n- [ ] Uses Go's standard layout format (reference date)\n- [ ] Wraps time.Parse() correctly\n- [ ] Returns Date on success\n- [ ] Returns error on parse failure\n- [ ] Never panics\n- [ ] Unit tests for various layouts\n- [ ] Unit tests for invalid inputs\n- [ ] Benchmark meets \u003c10µs target\n- [ ] Godoc with Go layout format explanation\n- [ ] Example tests showing EU vs US disambiguation","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:33.246073999+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T19:50:59.125974057+01:00","closed_at":"2026-02-11T19:50:59.125974057+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-wny","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:11.182490834+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-wny","depends_on_id":"quando-36t","type":"blocks","created_at":"2026-02-11T16:23:11.216169487+01:00","created_by":"Oliver Jakoubek"}]} {"id":"quando-wny","title":"Explicit parsing with layout","description":"Implement explicit parsing using Go's standard layout format.\n\n**API:**\n```go\nfunc ParseWithLayout(s, layout string) (Date, error)\n```\n\n**Purpose:**\nHandle ambiguous or custom formats by providing explicit layout\n\n**Examples:**\n```go\nParseWithLayout(\"01/02/2026\", \"02/01/2006\") // EU format\nParseWithLayout(\"01/02/2026\", \"01/02/2006\") // US format\nParseWithLayout(\"9. Februar 2026\", \"2. January 2006\") // German custom\n```\n\n**Implementation:**\n- Delegate to time.Parse() with layout\n- Wrap result in quando.Date\n- Return clear errors for invalid inputs\n\n## Acceptance Criteria\n- [ ] ParseWithLayout() implemented\n- [ ] Uses Go's standard layout format (reference date)\n- [ ] Wraps time.Parse() correctly\n- [ ] Returns Date on success\n- [ ] Returns error on parse failure\n- [ ] Never panics\n- [ ] Unit tests for various layouts\n- [ ] Unit tests for invalid inputs\n- [ ] Benchmark meets \u003c10µs target\n- [ ] Godoc with Go layout format explanation\n- [ ] Example tests showing EU vs US disambiguation","status":"closed","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-02-11T16:21:33.246073999+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-02-11T19:50:59.125974057+01:00","closed_at":"2026-02-11T19:50:59.125974057+01:00","close_reason":"Closed","dependencies":[{"issue_id":"quando-wny","depends_on_id":"quando-j2s","type":"blocks","created_at":"2026-02-11T16:23:11.182490834+01:00","created_by":"Oliver Jakoubek"},{"issue_id":"quando-wny","depends_on_id":"quando-36t","type":"blocks","created_at":"2026-02-11T16:23:11.216169487+01:00","created_by":"Oliver Jakoubek"}]}

View file

@ -390,38 +390,81 @@ func TestTimezonePreservation(t *testing.T) {
} }
} }
// BenchmarkAddDays benchmarks Add with Days // TestAdd_DSTSpringForward tests Add(1, Days) across DST spring forward transition
func BenchmarkAddDays(b *testing.B) { func TestAdd_DSTSpringForward(t *testing.T) {
date := Now() // March 31, 2024, 01:30 CET (before DST transition at 02:00)
b.ResetTimer() // At 02:00, clocks jump to 03:00 CEST
for i := 0; i < b.N; i++ { loc, err := time.LoadLocation("Europe/Berlin")
_ = date.Add(1, Days) if err != nil {
t.Skip("Europe/Berlin timezone not available")
}
// 1:30 AM on March 31, 2024 (CET = UTC+1)
before := From(time.Date(2024, 3, 31, 1, 30, 0, 0, loc))
// Add 1 day - should be 1:30 AM on April 1, 2024 (CEST = UTC+2)
after := before.Add(1, Days)
expected := time.Date(2024, 4, 1, 1, 30, 0, 0, loc)
if !after.Time().Equal(expected) {
t.Errorf("Add(1, Days) across DST spring forward: got %v, want %v", after.Time(), expected)
}
// Verify it's only 23 hours of actual time (due to DST)
duration := after.Time().Sub(before.Time())
if duration != 23*time.Hour {
t.Logf("Note: Duration is %v (23h expected due to DST spring forward)", duration)
} }
} }
// BenchmarkAddMonths benchmarks Add with Months // TestAdd_DSTFallBack tests Add(1, Days) across DST fall back transition
func BenchmarkAddMonths(b *testing.B) { func TestAdd_DSTFallBack(t *testing.T) {
date := Now() // October 27, 2024, 02:30 CEST (before DST transition at 03:00)
b.ResetTimer() // At 03:00, clocks fall back to 02:00 CET
for i := 0; i < b.N; i++ { loc, err := time.LoadLocation("Europe/Berlin")
_ = date.Add(1, Months) if err != nil {
t.Skip("Europe/Berlin timezone not available")
}
// 2:30 AM on October 27, 2024 (CEST = UTC+2)
before := From(time.Date(2024, 10, 27, 2, 30, 0, 0, loc))
// Add 1 day - should be 2:30 AM on October 28, 2024 (CET = UTC+1)
after := before.Add(1, Days)
expected := time.Date(2024, 10, 28, 2, 30, 0, 0, loc)
if !after.Time().Equal(expected) {
t.Errorf("Add(1, Days) across DST fall back: got %v, want %v", after.Time(), expected)
}
// Verify it's 25 hours of actual time (due to DST)
duration := after.Time().Sub(before.Time())
if duration != 25*time.Hour {
t.Logf("Note: Duration is %v (25h expected due to DST fall back)", duration)
} }
} }
// BenchmarkAddYears benchmarks Add with Years // TestAdd_DSTMultipleTimezones tests Add preserves local time across DST in multiple timezones
func BenchmarkAddYears(b *testing.B) { func TestAdd_DSTMultipleTimezones(t *testing.T) {
date := Now() timezones := []string{"Europe/Berlin", "America/New_York", "America/Los_Angeles"}
b.ResetTimer()
for i := 0; i < b.N; i++ { for _, tzName := range timezones {
_ = date.Add(1, Years) t.Run(tzName, func(t *testing.T) {
loc, err := time.LoadLocation(tzName)
if err != nil {
t.Skipf("%s timezone not available", tzName)
}
// Test adding days preserves local time regardless of DST
date := From(time.Date(2024, 3, 15, 14, 30, 0, 0, loc))
result := date.Add(30, Days)
// Should be same local time (14:30) 30 days later
if result.Time().Hour() != 14 || result.Time().Minute() != 30 {
t.Errorf("Add(30, Days) in %s: time changed from 14:30 to %02d:%02d",
tzName, result.Time().Hour(), result.Time().Minute())
}
})
} }
} }
// BenchmarkMethodChaining benchmarks chained operations
func BenchmarkMethodChaining(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Add(1, Months).Add(15, Days).Sub(2, Hours)
}
}

555
benchmark_test.go Normal file
View file

@ -0,0 +1,555 @@
package quando
import (
"testing"
"time"
)
// ============================================================================
// Arithmetic Benchmarks
// ============================================================================
// BenchmarkAddDays benchmarks Add with Days
func BenchmarkAddDays(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Add(1, Days)
}
}
// BenchmarkAddMonths benchmarks Add with Months
func BenchmarkAddMonths(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Add(1, Months)
}
}
// BenchmarkAddYears benchmarks Add with Years
func BenchmarkAddYears(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Add(1, Years)
}
}
// BenchmarkMethodChaining benchmarks chained operations
func BenchmarkMethodChaining(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Add(1, Months).Add(15, Days).Sub(2, Hours)
}
}
// ============================================================================
// Clock Benchmarks
// ============================================================================
// BenchmarkDefaultClock_Now benchmarks DefaultClock.Now()
func BenchmarkDefaultClock_Now(b *testing.B) {
clock := NewClock()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.Now()
}
}
// BenchmarkFixedClock_Now benchmarks FixedClock.Now()
func BenchmarkFixedClock_Now(b *testing.B) {
fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC)
clock := NewFixedClock(fixedTime)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.Now()
}
}
// BenchmarkDefaultClock_From benchmarks DefaultClock.From()
func BenchmarkDefaultClock_From(b *testing.B) {
clock := NewClock()
t := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.From(t)
}
}
// BenchmarkFixedClock_From benchmarks FixedClock.From()
func BenchmarkFixedClock_From(b *testing.B) {
fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC)
clock := NewFixedClock(fixedTime)
t := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.From(t)
}
}
// ============================================================================
// Date Conversion Benchmarks
// ============================================================================
// BenchmarkNow benchmarks the Now() function
func BenchmarkNow(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Now()
}
}
// BenchmarkFrom benchmarks the From() function
func BenchmarkFrom(b *testing.B) {
t := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = From(t)
}
}
// BenchmarkFromUnix benchmarks the FromUnix() function
func BenchmarkFromUnix(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = FromUnix(1707480000)
}
}
// BenchmarkTime benchmarks the Time() method
func BenchmarkTime(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Time()
}
}
// BenchmarkUnix benchmarks the Unix() method
func BenchmarkUnix(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Unix()
}
}
// ============================================================================
// Diff/Duration Benchmarks
// ============================================================================
// BenchmarkDurationSeconds benchmarks Seconds()
func BenchmarkDurationSeconds(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Seconds()
}
}
// BenchmarkDurationDays benchmarks Days()
func BenchmarkDurationDays(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Days()
}
}
// BenchmarkDurationMonths benchmarks Months()
func BenchmarkDurationMonths(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Months()
}
}
// BenchmarkDurationMonthsFloat benchmarks MonthsFloat()
func BenchmarkDurationMonthsFloat(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.MonthsFloat()
}
}
// BenchmarkDurationHuman benchmarks Human() with English
func BenchmarkDurationHuman(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 11, 17, 5, 30, 45, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Human(EN)
}
}
// BenchmarkDurationHumanGerman benchmarks Human() with German
func BenchmarkDurationHumanGerman(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 11, 17, 5, 30, 45, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Human(DE)
}
}
// ============================================================================
// Format Benchmarks
// ============================================================================
// BenchmarkFormat_ISO benchmarks Format with ISO format
func BenchmarkFormat_ISO(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(ISO)
}
}
// BenchmarkFormat_EU benchmarks Format with EU format
func BenchmarkFormat_EU(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(EU)
}
}
// BenchmarkFormat_US benchmarks Format with US format
func BenchmarkFormat_US(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(US)
}
}
// BenchmarkFormat_Long_EN benchmarks Format with Long format (English)
func BenchmarkFormat_Long_EN(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC)).WithLang(EN)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(Long)
}
}
// BenchmarkFormat_Long_DE benchmarks Format with Long format (German)
func BenchmarkFormat_Long_DE(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC)).WithLang(DE)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(Long)
}
}
// BenchmarkFormat_RFC2822 benchmarks Format with RFC2822 format
func BenchmarkFormat_RFC2822(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(RFC2822)
}
}
// BenchmarkFormatLayout_EN_Simple benchmarks FormatLayout with English (simple)
func BenchmarkFormatLayout_EN_Simple(b *testing.B) {
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(EN)
layout := "Monday, January 2, 2006"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.FormatLayout(layout)
}
}
// BenchmarkFormatLayout_EN_Numeric benchmarks FormatLayout with English (numeric)
func BenchmarkFormatLayout_EN_Numeric(b *testing.B) {
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(EN)
layout := "2006-01-02 15:04:05"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.FormatLayout(layout)
}
}
// BenchmarkFormatLayout_DE_Simple benchmarks FormatLayout with German (simple)
func BenchmarkFormatLayout_DE_Simple(b *testing.B) {
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(DE)
layout := "Monday, January 2, 2006"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.FormatLayout(layout)
}
}
// BenchmarkFormatLayout_DE_Complex benchmarks FormatLayout with German (complex)
func BenchmarkFormatLayout_DE_Complex(b *testing.B) {
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(DE)
layout := "Monday, January 2, 2006 at 15:04:05 MST (Mon, Jan)"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.FormatLayout(layout)
}
}
// ============================================================================
// Inspection Benchmarks
// ============================================================================
// BenchmarkWeekNumber benchmarks WeekNumber()
func BenchmarkWeekNumber(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.WeekNumber()
}
}
// BenchmarkQuarter benchmarks Quarter()
func BenchmarkQuarter(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.Quarter()
}
}
// BenchmarkDayOfYear benchmarks DayOfYear()
func BenchmarkDayOfYear(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.DayOfYear()
}
}
// BenchmarkIsWeekend benchmarks IsWeekend()
func BenchmarkIsWeekend(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.IsWeekend()
}
}
// BenchmarkIsLeapYear benchmarks IsLeapYear()
func BenchmarkIsLeapYear(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.IsLeapYear()
}
}
// BenchmarkInfo benchmarks Info()
func BenchmarkInfo(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.Info()
}
}
// ============================================================================
// Parse Benchmarks
// ============================================================================
// BenchmarkParse benchmarks the Parse function with different formats
func BenchmarkParse(b *testing.B) {
benchmarks := []struct {
name string
input string
}{
{"ISO format", "2026-02-09"},
{"ISO slash", "2026/02/09"},
{"EU format", "09.02.2026"},
{"RFC2822", "Mon, 09 Feb 2026 00:00:00 +0000"},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := Parse(bm.input)
if err != nil {
b.Fatalf("Parse failed: %v", err)
}
}
})
}
}
// BenchmarkParseError benchmarks error case (ambiguous format)
func BenchmarkParseError(b *testing.B) {
input := "01/02/2026" // Ambiguous format
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := Parse(input)
if err == nil {
b.Fatal("Expected error for ambiguous format")
}
}
}
// BenchmarkParseWithLayout benchmarks ParseWithLayout
func BenchmarkParseWithLayout(b *testing.B) {
layout := "02/01/2006"
input := "09/02/2026"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseWithLayout(input, layout)
}
}
// BenchmarkParseWithLayoutCustom benchmarks ParseWithLayout with custom layout
func BenchmarkParseWithLayoutCustom(b *testing.B) {
layout := "2. January 2006"
input := "9. February 2026"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseWithLayout(input, layout)
}
}
// BenchmarkParseRelativeKeyword benchmarks ParseRelative with keyword
func BenchmarkParseRelativeKeyword(b *testing.B) {
clock := NewFixedClock(time.Date(2026, 2, 15, 12, 0, 0, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseRelativeWithClock("today", clock)
}
}
// BenchmarkParseRelativeOffset benchmarks ParseRelative with offset
func BenchmarkParseRelativeOffset(b *testing.B) {
clock := NewFixedClock(time.Date(2026, 2, 15, 12, 0, 0, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseRelativeWithClock("+2 days", clock)
}
}
// ============================================================================
// Snap Benchmarks
// ============================================================================
// BenchmarkStartOfWeek benchmarks StartOf(Weeks)
func BenchmarkStartOfWeek(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Weeks)
}
}
// BenchmarkEndOfWeek benchmarks EndOf(Weeks)
func BenchmarkEndOfWeek(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Weeks)
}
}
// BenchmarkStartOfMonth benchmarks StartOf(Months)
func BenchmarkStartOfMonth(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Months)
}
}
// BenchmarkEndOfMonth benchmarks EndOf(Months)
func BenchmarkEndOfMonth(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Months)
}
}
// BenchmarkStartOfQuarter benchmarks StartOf(Quarters)
func BenchmarkStartOfQuarter(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Quarters)
}
}
// BenchmarkEndOfQuarter benchmarks EndOf(Quarters)
func BenchmarkEndOfQuarter(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Quarters)
}
}
// BenchmarkStartOfYear benchmarks StartOf(Years)
func BenchmarkStartOfYear(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Years)
}
}
// BenchmarkEndOfYear benchmarks EndOf(Years)
func BenchmarkEndOfYear(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Years)
}
}
// BenchmarkNext benchmarks the Next() method
func BenchmarkNext(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Next(time.Friday)
}
}
// BenchmarkPrev benchmarks the Prev() method
func BenchmarkPrev(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Prev(time.Friday)
}
}
// ============================================================================
// Unit Benchmarks
// ============================================================================
// BenchmarkUnitString benchmarks the String() method
func BenchmarkUnitString(b *testing.B) {
units := []Unit{Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, Years}
b.ResetTimer()
for i := 0; i < b.N; i++ {
u := units[i%len(units)]
_ = u.String()
}
}

View file

@ -189,42 +189,3 @@ func TestClock_Timezones(t *testing.T) {
} }
} }
// BenchmarkDefaultClock_Now benchmarks DefaultClock.Now()
func BenchmarkDefaultClock_Now(b *testing.B) {
clock := NewClock()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.Now()
}
}
// BenchmarkFixedClock_Now benchmarks FixedClock.Now()
func BenchmarkFixedClock_Now(b *testing.B) {
fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC)
clock := NewFixedClock(fixedTime)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.Now()
}
}
// BenchmarkDefaultClock_From benchmarks DefaultClock.From()
func BenchmarkDefaultClock_From(b *testing.B) {
clock := NewClock()
t := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.From(t)
}
}
// BenchmarkFixedClock_From benchmarks FixedClock.From()
func BenchmarkFixedClock_From(b *testing.B) {
fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC)
clock := NewFixedClock(fixedTime)
t := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = clock.From(t)
}
}

View file

@ -269,46 +269,6 @@ func TestDateTimezones(t *testing.T) {
} }
} }
// BenchmarkNow benchmarks the Now() function
func BenchmarkNow(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Now()
}
}
// BenchmarkFrom benchmarks the From() function
func BenchmarkFrom(b *testing.B) {
t := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = From(t)
}
}
// BenchmarkFromUnix benchmarks the FromUnix() function
func BenchmarkFromUnix(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = FromUnix(1707480000)
}
}
// BenchmarkTime benchmarks the Time() method
func BenchmarkTime(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Time()
}
}
// BenchmarkUnix benchmarks the Unix() method
func BenchmarkUnix(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Unix()
}
}
func TestIn(t *testing.T) { func TestIn(t *testing.T) {
tests := []struct { tests := []struct {

View file

@ -495,49 +495,6 @@ func TestDurationCrossBoundaries(t *testing.T) {
} }
} }
// BenchmarkDurationSeconds benchmarks Seconds()
func BenchmarkDurationSeconds(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Seconds()
}
}
// BenchmarkDurationDays benchmarks Days()
func BenchmarkDurationDays(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Days()
}
}
// BenchmarkDurationMonths benchmarks Months()
func BenchmarkDurationMonths(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Months()
}
}
// BenchmarkDurationMonthsFloat benchmarks MonthsFloat()
func BenchmarkDurationMonthsFloat(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.MonthsFloat()
}
}
// TestFloatPrecision verifies that float methods provide better precision // TestFloatPrecision verifies that float methods provide better precision
func TestFloatPrecision(t *testing.T) { func TestFloatPrecision(t *testing.T) {
@ -735,24 +692,3 @@ func TestDurationHumanAdaptiveGranularity(t *testing.T) {
} }
} }
func BenchmarkDurationHuman(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 11, 17, 5, 30, 45, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Human(EN)
}
}
func BenchmarkDurationHumanGerman(b *testing.B) {
start := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 11, 17, 5, 30, 45, 0, time.UTC)
dur := Diff(start, end)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dur.Human(DE)
}
}

View file

@ -229,54 +229,6 @@ func TestFormat_Immutability(t *testing.T) {
} }
} }
// Benchmarks
func BenchmarkFormat_ISO(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(ISO)
}
}
func BenchmarkFormat_EU(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(EU)
}
}
func BenchmarkFormat_US(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(US)
}
}
func BenchmarkFormat_Long_EN(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC)).WithLang(EN)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(Long)
}
}
func BenchmarkFormat_Long_DE(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC)).WithLang(DE)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(Long)
}
}
func BenchmarkFormat_RFC2822(b *testing.B) {
date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.Format(RFC2822)
}
}
// TestFormatLayout tests basic FormatLayout functionality // TestFormatLayout tests basic FormatLayout functionality
func TestFormatLayout(t *testing.T) { func TestFormatLayout(t *testing.T) {
@ -495,39 +447,49 @@ func TestFormatLayout_Immutability(t *testing.T) {
} }
} }
// Benchmarks for FormatLayout // TestFormat_UnknownFormatType tests Format() with an unknown format type
func BenchmarkFormatLayout_EN_Simple(b *testing.B) { func TestFormat_UnknownFormatType(t *testing.T) {
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(EN) date := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
layout := "Monday, January 2, 2006"
b.ResetTimer() // Cast an invalid int to Format type to test fallback
for i := 0; i < b.N; i++ { unknownFormat := Format(999)
_ = date.FormatLayout(layout)
result := date.Format(unknownFormat)
// Should fallback to ISO format (documented behavior)
expected := "2026-02-09"
if result != expected {
t.Errorf("Format(unknown) = %q, want %q (ISO fallback)", result, expected)
} }
} }
func BenchmarkFormatLayout_EN_Numeric(b *testing.B) { // TestFormatLong_EmptyLang tests formatLong() with empty lang
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(EN) func TestFormatLong_EmptyLang(t *testing.T) {
layout := "2006-01-02 15:04:05" // Create date with empty lang (should default to EN)
b.ResetTimer() date := Date{
for i := 0; i < b.N; i++ { t: time.Date(2026, 2, 9, 0, 0, 0, 0, time.UTC),
_ = date.FormatLayout(layout) lang: "", // Empty lang
}
result := date.formatLong()
// Should default to English format
expected := "February 9, 2026"
if result != expected {
t.Errorf("formatLong() with empty lang = %q, want %q", result, expected)
} }
} }
func BenchmarkFormatLayout_DE_Simple(b *testing.B) { // TestFormatString_Unknown tests String() for Format type with unknown value
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(DE) func TestFormatString_Unknown(t *testing.T) {
layout := "Monday, January 2, 2006" // Create an unknown format value
b.ResetTimer() unknownFormat := Format(999)
for i := 0; i < b.N; i++ {
_ = date.FormatLayout(layout) result := unknownFormat.String()
// Should return "unknown" or similar
if result == "" {
t.Error("Format.String() for unknown format should not be empty")
} }
} }
func BenchmarkFormatLayout_DE_Complex(b *testing.B) {
date := From(time.Date(2026, 2, 9, 14, 30, 45, 0, time.UTC)).WithLang(DE)
layout := "Monday, January 2, 2006 at 15:04:05 MST (Mon, Jan)"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.FormatLayout(layout)
}
}

View file

@ -390,52 +390,3 @@ func TestInfo_ConsistentWithIndividualMethods(t *testing.T) {
} }
} }
// Benchmarks
func BenchmarkWeekNumber(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.WeekNumber()
}
}
func BenchmarkQuarter(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.Quarter()
}
}
func BenchmarkDayOfYear(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.DayOfYear()
}
}
func BenchmarkIsWeekend(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.IsWeekend()
}
}
func BenchmarkIsLeapYear(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.IsLeapYear()
}
}
func BenchmarkInfo(b *testing.B) {
d := From(time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = d.Info()
}
}

View file

@ -215,63 +215,6 @@ func TestParseWhitespace(t *testing.T) {
} }
} }
// BenchmarkParse benchmarks the Parse function with different formats
func BenchmarkParse(b *testing.B) {
benchmarks := []struct {
name string
input string
}{
{"ISO format", "2026-02-09"},
{"ISO slash", "2026/02/09"},
{"EU format", "09.02.2026"},
{"RFC2822", "Mon, 09 Feb 2026 00:00:00 +0000"},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := Parse(bm.input)
if err != nil {
b.Fatalf("Parse failed: %v", err)
}
}
})
}
}
// BenchmarkParseError benchmarks error case (ambiguous format)
func BenchmarkParseError(b *testing.B) {
input := "01/02/2026" // Ambiguous format
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := Parse(input)
if err == nil {
b.Fatal("Expected error for ambiguous format")
}
}
}
func BenchmarkParseWithLayout(b *testing.B) {
layout := "02/01/2006"
input := "09/02/2026"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseWithLayout(input, layout)
}
}
func BenchmarkParseWithLayoutCustom(b *testing.B) {
layout := "2. January 2006"
input := "9. February 2026"
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseWithLayout(input, layout)
}
}
// containsSubstring is a helper function to check if a string contains a substring // containsSubstring is a helper function to check if a string contains a substring
func containsSubstring(s, substr string) bool { func containsSubstring(s, substr string) bool {
@ -839,23 +782,6 @@ func TestParseRelativeImmutability(t *testing.T) {
} }
} }
func BenchmarkParseRelativeKeyword(b *testing.B) {
clock := NewFixedClock(time.Date(2026, 2, 15, 12, 0, 0, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseRelativeWithClock("today", clock)
}
}
func BenchmarkParseRelativeOffset(b *testing.B) {
clock := NewFixedClock(time.Date(2026, 2, 15, 12, 0, 0, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseRelativeWithClock("+2 days", clock)
}
}
func TestMustParse_Success(t *testing.T) { func TestMustParse_Success(t *testing.T) {
tests := []struct { tests := []struct {
@ -924,3 +850,30 @@ func TestMustParse_PanicMessage(t *testing.T) {
MustParse("invalid-input") MustParse("invalid-input")
} }
// TestIsYearPrefix_EdgeCases tests edge cases for the isYearPrefix function
func TestIsYearPrefix_EdgeCases(t *testing.T) {
tests := []struct {
name string
input string
expected bool
}{
{"valid year 2026", "2026", true},
{"valid year 1000", "1000", true},
{"invalid year 0999", "0999", false}, // Starts with 0
{"invalid year 999", "999", false}, // Too short
{"invalid non-digits", "abcd", false},
{"invalid mixed", "20a6", false},
{"empty string", "", false},
{"year 9999", "9999", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isYearPrefix(tt.input)
if result != tt.expected {
t.Errorf("isYearPrefix(%q) = %v, want %v", tt.input, result, tt.expected)
}
})
}
}

View file

@ -465,77 +465,6 @@ func TestSnapTimezones(t *testing.T) {
} }
} }
// BenchmarkStartOfWeek benchmarks StartOf(Weeks)
func BenchmarkStartOfWeek(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Weeks)
}
}
// BenchmarkEndOfWeek benchmarks EndOf(Weeks)
func BenchmarkEndOfWeek(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Weeks)
}
}
// BenchmarkStartOfMonth benchmarks StartOf(Months)
func BenchmarkStartOfMonth(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Months)
}
}
// BenchmarkEndOfMonth benchmarks EndOf(Months)
func BenchmarkEndOfMonth(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Months)
}
}
// BenchmarkStartOfQuarter benchmarks StartOf(Quarters)
func BenchmarkStartOfQuarter(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Quarters)
}
}
// BenchmarkEndOfQuarter benchmarks EndOf(Quarters)
func BenchmarkEndOfQuarter(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Quarters)
}
}
// BenchmarkStartOfYear benchmarks StartOf(Years)
func BenchmarkStartOfYear(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.StartOf(Years)
}
}
// BenchmarkEndOfYear benchmarks EndOf(Years)
func BenchmarkEndOfYear(b *testing.B) {
date := Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = date.EndOf(Years)
}
}
// TestNext tests the Next() method for all weekdays // TestNext tests the Next() method for all weekdays
func TestNext(t *testing.T) { func TestNext(t *testing.T) {
@ -798,20 +727,29 @@ func TestNextPrevTimezones(t *testing.T) {
} }
} }
// BenchmarkNext benchmarks the Next() method // TestStartOf_UnsupportedUnit tests StartOf() with unsupported unit
func BenchmarkNext(b *testing.B) { func TestStartOf_UnsupportedUnit(t *testing.T) {
date := Now() date := From(time.Date(2026, 2, 15, 14, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ { // Seconds is not supported by StartOf
_ = date.Next(time.Friday) result := date.StartOf(Seconds)
// Should return unchanged date
if !result.Time().Equal(date.Time()) {
t.Errorf("StartOf(Seconds) should return unchanged date")
} }
} }
// BenchmarkPrev benchmarks the Prev() method // TestEndOf_UnsupportedUnit tests EndOf() with unsupported unit
func BenchmarkPrev(b *testing.B) { func TestEndOf_UnsupportedUnit(t *testing.T) {
date := Now() date := From(time.Date(2026, 2, 15, 14, 30, 45, 0, time.UTC))
b.ResetTimer()
for i := 0; i < b.N; i++ { // Seconds is not supported by EndOf
_ = date.Prev(time.Friday) result := date.EndOf(Seconds)
// Should return unchanged date
if !result.Time().Equal(date.Time()) {
t.Errorf("EndOf(Seconds) should return unchanged date")
} }
} }

View file

@ -136,12 +136,3 @@ func TestUnitComparability(t *testing.T) {
} }
} }
// BenchmarkUnitString benchmarks the String() method
func BenchmarkUnitString(b *testing.B) {
units := []Unit{Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, Years}
b.ResetTimer()
for i := 0; i < b.N; i++ {
u := units[i%len(units)]
_ = u.String()
}
}