Project

General

Profile

Patch #895 » 2ec7d368c_v3.diff

Harald Roesen, 2026-02-16 14:42

View differences:

ofstd/include/dcmtk/ofstd/ofdatime.h
*
* Module: ofstd
*
* Author: Joerg Riesmeier
* Author: Joerg Riesmeier, Harald Roesen
*
* Purpose: Combined class for date and time functions
*
......
/** set the date/time value to the given ISO formatted date/time string.
* The two ISO date/time formats supported by this function are
* - "YYYY-MM-DD HH:MM[:SS [&ZZ:ZZ]]" (with arbitrary delimiters) and
* - "YYYYMMDDHHMM[SS[&ZZZZ]]" (without delimiters, useful for DICOM datetime type)
* - "YYYY-MM-DD HH:MM[:SS[.FFFFFF] [&ZZ:ZZ]]" (with arbitrary delimiters) and
* - "YYYYMMDDHHMM[SS[.FFFFFF][&ZZZZ]]" (without delimiters, useful for DICOM datetime type)
*
* where the brackets enclose optional parts and the "&" is a placeholder for the sign
* symbol ("+" or "-").
* @note Please note that the optional fractional part of a second ".FFFFFF" (see
* getISOFormattedDateTime()) is not yet supported.
* symbol ("+" or "-"). The pattern "FFFFFF" contains a fractional part of a second.
* If specified, it shall contain one to six digits representing a range from 0 to 999999.
* @param formattedDateTime ISO formatted date/time value to be set
* @return OFTrue if input is valid and result variable has been set, OFFalse otherwise
*/
ofstd/include/dcmtk/ofstd/oftime.h
*
* Module: ofstd
*
* Author: Joerg Riesmeier
* Author: Joerg Riesmeier, Harald Roesen
*
* Purpose: Class for time functions
*
......
/** set the time value to the given ISO formatted time string.
* The two ISO time formats supported by this function are
* - "HH:MM[:SS [&ZZ:ZZ]]" (with arbitrary delimiters) and
* - "HHMM[SS[&ZZZZ]]" (without delimiters)
* - "HH:MM[:SS[.FFFFFF] [&ZZ:ZZ]]" (with arbitrary delimiters) and
* - "HHMM[SS[.FFFFFF][&ZZZZ]]" (without delimiters)
*
* where the brackets enclose optional parts and the "&" is a placeholder for the
* sign symbol ("+" or "-").
* @note Please note that the optional fractional part of a second ".FFFFFF" (see
* getISOFormattedTime()) is not yet supported.
* sign symbol ("+" or "-"). The pattern "FFFFFF" contains a fractional part of a second.
* If specified, it shall contain one to six digits representing a range from 0 to 999999.
* @param formattedTime ISO formatted time value to be set
* @return OFTrue if input is valid and result variable has been set, OFFalse otherwise
*/
ofstd/libsrc/ofdatime.cc
*
* Module: ofstd
*
* Author: Joerg Riesmeier
* Author: Joerg Riesmeier, Harald Roesen
*
* Purpose: Class for date and time functions (Source)
*
......
OFBool OFDateTime::setISOFormattedDateTime(const OFString &formattedDateTime)
{
OFBool result = OFFalse;
/* try to split 'formattedDateTime' into a 'formattedDate' and a 'formattedTime' */
const size_t length = formattedDateTime.length();
const size_t firstSep = formattedDateTime.find_first_not_of("0123456789");
const OFBool separators = (firstSep != OFString_npos);
/* check for supported formats: "YYYYMMDDHHMM[SS]" or "YYYYMMDDHHMMSS&ZZZZ" */
if (((((length == 12) || (length == 14)) && !separators) ||
((length == 19) && (firstSep == 14) && ((formattedDateTime[14] == '+') || (formattedDateTime[14] == '-')))))
/* a 'formattedDate' is at least eight characters long: YYYYMMDD */
if (length < 8)
{
return OFFalse;
}
/* (Weak) check if a delimiter is used. If so, we'll assert that it
* is always the same one, i.e. 'delim'.
*/
const char delim = formattedDateTime.at(4);
const OFBool delimsUsed = !isdigit(delim);
/* where will 'formattedTime' start within 'formattedDateTime'? */
size_t pos = 0;
if (delimsUsed && (length >= 10))
{
if (Date.setISOFormattedDate(formattedDateTime.substr(0, 8)) && Time.setISOFormattedTime(formattedDateTime.substr(8)))
result = OFTrue;
/* a 'formattedDate' with delimiters is at least ten characters long: YYYY-MM-DD */
pos = 10;
}
/* "YYYY-MM-DD HH:MM[:SS]" or "YYYY-MM-DD HH:MM:SS &ZZ:ZZ" */
else if ((length >= 16) && separators)
else if (!delimsUsed && (length >= 8))
{
/* a 'formattedDate' without delimiters is at least eight characters long: YYYYMMDD */
pos = 8;
}
else
{
return OFFalse;
}
/* remember the current date in case setting the formatted time will fail */
const OFDate preDate = getDate();
/* try to set the formatted date */
const OFString formattedDate = formattedDateTime.substr(0, pos);
const OFBool setDateStatus = Date.setISOFormattedDate(formattedDate);
/* setting the formatted date failed, time to return */
if (!setDateStatus)
{
if (Date.setISOFormattedDate(formattedDateTime.substr(0, 10)))
return OFFalse;
}
/* skip non digits in case delimiter character has been used */
if (delimsUsed)
{
while ((pos < length) && !isdigit(formattedDateTime.at(pos)))
{
size_t pos = 10;
/* search for first digit of the time value (skip arbitrary separators) */
while ((pos < length) && !isdigit(OFstatic_cast(unsigned char, formattedDateTime.at(pos))))
++pos;
if ((pos < length) && Time.setISOFormattedTime(formattedDateTime.substr(pos)))
result = OFTrue;
++pos;
}
}
return result;
const OFString formattedTime = formattedDateTime.substr(pos);
const OFBool setTimeStatus = Time.setISOFormattedTime(formattedTime);
if (!setTimeStatus)
{
/* setting the time failed, restore the former date */
if (preDate.isValid())
{
setDate(preDate);
}
else
{
Date.clear();
}
}
return setTimeStatus;
}
ofstd/libsrc/oftime.cc
*
* Module: ofstd
*
* Author: Joerg Riesmeier
* Author: Joerg Riesmeier, Harald Roesen
*
* Purpose: Class for time functions (Source)
*
......
return status;
}
OFBool OFTime::setISOFormattedTime(const OFString &formattedTime)
{
OFBool status = OFFalse;
const size_t length = formattedTime.length();
const size_t firstDelimiter = formattedTime.find_first_not_of("0123456789");
const OFBool hasDelimiters = (firstDelimiter != OFString_npos);
unsigned int hours, minutes, seconds;
/* check for supported formats: HHMM */
if ((length == 4) && !hasDelimiters)
{
/* extract "HH" and "MM" components from time string */
if (sscanf(formattedTime.c_str(), "%02u%02u", &hours, &minutes) == 2)
status = setTime(hours, minutes, 0 /*seconds*/);
if (length < 3) {
/* not enough input to scan */
return OFFalse;
}
/* HH:MM */
else if ((length == 5) && hasDelimiters)
{
/* extract "HH" and "MM" components from time string (ignore delimiter) */
if (sscanf(formattedTime.c_str(), "%02u%*c%02u", &hours, &minutes) == 2)
status = setTime(hours, minutes, 0 /*seconds*/);
}
/* HHMMSS */
else if ((length == 6) && !hasDelimiters)
size_t pos = 0;
const char delim = formattedTime.at(pos + 2);
const OFBool delimsUsed = !isdigit(delim);
unsigned int hours = 0;
unsigned int minutes = 0;
unsigned int seconds = 0;
double fractionalSeconds = 0.0;
double timeZone = OFTime::unspecifiedTimeZone;
{
/* extract "HH", "MM" and "SS" components from time string */
if (sscanf(formattedTime.c_str(), "%02u%02u%02u", &hours, &minutes, &seconds) == 3)
status = setTime(hours, minutes, seconds);
/* scan for HHMM or HH:MM - hours and minutes */
OFString hhmmFormat("%2u");
if (delimsUsed)
{
hhmmFormat += delim;
}
hhmmFormat += "%2u%n";
const int nCharactersExpected = (delimsUsed ? 5 : 4);
int nCharactersRead = 0;
const int nReceivedArgs =
sscanf(formattedTime.c_str() + pos, hhmmFormat.c_str(), &hours, &minutes, &nCharactersRead);
if ((nReceivedArgs == 2) && (nCharactersRead == nCharactersExpected))
{
pos += nCharactersRead;
}
else
{
/* found no valid HH[:]MM 'pattern' */
return OFFalse;
}
}
/* HH:MM:SS */
else if ((length == 8) && hasDelimiters)
/* is still input available? */
if (pos < length)
{
/* extract "HH", "MM" and "SS" components from time string (ignore delimiters) */
if (sscanf(formattedTime.c_str(), "%02u%*c%02u%*c%02u", &hours, &minutes, &seconds) == 3)
status = setTime(hours, minutes, seconds);
/* scan for SS or :SS - seconds */
OFString ssFormat;
if (delimsUsed)
{
ssFormat += delim;
}
ssFormat += "%2u%n";
const int nCharactersExpected = (delimsUsed ? 3 : 2);
int nCharactersRead = 0;
const int nReceivedArgs =
sscanf(formattedTime.c_str() + pos, ssFormat.c_str(), &seconds, &nCharactersRead);
if ((nReceivedArgs == 1) && (nCharactersRead == nCharactersExpected))
{
pos += nCharactersRead;
}
else
{
/* found no valid [:]SS 'pattern' */
return OFFalse;
}
}
/* HHMMSS&ZZZZ */
else if ((length == 11) && (firstDelimiter == 6) && ((formattedTime[6] == '+') || (formattedTime[6] == '-')))
/* is still input available that also starts with a dot ('.') character? */
if ((pos < length) && (formattedTime.at(pos) == '.'))
{
int tzHours;
unsigned int tzMinutes;
/* extract "HH", "MM", "SS" and "&ZZZZ" components from time string */
if (sscanf(formattedTime.c_str(), "%02u%02u%02u%03d%02u", &hours, &minutes, &seconds, &tzHours, &tzMinutes) == 5)
/* scan for .FFFFFF - fractional seconds */
char buffer[8] = {'\0'};
buffer[0] = '.';
++pos; /* overread the dot ('.') character at front */
int nCharactersRead = 0;
const int nReceivedArgs =
sscanf(formattedTime.c_str() + pos, "%6[0123456789]%n", (buffer + 1), &nCharactersRead);
OFBool fsSuccess = OFFalse;
fractionalSeconds = OFStandard::atof(buffer, &fsSuccess);
if ((nReceivedArgs == 1) && fsSuccess)
{
pos += nCharactersRead;
}
else
{
const double timeZone = (tzHours < 0) ? tzHours - OFstatic_cast(double, tzMinutes) / 60
: tzHours + OFstatic_cast(double, tzMinutes) / 60;
status = setTime(hours, minutes, seconds, timeZone);
/* found no valid .FFFFFF 'pattern' */
return OFFalse;
}
}
/* HH:MM:SS &ZZ:ZZ */
else if ((length >= 14) && hasDelimiters)
/* skip whitespaces in case delimiter character has been used */
if (delimsUsed)
{
/* first, extract "HH", "MM" and "SS" components from time string (ignore delimiters) */
if (sscanf(formattedTime.c_str(), "%02u%*c%02u%*c%02u", &hours, &minutes, &seconds) == 3)
const OFBool hasSpaceBeenSkipped = isspace(formattedTime.at(pos));
while ((pos < length) && isspace(formattedTime.at(pos)))
{
size_t pos = 8;
/* then search for the first digit of the time zone value (skip arbitrary delimiters) */
while ((pos < length) && !isdigit(OFstatic_cast(unsigned char, formattedTime.at(pos))))
++pos;
if (pos < length)
{
/* and finally, extract the time zone component from the time string */
int tzHours;
unsigned int tzMinutes;
/* ignore the delimiter "%c" */
if (sscanf(formattedTime.c_str() + pos - 1, "%03d%*c%02u", &tzHours, &tzMinutes) == 2)
{
const double timeZone = (tzHours < 0) ? tzHours - OFstatic_cast(double, tzMinutes) / 60
: tzHours + OFstatic_cast(double, tzMinutes) / 60;
status = setTime(hours, minutes, seconds, timeZone);
}
}
++pos;
}
if ((pos == length) && hasSpaceBeenSkipped)
{
/* we have skipped trailing spaces but reached end of input,
* this means that the input is/was malformed in the first place
*/
return OFFalse;
}
}
return status;
/* is still input available that also starts with a plus ('+') or minus ('-')
* character?
*/
if ((pos < length) && ((formattedTime.at(pos) == '+') || (formattedTime.at(pos) == '-')))
{
/* scan for &ZZZZ or &ZZ:ZZ - time zone */
OFString tzFormat("%3d");
if (delimsUsed)
{
tzFormat += delim;
}
tzFormat += "%2u%n";
int tzHours;
unsigned int tzMinutes;
int nCharactersRead = 0;
const int nReceivedArgs =
sscanf(formattedTime.c_str() + pos, tzFormat.c_str(), &tzHours, &tzMinutes, &nCharactersRead);
if (nReceivedArgs == 2)
{
timeZone = (tzHours < 0) ? tzHours - static_cast<double>(tzMinutes) / 60
: tzHours + static_cast<double>(tzMinutes) / 60;
pos += nCharactersRead;
}
else
{
/* found no valid &ZZ[:]ZZ 'pattern' */
return OFFalse;
}
}
/* is still input available? */
if (pos < length)
{
/* too much input */
return OFFalse;
}
/* all 'eaten up' */
return setTime(hours, minutes, seconds + fractionalSeconds, timeZone);
}
ofstd/tests/tofdatim.cc
*
* Module: ofstd
*
* Author: Joerg Riesmeier
* Author: Joerg Riesmeier, Harald Roesen
*
* Purpose: test program for classes OFDate, OFTime and OFDateTime
*
......
OFCHECK_EQUAL(time2.getSecond(), 30.1234);
OFCHECK_EQUAL(time2.getIntSecond(), 30);
/* check setting ISO formatted time */
OFCHECK(time1.setISOFormattedTime("1215"));
OFCHECK(time1.setISOFormattedTime("12:15"));
OFCHECK(time1.setISOFormattedTime("121530"));
OFCHECK(time1.setISOFormattedTime("12:15:30"));
OFCHECK( time1.setISOFormattedTime("1215"));
OFCHECK(!time1.setISOFormattedTime("1215."));
OFCHECK(!time1.setISOFormattedTime("1215.1"));
OFCHECK(!time1.setISOFormattedTime("1215.123456"));
OFCHECK(!time1.setISOFormattedTime("1215.1234567"));
OFCHECK( time1.setISOFormattedTime("12:15"));
OFCHECK(!time1.setISOFormattedTime("12:15."));
OFCHECK(!time1.setISOFormattedTime("12:15.1"));
OFCHECK(!time1.setISOFormattedTime("12:15.123456"));
OFCHECK(!time1.setISOFormattedTime("12:15.1234567"));
OFCHECK( time1.setISOFormattedTime("121530"));
OFCHECK(!time1.setISOFormattedTime("121530."));
OFCHECK( time1.setISOFormattedTime("121530.1"));
OFCHECK( time1.setISOFormattedTime("121530.123456"));
OFCHECK(!time1.setISOFormattedTime("121530.1234567"));
OFCHECK( time1.setISOFormattedTime("12:15:30"));
OFCHECK(!time1.setISOFormattedTime("12:15:30."));
OFCHECK( time1.setISOFormattedTime("12:15:30.1"));
OFCHECK( time1.setISOFormattedTime("12:15:30.123456"));
OFCHECK(!time1.setISOFormattedTime("12:15:30.1234567"));
OFCHECK( time1.setISOFormattedTime("121530+0100"));
OFCHECK(!time1.setISOFormattedTime("121530.+0100"));
OFCHECK( time1.setISOFormattedTime("121530.1+0100"));
OFCHECK( time1.setISOFormattedTime("121530.123456+0100"));
OFCHECK(!time1.setISOFormattedTime("121530.1234567+0100"));
OFCHECK(time1.setISOFormattedTime("121530+0100"));
OFCHECK_EQUAL(time1.getTimeZone(), 1.0);
OFCHECK(time1.setISOFormattedTime("12:15:30 -02:30"));
......
/* the "seconds" part is mandatory if time zone is present */
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15 -02:30"));
OFCHECK(!dateTime2.setISOFormattedDateTime("200012311015+0100"));
OFCHECK( dateTime2.setISOFormattedDateTime("20001231101530-0230"));
OFCHECK(!dateTime2.setISOFormattedDateTime("20001231101530.-0230"));
OFCHECK( dateTime2.setISOFormattedDateTime("20001231101530.1-0230"));
OFCHECK( dateTime2.setISOFormattedDateTime("20001231101530.123456-0230"));
OFCHECK(!dateTime2.setISOFormattedDateTime("20001231101530.1234567-0230"));
OFCHECK( dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30 -02:30"));
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30. -02:30"));
OFCHECK( dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30.1 -02:30"));
OFCHECK( dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30.123456 -02:30"));
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30.1234567 -02:30"));
/* check if date is restored in case setting time failed */
OFCHECK( dateTime2.setISOFormattedDateTime("2345-12-31 10:15:30 -02:30"));
const OFDate preDate = dateTime2.getDate();
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30 -0230"));
const OFDate postDate = dateTime2.getDate();
OFCHECK_EQUAL(preDate, postDate);
/* check handling of beginning and/or trailing spaces */
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30 "));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 2000-12-31 10:15:30"));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 2000-12-31 10:15:30 "));
OFCHECK(!dateTime2.setISOFormattedDateTime("20001231101530 "));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 20001231101530"));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 20001231101530 "));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 2000-12-31 10:15:30 -02:30"));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 2000-12-31 10:15:30 -02:30 "));
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30 -02:30 "));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 20001231101530-0230"));
OFCHECK(!dateTime2.setISOFormattedDateTime(" 20001231101530-0230 "));
OFCHECK(!dateTime2.setISOFormattedDateTime("20001231101530-0230 "));
}
(4-4/4)