-
Notifications
You must be signed in to change notification settings - Fork 19
/
age.go
148 lines (125 loc) 路 4.25 KB
/
age.go
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package gedcom
import (
"fmt"
"time"
)
// Year is an approximation for the duration of a year.
//
// This should not be used in calculations that require more than month-level
// precision.
//
// A year is approximated at 365.25 to take into account leap years.
const Year = time.Duration(float64(time.Hour) * 24 * 365.25)
// Age represents an age of an individual at a point in time.
//
// An Age is often paired with another Age to allow a range of time. For example
// an event could be an absolute like a christening, or it could represent a
// range such as the start and end age during a residence that spans multiple
// years.
//
// You should avoid initializing the struct directly, and instead use the most
// appropriate constructor.
type Age struct {
// Age is the duration of time from the point where the birth was
// determined.
//
// The age may be an estimation, see IsEstimate. The age might also be
// greater than the maximum living age of the individual, see IsAfterDeath.
Age time.Duration
// IsEstimate will be true when there was no birth event or the birth event
// is a range (like "Between 1943 to 1947").
IsEstimate bool
// IsKnown will be true if the age can be determined (either as exact or an
// estimation). The age cannot be determined if no estimated birth date is
// found or an event does not contain a usable date.
//
// When IsKnown is false you should not use the value of Age.
IsKnown bool
// See the AgeConstraint constants for a full explanation.
Constraint AgeConstraint
}
// NewUnknownAge returns a value that represents an age that is not known.
func NewUnknownAge() Age {
return Age{}
}
// NewAge will initialise a new known exact or estimate age age with the
// provided attributes.
func NewAge(age time.Duration, isEstimate bool, constraint AgeConstraint) Age {
return Age{
Age: age,
IsKnown: true,
IsEstimate: isEstimate,
Constraint: constraint,
}
}
// NewAgeWithYears creates an age by using the number of years.
//
// This is not precise as a year is averaged out at 365.25 days (see Year
// constant) but is useful when only whole years matter.
func NewAgeWithYears(years float64, isEstimate bool, constraint AgeConstraint) Age {
year := float64(Year)
age := time.Duration(years * year)
return NewAge(age, isEstimate, constraint)
}
// IsAfter is true if the right age is after (greater than) the left age. If
// both ages are equal this will return false.
func (age Age) IsAfter(age2 Age) bool {
return age.Age > age2.Age
}
// Years returns the approximate amount of years.
//
// The value is approximate because a year is both a variable amount of time and
// has to be combined with a point in time to be practical.
//
// Years can be used when 1 month resolution is enough. However, it's not
// recommended to use this for calculations. Instead use the Age value.
func (age Age) Years() float64 {
// ghost:ignore
return float64(age.Age) / float64(Year)
}
func constraintBetweenAges(estimatedBirthDate, estimatedDeathDate Date, age time.Duration) AgeConstraint {
if age < 0 {
return AgeConstraintBeforeBirth
}
if estimatedBirthDate.IsZero() {
return AgeConstraintUnknown
}
if estimatedDeathDate.IsZero() {
return AgeConstraintUnknown
}
birthTime := estimatedBirthDate.Time()
deathTime := estimatedDeathDate.Time()
if age > deathTime.Sub(birthTime) {
return AgeConstraintAfterDeath
}
return AgeConstraintLiving
}
// String returns an age in one of the following forms:
//
// unknown -- if IsKnown is false
// 20y -- living age is very close to the whole year
// 20y 5m -- living age with 5 months
// ~ 25y -- if IsEstimated is true
// ~ 22y 11m -- same as above
//
// String will not consider the age constraint.
//
// A special case is 0. When the age duration is zero (or less than half of one
// month) the estimate marker ("~") will not be shown because this would not
// make sense.
func (age Age) String() string {
if !age.IsKnown {
return "unknown"
}
years := int(age.Years())
// ghost:ignore
months := int((age.Years() - float64(years)) * 12)
estimateSign := ""
if age.IsEstimate && years+months > 0 {
estimateSign = "~ "
}
if months == 0 {
return fmt.Sprintf("%s%dy", estimateSign, years)
}
return fmt.Sprintf("%s%dy %dm", estimateSign, years, months)
}