The most annoying bugs I’ve dealt with while building a birth-chart engine were not about zodiac signs.
They were about time.
And the deeper I got into it, the more I realized that “birth time” is a much less simple input than it looks on a form.
A local datetime isn't enough
Take this:
1990-05-15 09:30
It looks precise.
But precise where?
Without a timezone, it doesn’t identify an instant.
So the calculation API I use takes both the local datetime and the IANA timezone:
chart = engine.natal(
local_datetime="1990-05-15T09:30:00",
timezone="Europe/London",
latitude=51.5074,
longitude=-0.1278,
)
I prefer an IANA zone like Europe/London over something like UTC+1.
The former describes a real timezone with historical rules.
The latter is just an offset.
DST makes this more interesting
During a daylight-saving fall-back transition, the same local clock time can occur twice.
So a value like:
01:30
may correspond to two different UTC instants.
That means the input looks exact to the user while still being ambiguous to the calculation engine.
There’s a strong temptation to quietly choose one.
I don’t like doing that.
If the input is genuinely ambiguous, I’d rather make the ambiguity explicit.
The same thing happens in the opposite direction during spring transitions.
Some local clock times never existed.
If the clock jumped directly from 01:59 to 03:00, then:
02:30
isn’t a valid local instant.
Again, silently “fixing” it is convenient.
But now the software has changed the user’s data.
Then there’s the bigger problem: no birth time
A lot of people simply don’t know what time they were born.
This creates a product decision.
You can say:
unknown → 12:00 PM
and suddenly everything works.
You get:
- Ascendant
- houses
- house cusps
- Midheaven
The object looks complete.
But none of those values are based on a birth time the person actually supplied.
That bothered me enough that I made unknown birth time a first-class state in the engine.
chart = engine.natal(
local_datetime="1992-11-03",
timezone="Asia/Ho_Chi_Minh",
latitude=21.0285,
longitude=105.8542,
unknown_time=True,
)
The result intentionally omits time-dependent fields.
No fake Rising sign.
No fake houses.
No fake precision.
Incomplete output can be more correct
Developers generally like complete data structures.
Something like:
{
"ascendant": "Cancer",
"house": 7
}
feels satisfying.
This:
{
"ascendant": null,
"house": null
}
feels like a failure.
But if the user doesn’t know the time, the second object is actually saying something more truthful.
The system is distinguishing:
I know the value.
from:
I have a field available where a value could go.
Those aren’t the same thing.
The Moon makes it even more interesting
Even without a birth time, you can still calculate a lot from the date.
But not everything has the same level of certainty.
The Moon moves fast enough that its exact degree — and occasionally even its sign near a boundary — can depend on the time of day.
So unknown-time handling can’t just be:
remove the Ascendant and everything else is exact.
You need to communicate uncertainty where it actually exists.
That’s one reason I like carrying warnings as structured output from the core rather than bolting them onto the UI later.
The UI should be reflecting the calculation state, not inventing its own policy.
This changed how I think about UX
Initially, I thought timezone and missing-time behavior were backend concerns.
Now I think they’re part of the product experience.
When the site tells someone:
Rising sign requires a reliable birth time.
that should not be marketing copy.
It should describe an actual engine constraint.
That connection is important to me.
The best trust messages in software are usually backed by behavior.
Not promises.
This isn’t really an astrology-specific lesson
The same problem appears whenever software works with incomplete or uncertain inputs.
Geolocation.
Financial records.
Sensor data.
AI-extracted structured fields.
User demographics.
There’s always a temptation to turn missing data into complete-looking data because downstream code becomes easier.
Sometimes the better API is the one willing to say:
unknown
I’ve become much more comfortable with that while building this engine.
The Python core is open source here:
https://github.com/getbirthchart-com/gbc-astro-engine
If you work with timezone-heavy applications, the astrology part is probably the least interesting thing in that repo. The time-handling decisions are where a lot of the real engineering lives.
Top comments (0)