forked from suyashkumar/dicom
-
Notifications
You must be signed in to change notification settings - Fork 1
/
time.go
177 lines (149 loc) · 4.43 KB
/
time.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
166
167
168
169
170
171
172
173
174
175
176
177
package dcmtime
import (
"fmt"
"strings"
"time"
)
// Time holds data for a parsed DICOM time (TM) value.
type Time struct {
// Time is the underlying time.Time value.
Time time.Time
// Precision with which the raw TM value was stored. For instance, a Date value
// with a precision of PrecisionHours ONLY stored the Hour.
Precision PrecisionLevel
}
// DCM converts internal time.Time value to dicom TM string, truncating the output
// to the DA value's Precision.
//
// NOTE: Time zones are ignored in this operation, as TM does not support encoding them.
// Make sure values are converted to UTC before passing if that is the desired output.
func (tm Time) DCM() string {
builder := strings.Builder{}
builder.WriteString(fmt.Sprintf("%02d", tm.Time.Hour()))
if !isIncluded(PrecisionMinutes, tm.Precision) {
return builder.String()
}
builder.WriteString(fmt.Sprintf("%02d", tm.Time.Minute()))
if !isIncluded(PrecisionSeconds, tm.Precision) {
return builder.String()
}
builder.WriteString(fmt.Sprintf("%02d", tm.Time.Second()))
if !isIncluded(PrecisionMS1, tm.Precision) {
return builder.String()
}
builder.WriteRune('.')
builder.WriteString(truncateMilliseconds(tm.Time.Nanosecond(), tm.Precision))
return builder.String()
}
// String implements fmt.Stringer.
func (tm Time) String() string {
builder := strings.Builder{}
builder.WriteString(fmt.Sprintf("%02d", tm.Time.Hour()))
if !isIncluded(PrecisionMinutes, tm.Precision) {
return builder.String()
}
builder.WriteString(fmt.Sprintf(":%02d", tm.Time.Minute()))
if !isIncluded(PrecisionSeconds, tm.Precision) {
return builder.String()
}
builder.WriteString(fmt.Sprintf(":%02d", tm.Time.Second()))
if !isIncluded(PrecisionMS1, tm.Precision) {
return builder.String()
}
builder.WriteRune('.')
builder.WriteString(truncateMilliseconds(tm.Time.Nanosecond(), tm.Precision))
return builder.String()
}
// Holds group indexes for a given source types regex.
type timeRegexGroups struct {
Hours int
Minutes int
Seconds int
Fractal int
}
// Cached info on TM time regex groups.
var timeRegexGroupsTM = timeRegexGroups{
Hours: tmRegexGroupHours,
Minutes: tmRegexGroupMinutes,
Seconds: tmRegexGroupSeconds,
Fractal: tmRegexGroupFractal,
}
// Cached info on DT time regex groups.
var timeRegexGroupsDT = timeRegexGroups{
Hours: dtRegexGroupHours,
Minutes: dtRegexGroupMinutes,
Seconds: dtRegexGroupSeconds,
Fractal: dtRegexGroupFractal,
}
func extractTime(
matches []string,
precisionIn PrecisionLevel,
isTM bool,
) (
hours durationInfo,
minutes durationInfo,
seconds durationInfo,
nanos durationInfo,
precisionOut PrecisionLevel,
err error,
) {
groupIndexes := timeRegexGroupsTM
if !isTM {
groupIndexes = timeRegexGroupsDT
}
hours, err = extractDurationInfo(matches, groupIndexes.Hours, false)
if err != nil {
return hours, minutes, seconds, nanos, precisionOut, err
}
precisionOut = updatePrecision(hours, precisionIn, PrecisionHours, false)
minutes, err = extractDurationInfo(matches, groupIndexes.Minutes, false)
if err != nil {
return hours, minutes, seconds, nanos, precisionOut, err
}
precisionOut = updatePrecision(minutes, precisionOut, PrecisionMinutes, false)
seconds, err = extractDurationInfo(matches, groupIndexes.Seconds, false)
if err != nil {
return hours, minutes, seconds, nanos, precisionOut, err
}
precisionOut = updatePrecision(seconds, precisionOut, PrecisionSeconds, false)
nanos, err = extractDurationInfo(matches, groupIndexes.Fractal, true)
if err != nil {
return hours, minutes, seconds, nanos, precisionOut, err
}
if nanos.PresentInSource {
precisionOut = nanos.FractalPrecision
}
return hours, minutes, seconds, nanos, precisionOut, err
}
// ParseTime converts DICOM TM (time) value to dcmtime.Time.
func ParseTime(tmString string) (Time, error) {
matches := tmRegex.FindStringSubmatch(tmString)
// If no full match is found, return an error
if len(matches) == 0 {
return Time{}, ErrParseTM
}
hours, minutes, seconds, nanos, precision, err := extractTime(
matches, PrecisionFull, true,
)
if err != nil {
return Time{}, err
}
// Return a new time.Time with the given values and UTC encoding.
parsed := time.Date(
// If these are set to 0, they try to roll back to the last
// date that makes sense, so we are going to set all
// of them to 1.
1,
1,
1,
hours.Value,
minutes.Value,
seconds.Value,
nanos.Value,
zeroTimezone,
)
return Time{
Time: parsed,
Precision: precision,
}, nil
}