Merge the spoken-plan zone fix (#259)
FormatRU printed the raw instant, so it read the plan's hours in whatever zone the value carried. The live case is the rest-of-day path: 'что дальше?' rebuilds a morning.Plan off ipc.DayPlan, and nothing there had put the instants in the asking clock's frame. It is the only producer of a Plan that skips BuildPlan, which has localized events and reminders since it was written. formatTime, the answer to 'когда я это сделал?', had the same shape on a fact's Ts, which is UTC out of the store. Each test builds its instants three hours off the machine's zone, so they fail under TZ=UTC as well. (V-614)
This commit is contained in:
@@ -159,7 +159,15 @@ func (p Plan) After(now time.Time) Plan {
|
||||
// FormatRU renders the plan as maven says it. Feminine self-reference,
|
||||
// informal address, no pet names — and no exhortation: she reads the day back,
|
||||
// she does not tell him to get on with it.
|
||||
//
|
||||
// Every hour is read in the plan's own zone — Date's, which BuildPlan sets from
|
||||
// the asking clock. Printed raw, an hour read whatever zone its instant arrived
|
||||
// in: an event or a reminder comes off the store as UTC, while a checklist line
|
||||
// is built local, so one spoken sentence named two zones. This is the voice
|
||||
// path, so that is what the owner heard (V-614); the same defect on the two web
|
||||
// pages was V-612.
|
||||
func (p Plan) FormatRU() string {
|
||||
zone := p.Date.Location()
|
||||
if len(p.Items) == 0 {
|
||||
// "что дальше?" after the last item of the day. The day was not empty,
|
||||
// it is over, and saying it was empty is a false statement about a day
|
||||
@@ -171,7 +179,7 @@ func (p Plan) FormatRU() string {
|
||||
}
|
||||
parts := make([]string, len(p.Items))
|
||||
for i, it := range p.Items {
|
||||
line := fmt.Sprintf("%s — %s", it.At.Format("15:04"), it.Text)
|
||||
line := fmt.Sprintf("%s — %s", it.At.In(zone).Format("15:04"), it.Text)
|
||||
if it.Uncertain {
|
||||
line = say.S(say.PlanUncertain, map[string]string{"line": line})
|
||||
}
|
||||
|
||||
@@ -151,6 +151,37 @@ func TestPlanFormatRU(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// One spoken sentence names one clock. An event and a reminder come off the
|
||||
// store as UTC and a checklist line is built in the asking clock's zone, so the
|
||||
// raw Format printed the two halves of one sentence in two zones (V-614). The
|
||||
// zones here are three hours off whatever this machine runs in, so the test
|
||||
// tells "read in his clock" apart from "read in the zone the instant arrived
|
||||
// in" under TZ=UTC as well.
|
||||
func TestPlanFormatRUReadsEveryHourInThePlansZone(t *testing.T) {
|
||||
_, off := time.Now().Zone()
|
||||
away := time.FixedZone("away", off+3*60*60)
|
||||
|
||||
stored := time.Date(2026, 8, 3, 14, 0, 0, 0, time.UTC)
|
||||
p := Plan{
|
||||
Date: time.Date(2026, 8, 3, 0, 0, 0, 0, away),
|
||||
Items: []PlanEntry{
|
||||
{At: stored, Text: "Планёрка", Kind: PlanEvent},
|
||||
{At: planAt(time.Date(2026, 8, 3, 0, 0, 0, 0, away), 10, 30),
|
||||
Text: "утро — осталось: витамины", Kind: PlanChecklist},
|
||||
},
|
||||
}
|
||||
got := p.FormatRU()
|
||||
if want := stored.In(away).Format("15:04"); !strings.Contains(got, want) {
|
||||
t.Errorf("the event is not read in the plan's zone (%s): %q", want, got)
|
||||
}
|
||||
if bad := stored.Format("15:04"); strings.Contains(got, bad) {
|
||||
t.Errorf("the event is read in the zone it was stored in (%s): %q", bad, got)
|
||||
}
|
||||
if !strings.Contains(got, "10:30") {
|
||||
t.Errorf("the checklist line moved zone: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanAfter(t *testing.T) {
|
||||
p, now := planFixture(t)
|
||||
rest := p.After(planAt(now, 11, 0))
|
||||
|
||||
Reference in New Issue
Block a user