1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
#include <ctime>
#include <string>
#include <cmath>
struct date_time
{
date_time( std::time_t simulated_time )
: last_update_time( std::time(nullptr) ), simulated_time(simulated_time) {}
date_time( int year, int month /* jan == 1 */, int day )
{
std::tm tm {} ;
tm.tm_year = year - 1900 ;
tm.tm_mon = month - 1 ;
tm.tm_mday = day ;
simulated_time = std::mktime( std::addressof(tm) ) ;
if( simulated_time == -1 ) throw std::out_of_range( "time is out of range" ) ;
last_update_time = std::time(nullptr) ;
}
static double ticks_per_sec() // just to be pedantically safe: almost always is 1 tick per second
{
static const int NTICKS = 10000 ;
static const double ticks_per_second = NTICKS / std::difftime( std::time_t(NTICKS*2), std::time_t(NTICKS) ) ;
return ticks_per_second ;
}
// call to update the simulated time. or call periodically
// from a background thread (with std::atomic<time_t> simulated_time)
void update()
{
const auto now = std::time(nullptr) ;
// add the elapsed time between now and last updated time
simulated_time += ticks_per_sec() * std::difftime( now, last_update_time ) ;
last_update_time = now ; // and set the last updated time to now
}
void advance_secs( int secs )
{
update() ;
simulated_time += secs * ticks_per_sec() ;
}
void advance_minutes( int minutes ) { advance_secs( minutes * 60 ) ; }
void advance_hours( int hrs ) { advance_minutes( hrs * 60 ) ; }
void advance_days( int days ) { advance_hours( days * 24 ) ; }
void advance_months( int months )
{
update() ;
std::tm tm = *std::localtime( std::addressof(simulated_time) ) ;
tm.tm_mon += months ;
simulated_time = std::mktime( std::addressof(tm) ) ;
if( simulated_time == -1 ) throw std::out_of_range( "time is out of range" ) ;
last_update_time = std::time(nullptr) ;
}
void advance_years( int yrs ) { advance_months( yrs * 12 ) ; }
std::time_t current_simulated_time() { update() ; return simulated_time ; }
std::string current_simulated_time_str()
{
update() ;
const auto ptm = std::localtime( std::addressof(simulated_time) ) ;
if(ptm) // if the c library supports handling this value for std::tm
{
char buffer[1024] {} ;
std::strftime( buffer, sizeof(buffer), "%A, %B %d %H:%M:%S %Y", ptm ) ;
return buffer ;
}
else return "time beyond what the C library can handle gracefully\n" ;
}
double difftime( const date_time& that ) const
{
return std::difftime( this->simulated_time, that.simulated_time ) +
std::difftime( this->last_update_time, that.last_update_time ) ;
}
private:
std::time_t last_update_time ; // actual calendar time
std::time_t simulated_time ; // simulated time at the time of last update
// save the state of the date_time (current simulated time) into an output stream
friend std::ostream& operator << ( std::ostream& stm, const date_time& t )
{ return stm << t.simulated_time ; }
// save the state of the updated date_time (updated simulated time) into an output stream
friend std::ostream& operator << ( std::ostream& stm, date_time& t )
{ return stm << t.current_simulated_time() ; }
// restore the state of the date_time (current simulated time) from an input stream
friend std::istream& operator >> ( std::istream& stm, date_time& t )
{
std::time_t stime ;
if( stm >> stime ) t = { stime } ;
return stm ;
}
// date_time comparisons are to within a second
friend bool operator== ( const date_time& a, const date_time& b )
{ return std::abs( a.difftime(b) ) < 1.0 ; }
friend bool operator!= ( const date_time& a, const date_time& b ) { return !( a == b ) ; }
friend bool operator< ( const date_time& a, const date_time& b )
{ return a.difftime(b) <= -1.0 ; }
friend bool operator> ( const date_time& a, const date_time& b )
{ return a.difftime(b) >= 1.0 ; }
friend bool operator>= ( const date_time& a, const date_time& b ) { return !( a < b ) ; }
friend bool operator<= ( const date_time& a, const date_time& b ) { return !( a > b ) ; }
};
|