Project

General

Profile

Patch #895 » 2ec7d368c.diff

Harald Roesen, 2026-02-12 11:45

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 OFDateTime::setISOFormattedDateTime(const OFString & formattedDateTime)
{
OFBool result = OFFalse;
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] == '-')))))
{
if (Date.setISOFormattedDate(formattedDateTime.substr(0, 8)) && Time.setISOFormattedTime(formattedDateTime.substr(8)))
result = OFTrue;
// try to split 'formattedDateTime' into a 'formattedDate' and a 'formattedTime'
size_t const length = formattedDateTime.length();
// a 'formattedDate' is at least eight characters long: YYYMMDD
if (length < 8) {
return OFFalse;
}
/* "YYYY-MM-DD HH:MM[:SS]" or "YYYY-MM-DD HH:MM:SS &ZZ:ZZ" */
else if ((length >= 16) && separators)
{
if (Date.setISOFormattedDate(formattedDateTime.substr(0, 10)))
{
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;
// (weak) check if a delimiter is used
char const delim = formattedDateTime.at(4);
OFBool const delimsUsed = !std::isdigit(delim);
// where will 'formattedTime' start within 'formattedDateTime'?
size_t pos = 0;
if (delimsUsed && (length >= 10)) {
// a 'formattedDate' with delimiters is at least ten characters long: YYYY-MM-DD
pos = 10;
}
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
OFDate const preDate = getDate();
// try to set the formatted date
OFString const formattedDate = formattedDateTime.substr(0, pos);
OFBool const setDateStatus = Date.setISOFormattedDate(formattedDate);
// setting the formatted date failed, time to return
if (!setDateStatus) {
return OFFalse;
}
// skip non digits in case delimiter character has been used
if(delimsUsed) {
while ((pos < length) && !std::isdigit(formattedDateTime.at(pos))) {
++pos;
}
}
return result;
OFString const formattedTime = formattedDateTime.substr(pos);
OFBool const 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 OFTime::setISOFormattedTime(OFString const & 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*/);
}
/* 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*/);
size_t const length = formattedTime.length();
if (length < 3) {
// not enough input to scan
return OFFalse;
}
/* HHMMSS */
else if ((length == 6) && !hasDelimiters)
size_t pos = 0;
char const delim = formattedTime.at(pos + 2);
OFBool const delimsUsed = !std::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";
int const nCharactersExpected = (delimsUsed ? 5 : 4);
int nCharactersRead = 0;
int const nReceivedArgs =
std::sscanf(formattedTime.c_str() + pos, hhmmFormat.data(), &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)
{
/* 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);
// Is still input available?
if (pos < length) {
// scan for SS or :SS - seconds
OFString ssFormat;
if (delimsUsed) {
ssFormat += delim;
}
ssFormat += "%2u%n";
int const nCharactersExpected = (delimsUsed ? 3 : 2);
int nCharactersRead = 0;
int const nReceivedArgs =
std::sscanf(formattedTime.c_str() + pos, ssFormat.data(), &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] == '-')))
{
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)
{
const double timeZone = (tzHours < 0) ? tzHours - OFstatic_cast(double, tzMinutes) / 60
: tzHours + OFstatic_cast(double, tzMinutes) / 60;
status = setTime(hours, minutes, seconds, timeZone);
// Is still input available that also starts with a dot ('.') character?
if ((pos < length) && (formattedTime.at(pos) == '.')) {
// scan for .FFFFFF - fractional seconds
char buffer[7] = {'\0'};
buffer[0] = '.';
++pos; // overread the dot ('.') character at front
int nCharactersRead = 0;
int const nReceivedArgs =
std::sscanf(formattedTime.c_str() + pos, "%6[0123456789]%n", (buffer + 1), &nCharactersRead);
if (nReceivedArgs == 1) {
pos += nCharactersRead;
fractionalSeconds = std::atof(buffer);
}
else {
// found no valid .FFFFFF 'pattern'
return false;
}
}
/* HH:MM:SS &ZZ:ZZ */
else if ((length >= 14) && hasDelimiters)
{
/* 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)
{
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);
}
}
// skip whitespaces in case delimiter character has been used
if (delimsUsed) {
while ((pos < length) && std::isspace(formattedTime.at(pos))) {
++pos;
}
}
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;
int const nReceivedArgs =
sscanf(formattedTime.c_str() + pos, tzFormat.data(), &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
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"));
OFDate const preDate = dateTime2.getDate();
OFCHECK(!dateTime2.setISOFormattedDateTime("2000-12-31 10:15:30 -0230"));
OFDate const postDate = dateTime2.getDate();
OFCHECK_EQUAL(preDate, postDate);
}
(2-2/4)