0

I put every date in a vector of struct (vector<value>) :

struct value {
    string code;
    string date;
    string name;
};

The format of the dates is "YYYY-MM-DD HH:MM:SS"

I want to get dates between two given dates (for example from 01/01/2016 to 01/02/2016). How i can do that?

Fr4ncx
  • 356
  • 6
  • 22
  • 1
    nice video [here](https://www.youtube.com/watch?v=tzyGjOm8AKo) code [here](https://github.com/HowardHinnant/date/blob/master/date.h) – sp2danny Jan 26 '16 at 09:26
  • 1
    if you used "YYYY/MM/DD" format you would be able to sort alphabetically by `date` field (w/o parsing it) and use `std::lower_bound` twice to get required range. – Andriy Tylychko Jan 26 '16 at 10:37
  • It depends on what form you need results in, but I'd use `copy_if` with a lambda (functor pre-11). – alcedine Jan 26 '16 at 10:42
  • I noticed your somewhat new to SO, If one of the answers here has solved your issue, don't forget to accept the solution. – John Bargman Jan 27 '16 at 10:47

3 Answers3

2

This problem is twofold:

  1. How do you get sortable date values from string representations
  2. How can you effectively sort said values.

Finding valid timestamps from a date-string

C++ uses the time_t object as a valid number of seconds from a set date (Jan 1, 1970 UTC) There's plenty of concise information about that, in every-case you may consider this an Integer-representation of time in seconds.

Next you need to know how to parse your data into a time-stamp: here are some rather some helpful links.

My preferred method is mktime - there's an example of that Here on stack-overflow. Also it seems someone else has done the same course as you ;)

You might want to consider using A function of your own design, if the date format is unusual. In this case, using scanf is often the simplest way - the interface of this function is somewhat old-school "c-style", but that doesn't change the simple fact it works, and well. Here's a link to someone reading a simple-date with scanf.

Turns out the code I wrote below is close to The answer to this great question

#include <stdio.h>
#include <time.h>  
time_t GetDateFromObject(value & Date_Object)
{
    char * Date_String = Date_Object.date.c_str();
    int year, month, day, hour, minute, second;
    if(sscanf(Date_String , "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second) == 6)
    { 
        time_t rawTime;
        time(&rawTime);
        struct tm * parsedTime;
        parsedTime = localtime(&rawTime);

        // tm_year is years since 1900
        parsedTime->tm_year = year - 1900;
        // tm_months is months since january
        parsedTime->tm_mon = month - 1;
        parsedTime->tm_mday = day;
        parsedTime->tm_hour = hour;
        parsedTime->tm_min = minute;
        parsedTime->tm_sec = second;
        return mktime(parsedTime);
   }
}

Association and sorting of dates

Once you know how to get a time_t from your date, you can start creating an associative array of the data - in this case I'll be using a map.

With that, here's an example of using a map to insert, sort, and output the data.

#include<iostream>
#include<map>
#include<vector>
#include<ctime>

struct value {
    std::string code;
    std::string date;
    std::string name;
};

void Print_Range(std::vector<value> & Data, value & Date_Start, value & Date_end)
{
    std::map<time_t, value *> Associated_Data;
    for(auto Date_Object : Array_Of_Dates)
    {
        time_t Object_Time = GetDateFromObject(Date_Object);
        Associated_Data.insert(std::make_pair(Object_Time, & Date_Object); 
    }
    //as the std::map is sorted by-default, 
    //we can know locate the iterator for any two time codes
    time_t Search_From = GetDateFromObject(Date_Start);
    time_t Search_To = GetDateFromObject(Date_End);
    auto Start_IT = Associated_Data.find(Search_From);
    auto End_IT = Associated_Data.find(Search_To);

    std::cout << "Printing all dates in range \n";
    for(auto IT=Start_IT; IT != End_IT; IT++)
    {
        std::cout << IT->Second->date << '\n';
    }
}

Notes:

  1. I use C++11 syntax here, if you don't understand Range based loops you might want to read up on them.
  2. I'm assuming the structure you described is stored in a vector.
  3. The "GetDateFromObject" function I'm using here is a placeholder for whatever function you use to get the timestamp)
  4. Inserting data into a map using std::make_pair.
  5. I'm holding pointers to the original value-objects.
Community
  • 1
  • 1
John Bargman
  • 1,267
  • 9
  • 19
2

The date format YYYY-MM-DD HH:MM:SS is special in that a lexicographic (letter by letter) comparison is the same as a time comparison, so you can just use that.

If your container isn't initially sorted by date, you'll have to go through all the dates one by one. std::copy_if provides a nice way of doing this:

std::vector<value> get_between(const std::vector<value>& v,
        const std::string& from, const std::string& to)
{
    std::vector<value> u;
    std::copy_if(v.begin(), v.end(), std::inserter(u, u.begin()),
    [from,to](const auto& val) {
        return val.date >= from && val.date <= to;
    });
    return u;
}

copy_if looks through [v.begin(), v.end()[ and plonks the elements into u from u.begin() onwards whenever the lambda returns true. They'll be in the same order as you gave them in.

If your range is sorted, you can use std::lower_bound and std::upper_bound to get the start and end iterators instead:

std::vector<value> get_between(const std::vector<value>& v,
        const std::string& from, const std::string& to)
{
    value fromv { "", from, "" };
    auto begin = std::lower_bound(v.begin(), v.end(), fromv,
    [](const auto& lhs, const auto& rhs) {
        return lhs.date < rhs.date;
    });

    value tov { "", to, "" };
    auto end = std::upper_bound(begin, v.end(), tov,
    [](const auto& lhs, const auto& rhs) {
        return lhs.date < rhs.date;
    });

    return std::vector<value>(begin, end);
}

lower_bound and upper_bound find the first value of at least from and the first value of more than to, so that the range [lb, ub[ is the range with values [from, to].

alcedine
  • 909
  • 5
  • 18
1

In example you could do like that: convert all dates to time_t value (numberical representation of the date and time) and then iterate through your vector and use normal comparation between three time_t numbers. For reference: man mktime, man strptime.

dmi
  • 1,424
  • 1
  • 9
  • 9
  • I am starting hating the anonimous downvoters demotivating helping other people :(. – dmi Jan 26 '16 at 09:27
  • I agree about said down-votes, you have my up-vote for trying to help. That said, I imagine your down votes came from - You didn't present any code (even to convert the date values) You referenced Man (Remember that most windows users don't know what man is, nor do they like using it when they do.) – John Bargman Jan 26 '16 at 09:53