Skip to content
Agentmart
Submit a tool

dates-streaks-and-statistics

Builds calendar-day logic: local date keys, an injected clock, midnight, DST, streaks, stats.

Skill by zakariaf

IndexedMIT★ 11
Write a review

Chalk Labs

Need help deploying dates-streaks-and-statistics?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/zakariaf-dates-streaks-and-statistics

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Builds calendar-day logic: local date keys, an injected clock, midnight, DST, streaks, stats. Use when code reads today, counts days, shows a streak or stats, or a date is a day off. Not for seeds (seeded-determinism-and-golden-vectors).

Use cases: productivity

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Node.js package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/dates-streaks-and-statistics

What's inside

Skill name
dates-streaks-and-statistics
Description
Builds calendar-day logic: local date keys, an injected clock, midnight, DST, streaks, stats. Use when code reads today, counts days, shows a streak or stats, or a date is a day off. Not for seeds (seeded-determinism-and-golden-vectors).
License
MIT
Instructions
211 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Files (56)

  • SKILL.md
  • shared.json · assets/
  • complete-task.ts · examples/
  • local-date.test.ts · examples/
  • local-date.ts · examples/
  • stats-cells.test.ts · examples/
  • stats-cells.ts · examples/
  • use-streak-card-model.ts · examples/
  • calendar-days.md · references/
  • checkers.md · references/
  • statistics.md · references/
  • streaks.md · references/
  • check-calendar-code.mjs · scripts/
  • check-lib.mjs · scripts/
  • check-streaks-and-stats.mjs · scripts/
  • app-modules.mjs · scripts/lib/
  • calendar-checks.mjs · scripts/lib/
  • day-logic-contract.mjs · scripts/lib/
  • load-error.mjs · scripts/lib/
  • source-scan.mjs · scripts/lib/
  • stats-checks.mjs · scripts/lib/
  • streak-checks.mjs · scripts/lib/
  • selftest.mjs · scripts/
  • day-logic.json · templates/contracts/
  • day-logic.schema.json · templates/contracts/
  • split-countdown.test.ts · templates/src/core/countdown/
  • split-countdown.ts · templates/src/core/countdown/
  • date-key.test.ts · templates/src/core/dates/
  • date-key.ts · templates/src/core/dates/
  • activity-stats.test.ts · templates/src/core/stats/
  • activity-stats.ts · templates/src/core/stats/
  • record-completion.test.ts · templates/src/core/stats/
  • record-completion.ts · templates/src/core/stats/
  • stats-summary.test.ts · templates/src/core/stats/
  • stats-summary.ts · templates/src/core/stats/
  • day-streak.test.ts · templates/src/core/streaks/
  • day-streak.ts · templates/src/core/streaks/
  • streak-summary.test.ts · templates/src/core/streaks/
  • streak-summary.ts · templates/src/core/streaks/
  • use-next-day-countdown.test.ts · templates/src/hooks/
  • use-next-day-countdown.ts · templates/src/hooks/
  • use-today.test.ts · templates/src/hooks/
  • use-today.ts · templates/src/hooks/
  • clock-port.ts · templates/src/services/clock/
  • fake-clock.test.ts · templates/src/services/clock/
  • fake-clock.ts · templates/src/services/clock/
  • system-clock-adapter.test.ts · templates/src/services/clock/
  • system-clock-adapter.ts · templates/src/services/clock/
  • package.json · tests/bases/app/
  • tsconfig.json · tests/bases/app/
  • CASES.json · tests/fixtures/check-calendar-code/
  • SUITE.json · tests/fixtures/check-calendar-code/
  • EXPECT.txt · tests/fixtures/check-calendar-code/good/
  • CASES.json · tests/fixtures/check-streaks-and-stats/
  • SUITE.json · tests/fixtures/check-streaks-and-stats/
  • EXPECT.txt · tests/fixtures/check-streaks-and-stats/good/

SKILL.md (first lines)

# Dates, streaks and statistics

Every "today", "due today", streak, countdown and statistic in an Expo app is computed on the user's
local civil-date keys from one injected clock, survives midnight, DST, travel and a clock set back,
is folded by pure tested functions in one write per event, and reaches the screen as a finished
summary on iOS and Android alike; two checkers prove it on the app's own code.

## Rules that must hold

