3

I have a program where I need to read the date being entered. I am able to read the date correctly if the date is entered D/MM/YYYY. When ever a date is entered as DD/MM/YYYY it reads incorrectly because the substr isn't accounting for days that has 2 digits in it .

so there are 4 different correct ways dates can be entered:

D/M/YYYY

DD/MM/YYYY

D/MM/YYYY

DD/M/YYYY

Furthermore, if an incorrect day/month is entered such as 100/4/2018 it hinders reading the rest of the string correctly. The year and month.

Through my own testing I had a for loop looking for the first "/" then reading what came before it but that didn't work.

How can I account for these different ways of entering dates?

MYDate::MYDate(std::string date) {

//int size = date.length();




SetYear(year_ = std::atoi(date.substr(5, 4).c_str()));
SetMonth(month_ = std::atoi(date.substr(3, 2).c_str()));
SetDay(day_ = std::atoi(date.substr(0, 2).c_str()));


/*
9/9/2001
09/09/2001
9/09/2001
09/9/2001
*/
}
darune
  • 10,480
  • 2
  • 24
  • 62
COYG
  • 1,538
  • 1
  • 16
  • 31
  • have you considered using regular expressions? – agent_bean Nov 09 '18 at 09:14
  • As long as you try to use hardcoded string positions, this problem will remain. Imagine you just met someone who has never seen a date in their life and tried to describe to them how to read a date; you certainly wouldn't tell them "characters in positions 3 and 4 represent the month". – aaaaaa123456789 Nov 09 '18 at 09:16
  • Have a look at [std::string and it's find method](https://en.cppreference.com/w/cpp/string/basic_string). Looking for the / is the right idea. How did it not work for you? – super Nov 09 '18 at 09:19
  • @rafaelgonalez I did but don't know where to start with them. – COYG Nov 09 '18 at 09:23
  • @super i actually did use find before the for loop. I think they didn't work through researching because the / is an escape character. So I tried // & \/ but no joy. – COYG Nov 09 '18 at 09:25
  • If you have something you tried but didn't work, edit it into your question, and tell us what went wrong with it. (Also, `/` is not an escape character.) – aaaaaa123456789 Nov 09 '18 at 09:30
  • @aaaaaa123456789 I deleted it but will write it and then edit. – COYG Nov 09 '18 at 09:32
  • @darune's answered worked perfect. – COYG Nov 09 '18 at 10:08

3 Answers3

3

You may just use an istringstream to some simple parsing if you replace the '/'. Like this:

std::replace( date.begin(), date.end(), '/', ' ');

std::istringstream stream(date);

stream >> day_;
stream >> month_;
stream >> year_;
darune
  • 10,480
  • 2
  • 24
  • 62
1

Use std::string::substr to split into tokens. Here is a naive example, which could be enough to get you started:

#include <iostream>
#include <string>

int main () {
    std::string s = "09/09/2001";
    std::string delimiter = "/";

    size_t pos = 0;
    std::string token;
    int i = 0;
    while ((pos = s.find(delimiter)) != std::string::npos) {
        token = s.substr(0, pos);
        !i++ ? std::cout << "Day: " << token << std::endl : std::cout << "Month: " << token << std::endl;
        s.erase(0, pos + delimiter.length());
    }
    std::cout << "Year: " << s << std::endl;
    return 0;
}

Output:

Day: 09
Month: 09
Year: 2001

I checked with all four inputs you asked for, and it works.

PS: You probably would like these tokens in numbers, so read How can I convert a std::string to int?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
0

There are various ways how you can extract\parse date and time from a date format, and here is what I would do:

#include <tuple>
#include <sstream>

#define IS_LEAP_YEAR(year) ((year) % 4 == 0 && (year) % 100 != 0 || (year) % 100 == 0 && (year) % 400 == 0 || (year) % 400 == 0)

std::tuple<int, int, int> GiveDateFromFormat(std::string const& format, char const delimiter = '\\')
{
    std::stringstream ss(format);
    std::string date, month, year;
    std::getline(ss, date, delimiter);
    std::getline(ss, month, delimiter);
    std::getline(ss, year, delimiter);
    auto date_num = std::stoi(date), month_num = std::stoi(month), year_num = std::stoi(year);
    if (month_num > 12 || month_num < 1 || date_num >(month_num % 2 == 0 && month_num != 2 ? 30 : 31) ||
        date_num > (IS_LEAP_YEAR(year_num) ? 29 : 28) && month_num == 2)
        throw std::invalid_argument("Date does not exist!");
    return std::make_tuple(date_num, month_num, year_num);
}

Note that the date checking is just for making sure, it is not required...

Much shortened, it can even be:

std::tuple<int, int, int> GiveDateFromFormat(std::string const& format, char const delimiter = '\\')
{
    std::stringstream ss(format);
    std::string date, month, year;
    std::getline(ss, date, delimiter);
    std::getline(ss, month, delimiter);
    std::getline(ss, year, delimiter);
    return std::make_tuple(std::stoi(date), std::stoi(month), std::stoi(year));
}

Usage:

int main()
{
    auto date_time = GiveDateFromFormat("29\\2\\2016");
    std::cout << "Date: " << std::get<0>(date_time) << std::endl
              << "Month: " << std::get<1>(date_time) << std::endl
              << "Year: " << std::get<2>(date_time) << std::endl;
    return 0;
}

Output:

Date: 29
Month: 2
Year: 2016

Ruks
  • 3,886
  • 1
  • 10
  • 22