How Falcon calculates night time automatically
Night time is one of those things that seems simple until you try to automate it. The sun goes down, you keep flying, those hours count differently. But figuring out exactly when the sun set relative to where your plane was at any given minute turns out to be a genuinely interesting problem.
Here is how we solved it.
The naive approach (and why it doesn't work)
At first glance you might think: just look up sunset at the departure airport and again at the arrival airport. If the flight crosses sunset, subtract.
The problem is your flight is moving. Departing Auckland at 6 PM local time heading west, the sun is setting behind you while the sky ahead is still light. A straight line between two sunset times ignores the fact that you covered 300 nautical miles in between. At those latitudes sunset can shift by minutes across that distance.
We needed per-minute precision along the actual route.
The algorithm
We use the Solar Position Algorithm, or SPA, developed by the National Renewable Energy Laboratory in the US. It is the same algorithm used by solar panel installers and astronomers. Feed it a time, a latitude, and a longitude, and it tells you exactly where the sun is in the sky.
The implementation comes from the spa Dart package, which is a direct port of the NREL reference code. It accounts for:
- Julian day and century calculations
- Earth's heliocentric longitude and latitude
- Nutation and obliquity of the ecliptic
- Atmospheric refraction at the horizon
- Parallax correction for the observer's position on Earth's surface
It is a lot of math for what boils down to a single question: is the sun more than 6 degrees below the horizon?
Why 6 degrees?
That is the civil twilight threshold. Aviation defines night as the period between the end of evening civil twilight and the beginning of morning civil twilight. Civil twilight ends when the sun's centre is 6 degrees below the horizon. That is the number we use.
You can find the constant at auto_night_calculator.dart line 6:
const _civilTwilightThreshold = -6.0;
How we sample the flight path
Once we have the departure and arrival airports with their coordinates (stored in the airports table as lat/lon), we divide the flight into one-minute segments:
final fraction = i / (numSamples == 0 ? 1 : numSamples);
final lat = depLat + (arrLat - depLat) * fraction;
final lon = depLon + (arrLon - depLon) * fraction;
For each minute we interpolate the position along a straight line between the two airports and ask SPA where the sun is. If the elevation is below -6 degrees, that minute counts as night.
This gives us a per-minute resolution across the whole flight. No guessing, no averaging, just a solid yes or no for every minute you were in the air.
The edge cases
A few things we had to handle:
- Flights shorter than 6 minutes always return zero night time. The sampling just doesn't have enough resolution to be meaningful.
- Missing coordinates short-circuit the calculation entirely. If either airport lacks lat/lon, we skip night calculation and let the pilot enter it manually.
- UTC anchoring is critical. Times are anchored to UTC midnight before adding minutes, otherwise local timezone offsets can corrupt the calculation. This is handled in
auto_night_calculator.dartaround line 80.
Where to find the code
Everything lives in falcon/app/lib/utils/auto_night_calculator.dart. The SPA package dependency is declared in pubspec.yaml as spa: ^1.3.0. The bridge between the UI and the calculator is in flight_time_calculator.dart, and the dialog that pilots interact with lives in widgets/flights/dialogs/calculate_time_dialog.dart.
The result gets stored as an integer of minutes in the night_time column of the flights table.
Why we built it instead of using an API
Sunrise/sunset APIs exist. Most of them are free for low usage. But they introduce a network dependency for something that should work offline. Falcon is offline-first. Every calculation runs on device, no signal required. The SPA algorithm is deterministic and fast. On a modern phone it evaluates a single minute in under a millisecond.
We also did not want to rate-limit our users. If you are logging 500 flights offline on a long trip, you should not have to wait for an API quota to reset.
The result
When you enter a flight in Falcon and both airports have coordinates, the app automatically computes your night time in the background. No manual lookup, no mental arithmetic, no digging through tables. Just the right number, every time.
The code is open source. You can read it, test it, and tell us if we got something wrong. That is the whole point.