2

I wrote this code snippet to generate random dates:

std::time_t curr_time = time(0);
std::time_t ten_years = 365 * 12 * 30 * 24 * 60;
std::time_t rand_date = curr_time - std::rand() % ten_years;
tm *ltm = std::localtime(&rand_date);
std::cout << ltm->tm_year + 1900 << " " << ltm->tm_mon + 1 << " " << ltm->tm_mday << std::endl;

However it always gives me the current date. What am i doing wrong?

WonderCsabo
  • 11,947
  • 13
  • 63
  • 105

6 Answers6

5

std::rand() may return rather small values, 0..32767 is the minimum range, and does so on some popular 32-bit platforms (MSVC among them). With time_t in seconds this only gives you about eight hours of random noise.

Try combining the results from a pair of std::rand calls instead. E.g. (std::time_t) std::rand() * RAND_MAX + std::rand() or switch to a better random number generator.

doynax
  • 4,285
  • 3
  • 23
  • 19
2

I would suggest you don't modify a time_t directly, as the implementation is not specified by the standard. Better to convert it a la this question: How to add one day to a time obtained from time()

Community
  • 1
  • 1
Phil H
  • 19,928
  • 7
  • 68
  • 105
  • `rand()` returns a value in range `[0, RAND_MAX]`, not `[0, 1]`. See http://www.cplusplus.com/reference/cstdlib/rand/ – Zong Nov 28 '13 at 16:49
2

I would suggest to do it differently, based on the CPP Reference:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

int main() {
    time_t timer;
    struct tm x_years;
    struct tm* current;
    int how_many_years = 10;
    srand (time(NULL));
    int randomYear = (rand()%how_many_years)+1;
    int randomMonth = (rand()%12)+1;
    int randomDays = (rand()%30)+1;


    time(&timer);  /* get current time; same as: timer = time(NULL)  */
    current = localtime(&timer);
    x_years.tm_hour = 0;
    x_years.tm_min = 0;
    x_years.tm_sec = 0;
    x_years.tm_year = current->tm_year - randomYear;
    x_years.tm_mon = (current->tm_mon - randomMonth) <= 0 ? current->tm_mon + (12-randomMonth) : current->tm_mon - randomMonth;
    x_years.tm_mday = (current->tm_mday - randomDays) <= 0 ? current->tm_mday + (30-randomDays) : current->tm_mday - randomDays;

    //returns seconds ever since the random generated date until now
    cout << "Years rolled back: " << randomYear << endl;
    cout << "Months rolled back: " << randomMonth << endl;
    cout << "Days rolled back: " << randomDays << endl;
    cout << endl;
    cout << "Current Year: " <<  current->tm_year+1900 << endl;
    cout << "Current Month: " <<  current->tm_mon << endl;
    cout << "Current Day: " <<  current->tm_mday << endl;
    cout << endl;
    cout << "Year: " <<  x_years.tm_year+1900 << endl;
    cout << "Month: " <<  x_years.tm_mon << endl;
    cout << "Day: " <<  x_years.tm_mday << endl;
}

EDIT

I have edited the code, and with it, you can even select how many years you want to go back. Basically, you go back X years in the time, and you can get the date rollbacked in the x_years struct.

Hope this helped!

Nelspike
  • 123
  • 2
  • 6
1

The following solution uses C++11 with an internal uniform_int_distribution:

// uniform_time_dist.h
#include <chrono>
#include <random>

template <class TimePoint>
class uniform_time_distribution{
public:
  uniform_time_distribution(TimePoint start, TimePoint end)
    : m_start(start), m_end(end),
    m_seconds(std::chrono::duration_cast<std::chrono::seconds>(end - start))
  {}

  template <class Generator>
  TimePoint operator()(Generator && g){
    std::uniform_int_distribution<std::chrono::seconds::rep> d(0, m_seconds.count());

    return m_start + std::chrono::seconds(d(g));
  }

private:
  TimePoint m_start;
  TimePoint m_end;
  std::chrono::seconds m_seconds;
};

You can use it like any other distribution function with a generator:

// uniform_time_dist.h -- continuation 
template <class TimePoint>
TimePoint randomTime(TimePoint start, TimePoint end){
  static std::random_device rd;
  static std::mt19937 gen(rd());

  uniform_time_distribution<TimePoint> t(start, end);

  return t(gen);
}

You can combine this method with your time_t functions by using clock::to_time_t:

#include <iostream>
#include "uniform_time_dist.h" // see above

using namespace std::chrono;

int main(){
  auto k = system_clock::to_time_t(randomTime(
    system_clock::now(),
    system_clock::now() + hours(24 * 365 * 10)));

  std::cout << std::ctime(&k);    
}

Note that the class above is just a small sketch, you should be able to improve it vastly in order to match the other distribution functions.

Zeta
  • 103,620
  • 13
  • 194
  • 236
  • Thanks! This is a fully working and very nicely written code. Unfortunately MINGW has a known bug: it crashes with `std::random_device`. I replaced the PRNG initialization with: `static std::mt19937 gen(std::time(nullptr));`. – WonderCsabo Nov 28 '13 at 18:03
  • 1
    @WonderCsabo: Yeah, I remember that one, haven't used `random_device` on Windows for some time, but I used this (rather ugly) switch a year ago: `inline unsigned long initial_seed(){ #ifdef __MINGW32__ return static_cast(std::time(0)); #else return static_cast(std::random_device()()); #endif }`. – Zeta Nov 28 '13 at 18:15
0

I managed to create a solution based on Phil's answer:

time_t currTime = time(0);
tm *ltm = std::localtime(&currTime);
ltm->tm_mday = std::rand() % 3650 * -1;
time_t next = mktime(ltm);
ltm = std::localtime(&next);
std::cout << ltm->tm_year + 1900 << " " << ltm->tm_mon + 1 << " " << ltm->tm_mday << std::endl;
WonderCsabo
  • 11,947
  • 13
  • 63
  • 105
0

If you are using boost libs you can use this class that I wrote to get random dates:

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};
madx
  • 6,723
  • 4
  • 55
  • 59