1. **A day is a local civil-date key from `clock.today()`, and day arithmetic is integer maths on
   keys.** `DateKey` `'YYYY-MM-DD'` through `src/core/dates/date-key.ts` (`addDays`, `daysBetween`,
   `isoWeekday`); never `toISOString().slice(0, 10)` or `getUTC*` for a day, never `new Date(key)` or
   `Date.parse(key)`, never a key built by hand from `getFullYear()`/`getMonth()`/`getDate()` outside
   `src/services/clock/` (a picker's `Date` converts through `local-date.ts` there), never a duration
   divided by 86,400,000. Why: the UTC day is wrong for hours every day outside UTC, a date-only string
   parses as UTC midnight, and a local day lasts 23, 24 or 25 hours. (`utc-day`, `date-only-parse`,
   `local-day-key`, `day-ms-arithmetic`, `date-math`)
2. **Only the clock adapter reads the wall clock; day, streak and statistics code receives time as
   values.** `ClockPort` (`nowMs`, `today`, `msUntilNextLocalDay`; architecture-and-boundaries owns
   it) is the one source; no `Date`, `Date.now`, `Math.random`, `Intl.DateTimeFormat` or `toLocale*`
   in day code. Why: one reader makes every rule a millisecond test with the fake clock, and two
   devices never disagree about the day. (`wall-clock`, `clock-countdown`)
3. **"Today" is read on every render, never cached.** `useToday()` re-renders at local midnight (a
   timer armed from `msUntilNextLocalDay()`, at most a minute long) and when the app returns to the
   foreground; handlers call `clock.today()` again; no `useState`, `useRef`, `useMemo` or module
   constant, no effect that copies `today()` into state. Why: a screen left open overnight, or resumed the next morning, otherwise shows
   yesterday. (`today-cached`, `today-hook`)
4. **An event belongs to the local day it started**, captured then and passed into every fold, which
   never re-reads the clock. Why: a session finished after midnight would otherwise count for a day the
   user never started it on. (`completion-record`)
5. **Streaks follow one pure model.** The first event of a day marks it and repeats change nothing; the
   streak lives while the last active day is today or yesterday; a longer gap restarts it at 1; the
   best is kept; a clock set back (or a flight west) never removes a done day or counts one twice; a
   derived streak agrees with the stored one. Grace, freezes and repairs are product data recorded as
   days, never branches in the rule. Why: these are the rules users check by hand.
   (`streak-first-of-day`, `streak-rule`, `streak-clock-back`, `streak-week`, `streak-derive`)
6. **Every number has one source of truth.** Derive it on read from rows the app keeps forever, or, when
   the detail is pruned, capped or deletable, store it as a counter updated by the same fold;
   never count a pruned window (`Object.keys(byDay).length`) and never store a copy of what you derive.
   Why: a 400-day streak must not read 60, and two sources drift. (`streak-prune`, `stats-best`,
   `view-stats`)
7. **A completion updates its row, the streak and the statistics in one transaction, before the
   confirmation shows.** One pure `recordCompletion(progress, completion)`; practice (a sample, a
   tutorial, a replay of a scored day) records nothing; a repeat on an active day counts in the
   statistics and leaves the streak untouched; folds never mutate their input. Why: a kill between two
   writes leaves a streak that contradicts the history. (`completion-record`, `fold-immutable`)
8. **Statistics fold by fixed rules.** Totals, successes, failures and time add up; any failure resets
   the run of successes and the longest is kept; per-day buckets cover a fixed window counted back from
   the newest day; custom counters have stable kebab-case ids (stored keys) and fold by `'sum'` or
   `'max'` over the final committed steps, so undone steps never count. Why: a renamed id orphans a
   stored total, and an undo must not inflate a record. (`stats-counts`, `stats-run`, `stats-days`,
   `stats-counters`)
9. **Screens only format a pure summary.** `buildStreakSummary` / `buildStatsSummary` produce every
   number, mark and flag (seven days oldest first with ISO weekdays, rates as 0..1 with a zero guard,
   whole-unit durations, `isEmpty` for a new user; statistics take the streak counts from
   `streakCountsOf`, never the streak section); components and hooks do no day arithmetic and read
   no raw buckets; labels and digits come from the i18n layer. Why: maths in a view is untested and
   differs between screens. (`day-maths-in-ui`, `view-streak`, `view-stats`)
10. **The time to the next day comes from `msUntilNextLocalDay()`.** `useNextDayCountdown()` with
    whole-minute snapshots and minutes rounded up by the pure `splitCountdown` in
    `src/core/countdown/` (never "0 h 0 min" before midnight); never
    `86_400_000 - nowMs % 86_400_000`. Why: only the adapter knows the zone and DST, and the modulo
    form is the time to midnight in London. (`view-countdown`, `day-maths-in-ui`)

## Workflow

1. **Read the reference for the task:**

   | Task | Read |
   |---|---|
   | "today", due days, midnight, DST, time zones, a date picker, a server date, a countdown, a date off by one | [references/calendar-days.md](references/calendar-days.md) |
   | a streak, a week strip, a daily check-in or daily challenge | [references/streaks.md](references/streaks.md) |
   | statistics, counters, the completion write, a stats screen, "Reset statistics" | [references/statistics.md](references/statistics.md) |
   | a checker failure, the contract, SKIP or NOT APPLICABLE lines, multi-package | [references/checkers.md](references/checkers.md) |

2. **Check the prerequisites.** The clock port, its system adapter and fake, and the composition root
   (`useServices`, `createTestServices`) come from architecture-and-boundaries; `date-key.ts` from
   seeded-determinism-and-golden-vectors; the render helpers (`@test/render-with-providers`) from
   unit-and-component-tests. This skill carries synced copies of the clock and date-key files in
   `templates/src/services/clock/` and `templates/src/core/dates/`: copy them only when the app lacks
   them, and never edit them in the app (the skills that own them change them).
3. **Copy the templates the app needs** to the paths their first lines name: always
   `templates/src/hooks/` (`use-today.ts`, `use-next-day-countdown.ts`, tests) and
   `templates/src/core/countdown/` (the pure `splitCountdown` the countdown hook imports); for streaks
   `templates/src/core/streaks/`; for statistics `activity-stats.ts` and `stats-summary.ts` from
   `templates/src/core/stats/`, plus `record-completion.ts` only when the app has streaks too (it is
   the one file that imports both folders); and `templates/contracts/day-logic.json` with its schema,
   setting a module to `null` when the app does not have it (the checkers refuse a module whose
   dependency is `null`). Each module ships with its test.
4. **Decide what counts.** Write the qualifying rule once in feature logic (`completionOf(task, today)`
   in [examples/complete-task.ts](examples/complete-task.ts)): which event marks a day, what is a
   success, which items are practice, which counters exist (kebab-case ids, `'sum'` or `'max'`).
5. **Wire the write.** The handler reads `clock.today()`, folds with `recordCompletion`, and writes the
   row and the progress in one repository transaction through `updateAndPublish`
   ([examples/complete-task.ts](examples/complete-task.ts); persistence-and-migrations owns the table
   and its migration, state-management-zustand the store and the publish).
6. **Wire the screens.** A model hook reads `useToday()`, the stored sections through selectors and
   `useNextDayCountdown()`, builds the summary and formats it with the i18n layer
   ([examples/use-streak-card-model.ts](examples/use-streak-card-model.ts),
   [examples/stats-cells.ts](examples/stats-cells.ts)); the component renders only what it gets.
7. **Test first for any rule change**, then run
   `npx jest src/core/streaks src/core/stats src/core/dates src/services/clock src/hooks --ci --selectProjects unit`
   (paths before the flag) and `npm run typecheck`.
8. **Run the checks** from the repo root: `node ${CLAUDE_SKILL_DIR}/scripts/check-streaks-and-stats.mjs .`
   and `node ${CLAUDE_SKILL_DIR}/scripts/check-calendar-code.mjs .`; fix every `FAIL` line (file, rule
   and fix are printed) and rerun until both print `RESULT: PASS`.

## Definition of done

- [ ] No day is computed from a UTC string, a `getUTC*` getter, a key parsed with `new Date(key)` or
      milliseconds divided by a day; every exception carries `// not-a-calendar-day: <why>`.
- [ ] Only `src/services/clock/*-adapter.ts` reads the wall clock (`Date.now()`, `new Date()`); day, streak and statistics code takes keys
      and numbers as arguments.
- [ ] Every screen that shows a day reads `useToday()`; handlers read `clock.today()` again; the
      countdown reads `useNextDayCountdown()`.
- [ ] The streak, statistics and completion modules are the templates or pass the same tests;
      `contracts/day-logic.json` names each module or sets it to `null`, never naming a module whose
      dependency is `null`.
- [ ] A completion is one transaction (row + progress) published after the commit, before the

From zakariaf/ReactNative-Skills, MIT.

More from zakariaf/ReactNative-Skills

Signals

Stars
11
Forks
2
Open issues
0
Last commit (this folder)
Oct 17, 2026 (13 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from zakariaf

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get dates-streaks-and-statistics running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying dates-streaks-and-statistics?

CallGet help
Cite this page

dates-streaks-and-statistics skill. Agent Mart. https://agentmart.dev/skills/zakariaf-dates-streaks-and-statistics (last updated 2026-10-24)