├── .travis.yml ├── LICENSE ├── README.md ├── sample.cc ├── sand.cpp └── sand.hpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | 4 | compiler: 5 | - clang 6 | - gcc 7 | 8 | install: 9 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.pre.sh | bash -x 10 | 11 | script: 12 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.build.sh | bash -x 13 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.run.sh | bash -x 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 r-lyeh (https://github.com/r-lyeh) 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sand 2 | ==== 3 | 4 | Sand is a lightweight timer controller (C++11). 5 | 6 | Features: 7 | - [x] Wrapper around Unix stamps, hires timers and calendars. 8 | - [x] Portable. Does not rely on standard C library or locales. 9 | - [x] Cross-platform, stand-alone, no external dependencies only. 10 | - [x] Tiny. One header and one source file. 11 | - [x] ZLIB/LibPNG licensed. 12 | 13 | ### API - time format (64-bit base10) 14 | ``` 15 | 18446744073709551615 16 | 00YYMMDDhhmmssxxxxxx xxx xxx = microseconds 17 | 18 | proposal 19 | 18446744073709551615 20 | 1TTTTYYMMDDhhmmssxxx TTTT = timezone, xxx = milliseconds 21 | ``` 22 | 23 | ### API 24 | ```c++ 25 | namespace sand 26 | { 27 | using point = uint64_t; // absolute time in milliseconds since Unix Epoch (1970/01/01 00:00:00.000 +00:00) 28 | using lapse = int64_t; // relative time in milliseconds between two timepoints; can be negative. 29 | using uint = unsigned int; 30 | 31 | // sleep thread for specified milliseconds (at least) 32 | void sleep( int64_t stamp ); 33 | 34 | // UTC absolute time, GMT timezone, local time and uptime since program epoch (in milliseconds) 35 | int64_t utc(); 36 | int64_t gmt(); 37 | int64_t now(); 38 | int64_t uptime(); 39 | 40 | // advance/rewind all clocks specified milliseconds (useful for QA and testing purposes) 41 | void shift( int64_t lapse ); 42 | 43 | // conversion to milliseconds 44 | int64_t nanoseconds( int64_t lapse ); 45 | int64_t microseconds( int64_t lapse ); 46 | int64_t milliseconds( int64_t lapse ); 47 | int64_t seconds( int64_t lapse ); 48 | int64_t minutes( int64_t lapse ); 49 | int64_t hours( int64_t lapse ); 50 | int64_t days( int64_t lapse ); 51 | int64_t weeks( int64_t lapse ); 52 | 53 | // conversion from milliseconds 54 | int64_t as_nanoseconds( int64_t lapse ); 55 | int64_t as_microseconds( int64_t lapse ); 56 | int64_t as_milliseconds( int64_t lapse ); 57 | int64_t as_seconds( int64_t lapse ); 58 | int64_t as_minutes( int64_t lapse ); 59 | int64_t as_hours( int64_t lapse ); 60 | int64_t as_days( int64_t lapse ); 61 | int64_t as_weeks( int64_t lapse ); 62 | 63 | // calendar 64 | int64_t date( int year, int month, int day ); 65 | int64_t time( int hour, int minute, int second, int millis = 0 ); 66 | int64_t datetime( int year, int month, int day, int hour, int minute, int second, int millis = 0 ); 67 | 68 | // extraction 69 | int year( int64_t stamp ); 70 | int month( int64_t stamp ); 71 | int day( int64_t stamp ); 72 | int hour( int64_t stamp ); 73 | int minute( int64_t stamp ); 74 | int second( int64_t stamp ); 75 | int millisecond( int64_t stamp ); 76 | 77 | // print 78 | std::string format( int64_t stamp, const std::string &format = "yyyy-mm-dd HH:MM:SS.MS" ); 79 | std::string pretty( int64_t lapse ); 80 | 81 | // serialization 82 | std::string str( int64_t stamp ); 83 | int64_t str( const std::string &ymdhmsmtz ); 84 | 85 | class sand::timer dt; 86 | // usage: 87 | // - int64_t nanoseconds_taken = dt.ns(); // (relative time) 88 | // - int64_t seconds_taken = dt.s(); // (relative time) 89 | // - int64_t milliseconds_taken = dt.ms(); // (relative time) 90 | // - see also .reset(); 91 | 92 | class sand::chrono chr(3.5); // in seconds 93 | // usage: 94 | // - chr.t() -> [0..1] (normalized floating time) 95 | // - see also .reset(); 96 | 97 | class sand::looper lp(3.5); // in seconds (similar to sand::chrono but will loop over and over) 98 | // usage: 99 | // - lp.t() -> [0..1][...] (normalized floating time) 100 | // - see also .reset(); 101 | } 102 | ``` 103 | 104 | ### Special notes 105 | - g++ users: both `-std=c++11` and `-lrt` may be required when compiling `sand.cpp` 106 | 107 | ### Changelog 108 | - v2.0.0 (2015/09/26) 109 | - A step forward towards portability and determinism. 110 | - v0.1.0 (2015/09/19) 111 | - Moved framerate locker to a library apart 112 | - v0.0.0 (2010/xx/xx) 113 | - Initial version 114 | -------------------------------------------------------------------------------- /sample.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sand.hpp" 5 | 6 | int main() { 7 | using namespace sand; 8 | 9 | sand::timer timer; 10 | 11 | std::cout << format( utc(), "yyyy-mm-ddTHH:MM:MS" ) << std::endl; 12 | std::cout << format( now(), "d/mmmm/yy HH:MM:SS.MS" ) << std::endl; 13 | std::cout << format( uptime(), "HH:MM:SS.MS" ) << std::endl; 14 | 15 | assert( pretty( seconds(- 0) ) == "right now" ); 16 | assert( pretty( seconds(- 1) ) == "a second ago" ); 17 | assert( pretty( seconds(- 2) ) == "2 seconds ago" ); 18 | assert( pretty( seconds(-60) ) == "a minute ago" ); 19 | assert( pretty( seconds(-61) ) == "a minute ago" ); 20 | assert( pretty( minutes(- 1) ) == "a minute ago" ); 21 | assert( pretty( minutes(-60) ) == "an hour ago" ); 22 | assert( pretty( minutes(-61) ) == "an hour ago" ); 23 | assert( pretty( hours(- 1) ) == "an hour ago" ); 24 | assert( pretty( hours(-24) ) == "yesterday" ); 25 | assert( pretty( hours(-25) ) == "yesterday" ); 26 | assert( pretty( days( -1) ) == "yesterday" ); 27 | assert( pretty( days( -5) ) == "5 days ago" ); 28 | assert( pretty( days( -7) ) == "7 days ago" ); 29 | assert( pretty( days(-12) ) == "12 days ago" ); 30 | assert( pretty( days(-13) ) == "13 days ago" ); 31 | assert( pretty( days(-14) ) == "2 weeks ago" ); 32 | assert( pretty( days(-32) ) == "a month ago" ); 33 | assert( pretty( weeks(-52) ) == "11 months ago" ); 34 | assert( pretty( weeks(-53) ) == "a year ago" ); 35 | 36 | assert( pretty( seconds(+ 0) ) == "right now" ); 37 | assert( pretty( seconds(+ 1) ) == "a second from now" ); 38 | assert( pretty( seconds(+ 2) ) == "2 seconds from now" ); 39 | assert( pretty( seconds(+60) ) == "a minute from now" ); 40 | assert( pretty( seconds(+61) ) == "a minute from now" ); 41 | assert( pretty( minutes(+ 1) ) == "a minute from now" ); 42 | assert( pretty( minutes(+60) ) == "an hour from now" ); 43 | assert( pretty( minutes(+61) ) == "an hour from now" ); 44 | assert( pretty( hours(+ 1) ) == "an hour from now" ); 45 | assert( pretty( hours(+24) ) == "tomorrow" ); 46 | assert( pretty( hours(+25) ) == "tomorrow" ); 47 | assert( pretty( days( +1) ) == "tomorrow" ); 48 | assert( pretty( days( +5) ) == "5 days from now" ); 49 | assert( pretty( days( +7) ) == "7 days from now" ); 50 | assert( pretty( days(+12) ) == "12 days from now" ); 51 | assert( pretty( days(+13) ) == "13 days from now" ); 52 | assert( pretty( days(+14) ) == "2 weeks from now" ); 53 | assert( pretty( days(+32) ) == "a month from now" ); 54 | assert( pretty( weeks(+52) ) == "11 months from now" ); 55 | assert( pretty( weeks(+53) ) == "a year from now" ); 56 | 57 | std::cout << pretty( str("2000-01-01 00:00:00") - now() ) << std::endl; 58 | 59 | { 60 | auto rtc = now(); 61 | auto then = str( "2010-12-31 23:59:59" ); 62 | std::cout << str(then) << std::endl; 63 | 64 | auto then2 = date(2010,12,31) + time(23,59,59,00); 65 | std::cout << str(then2) << std::endl; 66 | assert( str(then) == str(then2) ); 67 | 68 | assert( year(then) == 2010 ); 69 | assert( month(then) == 12 ); 70 | assert( day(then) == 31 ); 71 | assert( hour(then) == 23 ); 72 | assert( minute(then) == 59 ); 73 | assert( second(then) == 59 ); 74 | assert( millisecond(then) == 0 ); 75 | 76 | std::cout << "[ ] print "; 77 | std::cout << "- rtc : " << std::setprecision(20) << rtc << " -> " << str(rtc) << " -> " << pretty(now() - rtc); 78 | std::cout << "\r[x]" << std::endl; 79 | 80 | std::cout << "[ ] serialization "; 81 | std::cout << "- rtc : " << str(rtc); 82 | std::cout << "\r[x]" << std::endl; 83 | 84 | std::cout << "[ ] print "; 85 | std::cout << "- then : " << std::setprecision(20) << then << " -> " << str(then) << " -> " << pretty(then - now()); 86 | std::cout << "\r[x]" << std::endl; 87 | 88 | std::cout << "[ ] serialization "; 89 | std::cout << "- then : " << str(then); 90 | std::cout << "\r[x]" << std::endl; 91 | 92 | std::cout << "[ ] logical/arithmetical tests"; 93 | rtc ++; 94 | then ++; 95 | assert( rtc == rtc ); 96 | assert( rtc != then ); 97 | assert( rtc + 1 > rtc ); 98 | std::cout << "\r[x]" << std::endl; 99 | 100 | std::cout << "[ ] daylight savings tests"; 101 | auto winter = str( "2010-12-31T23:59:59.003Z" ); 102 | auto autumn = str( "2001-10-10 17:00:00" ); 103 | assert( str(winter) == "2010-12-31 23:59:59.003" ); 104 | assert( str(autumn) == "2001-10-10 17:00:00.000" ); 105 | std::cout << "\r[x]" << std::endl; 106 | } 107 | 108 | 109 | { 110 | auto 111 | past = str( "1972-06-10 17:00:00" ), 112 | present = now(), 113 | future = str( "2091-12-31 23:59:55" ); 114 | 115 | std::cout << str(past) << std::endl; 116 | 117 | assert( str(past) == "1972-06-10 17:00:00.000" ); 118 | //assert( str(present) == "your current time and date" ); 119 | assert( str(future) == "2091-12-31 23:59:55.000" ); 120 | 121 | past += seconds(1); 122 | present += seconds(1); 123 | future += seconds(6); 124 | 125 | assert( str(past) == "1972-06-10 17:00:01.000" ); 126 | //assert( str(present) == "your current time and date plus one second" ); 127 | assert( str(future) == "2092-01-01 00:00:01.000" ); 128 | } 129 | 130 | sand::chrono total(4); 131 | sand::looper looper(0.5); 132 | while( total.t() < 1 ) { 133 | std::cout << "looper: " << looper.t() << std::endl; 134 | sand::sleep(sand::seconds(1)/4); 135 | } 136 | 137 | std::cout << "---" << std::endl; 138 | std::cout << "All ok, " << timer.ms() << "ms " << std::endl; 139 | } 140 | -------------------------------------------------------------------------------- /sand.cpp: -------------------------------------------------------------------------------- 1 | // Sand, a functional time controller (C++11). ZLIB/LibPNG licensed. 2 | // - rlyeh ~~ listening to The Mission / Butterfly on a wheel 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "sand.hpp" 18 | 19 | #include 20 | #include 21 | #if !defined(SAND_USE_OMP) && ( defined(USE_OMP) || defined(_MSC_VER) /*|| defined(__ANDROID_API__)*/ ) 22 | # define SAND_USE_OMP 23 | # include 24 | #endif 25 | 26 | #pragma warning(push) 27 | #pragma warning(disable: 4996) // gmtime, localtime 28 | 29 | ///////////////////////////////////////////////////////////////////////////// 30 | 31 | namespace sand 32 | { 33 | namespace 34 | { 35 | int64_t clock() { 36 | # ifdef SAND_USE_OMP 37 | static auto const epoch = omp_get_wtime(); 38 | return (int64_t)(( omp_get_wtime() - epoch ) * 1000); 39 | # else 40 | static auto const epoch = std::chrono::steady_clock::now(); // milli ms > micro us > nano ns 41 | return (int64_t)std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now() - epoch ).count(); 42 | # endif 43 | } 44 | 45 | std::string floor( double f ) { 46 | return std::to_string( int( ::floor(f) ) ); 47 | } 48 | std::string ceil( double f ) { 49 | return std::to_string( int( ::ceil(f) ) ); 50 | } 51 | class custom : public std::string 52 | { 53 | public: 54 | 55 | custom() : std::string() 56 | {} 57 | 58 | template 59 | custom( const T &t ) : std::string() { 60 | std::stringstream ss; 61 | if( ss << t ) this->assign( ss.str() ); 62 | } 63 | 64 | std::deque< custom > tokenize( const std::string &delimiters ) const { // taken from wire::string::tokenize 65 | std::string map( 256, '\0' ); 66 | for( const unsigned char &ch : delimiters ) 67 | map[ ch ] = '\1'; 68 | std::deque< custom > tokens(1); 69 | for( const unsigned char &ch : *this ) { 70 | /**/ if( !map.at(ch) ) tokens.back().push_back( char(ch) ); 71 | else if( tokens.back().size() ) tokens.push_back( custom() ); 72 | } 73 | while( tokens.size() && !tokens.back().size() ) tokens.pop_back(); 74 | return tokens; 75 | } 76 | 77 | template 78 | T as() const { 79 | T t; 80 | std::stringstream ss; 81 | ss << *this; 82 | return ss >> t ? t : T(); 83 | } 84 | }; 85 | 86 | int64_t offset = 0; 87 | } 88 | 89 | int64_t gmt() { 90 | time_t t = std::time(0); 91 | struct tm *gtm = std::gmtime(&t); 92 | int hour1 = gtm->tm_hour; 93 | struct tm *ltm = std::localtime(&t); 94 | int hour2 = ltm->tm_hour; 95 | return hours(hour2 - hour1); 96 | } 97 | 98 | int64_t utc() { 99 | static const int64_t app_epoch = seconds(std::time(NULL)); 100 | return offset + app_epoch + sand::clock(); 101 | } 102 | 103 | int64_t now() { 104 | static const int64_t gmt_epoch = gmt(); 105 | return utc() + gmt_epoch; 106 | } 107 | 108 | int64_t uptime() { 109 | return offset + sand::clock(); 110 | } 111 | 112 | void shift( int64_t t ) { 113 | offset += seconds(t); 114 | } 115 | 116 | struct timestamp_t { 117 | int millis; // 0-999 118 | int second; // 0-59 119 | int minute; // 0-59 120 | int hour; // 0-23 121 | int day; // 0-30 122 | int month; // 0-11 123 | int year; // 0-xx (representing 1900-2xxx) 124 | }; 125 | enum : int { 126 | RTC_EPOCH_JULIAN_DAY = 2440588, // January 1st, 1970 127 | }; 128 | 129 | timestamp_t epoch_to_timestamp( int64_t epoch ) { 130 | int64_t year, month, day, l, n; 131 | 132 | timestamp_t dt; 133 | dt.millis = epoch % 1000; 134 | epoch /= 1000; 135 | 136 | // Reference: Fliegel, H. F. and van Flandern, T. C. (1968). 137 | // Communications of the ACM, Vol. 11, No. 10 (October, 1968). 138 | l = epoch / 86400 + 68569 + RTC_EPOCH_JULIAN_DAY; 139 | n = 4 * l / 146097; 140 | l = l - (146097 * n + 3) / 4; 141 | year = 4000 * (l + 1) / 1461001; 142 | l = l - 1461 * year / 4 + 31; 143 | month = 80 * l / 2447; 144 | day = l - 2447 * month / 80; 145 | l = month / 11; 146 | month = month + 2 - 12 * l; 147 | year = 100 * (n - 49) + year + l; 148 | 149 | dt.day = (int)day - 1; 150 | dt.month = (int)month - 1; 151 | dt.year = (int)year - 1900; 152 | 153 | epoch = epoch % (24 * 3600); 154 | dt.hour = (int)epoch / 3600; 155 | 156 | epoch = epoch % 3600; 157 | dt.minute = (int)epoch / 60; 158 | dt.second = (int)epoch % 60; 159 | return dt; 160 | } 161 | 162 | int64_t date( int year, int month, int day ) { 163 | // Reference: Fliegel, H. F. and van Flandern, T. C. (1968). 164 | // Communications of the ACM, Vol. 11, No. 10 (October, 1968). 165 | return days( day - 32075 - RTC_EPOCH_JULIAN_DAY 166 | + 1461 * (year + 4800 + (month - 14) / 12) / 4 167 | + 367 * (month - 2 - 12 * ((month - 14) / 12)) / 12 168 | - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 ); 169 | } 170 | 171 | int64_t time( int hour, int minute, int second, int millis ) { 172 | return hours(hour) + minutes(minute) + seconds(second) + milliseconds(millis); 173 | } 174 | 175 | int64_t datetime( int year, int month, int day, int hour, int minute, int second, int millis ) { 176 | return date( year, month, day ) + time( hour, minute, second, millis ); 177 | } 178 | 179 | namespace { 180 | // this function may look lame, but iostream is a hell and sprintf has portability issues 181 | // also, we only cover what we really need. 182 | std::string itoa(int x, int zerodigits = 0) { 183 | if( zerodigits == 4 ) { 184 | if( x >= 1000 ) return std::to_string(x); 185 | if( x >= 100 ) return "0" + std::to_string(x); 186 | if( x >= 10 ) return "00" + std::to_string(x); 187 | return "000" + std::to_string(x); 188 | } 189 | if( zerodigits == 3 ) { 190 | if( x >= 100 ) return std::to_string(x); 191 | if( x >= 10 ) return "0" + std::to_string(x); 192 | return "00" + std::to_string(x); 193 | } 194 | if( zerodigits == 2 ) { 195 | if( x >= 10 ) return std::to_string(x); 196 | return "0" + std::to_string(x); 197 | } 198 | return std::to_string(x); 199 | } 200 | std::string replace( std::string self, const std::string &target, const std::string &replacement ) { 201 | size_t found = 0; 202 | while( ( found = self.find( target, found ) ) != std::string::npos ) { 203 | self.replace( found, target.length(), replacement ); 204 | found += replacement.length(); 205 | } 206 | return self; 207 | } 208 | } 209 | 210 | std::string format( int64_t timestamp, const std::string &format ) { 211 | timestamp_t dt = epoch_to_timestamp( timestamp ); 212 | 213 | const char *mo[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", 214 | "Aug", "Sep", "Oct", "Nov", "Dec" }; 215 | const char *MO[] = { "January", "February", "March", "April", "May", "June", "July", 216 | "August", "September", "October", "November", "December" }; 217 | 218 | std::string timef = format; 219 | timef = replace(timef, "MS", itoa((int)dt.millis, 3)); 220 | timef = replace(timef, "SS", itoa((int)dt.second, 2)); 221 | timef = replace(timef, "MM", itoa((int)dt.minute, 2)); 222 | timef = replace(timef, "HH", itoa((int)dt.hour, 2)); 223 | timef = replace(timef, "yyyy", itoa((int)dt.year + 1900, 4)); 224 | timef = replace(timef, "yy", itoa((int)dt.year / 100, 2)); 225 | timef = replace(timef, "mmmm", "\1"); 226 | timef = replace(timef, "mmm", "\2"); 227 | timef = replace(timef, "mm", "\3"); 228 | timef = replace(timef, "m", "\4"); 229 | timef = replace(timef, "dd", "\5"); 230 | timef = replace(timef, "d", "\6"); 231 | timef = replace(timef, "\1", MO[dt.month]); 232 | timef = replace(timef, "\2", mo[dt.month]); 233 | timef = replace(timef, "\3", itoa((int)dt.month + 1, 2)); 234 | timef = replace(timef, "\4", itoa((int)dt.month + 1, 1)); 235 | timef = replace(timef, "\5", itoa((int)dt.day + 1, 2)); 236 | timef = replace(timef, "\6", itoa((int)dt.day + 1, 1)); 237 | 238 | /* 239 | %c - full datetime 240 | %x - full date 241 | %X - full time 242 | 243 | %Y - year YYYY (range 1900 to xxxx) 244 | %y - year YY (range 00 to 99) 245 | %C - The century number (year/100) as a 2-digit integer. (SU) 246 | 247 | %B - full month name 248 | %b - short month name 249 | %m - month MM (range 01 to 12) 250 | 251 | %A - full weekday name 252 | %a - short weekday name 253 | %d - day of the month DD (range 01 to 31). 254 | %e - day of the month D no leading zero. (range 1 to 31) 255 | %j - day of the year DDD (range 001 to 366). 256 | 257 | %V - The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year. See also %U and %W. (SU) 258 | 259 | %F - The date in %Y-%m-%d (the ISO 8601 date format). (C99) 260 | %r - The time in a.m. or p.m. notation %I:%M:%S %p. (SU) 261 | %R - The time in 24-hour notation (%H:%M). (SU) 262 | %T - The time in 24-hour notation (%H:%M:%S). (SU) 263 | 264 | %H - hour as a decimal number using a 24-hour clock (range 00 to 23). 265 | %H - hour as a decimal number using a 24-hour clock (range 0 to 23); no leading zero (TZ) 266 | %I - hour as a decimal number using a 12-hour clock (range 01 to 12). 267 | %I - hour as a decimal number using a 12-hour clock (range 1 to 12); no leading zero (TZ) 268 | 269 | %M - minute as a decimal number (range 00 to 59). 270 | 271 | %S - second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds.) 272 | %s - number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). (TZ) 273 | 274 | %P - Either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale. Noon is treated as "PM" and midnight as "AM". 275 | %p - Either "am" or "pm" according to the given time value, or the corresponding strings for the current locale. Noon is treated as "pm" and midnight as "am". 276 | 277 | %z - The +hhmm or -hhmm numeric timezone (that is, the hour and minute offset from UTC). (SU) 278 | %Z - The timezone or name or abbreviation. 279 | */ 280 | 281 | return timef; 282 | } 283 | 284 | // serialization 285 | 286 | std::string str( int64_t t ) { 287 | return format( t, "yyyy-mm-dd HH:MM:SS.MS" ); 288 | } 289 | 290 | int64_t str( const std::string &ymdhmsm ) { 291 | std::deque< custom > token = custom( ymdhmsm ).tokenize(" :/.TZ+-"); 292 | 293 | if( token.size() < 6 ) 294 | return 0; 295 | 296 | int year = token[0].as(); 297 | int month = token[1].as(); 298 | int day = token[2].as(); 299 | int hour = token[3].as(); 300 | int minute = token[4].as(); 301 | int second = token[5].as(); 302 | int millis = token.size() <= 6 ? 0 : token[6].as(); 303 | // timezone 304 | 305 | return date( year, month, day ) + time( hour, minute, second, millis ); 306 | } 307 | 308 | void sleep( int64_t millisecs ) { 309 | std::chrono::milliseconds duration( (int)(millisecs) ); 310 | std::this_thread::sleep_for( duration ); 311 | } 312 | 313 | int64_t nanoseconds( int64_t t ) { 314 | return t / 1000000; 315 | } 316 | int64_t microseconds( int64_t t ) { 317 | return t / 1000; 318 | } 319 | int64_t milliseconds( int64_t t ) { 320 | return t; 321 | } 322 | int64_t seconds( int64_t t ) { 323 | return t * 1000; 324 | } 325 | int64_t minutes( int64_t t ) { 326 | return t * seconds(60); 327 | } 328 | int64_t hours( int64_t t ) { 329 | return t * minutes(60); 330 | } 331 | int64_t days( int64_t t ) { 332 | return t * hours(24); 333 | } 334 | int64_t weeks( int64_t t ) { 335 | return t * days(7); 336 | } 337 | 338 | int64_t as_nanoseconds( int64_t t ) { 339 | return t * 1000000; 340 | } 341 | int64_t as_microseconds( int64_t t ) { 342 | return t * 1000; 343 | } 344 | int64_t as_milliseconds( int64_t t ) { 345 | return t; 346 | } 347 | int64_t as_seconds( int64_t t ) { 348 | return t / 1000; 349 | } 350 | int64_t as_minutes( int64_t t ) { 351 | return t / seconds(60); 352 | } 353 | int64_t as_hours( int64_t t ) { 354 | return t / minutes(60); 355 | } 356 | int64_t as_days( int64_t t ) { 357 | return t / hours(24); 358 | } 359 | int64_t as_weeks( int64_t t ) { 360 | return t / days(7); 361 | } 362 | 363 | int year( int64_t t ) { 364 | return custom( format( t, "yyyy" ) ).as(); 365 | } 366 | int month( int64_t t ) { 367 | return custom( format( t, "m" ) ).as(); 368 | } 369 | int day( int64_t t ) { 370 | return custom( format( t, "d" ) ).as(); 371 | } 372 | int hour( int64_t t ) { 373 | return custom( format( t, "HH" ) ).as(); 374 | } 375 | int minute( int64_t t ) { 376 | return custom( format( t, "MM" ) ).as(); 377 | } 378 | int second( int64_t t ) { 379 | return custom( format( t, "SS" ) ).as(); 380 | } 381 | int millisecond( int64_t t ) { 382 | return custom( format( t, "MS" ) ).as(); 383 | } 384 | 385 | // pretty (deictic) human time 386 | std::string pretty( int64_t reltime_ms ) { 387 | // based on code by John Resig (jquery.com) 388 | int abs_diff = (int)std::abs(reltime_ms / 1000); 389 | int day_diff = (int)std::floor(abs_diff / 86400); 390 | 391 | if( reltime_ms < 0 ) { 392 | if( day_diff == 0 ) { 393 | if( abs_diff <= 0 ) return "right now"; 394 | if( abs_diff <= 1 ) return "a second ago"; 395 | if( abs_diff < 60 ) return sand::floor(abs_diff) + " seconds ago"; 396 | if( abs_diff < 120 ) return "a minute ago"; 397 | if( abs_diff < 3600 ) return sand::floor(abs_diff/60) + " minutes ago"; 398 | if( abs_diff < 7200 ) return "an hour ago"; 399 | return sand::floor(abs_diff/3600) + " hours ago"; 400 | } 401 | if( day_diff == 1 ) return "yesterday"; 402 | if( day_diff <= 13 ) return std::to_string(day_diff) + " days ago"; 403 | if( day_diff < 31 ) return sand::ceil(day_diff/7) + " weeks ago"; 404 | if( day_diff < 62 ) return "a month ago"; 405 | if( day_diff < 365 ) return sand::ceil(day_diff/31) + " months ago"; 406 | if( day_diff < 730 ) return "a year ago"; 407 | return sand::ceil(day_diff/365) + " years ago"; 408 | } else { 409 | if( day_diff == 0 ) { 410 | if( abs_diff <= 0 ) return "right now"; 411 | if( abs_diff <= 1 ) return "a second from now"; 412 | if( abs_diff < 60 ) return sand::floor(abs_diff) + " seconds from now"; 413 | if( abs_diff < 120 ) return "a minute from now"; 414 | if( abs_diff < 3600 ) return sand::floor(abs_diff/60) + " minutes from now"; 415 | if( abs_diff < 7200 ) return "an hour from now"; 416 | return sand::floor(abs_diff/3600) + " hours from now"; 417 | } 418 | if( day_diff == 1 ) return "tomorrow"; 419 | if( day_diff <= 13 ) return std::to_string(day_diff) + " days from now"; 420 | if( day_diff < 31 ) return sand::ceil(day_diff/7) + " weeks from now"; 421 | if( day_diff < 62 ) return "a month from now"; 422 | if( day_diff < 365 ) return sand::ceil(day_diff/31) + " months from now"; 423 | if( day_diff < 730 ) return "a year from now"; 424 | return sand::ceil(day_diff/365) + " years from now"; 425 | } 426 | } 427 | } 428 | 429 | #pragma warning(pop) 430 | 431 | -------------------------------------------------------------------------------- /sand.hpp: -------------------------------------------------------------------------------- 1 | // Sand, a functional time controller (C++11). ZLIB/LibPNG licensed. 2 | // - rlyeh ~~ listening to The Mission / Butterfly on a wheel 3 | 4 | // @todo, max 64-bit 5 | // 18446744073709551615 6 | // 1TTTTYYMMDDhhmmssxxx TTTT = timezone, xxx = milliseconds 7 | // 18446744073709551615 8 | // 00YYMMDDhhmmssxxxxxx xxx xxx = microseconds 9 | 10 | #pragma once 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define SAND_VERSION "v2.0.0" /* (2015/09/26) Upgraded version - more portable, less error prone 17 | #define SAND_VERSION "v1.0.0" // (2013/04/12) Initial version */ 18 | 19 | namespace sand 20 | { 21 | using point = uint64_t; // absolute time in milliseconds since Unix Epoch (1970/01/01 00:00:00.000 +00:00) 22 | using lapse = int64_t; // relative time in milliseconds between two timepoints; can be negative. 23 | using uint = unsigned int; 24 | 25 | // sleep thread for specified milliseconds (at least) 26 | void sleep( int64_t stamp ); 27 | 28 | // UTC absolute time, GMT timezone, local time and uptime since program epoch (in milliseconds) 29 | int64_t utc(); 30 | int64_t gmt(); 31 | int64_t now(); 32 | int64_t uptime(); 33 | 34 | // advance/rewind all clocks specified milliseconds (useful for QA and testing purposes) 35 | void shift( int64_t lapse ); 36 | 37 | // conversion to milliseconds 38 | int64_t nanoseconds( int64_t lapse ); 39 | int64_t microseconds( int64_t lapse ); 40 | int64_t milliseconds( int64_t lapse ); 41 | int64_t seconds( int64_t lapse ); 42 | int64_t minutes( int64_t lapse ); 43 | int64_t hours( int64_t lapse ); 44 | int64_t days( int64_t lapse ); 45 | int64_t weeks( int64_t lapse ); 46 | 47 | // conversion from milliseconds 48 | int64_t as_nanoseconds( int64_t lapse ); 49 | int64_t as_microseconds( int64_t lapse ); 50 | int64_t as_milliseconds( int64_t lapse ); 51 | int64_t as_seconds( int64_t lapse ); 52 | int64_t as_minutes( int64_t lapse ); 53 | int64_t as_hours( int64_t lapse ); 54 | int64_t as_days( int64_t lapse ); 55 | int64_t as_weeks( int64_t lapse ); 56 | 57 | // calendar 58 | int64_t date( int year, int month, int day ); 59 | int64_t time( int hour, int minute, int second, int millis = 0 ); 60 | int64_t datetime( int year, int month, int day, int hour, int minute, int second, int millis = 0 ); 61 | 62 | // extraction 63 | int year( int64_t stamp ); 64 | int month( int64_t stamp ); 65 | int day( int64_t stamp ); 66 | int hour( int64_t stamp ); 67 | int minute( int64_t stamp ); 68 | int second( int64_t stamp ); 69 | int millisecond( int64_t stamp ); 70 | 71 | // print 72 | std::string format( int64_t stamp, const std::string &format = "yyyy-mm-dd HH:MM:SS.MS" ); 73 | std::string pretty( int64_t lapse ); 74 | 75 | // serialization 76 | std::string str( int64_t stamp ); 77 | int64_t str( const std::string &ymdhmsmtz ); 78 | 79 | // usage: 80 | // sand::timer dt; 81 | // [do something] 82 | // int64_t nanoseconds_taken = dt.ns(); (relative time) 83 | class timer 84 | { 85 | int64_t start; 86 | 87 | public: 88 | 89 | timer() : start( sand::now() ) 90 | {} 91 | 92 | void reset() { 93 | start = sand::now(); 94 | } 95 | 96 | int64_t s() { 97 | return as_seconds( sand::now() - start ); 98 | } 99 | int64_t ms() { 100 | return as_milliseconds( sand::now() - start ); 101 | } 102 | int64_t us() { 103 | return as_microseconds( sand::now() - start ); 104 | } 105 | int64_t ns() { 106 | return as_nanoseconds( sand::now() - start ); 107 | } 108 | }; 109 | 110 | // usage: 111 | // sand::chrono ch(3.5); // in seconds 112 | // ch.t() -> [0..1] (normalized floating time) 113 | class chrono 114 | { 115 | sand::timer dt; 116 | double top; 117 | 118 | public: 119 | 120 | explicit 121 | chrono( double _top = 1 ) : top(_top * 1000000000.0) 122 | {} 123 | 124 | double t() { 125 | if( top > 0 ) { 126 | double now = dt.ns() / top; 127 | return now >= 1.0 ? 1.0 : now; 128 | } 129 | return 1.0; 130 | } 131 | 132 | void reset() { 133 | dt.reset(); 134 | } 135 | 136 | void reset( int64_t _top ) { 137 | top = _top; 138 | dt.reset(); 139 | } 140 | }; 141 | 142 | // usage: 143 | // sand::looper l(3.5); // in seconds (similar to sand::chrono but will loop over and over) 144 | // l.t() -> [0..1][...] (normalized floating time) 145 | class looper 146 | { 147 | sand::timer dt; 148 | double factor; 149 | 150 | public: 151 | 152 | explicit 153 | looper( const double seconds = 1.0 ) : factor(1.0/seconds) 154 | {} 155 | 156 | double t() { 157 | double now = (dt.ns() / 1000000000.0) * factor; 158 | if( now < 1.0 ) return now; 159 | dt = sand::timer(); 160 | return 1.0; 161 | } 162 | 163 | void reset( double seconds = 1.0 ) { 164 | factor = 1.0/seconds; 165 | dt.reset(); 166 | } 167 | }; 168 | 169 | // @todo 170 | // every(s); // if( every(5.0) ) {} 171 | // once(); // if( once() ) {} 172 | } 173 | 174 | 175 | --------------------------------------------------------------------------------