2017-10-19 2 views
0

J'ai construit une fonction qui prend en entrée une date spécifique et retourne cette date au format std::chrono::milliseconds.Pouvons-nous prendre une variable `std :: chrono :: milliseconds` d'une plage du même type?

milliseconds lowerRangeBound = TimeStamp(mm, dd, HH, MM, SS, yyyy); 

Par exemple,

milliseconds a = TimeStamp(8/*month*/, 23/*day*/, 14/*hours*/, 46/*minutes*/, 32/*seconds*/, 2017/*year*/); 

retours dans un format de chaîne convertie: 2017.08.23-14.46.32

Ce que je veux maintenant faire et ne fonctionne pas est donné deux dates (milliseconds) de prendre un échantillon aléatoire date à l'intérieur de la gamme définie par ces deux dates. Par exemple, étant donné

milliseconds a = TimeStamp(8/*month*/, 23/*day*/, 13/*hours*/, 46/*minutes*/, 32/*seconds*/, 2017/*year*/); 
milliseconds b = TimeStamp(10/*month*/, 23/*day*/, 13/*hours*/, 46/*minutes*/, 32/*seconds*/, 2017/*year*/); 

le résultat attendu est un milliseconds c qui sous forme de chaîne est une date comme celui-ci, 2017.09.13-12.56.12. Notez que la sortie désirée est un milliseconds, le format de chaîne est fourni afin de parler en readable format.

Ce que j'ai essayé jusqu'à présent est de convertir chaque variable milliseconds en nombre long (.count()) et obtenir un randomlong dans la gamme [a,b]. Cependant, la date de sortie n'est pas pertinente: 1977.12.06-16.27.02.

Pourriez-vous donner un coup de main?

Merci à l'avance.

EDIT: Le soufflet de code est inspiré par cette link

milliseconds TimeStamp(int mm, int dd, int HH, int MM, int SS, int yyyy) { 

    tm ttm = tm(); 
    ttm.tm_year = yyyy - 1900; // Year since 1900 
    ttm.tm_mon = mm - 1; // Month since January 
    ttm.tm_mday = dd; // Day of the month [1-31] 
    ttm.tm_hour = HH; // Hour of the day [00-23] 
    ttm.tm_min = MM; 
    ttm.tm_sec = SS; 

    time_t ttime_t = mktime(&ttm); 
    system_clock::time_point time_point_result = std::chrono::system_clock::from_time_t(ttime_t); 
    milliseconds now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(time_point_result).time_since_epoch(); 
    return now_ms; 
} 

milliseconds getRandomTimestamp(int mm_1, int dd_1, int HH_1, int MM_1, int SS_1, int yyyy_1, 
    int mm_2, int dd_2, int HH_2, int MM_2, int SS_2, int yyyy_2, int N) { 

    milliseconds lowerRangeBound = fixedTimeStamp(mm_1, dd_1, HH_1, MM_1, SS_1, yyyy_1); 
    milliseconds upperRangeBound = fixedTimeStamp(mm_2, dd_2, HH_2, MM_2, SS_2, yyyy_2); 

    long lowerRange_ = lowerRangeBound.count(); 
    long upperRange_ = upperRangeBound.count(); 

    //long output = rand() % (upperRange_ - lowerRange_ + 1) + lowerRange_; 
    // rand() replaced after @Jarod42's suggestion. 
    std::default_random_engine generator; 
    std::uniform_int_distribution<int> distribution(lowerRange_, upperRange_); 
    long output = distribution(generator);   

    std::chrono::duration<long> dur(output); 
    return dur; 
} 
+1

