/src/dcmtk/dcmdata/libsrc/dcvrda.cc
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (C) 1994-2026, OFFIS e.V. |
4 | | * All rights reserved. See COPYRIGHT file for details. |
5 | | * |
6 | | * This software and supporting documentation were developed by |
7 | | * |
8 | | * OFFIS e.V. |
9 | | * R&D Division Health |
10 | | * Escherweg 2 |
11 | | * D-26121 Oldenburg, Germany |
12 | | * |
13 | | * |
14 | | * Module: dcmdata |
15 | | * |
16 | | * Author: Gerd Ehlers, Andreas Barth, Joerg Riesmeier |
17 | | * |
18 | | * Purpose: Implementation of class DcmDate |
19 | | * |
20 | | */ |
21 | | |
22 | | #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ |
23 | | |
24 | | #include "dcmtk/dcmdata/dcvrda.h" |
25 | | #include "dcmtk/dcmdata/dcvrtm.h" |
26 | | #include "dcmtk/ofstd/ofstd.h" |
27 | | #include "dcmtk/dcmdata/dcmatch.h" |
28 | | |
29 | | |
30 | | // ******************************** |
31 | | |
32 | | |
33 | | DcmDate::DcmDate(const DcmTag &tag, |
34 | | const Uint32 len) |
35 | 0 | : DcmByteString(tag, len) |
36 | 0 | { |
37 | 0 | setMaxLength(10); |
38 | 0 | setNonSignificantChars("\\"); |
39 | 0 | } |
40 | | |
41 | | |
42 | | DcmDate::DcmDate(const DcmDate &old) |
43 | 0 | : DcmByteString(old) |
44 | 0 | { |
45 | 0 | } |
46 | | |
47 | | |
48 | | DcmDate::~DcmDate() |
49 | 0 | { |
50 | 0 | } |
51 | | |
52 | | |
53 | | DcmDate &DcmDate::operator=(const DcmDate &obj) |
54 | 0 | { |
55 | 0 | DcmByteString::operator=(obj); |
56 | 0 | return *this; |
57 | 0 | } |
58 | | |
59 | | |
60 | | OFCondition DcmDate::copyFrom(const DcmObject &rhs) |
61 | 0 | { |
62 | 0 | if (this != &rhs) |
63 | 0 | { |
64 | 0 | if (rhs.ident() != ident()) return EC_IllegalCall; |
65 | 0 | *this = OFstatic_cast(const DcmDate &, rhs); |
66 | 0 | } |
67 | 0 | return EC_Normal; |
68 | 0 | } |
69 | | |
70 | | |
71 | | // ******************************** |
72 | | |
73 | | |
74 | | DcmEVR DcmDate::ident() const |
75 | 0 | { |
76 | 0 | return EVR_DA; |
77 | 0 | } |
78 | | |
79 | | |
80 | | OFCondition DcmDate::checkValue(const OFString &vm, |
81 | | const OFBool oldFormat) |
82 | 0 | { |
83 | 0 | OFString strVal; |
84 | | /* get "raw value" without any modifications (if possible) */ |
85 | 0 | OFCondition l_error = getStringValue(strVal); |
86 | 0 | if (l_error.good()) |
87 | 0 | l_error = DcmDate::checkStringValue(strVal, vm, oldFormat); |
88 | 0 | return l_error; |
89 | 0 | } |
90 | | |
91 | | |
92 | | // ******************************** |
93 | | |
94 | | |
95 | | OFCondition DcmDate::getOFString(OFString &stringVal, |
96 | | const unsigned long pos, |
97 | | OFBool normalize) |
98 | 0 | { |
99 | 0 | OFCondition l_error = DcmByteString::getOFString(stringVal, pos, normalize); |
100 | 0 | if (l_error.good() && normalize) |
101 | 0 | normalizeString(stringVal, !MULTIPART, !DELETE_LEADING, DELETE_TRAILING); |
102 | 0 | return l_error; |
103 | 0 | } |
104 | | |
105 | | |
106 | | // ******************************** |
107 | | |
108 | | |
109 | | OFCondition DcmDate::getOFDate(OFDate &dateValue, |
110 | | const unsigned long pos, |
111 | | const OFBool supportOldFormat) |
112 | 0 | { |
113 | 0 | OFString dicomDate; |
114 | | /* convert the current element value to OFDate format */ |
115 | 0 | OFCondition l_error = getOFString(dicomDate, pos); |
116 | 0 | if (l_error.good()) |
117 | 0 | l_error = getOFDateFromString(dicomDate, dateValue, supportOldFormat); |
118 | 0 | else |
119 | 0 | dateValue.clear(); |
120 | 0 | return l_error; |
121 | 0 | } |
122 | | |
123 | | |
124 | | OFCondition DcmDate::getISOFormattedDate(OFString &formattedDate, |
125 | | const unsigned long pos, |
126 | | const OFBool supportOldFormat) |
127 | 0 | { |
128 | 0 | OFString dicomDate; |
129 | | /* get current element value and convert to ISO formatted date */ |
130 | 0 | OFCondition l_error = getOFString(dicomDate, pos); |
131 | 0 | if (l_error.good()) |
132 | 0 | l_error = getISOFormattedDateFromString(dicomDate, formattedDate, supportOldFormat); |
133 | 0 | else |
134 | 0 | formattedDate.clear(); |
135 | 0 | return l_error; |
136 | 0 | } |
137 | | |
138 | | |
139 | | OFCondition DcmDate::setCurrentDate() |
140 | 0 | { |
141 | 0 | OFString dicomDate; |
142 | | /* set the element value to the current system date */ |
143 | 0 | OFCondition l_error = getCurrentDate(dicomDate); |
144 | 0 | if (l_error.good()) |
145 | 0 | l_error = putOFStringArray(dicomDate); |
146 | 0 | return l_error; |
147 | 0 | } |
148 | | |
149 | | |
150 | | OFCondition DcmDate::setOFDate(const OFDate &dateValue) |
151 | 0 | { |
152 | 0 | OFString dicomDate; |
153 | | /* convert OFDate value to DICOM DA format and set the element value */ |
154 | 0 | OFCondition l_error = getDicomDateFromOFDate(dateValue, dicomDate); |
155 | 0 | if (l_error.good()) |
156 | 0 | l_error = putOFStringArray(dicomDate); |
157 | 0 | return l_error; |
158 | 0 | } |
159 | | |
160 | | |
161 | | // ******************************** |
162 | | |
163 | | |
164 | | OFCondition DcmDate::getCurrentDate(OFString &dicomDate) |
165 | 0 | { |
166 | 0 | OFCondition l_error = EC_IllegalCall; |
167 | 0 | OFDate dateValue; |
168 | | /* get the current system date */ |
169 | 0 | if (dateValue.setCurrentDate()) |
170 | 0 | { |
171 | | /* format: YYYYMMDD */ |
172 | 0 | if (dateValue.getISOFormattedDate(dicomDate, OFFalse /*showDelimiter*/)) |
173 | 0 | l_error = EC_Normal; |
174 | 0 | } |
175 | | /* set default date if an error occurred */ |
176 | 0 | if (l_error.bad()) |
177 | 0 | { |
178 | | /* format: YYYYMMDD */ |
179 | 0 | dicomDate = "19000101"; |
180 | 0 | } |
181 | 0 | return l_error; |
182 | 0 | } |
183 | | |
184 | | |
185 | | OFCondition DcmDate::getDicomDateFromOFDate(const OFDate &dateValue, |
186 | | OFString &dicomDate) |
187 | 0 | { |
188 | 0 | OFCondition l_error = EC_IllegalParameter; |
189 | | /* convert OFDate value to DICOM DA format */ |
190 | 0 | if (dateValue.getISOFormattedDate(dicomDate, OFFalse /*showDelimiter*/)) |
191 | 0 | l_error = EC_Normal; |
192 | 0 | return l_error; |
193 | 0 | } |
194 | | |
195 | | |
196 | | OFCondition DcmDate::getOFDateFromString(const OFString &dicomDate, |
197 | | OFDate &dateValue, |
198 | | const OFBool supportOldFormat) |
199 | 0 | { |
200 | 0 | return getOFDateFromString(dicomDate.c_str(), dicomDate.size(), dateValue, supportOldFormat); |
201 | 0 | } |
202 | | |
203 | | |
204 | | OFCondition DcmDate::getOFDateFromString(const char* dicomDate, |
205 | | const size_t dicomDateSize, |
206 | | OFDate &dateValue) |
207 | 0 | { |
208 | 0 | return getOFDateFromString(dicomDate, dicomDateSize, dateValue, OFTrue); |
209 | 0 | } |
210 | | |
211 | | |
212 | | OFCondition DcmDate::getOFDateFromString(const char* dicomDate, |
213 | | const size_t dicomDateSize, |
214 | | OFDate &dateValue, |
215 | | const OFBool supportOldFormat) |
216 | 0 | { |
217 | | // clear result variable |
218 | 0 | dateValue.clear(); |
219 | | // fixed length 8 bytes required by DICOM part 5: YYYYMMDD |
220 | 0 | if ((dicomDateSize == 8) && OFStandard::checkDigits<8>(dicomDate)) |
221 | 0 | { |
222 | | // extract components from date string |
223 | 0 | if |
224 | 0 | ( |
225 | 0 | dateValue.setDate |
226 | 0 | ( |
227 | 0 | OFStandard::extractDigits<unsigned int,4>(dicomDate), |
228 | 0 | OFStandard::extractDigits<unsigned int,2>(dicomDate + 4), |
229 | 0 | OFStandard::extractDigits<unsigned int,2>(dicomDate + 6) |
230 | 0 | ) |
231 | 0 | ) |
232 | 0 | { |
233 | 0 | return EC_Normal; |
234 | 0 | } |
235 | 0 | } |
236 | | // old prior V3.0 version of VR=DA with fixed length 10 bytes: YYYY.MM.DD |
237 | 0 | else if |
238 | 0 | ( |
239 | 0 | supportOldFormat && (dicomDateSize == 10) && (dicomDate[4] == '.') && (dicomDate[7] == '.') && |
240 | 0 | OFStandard::checkDigits<4>(dicomDate) && |
241 | 0 | OFStandard::checkDigits<2>(dicomDate + 5) && |
242 | 0 | OFStandard::checkDigits<2>(dicomDate + 8) |
243 | 0 | ) |
244 | 0 | { |
245 | | // extract components from date string |
246 | 0 | if |
247 | 0 | ( |
248 | 0 | dateValue.setDate |
249 | 0 | ( |
250 | 0 | OFStandard::extractDigits<unsigned int, 4>(dicomDate), |
251 | 0 | OFStandard::extractDigits<unsigned int, 2>(dicomDate + 5), |
252 | 0 | OFStandard::extractDigits<unsigned int, 2>(dicomDate + 8) |
253 | 0 | ) |
254 | 0 | ) |
255 | 0 | { |
256 | 0 | return EC_Normal; |
257 | 0 | } |
258 | 0 | } |
259 | 0 | return EC_IllegalParameter; |
260 | 0 | } |
261 | | |
262 | | |
263 | | OFCondition DcmDate::getISOFormattedDateFromString(const OFString &dicomDate, |
264 | | OFString &formattedDate, |
265 | | const OFBool supportOldFormat) |
266 | 0 | { |
267 | 0 | OFCondition l_error = EC_Normal; |
268 | 0 | if (!dicomDate.empty()) |
269 | 0 | { |
270 | 0 | OFDate dateValue; |
271 | | /* convert string to OFDate */ |
272 | 0 | l_error = getOFDateFromString(dicomDate, dateValue, supportOldFormat); |
273 | 0 | if (l_error.good()) |
274 | 0 | { |
275 | | /* convert OFDate to ISO formatted date */ |
276 | 0 | if (!dateValue.getISOFormattedDate(formattedDate)) |
277 | 0 | l_error = EC_CorruptedData; |
278 | 0 | } |
279 | | /* clear the result variable in case of error */ |
280 | 0 | if (l_error.bad()) |
281 | 0 | formattedDate.clear(); |
282 | 0 | } else { |
283 | | /* input string is empty, so is the result string */ |
284 | 0 | formattedDate.clear(); |
285 | 0 | } |
286 | 0 | return l_error; |
287 | 0 | } |
288 | | |
289 | | |
290 | | // ******************************** |
291 | | |
292 | | |
293 | | OFBool DcmDate::check(const char* dicomDate, |
294 | | const size_t dicomDateSize) |
295 | 0 | { |
296 | 0 | return check(dicomDate, dicomDateSize, OFFalse); |
297 | 0 | } |
298 | | |
299 | | |
300 | | OFBool DcmDate::check(const char* dicomDate, |
301 | | const size_t dicomDateSize, |
302 | | const OFBool supportOldFormat) |
303 | 0 | { |
304 | 0 | switch (DcmElement::scanValue("da", dicomDate, dicomDateSize)) |
305 | 0 | { |
306 | 0 | case 2 /* DA */: |
307 | 0 | case 17 /* dubious DA (pre 1850 or post 2049) */: |
308 | 0 | return OFTrue; |
309 | 0 | case 3 /* old style DA */: |
310 | 0 | return supportOldFormat; |
311 | 0 | default: |
312 | 0 | return OFFalse; |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | | |
317 | | OFCondition DcmDate::checkStringValue(const OFString &value, |
318 | | const OFString &vm, |
319 | | const OFBool oldFormat) |
320 | 0 | { |
321 | 0 | OFCondition result = EC_Normal; |
322 | 0 | const size_t valLen = value.length(); |
323 | 0 | if (valLen > 0) |
324 | 0 | { |
325 | 0 | size_t posStart = 0; |
326 | 0 | unsigned long vmNum = 0; |
327 | | /* iterate over all value components */ |
328 | 0 | while (posStart != OFString_npos) |
329 | 0 | { |
330 | 0 | ++vmNum; |
331 | | /* search for next component separator */ |
332 | 0 | const size_t posEnd = value.find('\\', posStart); |
333 | 0 | const size_t length = (posEnd == OFString_npos) ? valLen - posStart : posEnd - posStart; |
334 | 0 | if (dcmEnableVRCheckerForStringValues.get()) |
335 | 0 | { |
336 | | /* check value representation */ |
337 | 0 | if (!check(value.data() + posStart, length, oldFormat)) |
338 | 0 | { |
339 | 0 | result = EC_ValueRepresentationViolated; |
340 | 0 | break; |
341 | 0 | } |
342 | 0 | } |
343 | 0 | posStart = (posEnd == OFString_npos) ? posEnd : posEnd + 1; |
344 | 0 | } |
345 | 0 | if (result.good() && !vm.empty()) |
346 | 0 | { |
347 | | /* check value multiplicity */ |
348 | 0 | result = DcmElement::checkVM(vmNum, vm); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | return result; |
352 | 0 | } |
353 | | |
354 | | |
355 | | OFBool DcmDate::matches(const OFString &key, |
356 | | const OFString &candidate, |
357 | | const OFBool enableWildCardMatching) const |
358 | 0 | { |
359 | 0 | OFstatic_cast(void, enableWildCardMatching); |
360 | 0 | return DcmAttributeMatching::rangeMatchingDate(key.c_str(), key.length(), candidate.c_str(), candidate.length()); |
361 | 0 | } |
362 | | |
363 | | |
364 | | OFBool DcmDate::combinationMatches(const DcmElement &keySecond, |
365 | | const DcmElement &candidateFirst, |
366 | | const DcmElement &candidateSecond) const |
367 | 0 | { |
368 | 0 | if ((keySecond.ident() == EVR_TM) && (candidateFirst.ident() == EVR_DA) && (candidateSecond.ident() == EVR_TM)) |
369 | 0 | { |
370 | | // do many const casts, but we do not modify the value, I promise... |
371 | 0 | DcmDate &queryDate = OFconst_cast(DcmDate &, *this); |
372 | 0 | DcmDate &candidateDate = OFconst_cast(DcmDate &, OFstatic_cast(const DcmDate &, candidateFirst)); |
373 | 0 | DcmTime &queryTime = OFconst_cast(DcmTime &, OFstatic_cast(const DcmTime &, keySecond)); |
374 | 0 | DcmTime &candidateTime = OFconst_cast(DcmTime &, OFstatic_cast(const DcmTime &, candidateSecond)); |
375 | 0 | OFString a0, a1, b0, b1; |
376 | | // no support for VM>1 so far! |
377 | 0 | return queryDate.getOFString(a0, 0, OFTrue).good() && queryTime.getOFString( a1, 0, OFTrue).good() && |
378 | 0 | candidateDate.getOFString(b0, 0, OFTrue).good() && candidateTime.getOFString( b1, 0, OFTrue).good() && |
379 | 0 | DcmAttributeMatching::rangeMatchingDateTime |
380 | 0 | ( |
381 | 0 | a0.c_str(), a0.length(), a1.c_str(), a1.length(), b0.c_str(), b0.length(), b1.c_str(), b1.length() |
382 | 0 | ); |
383 | 0 | } |
384 | 0 | return OFFalse; |
385 | 0 | } |