String to Date with exceptions c++

May 23, 2020 at 8:11pm
I have a task to make static method getDateFromString in class Date that receives the date as a string as the only parameter and returns it as Date. I also need to check whether the date is in the correct format using UnparseableDateException (with which I also am having troubles) exception class before converting it to Date. Here is my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//UnparseableDateException.h

class UnparseableDateException : public exception {
    private:
        string message;
    public:
        UnparseableDateException() : message ("Unparseable date: 21.5a.2020"){}
        const char* what() const noexcept override{
        return message.c_str();
        }

    };


//Date.h

class Date {
private:
    int day;
    int month;
    int year;
public:
    Date(int day, int month, int year);

    int getDay() const;

    int getMonth() const;

    int getYear() const;

    string toString() const;

    static string getDateFromString(string a);
};

//Date.cpp
#include "Date.h"

int Date::getDay() const {
    return day;
}

int Date::getMonth() const {
    return month;
}

int Date::getYear() const {
    return year;
}

Date::Date(int day, int month, int year) {
this->day = day;
this->month = month;
this->year = year;
}

string Date::toString() const {
    stringstream ss;
    ss << "day: " << day << " month: " << month << " year: " << year;
    return ss.str();
}

string Date::getDateFromString(string a) {
 // ???
}
Last edited on May 23, 2020 at 8:11pm
May 23, 2020 at 8:31pm
To start, you have to define what the requirement for a valid date and an invalid date string is. Then, write code that fulfills that requirement.

Is "21.5.2020" (Day.Month.Year) the format you're saying is valid?
What deviation from this is allowed?
Is "21.05.2020" valid? What about "21/5/2020"?
What about dates that are "parseable", but in valid, like "29/2/2019"? Should that also throw an exception?

Anyway, the first part is just parsing the date in some expected format.
So if your expected format is "M.D.Y", then I suggest using a combination of stringstreams with '.' being the delimter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Example program
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <stdexcept>

using namespace std;

struct Date {
    int day;
    int month;
    int year;
};

bool is_number(string str)
{
    for (size_t i = 0; i < str.length(); i++)
    {
        if (!isdigit(str[i]))
        {
            return false;   
        }
    }
    return true;
}

Date parse(string str)
{
    istringstream iss(str);
    
    string day_str;
    getline(iss, day_str, '.');
    
    string month_str;
    getline(iss, month_str, '.');
    
    string year_str; 
    getline(iss, year_str);
    
    if (!is_number(day_str))
    {
        throw exception();   
    }
    int day = atoi(day_str.c_str());
    if (!day)
    {
        throw exception();
    }
    
    if (!is_number(month_str))
    {
        throw exception();   
    }
    int month = atoi(month_str.c_str());
    if (!month)
    {
        throw exception();
    }
    
    if (!is_number(year_str))
    {
        throw exception();   
    }
    int year = atoi(year_str.c_str());
    if (!year)
    {
        throw exception();
    }
     
    return Date{day, month, year};
}

int main()
{
    try
    {
        Date date = parse("21.5a.2020");
        cout << date.day << " " << date.month << " " << date.year << '\n';
    }
    catch (...)
    {
        cout << "Invalid date\n";   
    }

}

(My implementation could be reduced by using an array int[3] and some for loops)
Last edited on May 23, 2020 at 8:38pm
May 23, 2020 at 8:31pm
Just do the reverse of your toString function, and throw the exception if you can’t read one of the pieces of information.
May 23, 2020 at 8:46pm
No deviation is allowed from that format. It should be Day . (dot) month (with no 0 if single digit) . (dot) year
May 23, 2020 at 9:42pm
See this, there is a validation list, if date is bad exception is thrown, otherwise date is printed to console, see code comments for more info:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <exception>
using namespace std;

vector<int> Date::getDateFromString(string str)
{
        // holds extracted date
	std::vector<int> date;

        // holds validation list
	std::vector<char> valid = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };

        // sample to work with (day, month of year)
	std::istringstream ss(str);

        // ignore dots
	while (std::getline(ss, str, '.'))
	{
                // temporary
                string temp = str;

                // doing validation here, accepting only numbers
		for (const auto& ref : str)
			if (std::find(valid.begin(), valid.end(), ref) == valid.end())
				throw std::invalid_argument("date is not valid");
			else if (ref == '0' && str.size() == 2)
			{
                                // remove zero if found
				std::size_t pos = temp.find('0');
				temp.erase(pos, pos);
			}

                // if OK convert to integer and store day, month and year
		int num = std::atoi(str.c_str());

		date.push_back(num);
	}

	return date;
}

// TEST CASE
int main() try
{
	// try to screw up this date to see exception
	std::string date("21.5.2020");
	auto result = Date::getDateFromString(date);

        // show result, integers (day, month and year)
	for (const auto& ref : result)
	{
		std::cout << ref << std::endl;
	}
}
catch (std::invalid_argument& ex)
{
	// If invalid input exception is trhown and programs aborts
	std::cout << ex.what() << std::endl;
}
Last edited on May 23, 2020 at 10:17pm
May 23, 2020 at 10:18pm
I have edited my post, can you try again please.

What compiler are you using? what do you use to compile code?

don't forget the headers from my post!
Last edited on May 23, 2020 at 10:19pm
May 23, 2020 at 10:31pm
I am using CLion, but now it works fine i forgot <algorithm>...
I have only one question: In main when i include class "Date.h" i 've got error like this:
error: 'vector' does not name a type; did you mean 'perror'?
This error shows even when i only add #include "Date.h" and main is empty, and when i delete "Date.h" from main.cpp i am not having errors, what could possibly be the problem here?
May 23, 2020 at 10:35pm
Put this line on top of your Date.h
#include <vector>

also inside your header function getDateFromString should be using be std::vector not vector

example:
std::vector<int> Date::getDateFromString(std::string str);

Alternatively copy all header includes into your headers to be 100% sure.

btw. is this for your assignment?
Last edited on May 23, 2020 at 10:38pm
May 23, 2020 at 10:39pm
Okay, it worked finally :D thanks, and yes it's for assignment
May 23, 2020 at 10:41pm
OK, I'm glad it did, but don't be silly, your teacher will know you didn't write this ;)
May 23, 2020 at 10:51pm
I don't use this code, I just want to see how it works, so I can learn it and do it myself.
May 23, 2020 at 11:07pm
that's good to hear!
Topic archived. No new replies allowed.