* Qu'avez-vous essayé? S'il vous plaît nous montrer un [Exemple minimal, complet et vérifiable] (http://stackoverflow.com/help/mcve), au lieu de simplement le décrire. –

+2

hors-sujet à votre question: 'myTimeStamp (8, 23, 13, 46, 32, 2017);' est un horrible, la tête-mal, erreur conception sujettes – bolov

+0

J'espère que le poste est maintenant plus attrayant. Merci pour l'intérêt en passant! – Darkmoor

Répondre

2

Il se pourrait que vous obtenez confus avec les mathématiques en raison des préoccupations de mélange.

Je suggère que vous écrivez votre routine "timestamp aléatoire" en termes de millisecondes.

Vous pouvez effectuer les conversions de/vers les horodatages dans une étape distincte.

Je voudrais écrire quelque chose de routine comme ceci:

#include<random> 
#include<chrono> 
#include<cassert> 
#include<iostream> 

template<class Eng> 
std::chrono::milliseconds getRandomTimestamp(Eng& eng, 
              std::chrono::milliseconds low, 
              std::chrono::milliseconds high) 
{ 
    // deduce the actual integer type. 
    // Otherwise we'll have to guess what T is when using 
    // uniform_int_distribution<T> 
    using count_type = decltype(low.count()); 

    // from this we can deduce the correct integer distribution 
    using dist_type = std::uniform_int_distribution<count_type>; 

    // create the distribution generator 
    auto dist = dist_type(low.count(), high.count()); 

    // and apply it to the random generator, returning 
    // milliseconds somewhere between low and high 
    return std::chrono::milliseconds(dist(eng));  
} 


int main() 
{ 
    using namespace std::literals; 

    std::random_device dev; 
    std::default_random_engine eng(dev()); 

    auto t1 = 10'000'000ms; 
    auto t2 = 11'000'000ms; 
    auto t3 = getRandomTimestamp(eng, t1, t2); 

    assert(t1 <= t3); 
    assert(t3 <= t2); 

    std::cout << t1.count() << std::endl; 
    std::cout << t2.count() << std::endl; 
    std::cout << t3.count() << std::endl; 
} 
+0

Merci pour l'intérêt! – Darkmoor

1

Vous pouvez faire ce beaucoup plus facile si vous prenez avantage de Howard Hinnant's free, open-source header-only date/time library. La syntaxe est concise et très lisible. Par exemple, vous pouvez créer chrono::time_point s de n'importe quelle précision (par exemple milliseconds) avec date::sys_time<std::chrono::milliseconds>.

Comme votre problème concerne time_point s par opposition à durations s, c'est le meilleur moyen de passer du point de vue de la sécurité de type. Voici une façon d'écrire getRandomTimestamp:

#include "date/date.h" 
#include <iostream> 
#include <random> 

date::sys_time<std::chrono::milliseconds> 
getRandomTimestamp(date::sys_time<std::chrono::milliseconds> lowerBound, 
        date::sys_time<std::chrono::milliseconds> upperBound) 
{ 
    static std::default_random_engine generator; 
    using namespace std::chrono; 
    std::uniform_int_distribution<milliseconds::rep> distribution{ 
     lowerBound.time_since_epoch().count(), 
     upperBound.time_since_epoch().count()}; 
    return date::sys_time<milliseconds>{milliseconds{distribution(generator)}}; 
} 

Maintenant, votre seul problème est de savoir comment créer ces time_point s!Et cela se révèle être trivial:

int 
main() 
{ 
    using namespace date::literals; 
    using namespace std::chrono_literals; 
    auto t1 = date::sys_days{2017_y/8/23} + 13h + 46min + 32s; 
    auto t2 = date::sys_days{2017_y/10/23} + 13h + 46min + 32s; 
    auto t3 = getRandomTimestamp(t1, t2); 
    // ... 
} 

Au-dessus date::sys_days est juste une autre chrono::time_point mais avec une précision de days au lieu de milliseconds (il aurait pu également orthographié date::sys_time<date::days>). Et 2017_y/8/23 est un littéral de type date::year_month_day qui signifie 2017.08.23. J'ai profité des littéraux chrono de C++ 14. Si vous êtes en C++ 11, vous devez utiliser hours{13} à la place de 13h.

Enfin, vous pouvez formater et diffuser en t3 très facilement dans une grande variété de formats, par exemple:

std::cout << date::format("%Y.%m.%d-%T", t3) << '\n'; 

Cette sortie juste pour moi:

2017.09.04-17:14:04.220 

Si vous avez Exécuter- temps des valeurs entières au lieu de littéraux en entrée, qui est facilement manipulé et:

using namespace date; 
auto t1 = sys_days{year{y}/m/d} + hours{h} + ... 

Et si vous avez besoin d'obtenir l'une des valeurs de champ de t3, qui est également très facile, par exemple:

sys_days sd = floor<days>(t3); 
time_of_day<milliseconds> tod(t3-sd); 
year_month_day ymd = sd; 

ymd a getters pour l'année, le mois et le jour, et tod a getters pendant des heures, minutes , secondes et millisecondes (cette dernière étant orthographiée subseconds).