oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package calc
2
3import "time"
4
5func init() {
6 registerClock("NOW", fnNow)
7 registerClock("TODAY", fnToday)
8}
9
10// excelEpoch is day zero in spreadsheet date-serial numbering — Dec 30,
11// 1899, the convention both Excel and LibreOffice Calc use by default.
12// A date is not a distinct Value kind in this engine: it is a plain
13// number that a date number format later displays as a calendar date —
14// the same "currency is a format, not a type" rule (global constraint)
15// extended to dates. Cell values are IEEE doubles end to end, so this
16// applies here exactly as everywhere else: no rounding at this boundary.
17var excelEpoch = time.Date(1899, time.December, 30, 0, 0, 0, 0, time.UTC)
18
19// dateSerial converts t to spreadsheet date-serial form: whole days since
20// excelEpoch, with the time of day as a fraction of one day.
21func dateSerial(t time.Time) float64 {
22 return t.UTC().Sub(excelEpoch).Hours() / 24
23}
24
25// fnNow returns the pass's pinned instant (env.Now()) as a date serial
26// with a fractional day for the time of day. It must call env.Now() and
27// nothing else — see corpus_test.go's TestClockIsPinnedWithinAPass,
28// written specifically to catch a version of this function that reached
29// past env for a live clock instead of the one the pass pinned.
30//
31// NOW is volatile — its answer differs on every recalculation pass by
32// design — so it is never checked against testdata/corpus/formulas.json
33// (see testdata/corpus/volatile-excluded.txt); nothing about that
34// exclusion weakens the guarantee this function itself must hold, that
35// two calls within ONE pass agree.
36func fnNow(env Env, args []Value) Value {
37 if len(args) != 0 {
38 return errValue(ErrValue)
39 }
40 return Value{Kind: KindNum, Num: dateSerial(env.Now())}
41}
42
43// fnToday is fnNow with the time of day truncated to midnight.
44func fnToday(env Env, args []Value) Value {
45 if len(args) != 0 {
46 return errValue(ErrValue)
47 }
48 t := env.Now().UTC()
49 midnight := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC)
50 return Value{Kind: KindNum, Num: dateSerial(midnight)}
51}
52