-
Notifications
You must be signed in to change notification settings - Fork 2
/
format.go
141 lines (114 loc) · 4.84 KB
/
format.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
// Copyright 2015 Rick Beton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package period
import (
"io"
"strings"
"github.com/govalues/decimal"
"github.com/rickb777/plural"
)
// Period converts the period to ISO-8601 string form.
// If there is a decimal fraction, it will be rendered using a decimal point separator
// (not a comma).
func (period Period) Period() ISOString {
return ISOString(period.String())
}
// ISOString converts the period to ISO-8601 string form.
// If there is a decimal fraction, it will be rendered using a decimal point separator.
// (not a comma).
func (period Period) String() string {
buf := &strings.Builder{}
_, _ = period.WriteTo(buf)
return buf.String()
}
// WriteTo converts the period to ISO-8601 form.
func (period Period) WriteTo(w io.Writer) (int64, error) {
aw := adapt(w)
if period == Zero {
_, _ = aw.WriteString(string(CanonicalZero))
return uwSum(aw)
}
if period.neg {
_ = aw.WriteByte('-')
}
_ = aw.WriteByte('P')
writeField(aw, period.years, Year)
writeField(aw, period.months, Month)
writeField(aw, period.weeks, Week)
writeField(aw, period.days, Day)
if period.hours.Coef() != 0 || period.minutes.Coef() != 0 || period.seconds.Coef() != 0 {
_ = aw.WriteByte('T')
writeField(aw, period.hours, Hour)
writeField(aw, period.minutes, Minute)
writeField(aw, period.seconds, Second)
}
return uwSum(aw)
}
func writeField(w usefulWriter, field decimal.Decimal, fieldDesignator Designator) {
if field.Coef() != 0 {
_, _ = w.WriteString(field.String())
_ = w.WriteByte(fieldDesignator.Byte())
}
}
//-------------------------------------------------------------------------------------------------
// Format converts the period to human-readable form using DefaultFormatLocalisation.
// To adjust the result, see the Normalise, NormaliseDaysToYears, Simplify and SimplifyWeeksToDays methods.
func (period Period) Format() string {
return period.FormatLocalised(DefaultFormatLocalisation)
}
// FormatLocalised converts the period to human-readable form in a localisable way.
// To adjust the result, see the Normalise, NormaliseDaysToYears, Simplify and SimplifyWeeksToDays methods.
func (period Period) FormatLocalised(config FormatLocalisation) string {
if period.IsZero() {
return config.ZeroValue
}
parts := make([]string, 0, 7)
parts = appendNonBlank(parts, formatField(period.years, config.Negate, config.YearNames))
parts = appendNonBlank(parts, formatField(period.months, config.Negate, config.MonthNames))
parts = appendNonBlank(parts, formatField(period.weeks, config.Negate, config.WeekNames))
parts = appendNonBlank(parts, formatField(period.days, config.Negate, config.DayNames))
parts = appendNonBlank(parts, formatField(period.hours, config.Negate, config.HourNames))
parts = appendNonBlank(parts, formatField(period.minutes, config.Negate, config.MinuteNames))
parts = appendNonBlank(parts, formatField(period.seconds, config.Negate, config.SecondNames))
return strings.Join(parts, ", ")
}
func formatField(field decimal.Decimal, negate func(string) string, names plural.Plurals) string {
number, _ := field.Float64()
if number < 0 {
return negate(names.FormatFloat(float32(-number)))
}
return names.FormatFloat(float32(number))
}
func appendNonBlank(parts []string, s string) []string {
if s == "" {
return parts
}
return append(parts, s)
}
type FormatLocalisation struct {
// ZeroValue is the string that represents a zero period "P0D".
ZeroValue string
// Negate alters a format string when the value is negative.
Negate func(string) string
// The plurals provide the localised format names for each field of the period.
// Each is a sequence of plural cases where the first match is used, otherwise the last one is used.
// The last one must include a "%v" placeholder for the number.
YearNames, MonthNames, WeekNames, DayNames plural.Plurals
HourNames, MinuteNames, SecondNames plural.Plurals
}
// DefaultFormatLocalisation provides the formatting strings needed to format Period values in vernacular English.
var DefaultFormatLocalisation = FormatLocalisation{
ZeroValue: "zero",
Negate: func(s string) string { return "minus " + s },
// YearNames provides the English default format names for the years part of the period.
// This is a sequence of plurals where the first match is used, otherwise the last one is used.
// The last one must include a "%v" placeholder for the number.
YearNames: plural.FromZero("", "%v year", "%v years"),
MonthNames: plural.FromZero("", "%v month", "%v months"),
WeekNames: plural.FromZero("", "%v week", "%v weeks"),
DayNames: plural.FromZero("", "%v day", "%v days"),
HourNames: plural.FromZero("", "%v hour", "%v hours"),
MinuteNames: plural.FromZero("", "%v minute", "%v minutes"),
SecondNames: plural.FromZero("", "%v second", "%v seconds"),
}