magrittr. Every %>% is now the base pipe |>, and
magrittr is gone from DESCRIPTION.%>% call was transformed the
way R's parser transforms |>, and the resulting tree was required to match
the tree parsed from the rewritten file. A file whose trees disagreed was left
untouched and converted by hand instead.%>% is no longer exported. R/utils-pipe.R existed only to re-export it and
is deleted. Code that relied on library(cstidy) for the pipe must use |>
or attach magrittr itself.R/csfmt_rts_v2.R held 1091 code lines, and the shared CI workflow fails any
R/*.R file over 1000. The methods now live in four sibling files:
csfmt_rts_v2_heal.R, csfmt_rts_v2_set.R, csfmt_rts_v2_structure.R and
csfmt_rts_v2_expand_time.R.csfmt_rts_v2.R keeps the format definition. csfmt_rts_v3.R reads
formats$csfmt_rts_data_v2$unified while it loads, and R sources R/ in C
collation order.DESCRIPTION Authors@R now declares that holder with role = "cph".
It declared no copyright holder at all, and neither did any other package
in the fleet. Nothing in R CMD check reports that.CLAUDE.md now carries a Licensing section, so the year gets checked
rather than silently ageing.Documentation only. No executable line changed. vignettes/cstidy.Rmd holds the
pkgdown "Get started" slot, and it opened on a # csfmt_rts_data_v2 heading, so
the package's front door taught the deprecated format first. An overview now
sits in front of that heading. Every existing section is unchanged and unmoved.
R/, zero in README.md, zero in index.md, zero in NEWS.md and
zero in the three vignettes. No claim changed. Two garbled sentences in this
file were repaired without changing what they claim. The first read "So v3 is
an analysis storable but unvalidated by csdb". The second read "has no v3
equivalent, so The validator is an ordinary argument". The three measured v2
and v3 claims keep their bases. Counted on the unified set, v2 derives 18
columns and v3 derives 11. v3 is weekly-only. csdb can store a v3 table and
cannot check one.set_csfmt_rts_data_v1(), set_csfmt_rts_data_v2() and
set_csfmt_rts_data_v3(), each on its own copy of generate_test_data(),
give c("csfmt_rts_data_vN", "data.table", "data.frame") for N = 1, 2 and 3.
inherits(d3, "csfmt_rts_data_v2") is FALSE. No format inherits from
another.length(attr(x, "format_unified")), v2
is 18 and v3 is 11. Counted on an object, a generate_test_data() table set
to v2 has 19 columns and still has 19 after set_csfmt_rts_data_v3(), with
nothing lost and nothing gained. What a move to v3 costs is the guarantee,
not the data.remove_class_csfmt_rts_data() before the v3 setter, and
the comment says why. set_csfmt_rts_data_v3() applied to a table that
still carries the csfmt_rts_data_v2 class returns a csfmt_rts_data_v2:
healing calls [, no [.csfmt_rts_data_v3 exists, so [.csfmt_rts_data_v2
runs and re-asserts its own class. Measured, not changed — no file in R/ was
touched. With create_unified_columns = FALSE, heal = FALSE no [ runs and
the class stacks as
c("csfmt_rts_data_v3", "csfmt_rts_data_v2", "data.table", "data.frame").isoyearweek table set to v3
comes back with isoyear, isoweek, season, seasonweek and date all
filled. A two-row daily table carrying granularity_time = "date" set to v3
keeps both date values and gets NA in isoyear, isoweek, isoyearweek,
season and seasonweek. The same daily input set to v2 derives all of them.csdb is
neither an Import nor a Suggest of cstidy, so no vignette chunk may call it.
Checked against csdb at commit 7b4f7cc: its NAMESPACE exports
validator_field_types_csfmt_rts_data_v1(),
validator_field_types_csfmt_rts_data_v2(),
validator_field_contents_csfmt_rts_data_v1() and
validator_field_contents_csfmt_rts_data_v2(), and the string
csfmt_rts_data_v3 appears nowhere in that repository. So a v3 table is
storable by csdb but unvalidated by it. That is the main limit on the
direction.csalert is named as the downstream consumer, verified by running it.
csalert::ens_collapse(ens, probs = 0.5, heal = TRUE) returns class
c("csfmt_rts_data_v3", "data.table", "data.frame"); the same call with
heal = FALSE returns a plain data.table.R/
contains four warning() calls, and the smart-assignment section deliberately
triggers one of them by setting two time columns at once. Two things are
true, and both are written. No .Deprecated(), .Defunct() or
lifecycle::deprecate_warn() appears anywhere in R/.
set_csfmt_rts_data_v1() and set_csfmt_rts_data_v2() on a
generate_test_data() table emit no warning and no message.Documentation only. No executable line changed. csfmt_rts_data_v3 is now the
target format, so csfmt_rts_data_v1 and csfmt_rts_data_v2 are marked
deprecated in roxygen.
The mark is a signpost, not an alarm. No .Deprecated(), no
lifecycle::deprecate_warn(), no warning(), no message(), and no new
dependency. Every deprecated function behaves exactly as it did in 2026.8.4
and prints nothing extra. norsyss.cs9 runs on csfmt_rts_data_v2 nightly;
a run-time warning would flood its logs for something nobody can act on yet.
What carries the mark. One @section Deprecated: block, worded the same
way throughout, on: csfmt_rts_data_v1(), set_csfmt_rts_data_v1(),
heal_time_csfmt_rts_data_v1(), csfmt_rts_data_v2(),
set_csfmt_rts_data_v2(), heal_time_csfmt_rts_data_v2(), and the v1 and v2
S3 methods for [, summary(), unique_time_series(), expand_time_to(),
identify_data_structure() and plot(). Two titles gained a
"(deprecated)" suffix: set_csfmt_rts_data_v2 and
heal_time_csfmt_rts_data_v2. They match the two v1 titles that already had
one, so the reference index reads the same way down the list.
Nothing on v3 is deprecated: csfmt_rts_data_v3(),
set_csfmt_rts_data_v3(), unique_time_series.csfmt_rts_data_v3() and
remove_class_csfmt_rts_data() are unmarked. The
identify_data_structure() generic and its tbl_Microsoft SQL Server
method are unmarked too; only the csfmt_rts_data_v2 method carries the
note, and the note says so.
v1 to v2 is lossless on the two properties measured. All 16 of v1's
unified columns are among v2's 18, the extra two being isoquarter and
isoyearquarter. heal_time_csfmt_rts_data_v2() accepts every
granularity_time heal_time_csfmt_rts_data_v1() accepts ("date",
"isoyearweek", "isoyear") and "season" as well. csdb exports a field-type
validator for both formats.
v2 to v3 has limits, and the v2 note states each one with the base it was measured on. Three caveats, all measured:
names(attr(x, "format_unified")), the columns each format creates when
the input lacks them. The seven columns in v2's unified set and not in
v3's are granularity_time, border, isoquarter, isoyearquarter,
calyear, calmonth and calyearmonth. Deriving is not dropping.
set_csfmt_rts_data_v3() removes no column. An existing v2 object
converted to v3 keeps every column it had, granularity_time and border
included. v3 healing still refreshes isoquarter and isoyearquarter
when the columns are present. The next base is an object, not the unified
set. A v2 table with one value column, whose input carried
granularity_time and border, has 19 columns. Its v3 conversion has 19
too. The same input without those two columns gives 19 under v2 and 12
under v3. What a move to v3 costs is the guarantee, not the data.heal.csfmt_rts_data_v3() derives from isoyearweek alone, and the
isoyearweek lookup holds no calendar values at all. calyear, calmonth
and calyearmonth are NA for all 7829 rows of the isoyearweek lookup,
under v2 as much as under v3. Only the date lookup carries them, in all
54789 of its rows. So aggregation by calendar month or calendar year needs
a v2 table healed on date. A daily table converted to v3 keeps its
date values and gets nothing else healed.csdb cannot CHECK a v3 table, though it can store one. csdb exports
validator_field_types_csfmt_rts_data_v1() and
validator_field_types_csfmt_rts_data_v2(), and has no v3 equivalent. The
validator is an ordinary argument to csdb::DBTable_v9$new(), so passing
validator_field_types_blank() stores a v3 fine. What is missing is the
column check, not the storage.So v2 is deprecated as a direction of travel, not because a finished replacement exists. Continue with v2 in any of these three cases:
heal_time_csfmt_rts_data_v2() is deprecated as a public entry point
only. heal.csfmt_rts_data_v3() calls it to derive v3's time columns, so
it is still the healing engine behind set_csfmt_rts_data_v3(). Its note
says that rather than implying it is on the way out.
Documentation only. The R files were also reformatted. Comparing the parsed code against the previous version, every remaining difference is a brace added around a single-statement body, so no function changed behaviour.
Runnable examples on the two help pages that had none. They cover four
exports: set_csfmt_rts_data_v1(), csfmt_rts_data_v1(),
set_csfmt_rts_data_v3() and csfmt_rts_data_v3(). Each of the two examples
runs the by-reference setter and the copying constructor side by side. The
example shows the difference between them. It does not just describe it. The pipe operator's
page still has no example; it is a re-export marked @keywords internal.
Every export now resolves to a help page carrying \seealso. Where a
vignette runs the function in a code chunk, the \seealso names that
vignette. Where no vignette runs it, the \seealso says so plainly. No
vignette runs heal_time_csfmt_rts_data_v1(),
heal_time_csfmt_rts_data_v2(), set_csfmt_rts_data_v1(),
csfmt_rts_data_v1(), set_csfmt_rts_data_v3(), csfmt_rts_data_v3() or
csfmt_rts_data_v2(). The Benchmarks vignette names
set_csfmt_rts_data_v1() in a heading, but it is precompiled and carries no
runnable code.
Two new @family groups. Both are contractual, not topical, so the
members really are interchangeable at a call site.
csfmt format converters — set_csfmt_rts_data_v1(),
set_csfmt_rts_data_v2(), set_csfmt_rts_data_v3(). All three take
(x, create_unified_columns = TRUE, heal = TRUE), stop unless x is a
data.table, and return x invisibly with that version's class prepended.time healing lookups — heal_time_csfmt_rts_data_v1() and
heal_time_csfmt_rts_data_v2(). Both take
(x, cols, granularity_time = "date") and return a data.table. They are
not identical: v1 accepts a granularity_time of "date", "isoyearweek" or
"isoyear", while v2 accepts those plus "season" and can also return
isoquarter and isoyearquarter.The constructor/setter pairing (csfmt_rts_data_v3() against
set_csfmt_rts_data_v3()) was considered and rejected. The two share formals
and return class but not semantics: the setter modifies its argument, the
constructor leaves it untouched. Swapping one for the other silently changes
whether the caller's table is rewritten. They already share one help page.
Corrections to help pages that described the code wrongly. Each was checked by running the function.
generate_test_data() was documented as returning a csfmt_rts_data_v2.
It returns a plain data.table; you pass it to a setter yourself.expand_time_to() was documented as returning a csfmt_rts_data_v2. The
class is dropped from the result.remove_class_csfmt_rts_data() was documented as having no return value.
It returns x invisibly, which is what lets two vignettes use it inside a
pipe.set_csfmt_rts_data_v1() and set_csfmt_rts_data_v2() each carried two
contradictory \value paragraphs, one of them claiming no return value.
The false paragraph is gone from both.data.frame. It does not; both stop unless x is a data.table.csfmt_rts_data_v2 was described as having 16 unified columns while
listing 18. It has 18.csfmt_rts_data_v3 was described as having the same unified columns as
csfmt_rts_data_v2. It has 11 of that format's 18.heal now says it needs granularity_time on
set_csfmt_rts_data_v1() and set_csfmt_rts_data_v2(). Time healing reads
granularity_time to decide which time column the others derive from. A
table carrying isoyearweek but no granularity_time comes back with its
time columns still NA. set_csfmt_rts_data_v3() is weekly-only and does
not need it.
pkgdown/ and Rplots.pdf added to .Rbuildignore.
The season boundary now matches cstime. The internal healing tables
put the season cut-point at isoweek 30; cstime puts it at isoweek 35
(cstime::isoyearweek_to_season_c("2020-34") is 2019/2020, "2020-35" is
2020/2021). The tables have been regenerated, so about five weeks per year
change season: 2020-30 heals to 2019/2020 where it previously gave
2020/2021. This affects set_csfmt_rts_data_v2() and, through
heal_time_csfmt_rts_data_v2(), the new set_csfmt_rts_data_v3(). Anyone
upgrading from 2025.10.27 should expect season-grouped results (seasonal
baselines, by-season summaries) to shift accordingly.
The version string 2025.10.27 is ambiguous about this. The regeneration
landed two days after that version was submitted to CRAN, and the version was
not bumped at the time. So a 2025.10.27 installed from CRAN uses the
isoweek-30 boundary. A 2025.10.27 installed from GitHub main after 2025-10-29
uses isoweek 35. packageVersion() cannot tell them apart. If you need to know
which one you have, test it rather than trust the version:
cstidy::set_csfmt_rts_data_v3(data.table::data.table(isoyearweek = "2020-34", location_code = "nation_nor"))$season returns 2019/2020 under the current
(isoweek 35) boundary and 2020/2021 under the old one.
Inclusion of csfmt_rts_data_v3: a slim, weekly-only clean csfmt format (11
columns) with an explicit heal step and a content-hash time_series_id.
isoyear, isoweek, season, seasonweek, date, granularity_geo and
country_iso3 are all healed from isoyearweek. This is the format that
csalert's collapse(heal = TRUE) targets.
print.csfmt_rts_data_v1.cstidy::set_csfmt_rts_data_v1 is now 1.5x faster due to using the upgraded cstime package that now uses binary searches. An 80 million row dataset is now processed in 2 minutes, instead of 3.cstidy::set_csfmt_rts_data_v1 is now 6x faster due to using the upgraded cstime package that now uses csutil::apply_fn_via_hash_table. An 80 million row dataset is now processed in 3 minutes, instead of 20.