Explain a supplied Schengen 90-in-180 date plan day by day without making an eligibility decision
  • JavaScript 79.3%
  • CSS 11.2%
  • HTML 9.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-18 11:31:22 +00:00
.forgejo/workflows Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
.github/workflows Release Why180 0.1.0 2026-07-15 19:04:13 +00:00
bin Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
docs Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
examples Release Why180 0.1.0 2026-07-15 19:04:13 +00:00
src Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
test Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
web Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
.gitignore Release Why180 0.1.0 2026-07-15 19:04:13 +00:00
index.html Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
LICENSE Release Why180 0.1.0 2026-07-15 19:04:13 +00:00
package-lock.json Release Why180 0.1.0 2026-07-15 19:04:13 +00:00
package.json Harden Why180 input and CSV boundaries 2026-07-15 19:11:40 +00:00
README.md Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
SECURITY.md Harden Why180 0.1.0 release 2026-07-18 11:31:22 +00:00
styles.css Release Why180 0.1.0 2026-07-15 19:04:13 +00:00

Why180

Why180 explains one narrow calendar calculation: for each day in a planned Schengen short stay, which dates fall inside the moving 180-day lookback and how many supplied stay days are counted.

$ why180 --stay 2014-04-19:2014-04-21:First \
    --plan 2014-06-18:2014-09-11:Second
Why180 rolling-window explanation
All 86 planned days stay at or below 90 in the modeled rolling windows. Peak: 89 days.

The entry-day number is not treated as the answer for an entire trip. Why180 shows every planned day, the first date above the numeric limit if one appears, and any supplied past day that leaves the lookback as the window moves.

The boundary comes first

Why180 is an independent educational planning aid. It does not decide whether the 90-in-180 rule applies to a person, issue legal or immigration advice, authorise entry, read EES records, or replace the official European Commission calculator or a decision by competent authorities.

The model assumes:

  • every supplied past date should be counted under the ordinary 90-in-180 short-stay calculation;
  • entry and exit dates both count as days of stay;
  • all stays are represented as complete YYYY-MM-DD date ranges;
  • every supplied past stay ends before the planned stay begins; and
  • the plan is one continuous future stay.

It does not model shorter limits printed on a visa, entries remaining on a single- or double-entry visa, long-stay visas, residence permits, EU/free- movement family rules, bilateral arrangements, local-border rules, extensions, or any other entry condition. If any of those may matter, this numeric trace is not an eligibility conclusion.

Use it

Requires Node.js 20 or newer.

# Install from the Codeberg package registry after a release is published
npm install --global [email protected] \
  --registry=https://codeberg.org/api/packages/automa-tan/npm/

# Explain dates directly
why180 \
  --stay 2025-09-29:2025-12-13:Autumn \
  --plan 2026-03-17:2026-05-03:Spring

# Read a JSON plan and export the daily trace
why180 --input examples/plan.json --format csv -o trace.csv

# Pipe JSON from another local tool
cat examples/plan.json | why180 --input - --format json

Formats are text, JSON, and CSV. --strict exits with status 1 when the input produces a review warning while still returning the report. Invalid input exits with status 2. CSV export prefixes any formula-leading cell with an apostrophe before quoting it, so labels beginning with =, +, -, or @ remain text when the report is opened in spreadsheet software. Labels containing ASCII control characters are rejected.

The static browser workspace provides the same trace. Run npm run serve, then open the displayed local address. The preview server exposes only the application files it needs with a restrictive content-security policy, no-store, no-referrer, and nosniff headers.

Private reports and bounded input

Travel dates and labels can reveal location history, future plans, visa-sensitive patterns, and household relationships. Text, JSON, and CSV reports retain exact supplied and planned dates, labels, rolling windows, per-trip contributions, warnings, and the first modeled exceedance. Treat them as sensitive travel records.

CLI report files use mode 0600, are created exclusively, refuse overwrite, and are fully written before --strict returns status 1. Common input and output filesystem failures do not echo absolute machine paths. JSON file and stdin input are limited to 1 MiB before parsing. The calculation separately limits input to 100 past trips, 366 days per trip, and one planned trip of at most 366 days.

Why180 has no telemetry, analytics, upload, EES lookup, or other product network behavior. The browser performs the calculation in the page; its official-source links navigate only when a user chooses them.

Library

import { analyzePlan } from "why180";

const result = analyzePlan({
  stays: [{ entry: "2014-04-19", exit: "2014-04-21", label: "First visit" }],
  plan: { entry: "2014-06-18", exit: "2014-09-11", label: "Second visit" }
});

console.log(result.timeline.at(-1).used); // 89

Official basis and source dates

The implementation follows Article 6 of the Schengen Borders Code: the 180-day period preceding each day of stay is considered, and entry and exit are the first and last days of stay. The European Commission's short-stay page describes the same daily lookback and provides the official calculator.

The legal text and Commission calculator page were rechecked 18 July 2026. The dynamic EES notices were last visually checked 15 July 2026:

The EES notice describes limitations in what its online result can reflect during the 20252026 system transition. Those are data/system limitations; the official EES FAQ says the implementation does not change the short-stay rule. See the dated source note before comparing results.

Development

npm ci
npm run test:all
node --test --experimental-test-coverage
npm pack --dry-run

License

MIT