forked from 1set/cronrange
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserialize.go
165 lines (149 loc) · 4.15 KB
/
serialize.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package cronrange
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/robfig/cron/v3"
)
var (
strSingleWhitespace = ` `
strDoubleQuotation = `"`
strSemicolon = `;`
strMarkDuration = `DR=`
strMarkTimeZone = `TZ=`
errIncompleteExpr = errors.New("expression should contain at least two parts")
errMissDurationExpr = errors.New("duration is missing from the expression")
errEmptyExpr = errors.New("expression is empty")
errJSONNoQuotationFix = errors.New(`json string should start and end with '"'`)
)
// String returns a normalized CronRange expression, which can be consumed by ParseString().
func (cr CronRange) String() string {
sb := strings.Builder{}
sb.Grow(36)
if cr.duration > 0 {
sb.WriteString(strMarkDuration)
if cr.version == Version2 {
sb.WriteString(cr.duration.String())
} else {
sb.WriteString(strconv.FormatUint(uint64(cr.duration/time.Minute), 10))
}
sb.WriteString(strSemicolon)
sb.WriteString(strSingleWhitespace)
}
if len(cr.timeZone) > 0 {
sb.WriteString(strMarkTimeZone)
sb.WriteString(cr.timeZone)
sb.WriteString(strSemicolon)
sb.WriteString(strSingleWhitespace)
}
sb.WriteString(cr.cronExpression)
return sb.String()
}
// ParseString attempts to deserialize the given expression or return failure if any parsing errors occur.
func ParseString(s string) (cr *CronRange, err error) {
cr, err = internalParseString(s, cronParser)
return
}
func internalParseString(s string, cp cron.Parser) (cr *CronRange, err error) {
if s == "" {
err = errEmptyExpr
return
}
var (
cronExpr, timeZone, durStr string
durMin uint64
parts = strings.Split(s, strSemicolon)
idxExpr = len(parts) - 1
version int
duration time.Duration
)
if idxExpr == 0 {
err = errIncompleteExpr
return
}
PL:
for idx, part := range parts {
part = strings.TrimSpace(part)
// skip empty part
if part == "" {
continue
}
switch {
case idx == idxExpr:
// cron expression must be the last part
cronExpr = part
case strings.HasPrefix(part, strMarkDuration):
durStr = part[len(strMarkDuration):]
if duration, err = time.ParseDuration(durStr); err == nil {
if duration > 0 {
version = Version2
}
}
if durMin, err = strconv.ParseUint(durStr, 10, 64); err != nil {
if version != Version2 {
break PL
}
err = nil
}
case strings.HasPrefix(part, strMarkTimeZone):
timeZone = part[len(strMarkTimeZone):]
default:
err = fmt.Errorf(`expression got unknown part: %q`, part)
}
}
if err == nil {
if len(durStr) > 0 {
if version == Version2 {
cr, err = Create(cronExpr, timeZone, duration, cp)
} else {
cr, err = internalNew(cronExpr, timeZone, time.Duration(durMin)*time.Minute, cp)
}
} else {
err = errMissDurationExpr
}
}
return
}
func ParseStringWithCronParser(s string, cp cron.Parser) (cr *CronRange, err error) {
cr, err = internalParseString(s, cp)
return
}
// MarshalJSON implements the encoding/json.Marshaler interface for serialization of CronRange.
func (cr CronRange) MarshalJSON() ([]byte, error) {
expr := cr.String()
if expr == "" {
return []byte("null"), nil
}
return json.Marshal(expr)
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface for deserialization of CronRange.
func (cr *CronRange) UnmarshalJSON(b []byte) (err error) {
// Precondition checks
raw := string(b)
if raw == "" {
return errEmptyExpr
}
if !(strings.HasPrefix(raw, strDoubleQuotation) && strings.HasSuffix(raw, strDoubleQuotation) && len(raw) >= 2) {
return errJSONNoQuotationFix
}
// Extract and treat as CronRange expression
var newCr *CronRange
if newCr, err = ParseString(raw[1 : len(raw)-1]); err == nil {
*cr = *newCr
}
return
}
// String returns a string representing time range with formatted time values in Internet RFC 3339 format.
func (tr TimeRange) String() string {
sb := strings.Builder{}
sb.Grow(54)
sb.WriteString("[")
sb.WriteString(tr.Start.Format(time.RFC3339))
sb.WriteString(",")
sb.WriteString(tr.End.Format(time.RFC3339))
sb.WriteString("]")
return sb.String()
}