- JavaScript 79.3%
- CSS 11.2%
- HTML 9.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| .github/workflows | ||
| bin | ||
| docs | ||
| examples | ||
| src | ||
| test | ||
| web | ||
| .gitignore | ||
| index.html | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SECURITY.md | ||
| styles.css | ||
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-DDdate 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:
- Schengen Borders Code, Regulation (EU) 2016/399, Article 6
- European Commission short-stay calculator page, dated 27 October 2025
- Official published calculation example, including 19–21 April 2014 plus 18 June–11 September 2014 = 89 days
- EES “Check how long you can stay” notice, checked 15 July 2026
The EES notice describes limitations in what its online result can reflect during the 2025–2026 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