3

I am making a project that has to keep track of dates associated with books. I store the dates as strings. I need to print out all books that were published after a given date.

Below is a loop similar to what I do in my code which replicates a problem of inconsistently comparing the value of two dates.

#include <time.h>
#include <stdio.h>
#include <string>
#include <ctime>
#include <vector>

int main()
{
    std::string comp_against = "11/1995";
    std::vector<std::string> dates = {"11/1995", "10/1990", "03/2004", "2/1992", "11/1995"};

    for(auto it = dates.begin(); it != dates.end(); ++it)
    {
        std::string date = *it;

        struct tm t1;
        struct tm t2;

        // parse the dates with mm/YYYY format
        strptime(comp_against.c_str(), "%m/%Y", &t1);
        strptime(date.c_str(), "%m/%Y", &t2);

        std::time_t s1 = mktime(&t1);
        std::time_t s2 = mktime(&t2);

        printf("%s > %s: %s\n", date.c_str(), comp_against.c_str(), (s2 > s1 ? "true" : "false"));
    }

    return 0;
}

The output on my computer:

11/1995 > 11/1995: false        <- these two are different
10/1990 > 11/1995: false                |
03/2004 > 11/1995: true                 |
2/1992 > 11/1995: false                 |
11/1995 > 11/1995: true         <- these two are different

When I run my actual code, the problem is reversed. When the date "11/1995" is compared against itself at the beginning of the loop on the first iteration, the s2>s1 evaluates to true rather than false like the output above and the second comparison evaluates to false.

EDIT: If I do the comparison with difftime, I get the same issue. Add printf("%s > %s: %s\n", date.c_str(), comp_against.c_str(), (difftime(s2, s1) > 0.0 ? "true" : "false")); and printf("\n"); after the printf in the code above and you get the below output.

11/1995 > 11/1995: false        <- different
11/1995 > 11/1995: false              |

10/1990 > 11/1995: false              |
10/1990 > 11/1995: false              |

03/2004 > 11/1995: true               |
03/2004 > 11/1995: true               |

2/1992 > 11/1995: false               |
2/1992 > 11/1995: false               |

11/1995 > 11/1995: true               |
11/1995 > 11/1995: true        <- different
K. Shores
  • 875
  • 1
  • 18
  • 46

3 Answers3

4

From the strptime(3) reference:

In principle, this function does not initialize tm but stores the values specified. This means that tm should be initialized before the call.

Solution:

struct tm t1 = {}; // same for t2 ...
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
4

Your strptime extractions are not populating everything in the struct tm constructs you're supplying to them; they're only populating the items needed to manufacture the format data you requested.

Change your struct tm declarations to this:

struct tm t1{}; // value initialize members
struct tm t2{}; // here too.

assuming you're using a C++11 compliant toolchain. Doing so, the result is:

11/1995 > 11/1995: false
10/1990 > 11/1995: false
03/2004 > 11/1995: true
2/1992 > 11/1995: false
11/1995 > 11/1995: false

And in case you wondered how:

printf("%ld %ld : %s > %s: %s\n", s1, s2, date.c_str(),
       comp_against.c_str(), (s2 > s1 ? "true" : "false"));

which produced this without value-initializing your tm structs:

815126400 815126400 : 11/1995 > 11/1995: false
817804800 657360000 : 10/1990 > 11/1995: false
815212800 1080720000 : 03/2004 > 11/1995: true
815212800 699523200 : 2/1992 > 11/1995: false
815212800 815299200 : 11/1995 > 11/1995: true

and produces this with initialization:

815126400 815126400 : 11/1995 > 11/1995: false
815126400 654681600 : 10/1990 > 11/1995: false
815126400 1078041600 : 03/2004 > 11/1995: true
815126400 696844800 : 2/1992 > 11/1995: false
815126400 815126400 : 11/1995 > 11/1995: false
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
1

Using this free, open-source, header-only, C++11/14 date library, here is a way to make your code simpler and less error prone:

#include "date.h"
#include <iostream>
#include <vector>

int main()
{
    using namespace date::literals;
    auto comp_against = 1995_y/11;
    std::vector<date::year_month> dates = {1995_y/11, 1990_y/10, 2004_y/03,
                                           1992_y/2, 1995_y/11};

    std::cout << std::boolalpha;
    for(auto it = dates.begin(); it != dates.end(); ++it)
        std::cout << *it << " > " << comp_against << ": "
                  << (*it > comp_against) << '\n';
}

The output is:

1995/Nov > 1995/Nov: false
1990/Oct > 1995/Nov: false
2004/Mar > 1995/Nov: true
1992/Feb > 1995/Nov: false
1995/Nov > 1995/Nov: false
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577