aboutsummaryrefslogtreecommitdiff
path: root/internal/mainsa/date.go
blob: 81570da718a75980f4021a70b5768c534ef87ca5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package mainsa

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

const (
	sunoLen  = 8 * time.Hour
	linjaLen = sunosInLinja * sunoLen
	sikeLen  = linjasInSike * linjaLen
	tawaLen  = sikesInTawa * sikeLen
	suliLen  = tawasInSuli * tawaLen
)

const (
	sunosInLinja = 3
	linjasInSike = 3
	sikesInTawa  = 3
	tawasInSuli  = 4

	sunosInSike = sunosInLinja * linjasInSike
	sunosInTawa = sunosInSike * sikesInTawa
	sunosInSuli = sunosInTawa * tawasInSuli
)

//go:generate go tool stringer -type=Tawa

// Tawa is a set of 3 cycles, or a season.
type Tawa int

// The four tawas of a year.
const (
	Kasi Tawa = 1 + iota
	Seli
	Sin
	Lete
)

//go:generate go tool stringer -type=Sike

// Sike is a set of 3 threads, or a month.
type Sike int

// The three sikes.
const (
	Kama Sike = 1 + iota
	Poka
	Monsi
)

//go:generate go tool stringer -type=Nanpa

// Nanpa is a generic toki pona number
type Nanpa int

// Numbers
const (
	Wan Nanpa = 1 + iota
	Tu
	Mute
)

// TenpoNimi is the name of a given time. The remainder time is given as a time.Duration and should be shown in hour:minute format using time constant "3:04".
type TenpoNimi struct {
	Year      int   // suli
	Season    Tawa  // tawa
	Month     Sike  // sike
	Week      Nanpa // linja
	Day       Nanpa // suno
	Remainder time.Duration
}

func awen(d time.Duration) string {
	return fmt.Sprintf("%d:%02d", int(d.Hours()), int(d.Minutes())%60)
}

func (tn TenpoNimi) String() string {
	return strings.ToLower(fmt.Sprintf(
		"suli %d tawa %s sike %s linja %s suno %s awen %s",
		tn.Year,
		tn.Season,
		tn.Month,
		tn.Week,
		tn.Day,
		awen(tn.Remainder),
	))
}

const zeroDateUnix = 1538870400

// YearZero is the arbitrary anchor date from plane 432 to ma Insa year 1 planting season, coming cycle, first week, first day..
var YearZero = time.Unix(zeroDateUnix, 0)

// Errors
var (
	ErrBeforeEpoch = errors.New("mainsa: time before zero date")
)

// At returns the time in ma Insa for a given Go time.
func At(t time.Time) (TenpoNimi, error) {
	if t.Before(YearZero) {
		return TenpoNimi{}, ErrBeforeEpoch
	}

	dur := t.Sub(YearZero)

	var linjas = int(dur / linjaLen) // week => 3 days
	var sikes = int(dur / sikeLen)   // month => 3 weeks
	var tawas = int(dur / tawaLen)   // season => 3 months
	var sulis = int(dur / suliLen)   // year => 4 seasons
	sunosd := float64(dur) / float64(sunoLen)
	sunos := int(sunosd)
	rem := time.Duration((sunosd - float64(sunos)) * float64(sunoLen)).Round(time.Minute)

	result := TenpoNimi{
		Year:      sulis,
		Season:    Tawa(tawas%tawasInSuli) + 1,
		Month:     Sike(sikes%sikesInTawa) + 1,
		Week:      Nanpa(linjas%linjasInSike) + 1,
		Day:       Nanpa(sunos%sunosInLinja) + 1,
		Remainder: rem,
	}

	return result, nil
}