Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}