Two Dayfile Fixtures When the Column Set Forks

Two dayfile test URLs exist because column sets fork. Keep a v1 fixture and a v2 fixture; never merge schemas or expose a live /test/ parser.

Back to Cumulus weather software guides

/test/dayfile1.php exists because /test/dayfile.php was not enough. When a daily-summary format grows a new column, or when Cumulus 1 and Cumulus MX disagree about how wide a line must be, a single fixture starts lying. The second URL is the alternate column set: a v1 file and a v2 file, two goldens, two expected maps. It is not a live debugger, not a second copy of the same article, and not current weather.

A public /test/ pair that prints both files in a browser is a security smell and a scientific smell. It leaks how many schemas you still support, and it trains you to accept whichever parse returned HTML. Validation belongs in a private harness that fails when you feed v1 bytes to a v2 map.

What the appendix is remains on dayfile.txt as a daily climate appendix. How to assert row count, max, min, and rain on one pinned generation is /test/dayfile.php. Moving an older appendix toward MX fussiness is the phpdayfile project path. This page is why the test tree forked.

Historical context

Steve Loft’s dayfile started small and accreted fields as gust times, pressure extrema, rain rate, averages, wind run, solar, UV, and later derived indices appeared. The Cumulus Wiki (last checked 13 August 2026) is explicit:

  • Cumulus 1 accepts lines with 15 to 45 fields, because early builds stored 15 and later builds appended columns.
  • Each MX release expects a particular width. From some 3.4.x builds onward MX reads the whole file into a fixed array; from 3.9.x that array drives historic charts. Lines written back match the running build.
  • MX 3.6.x added fields (including a 3.6.12 episode that briefly used 54 columns); from 3.7.0 the documented width settled at 52 fields, including humidex. Treat those numbers as Wiki-reported history, not as a promise that your installed build matches.

A PHP table written against a remembered 2008 column list will read an MX line and put humidex in the rain slot, or read a 15-field C1 line and pad rain with zero because “the rain column is index 20.” Both failures look like weather. They are schema bugs.

Historical TNET named a second test script dayfile1. That is the right instinct: when the contract forks, fork the fixture. Do not rename the first file in place and hope old goldens still mean the same cells.

What “v1 versus v2” means here

Do not treat v1/v2 as marketing versions of Cumulus. Treat them as two documented layouts you still parse.

A practical split many stations actually needed:

| | Fixture A (dayfile test) | Fixture B (dayfile1 test) | |---|---|---| | Typical origin | Cumulus 1 core / short lines | Later C1 or an MX build with a fixed width | | Field count | Short, maybe 15–20 documented columns | Longer; extra gust, solar, derived indices | | Delimiter / decimal | Locale-tolerant C1 habits | MX-consistent separator and decimal | | Missing tail fields | Absent, not zero | Present as empty or as MX missing tokens | | Parser map | schema_c1_core | schema_mx_52 (or the build you pin) |

Your names should include the generation: dayfile-c1-15field.fixture, dayfile-mx-3.7-52field.fixture. dayfile1 as a URL is historical. dayfile1 as a filename in a repository is too vague.

MX documents fields for the installed build in dayfileheader.txt. Pin a copy of that header next to fixture B. Pin a comment block next to fixture A that lists the C1 columns you actually map. Do not share one header between both tests.

Why merging schemas in one test fails

A clever parser that “detects field count and switches” is reasonable in production. A clever test that feeds a mixed file and asserts only that parse() returns an array is not. It will pass while:

  • rain on short rows is read from the wrong index;
  • long rows’ extra columns are ignored, so a later MX-only field never has a golden;
  • a locale semicolon file is split on commas “because the v2 fixture used commas,” and two fields become one.

Keep two entry points in the harness, even if production has one function with a schema argument:

  1. test_dayfile_schema_a opens fixture A, asserts the A map, asserts A’s row count and rain column.
  2. test_dayfile_schema_b opens fixture B, asserts the B map, including at least one column that does not exist in A.

Then add a negative test: pass fixture A to the B map and expect a loud failure (wrong width, missing header contract, or an explicit schema_mismatch). Silent success on that pair is how monthly rainfall becomes a wind run.

Do not invent a hybrid golden that concatenates A’s dates with B’s extra columns filled from imagination. If you need a full-width MX file from short C1 history, that is a migration with documented fill rules (the Wiki’s Create Missing utility exists for that). Migration output is a new fixture, with a new name, reviewed as derived data—not as observations Cumulus stored that night.

Alternate columns that actually bite

When you choose what fixture B must prove, pick columns that move.

  • Rain total versus rain rate. A width change that inserts a rate field before the daily total will shift every later index. Assert both names on B. A has only the total.
  • Temperature mean. Derived daily mean is not max, not min, and not a sensor. If B includes it, the golden must mark it derived.
  • Solar / UV. Absent on many C1 cores. If B includes them, A’s parser must not invent 0 sunshine for those days.
  • Feels-like / humidex. MX additions. They are not dry-bulb. A test named dayfile1 that only repeats max/min/rain from the first article is a clone; include at least one forked field.

Times of extrema use HH:mm on MX and were sloppier on C1. Fixture B should include a row whose time separator is the documented MX colon, and the B parser should reject a letter in that cell. Do not “fix” C1 times inside the B parser.

Two URLs, one operator mistake

The operator mistake is to keep both PHP tests on the public host pointed at the same live dayfile.txt. Then dayfile and dayfile1 always agree, and you learn nothing. Point each test at its own file, or stop using HTTP tests.

The other mistake is to delete fixture A when you “fully migrate.” Old backup folders, a second station, and a user who never upgraded still produce short lines. Drop A only when the parser drops C1 support in the same commit.

A public pair of /test/dayfile*.php scripts also tells an outsider that you have two parsers, where the data directory is, and often the Cumulus flavour. Block the directory. Run the two schemas in CI.

Practical checklist

  • Two fixtures, two schema identifiers, two goldens.
  • Header or comment documents field indexes per fixture; they are allowed to disagree.
  • Negative test: A bytes into B map fails closed.
  • No padding of short rows with zero to satisfy a long map.
  • Migration fills, if any, live in a third, labeled derived file—not in A or B goldens.
  • Neither fixture is presented as this week’s climate.

Unit and date conversion between station-software families is a different workflow (convert1). Do not hide a C-to-F conversion inside a schema-B test. Schema tests check which cell is rain. Conversion tests check a labeled transform.

Modern relevance

Format forks are how long-lived observation archives lose provenance. TNET’s public discussion of schema identity, native units, and quality control is data sources, quality controls, and methodology. Observed daily extrema versus derived fills after a migration are the kind of distinction how the service works keeps in public vocabulary. The Cumulus hub lists the related pages. This /test/ URL stays the fork, not a second live table.

Sources