Inspiration

I wanted to know what the weather was like on the day I was born. After that, I wanted the same for the other days I remember: what the sky looked like on the days that mattered.

The records exist. The Japan Meteorological Agency (JMA) publishes daily observations from its stations, and some of them go back to the 1870s. The data is free to use with attribution, but it sits behind search forms that nobody opens to look up a birthday.

What it does

That Day's Weather shows the weather that was actually observed on a past day, not a forecast or a model estimate.

  • Save a day with a date and a place in Japan, such as a birthday, a wedding or a trip. The app shows that day's weather (sunny, cloudy, rain or snow) with the high and low.
  • For every saved day, it lines up the same date in past years, one row per year, with counts of sunny and rainy years and the average temperature.
  • The Today tab puts today's live observation next to the same date in past years.
  • The weather is stored with the memory, so it opens offline and never changes after you save it.
  • There's no account or sign-in. You can move your data between iOS and Android by exporting a backup file.

Recent years are free. Plus unlocks older years, every month in the calendar and stats across all years. It's sold as Monthly, Yearly or Lifetime, all through RevenueCat.

How we built it

One Kotlin codebase for both stores. The app uses Compose Multiplatform. The design is fully custom and uses almost no native UI, so writing it once in Compose cost nothing in fidelity. About 15,400 lines of Kotlin live in commonMain. The platform code is roughly 1,100 lines of Kotlin across androidMain and iosMain, plus under 200 lines of Swift for the iOS entry point. expect/actual covers only what has to touch the device: the HTTP engine, the SQLite driver, place-name search through the OS geocoder, file import and export, and ads.

Payments in shared code. RevenueCat's purchases-kmp is called from commonMain. The paywall and the entitlement check live in one implementation, and both platforms read the same result.

A thin backend. A small Cloudflare Worker serves JMA's records to the app. It picks the nearest of 884 stations that record both temperature and sunshine, and caches the results. It holds no user data.

Challenges we ran into

Turning raw observations into "sunny" or "rainy". JMA's human-written weather summary is the most reliable signal, but it stopped at most stations when they were automated, so recent days usually don't have one. The app falls back to precipitation and to the ratio of sunshine hours to possible daylight, with daylight computed from latitude and date. My first rule trusted the summary whenever it existed. That turned out to cover daytime only: in Tokyo, 449 of 6,078 days (7.4%) had rain overnight and were shown as dry. Now precipitation from either source counts, and the written summary only decides between sunny and cloudy.

Picking a station without a distance cap. A simple radius doesn't work. Omiya is 26.5 km from its fallback station, and Ube, a city of 160,000, is 28.2 km from its nearest one. Any cap that rejects Omiya also rejects Ube. So the rule measures the detour from the nearest station, not the absolute distance.

What we learned

Old weather records are patchier than I expected. Tokyo has sunshine data only from 1961, so before that the app can tell rain and snow but not sunny from cloudy. For those days it shows "Unknown" instead of guessing. Okinawa has neither sunshine nor written summaries until around 1970, because it was under US administration. Across all stations, the app can name the weather for 89.3% of the days in the archive.

What surprised me in the other direction is how much has survived. Osaka can tell sunny from cloudy as far back as 1901. Hakodate's temperature record starts in 1872 and Tokyo's in 1875. For many places in Japan, you can still look up the weather from a hundred years ago.

What's next

Places outside Japan. JMA only covers Japan, so the plan is to add Open-Meteo's historical data for everywhere else. The worker already hides where the data comes from, so the app itself shouldn't need to change.

Built With

  • admob
  • android
  • cloudflare-r2
  • cloudflare-workers
  • compose-multiplatform
  • durable-objects
  • firebase
  • ios
  • jetpack-compose
  • kotlin
  • kotlin-multiplatform
  • kotlinx-serialization
  • ktor
  • revenuecat
  • sqldelight
  • swift
  • typescript
Share this project:

Updates

Submission history