OpenHours is a public Elixir package published on Hex.pm. It provides business hours calculations: checking if a DateTime falls within working hours, generating time slots, and handling holidays, shifts, breaks, and timezones.
- Run
mix formatbefore committing. The project uses a line length of 100 characters (see.formatter.exs). - Follow the official Elixir naming conventions:
snake_casefor functions and variables,PascalCasefor modules. - Use pattern matching and guard clauses over conditional branching when possible.
- Prefer pipeline (
|>) style when transforming data through multiple steps. - Keep functions short and focused. Extract private helpers with
defpwhen a function grows beyond a single responsibility.
- Public API functions must have
@docand@spectypespecs. Every public function should be documented with at least one usage example in its@doc(these serve as doctests). - Use
@moduledocat the top of every module to describe its purpose. - Keep the public API surface small. Expose only what users need; use
defpfor implementation details. - The schedule struct (
OpenHours.Schedule) is always passed as the first argument to public functions, making the API pipe-friendly.
- Run tests with
mix test. - Write tests in
test/mirroring thelib/directory structure (e.g.,lib/open_hours/schedule.ex->test/open_hours/schedule_test.exs). - Use
doctestin test modules to validate documentation examples. Adddoctest ModuleNameto the corresponding test file when a module has documented examples. - Cover edge cases: boundaries of time intervals, midnight crossings, DST transitions, holidays falling on shift days, empty schedules.
- Use descriptive test names that explain the scenario, not the implementation.
- Keep dependencies minimal. This is a library — every dependency is inherited by users.
- Runtime dependencies must be justified. Development-only dependencies go in the
:devor:testenvironments. - After adding or updating dependencies, run
mix deps.getand verifymix.lockis consistent.
- Generate docs with
mix docsand review them locally before publishing. - Use
ex_docfeatures: link to other modules withModule, link to functions withfunction/arity. - Keep the README focused on getting started. Detailed API docs belong in
@doc/@moduledoc.
- The package is published to Hex.pm automatically when a GitHub release is created (via
.github/workflows/publish.yml). - Never publish manually. Let the release automation handle it (see Release Workflow below).
- The version in
mix.exsis managed by release-please. Do not bump it manually.
This project uses Conventional Commits. Every commit message must follow this format:
<type>(<optional scope>): <description>
[optional body]
| Type | Purpose | Appears in changelog? |
|---|---|---|
feat |
New feature or functionality | Yes (Features) |
fix |
Bug fix | Yes (Bug Fixes) |
perf |
Performance improvement | Yes (Performance Improvements) |
revert |
Reverts a previous commit | Yes (Reverts) |
docs |
Documentation changes only | Yes (Documentation) |
deps |
Dependency updates | Yes (Dependencies) |
chore |
Maintenance, config, non-deps housekeeping | No (hidden) |
refactor |
Code restructuring, no behavior change | No (hidden) |
test |
Adding or updating tests | No (hidden) |
ci |
CI/CD pipeline changes | No (hidden) |
- Add
!after the type for breaking changes:feat!: remove deprecated function - Or add a
BREAKING CHANGE:footer in the commit body. - Breaking changes trigger a minor version bump (pre-1.0) or major version bump (post-1.0).
feat: add duration calculation between two DateTimes
fix: handle DST transition in time slot generation
docs: add examples for schedule configuration
deps: bump tzdata from 1.1.1 to 1.1.2
refactor: extract interval walking into shared helper
test: cover midnight crossing edge case in TimeSlot.between
This project uses release-please for automated versioning and changelog generation.
- Commits land on
mainfollowing conventional commit format. - Release-please automatically creates/updates a "Release PR" that:
- Bumps the version in
mix.exsbased on commit types (feat= minor,fix= patch). - Updates
CHANGELOG.mdwith entries grouped by commit type. - Updates
.release-please-manifest.jsonwith the new version.
- Bumps the version in
- When the Release PR is merged, release-please creates a GitHub Release.
- The GitHub Release triggers
.github/workflows/publish.yml, which publishes to Hex.pm.
- Never edit
CHANGELOG.mdmanually — release-please owns it. - Never bump the version in
mix.exsmanually — release-please owns it. - Never create GitHub releases manually — release-please owns them.
- The release type is
elixir(configured inrelease-please-config.json).
Dependabot is configured (.github/dependabot.yml) to check for updates daily on two ecosystems:
- Mix dependencies — uses
depscommit prefix so updates appear in the changelog under "Dependencies". - GitHub Actions — uses
cicommit prefix (hidden from changelog).
- Review dependency update PRs for breaking changes before merging.
- Ensure CI passes on Dependabot PRs before merging.
- Prefer merging Dependabot PRs promptly to avoid falling behind on security patches.
CI runs on every pull request and push to main (.github/workflows/ci.yml):
- Tests: Run on two Elixir/OTP version pairs (1.16/26.2 and 1.18/27.1).
- Linting (runs on 1.16/26.2 only):
mix format --check-formattedmix deps.unlock --check-unused
All checks must pass before merging. If format check fails, run mix format locally and commit the changes.