Skip to content

Goals and curves

tilttek/writing-goals

A goal

$goal = new Goal(
    'novel-2026',                         // the goal's identifier
    ResourceReference::of('member', '44'), // its author
    50_000,                               // the target
    Day::fromString('2026-11-01'),        // the first day, included
    Day::fromString('2026-11-30'),        // the deadline, included
    Unit::Words,                          // or Unit::Pages
    Curve::linear(),                      // the default
    WritingDays::everyDay(),              // the default
    'America/Montreal',                   // the author's timezone, UTC by default
);
ArgumentAccepted
$id1 to 128 letters, digits, _, ., : or -, starting with a letter or a digit
$target1 to 1 000 000 000
$start, $deadlineDays from 1 to 3 660 days apart, both included, with one writing day at least between them
$timezoneAny timezone PHP knows

writingDaysOfPlan() lists the goal's writing days in order. A goal is a value: to extend a deadline or change a curve, save a new Goal with the same identifier; what was logged stays.

Days

A Day is a date without a time: Day::fromString('2026-11-01'), Day::of($moment, $timezone) for the date a moment falls on, plus(), daysUntil(), isBefore(), isAfter(), equals(), weekday() — 1 for Monday to 7 for Sunday — and toString(). Two days compare as two integers, the days since 1970-01-01. The author's today is the date in the goal's timezone: at 23:30 in Montreal, a count goes to that day, not to the next day in UTC.

Writing days

WritingDays::everyDay();
WritingDays::on(1, 2, 3, 4, 5);                                    // Monday to Friday
WritingDays::on(1, 3, 5)->except(Day::fromString('2026-11-11'));   // less a date set aside

A day that is not a writing day has no quota; an author sets aside up to 366 dates.

Curves

A curve gives each writing day a weight by its place among all the writing days of the plan:

CurveWeights over 4 writing daysFor
Curve::linear()1, 1, 1, 1The same share each day
Curve::frontLoaded()7, 6, 5, 4A strong start, a lighter end; the first day weighs about twice the last
Curve::backLoaded()4, 5, 6, 7A gentle start that builds up
Curve::custom([1, 0, 1])Stretched over the daysThe author's own shape: here, a pause in the middle third

A custom curve has 1 to 366 weights of 0 to 1 000, one above 0 at least. The weights are stretched over the writing days: with three weights and six days, each weight covers two days. A day of weight 0 asks for nothing while other days are left; when only days of weight 0 remain, they share what remains equally, so a goal never asks for the impossible.