fix_1416_iptc_DateCreated (#1547)
* fix_1416_iptc_DateCreated * Fix unit tests * DateValue:read 2nd iteration on pre-condition * test with ISO_8601 date format * Use std::regex for ISO 8601 basic & extended date formats * Use std::regex for ISO 8601 basic & extended time formats * Add more tests & notes for DateValue & TimeValue * Comment tests using local calendar times * DateValue::write also adds padding to year field Co-authored-by: Luis Díaz Más <piponazo@gmail.com>
This commit is contained in:
parent
fd8447129c
commit
13a2cf336d
@ -979,7 +979,6 @@ namespace Exiv2 {
|
||||
|
||||
//! Simple Date helper structure
|
||||
struct EXIV2API Date {
|
||||
Date() = default;
|
||||
int year{0}; //!< Year
|
||||
int month{0}; //!< Month
|
||||
int day{0}; //!< Day
|
||||
@ -1031,6 +1030,7 @@ namespace Exiv2 {
|
||||
@return Number of characters written.
|
||||
*/
|
||||
long copy(byte* buf, ByteOrder byteOrder = invalidByteOrder) const override;
|
||||
|
||||
//! Return date struct containing date information
|
||||
virtual const Date& getDate() const;
|
||||
long count() const override;
|
||||
@ -1150,31 +1150,6 @@ namespace Exiv2 {
|
||||
//@}
|
||||
|
||||
private:
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
/*!
|
||||
@brief Set time from \em buf if it conforms to \em format
|
||||
(3 input items).
|
||||
|
||||
This function only sets the hour, minute and second parts of time_.
|
||||
|
||||
@param buf A 0 terminated C-string containing the time to parse.
|
||||
@param format Format string for sscanf().
|
||||
@return 0 if successful, else 1.
|
||||
*/
|
||||
int scanTime3(const char* buf, const char* format);
|
||||
/*!
|
||||
@brief Set time from \em buf if it conforms to \em format
|
||||
(6 input items).
|
||||
|
||||
This function sets all parts of time_.
|
||||
|
||||
@param buf A 0 terminated C-string containing the time to parse.
|
||||
@param format Format string for sscanf().
|
||||
@return 0 if successful, else 1.
|
||||
*/
|
||||
int scanTime6(const char* buf, const char* format);
|
||||
//@}
|
||||
|
||||
//! @name Accessors
|
||||
//@{
|
||||
|
||||
194
src/value.cpp
194
src/value.cpp
@ -27,16 +27,17 @@
|
||||
#include "unused.h"
|
||||
|
||||
// + standard includes
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctype.h>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
@ -776,13 +777,13 @@ namespace Exiv2 {
|
||||
}
|
||||
|
||||
int LangAltValue::read(const std::string& buf)
|
||||
{
|
||||
{
|
||||
std::string b = buf;
|
||||
std::string lang = "x-default";
|
||||
if (buf.length() > 5 && buf.substr(0, 5) == "lang=") {
|
||||
static const char* ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
static const char* ALPHA_NUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
|
||||
const std::string::size_type pos = buf.find_first_of(' ');
|
||||
if (pos == std::string::npos) {
|
||||
lang = buf.substr(5);
|
||||
@ -796,7 +797,7 @@ namespace Exiv2 {
|
||||
|
||||
if (lang.empty() || lang.find('"') != lang.length() - 1)
|
||||
throw Error(kerInvalidLangAltValue, buf);
|
||||
|
||||
|
||||
lang = lang.substr(0, lang.length()-1);
|
||||
}
|
||||
|
||||
@ -809,7 +810,7 @@ namespace Exiv2 {
|
||||
if (lang.at(charPos) != '-' || lang.find_first_not_of(ALPHA_NUM, charPos+1) != std::string::npos)
|
||||
throw Error(kerInvalidLangAltValue, buf);
|
||||
}
|
||||
|
||||
|
||||
b.clear();
|
||||
if (pos != std::string::npos) b = buf.substr(pos+1);
|
||||
}
|
||||
@ -906,51 +907,30 @@ namespace Exiv2 {
|
||||
|
||||
int DateValue::read(const byte* buf, long len, ByteOrder /*byteOrder*/)
|
||||
{
|
||||
// Hard coded to read Iptc style dates
|
||||
if (len != 8) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedDateFormat) << "\n";
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
// Make the buffer a 0 terminated C-string for sscanf
|
||||
char b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
std::memcpy(b, reinterpret_cast<const char*>(buf), 8);
|
||||
int scanned = sscanf(b, "%4d%2d%2d",
|
||||
&date_.year, &date_.month, &date_.day);
|
||||
if ( scanned != 3
|
||||
|| date_.year < 0
|
||||
|| date_.month < 1 || date_.month > 12
|
||||
|| date_.day < 1 || date_.day > 31) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedDateFormat) << "\n";
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
const std::string str(reinterpret_cast<const char*>(buf), len);
|
||||
return read(str);
|
||||
}
|
||||
|
||||
int DateValue::read(const std::string& buf)
|
||||
{
|
||||
// Hard coded to read Iptc style dates
|
||||
if (buf.length() < 8) {
|
||||
// ISO 8601 date formats:
|
||||
// https://web.archive.org/web/20171020084445/https://www.loc.gov/standards/datetime/ISO_DIS%208601-1.pdf
|
||||
static const std::regex reExtended(R"(^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]))");
|
||||
static const std::regex reBasic(R"(^(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01]))");
|
||||
std::smatch sm;
|
||||
|
||||
// Note: We use here regex_search instead of regex_match, because the string can be longer than expected and
|
||||
// also contain the time
|
||||
if (std::regex_search(buf, sm, reExtended) || std::regex_search(buf, sm, reBasic)) {
|
||||
date_.year = std::stoi(sm[1].str());
|
||||
date_.month = std::stoi(sm[2].str());
|
||||
date_.day = std::stoi(sm[3].str());
|
||||
return 0;
|
||||
}
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedDateFormat) << "\n";
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
int scanned = sscanf(buf.c_str(), "%4d-%2d-%2d",
|
||||
&date_.year, &date_.month, &date_.day);
|
||||
if ( scanned != 3
|
||||
|| date_.year < 0
|
||||
|| date_.month < 1 || date_.month > 12
|
||||
|| date_.day < 1 || date_.day > 31) {
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedDateFormat) << "\n";
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DateValue::setDate(const Date& src)
|
||||
@ -962,9 +942,11 @@ namespace Exiv2 {
|
||||
|
||||
long DateValue::copy(byte* buf, ByteOrder /*byteOrder*/) const
|
||||
{
|
||||
// \note Here the date is copied in the Basic format YYYYMMDD, as the IPTC key Iptc.Application2.DateCreated
|
||||
// wants it. Check https://exiv2.org/iptc.html
|
||||
|
||||
// sprintf wants to add the null terminator, so use oversized buffer
|
||||
char temp[9];
|
||||
|
||||
int wrote = snprintf(temp, sizeof(temp), "%04d%02d%02d", date_.year, date_.month, date_.day);
|
||||
assert(wrote == 8);
|
||||
std::memcpy(buf, temp, wrote);
|
||||
@ -993,8 +975,9 @@ namespace Exiv2 {
|
||||
|
||||
std::ostream& DateValue::write(std::ostream& os) const
|
||||
{
|
||||
// Write DateValue in ISO 8601 Extended format: YYYY-MM-DD
|
||||
std::ios::fmtflags f( os.flags() );
|
||||
os << date_.year << '-' << std::right
|
||||
os << std::setw(4) << std::setfill('0') << date_.year << '-' << std::right
|
||||
<< std::setw(2) << std::setfill('0') << date_.month << '-'
|
||||
<< std::setw(2) << std::setfill('0') << date_.day;
|
||||
os.flags(f);
|
||||
@ -1044,83 +1027,50 @@ namespace Exiv2 {
|
||||
|
||||
int TimeValue::read(const byte* buf, long len, ByteOrder /*byteOrder*/)
|
||||
{
|
||||
// Make the buffer a 0 terminated C-string for scanTime[36]
|
||||
char b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
std::memcpy(b, reinterpret_cast<const char*>(buf), (len < 12 ? len : 11));
|
||||
// Hard coded to read HHMMSS or Iptc style times
|
||||
int rc = 1;
|
||||
if (len == 6) {
|
||||
// Try to read (non-standard) HHMMSS format
|
||||
rc = scanTime3(b, "%2d%2d%2d");
|
||||
}
|
||||
if (len == 11) {
|
||||
rc = scanTime6(b, "%2d%2d%2d%1c%2d%2d");
|
||||
}
|
||||
if (rc) {
|
||||
rc = 1;
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedTimeFormat) << "\n";
|
||||
#endif
|
||||
}
|
||||
return rc;
|
||||
const std::string str(reinterpret_cast<const char*>(buf), len);
|
||||
return read(str);
|
||||
}
|
||||
|
||||
int TimeValue::read(const std::string& buf)
|
||||
{
|
||||
// Hard coded to read H:M:S or Iptc style times
|
||||
int rc = 1;
|
||||
if (buf.length() < 9) {
|
||||
// Try to read (non-standard) H:M:S format
|
||||
rc = scanTime3(buf.c_str(), "%d:%d:%d");
|
||||
}
|
||||
else {
|
||||
rc = scanTime6(buf.c_str(), "%d:%d:%d%1c%d:%d");
|
||||
}
|
||||
if (rc) {
|
||||
rc = 1;
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedTimeFormat) << "\n";
|
||||
#endif
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
// ISO 8601 time formats:
|
||||
// https://web.archive.org/web/20171020084445/https://www.loc.gov/standards/datetime/ISO_DIS%208601-1.pdf
|
||||
// Not supported formats:
|
||||
// 4.2.2.4 Representations with decimal fraction: 232050,5
|
||||
static const std::regex re(R"(^(2[0-3]|[01][0-9]):?([0-5][0-9])?:?([0-5][0-9])?$)");
|
||||
static const std::regex reExt(R"(^(2[0-3]|[01][0-9]):?([0-5][0-9]):?([0-5][0-9])(Z|[+-](?:2[0-3]|[01][0-9])(?::?(?:[0-5][0-9]))?)$)");
|
||||
|
||||
int TimeValue::scanTime3(const char* buf, const char* format)
|
||||
{
|
||||
int rc = 1;
|
||||
Time t;
|
||||
int scanned = sscanf(buf, format, &t.hour, &t.minute, &t.second);
|
||||
if ( scanned == 3
|
||||
&& t.hour >= 0 && t.hour < 24
|
||||
&& t.minute >= 0 && t.minute < 60
|
||||
&& t.second >= 0 && t.second < 60) {
|
||||
time_ = t;
|
||||
rc = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
std::smatch sm;
|
||||
if (std::regex_match(buf, sm, re) || std::regex_match(buf, sm, reExt)) {
|
||||
time_.hour = sm.length(1) ? std::stoi(sm[1].str()) : 0;
|
||||
time_.minute = sm.length(2) ? std::stoi(sm[2].str()) : 0;
|
||||
time_.second = sm.length(3) ? std::stoi(sm[3].str()) : 0;
|
||||
if (sm.size() > 4)
|
||||
{
|
||||
std::string str = sm[4].str();
|
||||
const auto strSize = str.size();
|
||||
auto posColon = str.find(':');
|
||||
|
||||
int TimeValue::scanTime6(const char* buf, const char* format)
|
||||
{
|
||||
int rc = 1;
|
||||
Time t;
|
||||
char plusMinus = 0;
|
||||
int scanned = sscanf(buf, format, &t.hour, &t.minute, &t.second,
|
||||
&plusMinus, &t.tzHour, &t.tzMinute);
|
||||
if ( scanned == 6
|
||||
&& t.hour >= 0 && t.hour < 24
|
||||
&& t.minute >= 0 && t.minute < 60
|
||||
&& t.second >= 0 && t.second < 60
|
||||
&& t.tzHour >= 0 && t.tzHour < 24
|
||||
&& t.tzMinute >= 0 && t.tzMinute < 60) {
|
||||
time_ = t;
|
||||
if (plusMinus == '-') {
|
||||
time_.tzHour *= -1;
|
||||
time_.tzMinute *= -1;
|
||||
if (posColon == std::string::npos) {
|
||||
// Extended format
|
||||
time_.tzHour = std::stoi(str.substr(0,3));
|
||||
if (strSize > 3) {
|
||||
int minute = std::stoi(str.substr(3));
|
||||
time_.tzMinute = time_.tzHour < 0 ? -minute : minute;
|
||||
}
|
||||
} else {
|
||||
// Basic format
|
||||
time_.tzHour = std::stoi(str.substr(0, posColon));
|
||||
int minute = std::stoi(str.substr(posColon+1));
|
||||
time_.tzMinute = time_.tzHour < 0 ? -minute : minute;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
#ifndef SUPPRESS_WARNINGS
|
||||
EXV_WARNING << Error(kerUnsupportedTimeFormat) << "\n";
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
void TimeValue::setTime( const Time& src )
|
||||
@ -1130,6 +1080,8 @@ namespace Exiv2 {
|
||||
|
||||
long TimeValue::copy(byte* buf, ByteOrder /*byteOrder*/) const
|
||||
{
|
||||
// NOTE: Here the time is copied in the Basic format HHMMSS:HHMM, as the IPTC key Iptc.Application2.TimeCreated
|
||||
// wants it. Check https://exiv2.org/iptc.html
|
||||
char temp[12];
|
||||
char plusMinus = '+';
|
||||
if (time_.tzHour < 0 || time_.tzMinute < 0)
|
||||
@ -1167,8 +1119,10 @@ namespace Exiv2 {
|
||||
|
||||
std::ostream& TimeValue::write(std::ostream& os) const
|
||||
{
|
||||
// Write TimeValue in ISO 8601 Extended format: hh:mm:ss±hh:mm
|
||||
char plusMinus = '+';
|
||||
if (time_.tzHour < 0 || time_.tzMinute < 0) plusMinus = '-';
|
||||
if (time_.tzHour < 0 || time_.tzMinute < 0)
|
||||
plusMinus = '-';
|
||||
|
||||
std::ios::fmtflags f( os.flags() );
|
||||
os << std::right
|
||||
|
||||
@ -642,11 +642,11 @@ def addModTest(filename):
|
||||
stdin = """
|
||||
a Iptc.Application2.Headline The headline I am
|
||||
a Iptc.Application2.Keywords Yet another keyword
|
||||
m Iptc.Application2.DateCreated 2004-8-3
|
||||
m Iptc.Application2.DateCreated 2004-08-03
|
||||
a Iptc.Application2.Urgency 3
|
||||
m Iptc.Application2.SuppCategory "bla bla ba"
|
||||
a Iptc.Envelope.ModelVersion 2
|
||||
a Iptc.Envelope.TimeSent 14:41:0-05:00
|
||||
a Iptc.Envelope.TimeSent 14:41:00-05:00
|
||||
a Iptc.Application2.RasterizedCaption 230 42 34 2 90 84 23 146
|
||||
""".lstrip('\n').encode()
|
||||
Executer('iptctest {tmp}', vars(), stdin=stdin)
|
||||
|
||||
@ -19,8 +19,13 @@
|
||||
*/
|
||||
|
||||
#include "value.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
using namespace Exiv2;
|
||||
|
||||
TEST(ADateValue, isDefaultConstructed)
|
||||
@ -31,7 +36,7 @@ TEST(ADateValue, isDefaultConstructed)
|
||||
ASSERT_EQ(0, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
TEST(ADateValue, isConstructedWithArgs)
|
||||
TEST(ADateValue, canBeConstructedWithValidDate)
|
||||
{
|
||||
const DateValue dateValue (2018, 4, 2);
|
||||
ASSERT_EQ(2018, dateValue.getDate().year);
|
||||
@ -39,6 +44,45 @@ TEST(ADateValue, isConstructedWithArgs)
|
||||
ASSERT_EQ(2, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
/// \todo Probably we should avoid this ...
|
||||
TEST(ADateValue, canBeConstructedWithInvalidDate)
|
||||
{
|
||||
const DateValue dateValue (2018, 13, 69);
|
||||
ASSERT_EQ(2018, dateValue.getDate().year);
|
||||
ASSERT_EQ(13, dateValue.getDate().month);
|
||||
ASSERT_EQ(69, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
TEST(ADateValue, setsValidDateCorrectly)
|
||||
{
|
||||
DateValue dateValue;
|
||||
DateValue::Date date;
|
||||
date.year = 2018;
|
||||
date.month = 4;
|
||||
date.day = 2;
|
||||
|
||||
dateValue.setDate(date);
|
||||
ASSERT_EQ(2018, dateValue.getDate().year);
|
||||
ASSERT_EQ(4, dateValue.getDate().month);
|
||||
ASSERT_EQ(2, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
/// \todo Probably we should avoid this ...
|
||||
TEST(ADateValue, setsInvalidDateCorrectly)
|
||||
{
|
||||
DateValue dateValue;
|
||||
DateValue::Date date;
|
||||
date.year = 2018;
|
||||
date.month = 13;
|
||||
date.day = 69;
|
||||
|
||||
dateValue.setDate(date);
|
||||
ASSERT_EQ(2018, dateValue.getDate().year);
|
||||
ASSERT_EQ(13, dateValue.getDate().month);
|
||||
ASSERT_EQ(69, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST(ADateValue, readFromByteBufferWithExpectedSize)
|
||||
{
|
||||
@ -54,7 +98,7 @@ TEST(ADateValue, doNotReadFromByteBufferWithoutExpectedSize)
|
||||
{
|
||||
DateValue dateValue;
|
||||
const byte date[8] = {0x32, 0x30, 0x31, 0x38, 0x30, 0x34, 0x30, 0x32 }; // 20180402
|
||||
ASSERT_EQ(1, dateValue.read(date, 9));
|
||||
ASSERT_EQ(1, dateValue.read(date, 6));
|
||||
}
|
||||
|
||||
TEST(ADateValue, doNotReadFromByteBufferWithExpectedSizeButNotCorrectContent)
|
||||
@ -65,7 +109,7 @@ TEST(ADateValue, doNotReadFromByteBufferWithExpectedSizeButNotCorrectContent)
|
||||
}
|
||||
|
||||
|
||||
TEST(ADateValue, readFromStringWithExpectedSize)
|
||||
TEST(ADateValue, readFromStringWithExpectedSizeAndDashes)
|
||||
{
|
||||
DateValue dateValue;
|
||||
const std::string date ("2018-04-02");
|
||||
@ -75,29 +119,90 @@ TEST(ADateValue, readFromStringWithExpectedSize)
|
||||
ASSERT_EQ(2, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
TEST(ADateValue, doNotReadFromStringWithoutExpectedSize)
|
||||
TEST(ADateValue, readFromStringWithExpectedSizeWithoutDashes)
|
||||
{
|
||||
DateValue dateValue;
|
||||
const std::string date ("20180402");
|
||||
ASSERT_EQ(1, dateValue.read(date));
|
||||
ASSERT_EQ(0, dateValue.read(date));
|
||||
ASSERT_EQ(2018, dateValue.getDate().year);
|
||||
ASSERT_EQ(4, dateValue.getDate().month);
|
||||
ASSERT_EQ(2, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
TEST(ADateValue, readFromStringWithTime)
|
||||
{
|
||||
DateValue dateValue;
|
||||
const std::string date ("2018-04-02T12:01:44.999999999");
|
||||
ASSERT_EQ(0, dateValue.read(date));
|
||||
ASSERT_EQ(2018, dateValue.getDate().year);
|
||||
ASSERT_EQ(4, dateValue.getDate().month);
|
||||
ASSERT_EQ(2, dateValue.getDate().day);
|
||||
}
|
||||
|
||||
TEST(ADateValue, doNotReadFromStringWithoutExpectedSize)
|
||||
{
|
||||
DateValue dateValue;
|
||||
ASSERT_EQ(1, dateValue.read("2018-04-0"));
|
||||
ASSERT_EQ(1, dateValue.read("2018040"));
|
||||
}
|
||||
|
||||
TEST(ADateValue, doNotReadFromStringWithExpectedSizeButNotCorrectContent)
|
||||
{
|
||||
DateValue dateValue;
|
||||
const std::string date ("2018-aa-bb");
|
||||
ASSERT_EQ(1, dateValue.read(date));
|
||||
ASSERT_EQ(1, dateValue.read("2018-24-02"));
|
||||
ASSERT_EQ(1, dateValue.read("2018-aa-bb"));
|
||||
ASSERT_EQ(1, dateValue.read("2018aabb"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST(ADateValue, copyToByteBuffer)
|
||||
TEST(ADateValue, writesRecentDateToExtendedFormat)
|
||||
{
|
||||
const DateValue dateValue (2018, 4, 2);
|
||||
const byte expectedDate[8] = {0x32, 0x30, 0x31, 0x38, 0x30, 0x34, 0x30, 0x32 }; // 20180402
|
||||
byte buffer[8];
|
||||
ASSERT_EQ(8, dateValue.copy(buffer));
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_EQ(expectedDate[i], buffer[i]);
|
||||
}
|
||||
const DateValue dateValue (2021, 12, 1);
|
||||
std::ostringstream stream;
|
||||
dateValue.write(stream);
|
||||
ASSERT_EQ("2021-12-01", stream.str());
|
||||
}
|
||||
|
||||
TEST(ADateValue, writesVeryOldDateToExtendedFormat)
|
||||
{
|
||||
const DateValue dateValue (1, 1, 1);
|
||||
std::ostringstream stream;
|
||||
dateValue.write(stream);
|
||||
ASSERT_EQ("0001-01-01", stream.str());
|
||||
}
|
||||
|
||||
TEST(ADateValue, copiesToByteBufferWithBasicFormat)
|
||||
{
|
||||
const DateValue dateValue (2021, 12, 1);
|
||||
std::array<byte, 8> buf;
|
||||
buf.fill(0);
|
||||
|
||||
const byte expectedDate[10] = {'2', '0', '2', '1', '1', '2', '0', '1'};
|
||||
ASSERT_EQ(8, dateValue.copy(buf.data()));
|
||||
ASSERT_TRUE(std::equal(buf.begin(), buf.end(), expectedDate));
|
||||
}
|
||||
|
||||
// I used https://www.epochconverter.com/ for knowing the expectations
|
||||
/* These functions convert the time to the local calendar time. Find a way to do the conversions with UTC
|
||||
|
||||
TEST(ADateValue, toLong)
|
||||
{
|
||||
const DateValue dateValue (2021, 12, 1);
|
||||
long val = dateValue.toLong();
|
||||
ASSERT_EQ(1638313200, val);
|
||||
}
|
||||
|
||||
TEST(ADateValue, toFloat)
|
||||
{
|
||||
const DateValue dateValue (2021, 12, 1);
|
||||
long val = dateValue.toFloat();
|
||||
ASSERT_FLOAT_EQ(1638313200.f, val);
|
||||
}
|
||||
|
||||
TEST(ADateValue, toRational)
|
||||
{
|
||||
const DateValue dateValue (2021, 12, 1);
|
||||
auto val = dateValue.toRational();
|
||||
ASSERT_EQ(1638313200, val.first);
|
||||
ASSERT_EQ(1, val.second);
|
||||
}
|
||||
*/
|
||||
|
||||
@ -44,7 +44,37 @@ TEST(ATimeValue, isConstructedWithArgs)
|
||||
|
||||
/// \todo add tests to check what happen with values out of valid ranges
|
||||
|
||||
TEST(ATimeValue, canBeReadFromStringHMS)
|
||||
TEST(ATimeValue, canBeReadFromCompleteBasicFormatString)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("235502");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(2, value.getTime().second);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromReducedBasicFormatString_HHMM)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("2355");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(0, value.getTime().second);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromReducedBasicFormatString_HH)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("23");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(0, value.getTime().minute);
|
||||
ASSERT_EQ(0, value.getTime().second);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromCompleteExtendedFormatString)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("23:55:02");
|
||||
@ -52,45 +82,86 @@ TEST(ATimeValue, canBeReadFromStringHMS)
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(2, value.getTime().second);
|
||||
ASSERT_EQ(0, value.getTime().tzHour);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromReducedExtendedFormatString_HHMM)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("23:55");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(0, value.getTime().second);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromBasicStringWithTimeZoneDesignatorPositive)
|
||||
{
|
||||
TimeValue value;
|
||||
std::string hms("152746+0100");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(15, value.getTime().hour);
|
||||
ASSERT_EQ(27, value.getTime().minute);
|
||||
ASSERT_EQ(46, value.getTime().second);
|
||||
ASSERT_EQ(1, value.getTime().tzHour);
|
||||
ASSERT_EQ(0, value.getTime().tzMinute);
|
||||
|
||||
value = TimeValue();
|
||||
hms = "152746+02";
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(15, value.getTime().hour);
|
||||
ASSERT_EQ(27, value.getTime().minute);
|
||||
ASSERT_EQ(46, value.getTime().second);
|
||||
ASSERT_EQ(2, value.getTime().tzHour);
|
||||
ASSERT_EQ(0, value.getTime().tzMinute);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromWideString)
|
||||
TEST(ATimeValue, canBeReadFromExtendedStringWithTimeZoneDesignatorPositive)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("23:55:02+04:04");
|
||||
std::string hms("23:55:02+04:04");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(2, value.getTime().second);
|
||||
ASSERT_EQ(4, value.getTime().tzHour);
|
||||
ASSERT_EQ(4, value.getTime().tzMinute);
|
||||
|
||||
value = TimeValue();
|
||||
hms = "23:44:03+04";
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(44, value.getTime().minute);
|
||||
ASSERT_EQ(3, value.getTime().second);
|
||||
ASSERT_EQ(4, value.getTime().tzHour);
|
||||
ASSERT_EQ(0, value.getTime().tzMinute);
|
||||
}
|
||||
|
||||
TEST(ATimeValue, canBeReadFromWideStringNegative)
|
||||
TEST(ATimeValue, canBeReadFromExtendedStringWithTimeZoneDesignatorNegative)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("23:55:02-04:04");
|
||||
std::string hms("23:55:02-04:04");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(2, value.getTime().second);
|
||||
ASSERT_EQ(-4, value.getTime().tzHour);
|
||||
ASSERT_EQ(-4, value.getTime().tzMinute);
|
||||
|
||||
value = TimeValue();
|
||||
hms = "23:44:03-04";
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(44, value.getTime().minute);
|
||||
ASSERT_EQ(3, value.getTime().second);
|
||||
ASSERT_EQ(-4, value.getTime().tzHour);
|
||||
ASSERT_EQ(0, value.getTime().tzMinute);
|
||||
}
|
||||
|
||||
/// \todo check what we should do here.
|
||||
TEST(ATimeValue, canBeReadFromWideStringOther)
|
||||
TEST(ATimeValue, cannotBeReadFromStringWithTimeZoneDesignatorWithoutSymbol)
|
||||
{
|
||||
TimeValue value;
|
||||
const std::string hms("23:55:02?04:04");
|
||||
ASSERT_EQ(0, value.read(hms));
|
||||
ASSERT_EQ(23, value.getTime().hour);
|
||||
ASSERT_EQ(55, value.getTime().minute);
|
||||
ASSERT_EQ(2, value.getTime().second);
|
||||
ASSERT_EQ(4, value.getTime().tzHour);
|
||||
ASSERT_EQ(4, value.getTime().tzMinute);
|
||||
ASSERT_EQ(1, value.read(hms));
|
||||
}
|
||||
|
||||
TEST(ATimeValue, cannotReadFromStringWithBadFormat)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user