Creating a timeline with timers and events

Pages: 12
Hello everyone.

I would like to create a timeline, setting a starting time and setting some time of events that triggers at specific time.

Any hint on how to proceed?

Thank you
struct timeline
{
sometimetype timestamp;
string description;
};

vector<timeline>;
...

whenever something happens, pushback "now" time and what it was?
At the end, just iterate over the vector and crank out a report/graphic/something for your timeline.

you can clearly add more stuff to the struct and make it more fancy, this is just the bare-bones idea.

Last edited on
Sorry, maybe i haven't well exaplained my problem.

I would like to set the date and time for example starting from 1 January 2141 and init a timer from this point in time.

Then, i would like to save the date and restart the timer from the saved date.
you want a simulated timer for any date or you want a future timer to start on a future date?

This sounds more complex, involving at least a file for persistence.
I generally try to do the keep it simple approach. I wonder if your basic approach could just be a 64 bit seconds since 1970 type value, with a few tagalong items for whatever else you need (what it is, is it active, ... whatever).

"you want a simulated timer for any date or you want a future timer to start on a future date?"

Thank you for the reply.

Assuming i want to simulate that we are in the future (such in some games) and i have to manage date and time in that future, create events and so on...

I start set a date 01/01/2140, the start the timer, updating secondws, hours and days....

When i quit the game i store the current date/time in a file, when i restart the game i restart from the saved date/time
Assuming that the range of simulated time that we want to deal with is within the range of what the library can handle (for some typical values, see: http://www.cplusplus.com/forum/general/218890/#msg1009313
note that the GNU library is somewhat limited - upto year 2261 or so),
we could store the current simulated time in a std::time_t. This is an arithmetic type; so writing it to a file and reading it back later is directly supported.

Something along these lines, perhaps:
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
#include <iostream>
#include <ctime>
#include <string>

struct timer
{
    timer( std::time_t simulated_time )
        : last_update_time( std::time(nullptr) ), simulated_time(simulated_time) {}

    timer( 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) ;
    }

    // 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) ;
        simulated_time += std::difftime( now, last_update_time ) ;
        last_update_time = now ;
    }

    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 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" ;
    }

    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 timer (current simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, const timer& t )
    { return stm << t.simulated_time ; }

    // restore the state of the timer  (current simulated time) from an input stream
    friend std::istream& operator >> ( std::istream& stm, timer& t )
    {
        std::time_t stime ;
        if( stm >> stime ) t = { stime } ;
        return stm ;
    }
};


Simple example usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
    timer t { 2056, 7, 12 } ;
    std::cout << t << '\n' ;

    for( int i = 0 ; i < 10 ; ++i )
    {
        std::cout << "press enter: " && std::cin.get() ;
        std::cout << "simulated time: " << t.current_simulated_time_str() << '\n' ;
    }

    std::cout << t << '\n' ;
}

Thank you.

I managed to create something similar, but your code is better for sure.

I'll try study the code.

Is this "custom" time updatable by seconds, hours or days?

"std::out_of_range" is this part of std?


Btw the code works on simulator online, but doesn't work on my desktop. I get:

"main.cpp|2451|error: expected ';' before 't';|
Last edited on
> Is this "custom" time updatable by seconds, hours or days?

As written, the functions timer::current_simulated_time() and timer::current_simulated_time_str() would return the current updated time; that is the reason why they are not const-qualified.

As long as we use one of these functions to retrieve the current simulated time we would get the most up to date time, accurate to within a second or so.

I had missed updating the time in the stream insertion operator; ideally it should have an overload:
1
2
3
4
5
6
7
    // save the state of the timer (current simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, const timer& t ) // const timer
    { return stm << t.simulated_time ; }

    // save the state of the timer (updated current simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, timer& t ) // non-const timer; update and save the up to date state
    { return stm << t.current_simulated_time() ; }



> "std::out_of_range" is this part of std?

Yes. http://en.cppreference.com/w/cpp/error/out_of_range
Last edited on
Thank you.

Can't set the date years 2140. Always remain to 1970.

about the new function: should substitute the other function in the previous code?

Moreover, could be possible to autoupdate the time each second?

What if i want to update at certain point the date jumping forward by 7 days?

Last edited on
Always out of range, don't understand why
> "main.cpp|2451|error: expected ';' before 't';|

Please tell me that the 2451 is not the line number; surely you can't be having a main.cpp with 2451+ lines.


> about the new function: should substitute the other function in the previous code?

You can just add the new function (it would become an overloaded overloaded operator).


> Can't set the date years 2140. Always remain to 1970.

It would help if you specify which compiler/library is being used (include the version).
All the mainstream implementations can handle years up to 2900 AD.

Linux, clang++ / libc++ : at least up to 3200 AD
Linux, g++ / libstdc++ : at least up to 3200 AD
http://coliru.stacked-crooked.com/a/1f2971a1ec691de5

Microsoft, GNU/MinGW : up to 2900 AD or so
http://rextester.com/VRRH57759


> What if i want to update at certain point the date jumping forward by 7 days?

See the highlighted portion of the code:

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
#include <iostream>
#include <ctime>
#include <string>

struct timer
{
    timer( std::time_t simulated_time )
        : last_update_time( std::time(nullptr) ), simulated_time(simulated_time) {}

    timer( 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 pedanticaly 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" ;
    }

    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 timer (current simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, const timer& t )
    { return stm << t.simulated_time ; }

    // save the state of the updated timer (updated simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, timer& t )
    { return stm << t.current_simulated_time() ; }

    // restore the state of the timer  (current simulated time) from an input stream
    friend std::istream& operator >> ( std::istream& stm, timer& t )
    {
        std::time_t stime ;
        if( stm >> stime ) t = { stime } ;
        return stm ;
    }
};



> Moreover, could be possible to autoupdate the time each second?

Possible (with a background thread).
But what would be the use-case for doing something like that?
Thank you for the reply.

Main have few lines.

The compiler is GNU. I'm using minGW under Windows. GCC version is 4.9.2
> Main have few lines.

2451 is not 'a few lines'. Factor it out into separate, manageable components.

> The compiler is GNU. I'm using minGW under Windows. GCC version is 4.9.2

I do not have access to that version.

I've tested with the versions I have immediate access to, and this is what I get:

Nuwen: https://nuwen.net/mingw.html
MinGW version: 5.0
GNU compiler version: 7.1.0
-----------------------------------------------
    initial: Monday, October 17 00:00:00 2140
   +73 secs: Monday, October 17 00:01:13 2140
   -88 mins: Sunday, October 16 22:33:13 2140
  -423 days: Thursday, August 20 22:33:13 2139
-142 months: Monday, October 20 22:33:13 2127
  +75 years: Wednesday, October 20 22:33:13 2202
 +100 years: Monday, October 20 22:33:13 2302
 +100 years: Sunday, October 20 22:33:13 2402
 +100 years: Friday, October 20 22:33:13 2502
 +100 years: Wednesday, October 20 22:33:13 2602
 +100 years: Monday, October 20 22:33:13 2702
 +100 years: Sunday, October 20 22:33:13 2802
 +100 years: Friday, October 20 22:33:13 2902
 +100 years: *** error: time is out of range


TDM-GCC-64 http://tdm-gcc.tdragon.net/index.php/
MinGW version: 4.0
GNU compiler version: 5.1.0
-----------------------------------------------
    initial: Monday, October 17 00:00:00 2140
   +73 secs: Monday, October 17 00:01:13 2140
   -88 mins: Sunday, October 16 22:33:13 2140
  -423 days: Thursday, August 20 22:33:13 2139
-142 months: Monday, October 20 22:33:13 2127
  +75 years: Wednesday, October 20 22:33:13 2202
 +100 years: Monday, October 20 22:33:13 2302
 +100 years: Sunday, October 20 22:33:13 2402
 +100 years: Friday, October 20 22:33:13 2502
 +100 years: Wednesday, October 20 22:33:13 2602
 +100 years: Monday, October 20 22:33:13 2702
 +100 years: Sunday, October 20 22:33:13 2802
 +100 years: Friday, October 20 22:33:13 2902
 +100 years: *** error: time is out of range


The code is almost identical to this http://coliru.stacked-crooked.com/a/1f2971a1ec691de5

This is how it could be factored into separate components:

Header timer.h
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
#ifndef TIMER_H_INCLUDED
#define TIMER_H_INCLUDED

#include <ctime>
#include <string>
#include <iostream>

struct timer
{
    timer( std::time_t simulated_time )
        : last_update_time( std::time(nullptr) ), simulated_time(simulated_time) {}

    timer( int year, int month /* jan == 1 */, int day ) ;

    static double ticks_per_sec() ; // just to be pedanticaly safe: almost always is 1 tick per second

    // call to update the simulated time. or call periodically
    // from a background thread (with std::atomic<time_t> simulated_time)
    void update() ;

    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 ) ;
    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() ;

    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 timer (current simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, const timer& t )
    { return stm << t.simulated_time ; }

    // save the state of the updated timer (updated simulated time) into an output stream
    friend std::ostream& operator << ( std::ostream& stm, timer& t )
    { return stm << t.current_simulated_time() ; }

    // restore the state of the timer  (current simulated time) from an input stream
    friend std::istream& operator >> ( std::istream& stm, timer& t )
    {
        std::time_t stime ;
        if( stm >> stime ) t = { stime } ;
        return stm ;
    }
};

#endif // TIMER_H_INCLUDED 


Implementation timer.cpp
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
#include "timer.h"

timer::timer( 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) ;
}

double timer::ticks_per_sec()
{
    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 ;
}

void timer::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 timer::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) ;
}

std::string timer::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" ;
}


Test frame main.cpp
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
#include <iostream>
#include "timer.h"

int main()
{
    #ifdef __MINGW64__
        std::cout << "MinGW version: " << __MINGW64_VERSION_STR << '\n'
                  << "GNU compiler version: " << __VERSION__
                  << "\n-----------------------------------------------\n" ;

    #endif // __MINGW64__

    timer t( 2140, 10, 17 ) ;
    std::cout << "    initial: " << t.current_simulated_time_str() << '\n' ;

    t.advance_secs(73) ; // forward by 73 seconds
    std::cout << "   +73 secs: " << t.current_simulated_time_str() << '\n' ;

    t.advance_minutes( -88 ) ; // back by 88 minutes
    std::cout << "   -88 mins: " << t.current_simulated_time_str() << '\n' ;

    t.advance_days(-423) ; // back by 423 days
    std::cout << "  -423 days: " << t.current_simulated_time_str() << '\n' ;

    t.advance_months(-142) ; // back by 142 months
    std::cout << "-142 months: " << t.current_simulated_time_str() << '\n' ;

    t.advance_years(75) ; // forward by 75 years
    std::cout << "  +75 years: "<< t.current_simulated_time_str() << '\n' ;

    try
    {
        for( int i = 0 ; i < 10 ; ++i )
        {

            std::cout << " +100 years: " ;
            t.advance_years(100) ; // forward by 100 years
            std::cout << t.current_simulated_time_str() << '\n' ;
        }
    }
    catch( const std::exception& e )
    {
        std::cerr << "*** error: " << e.what() << '\n' ;
    }
}


Try this code out on the implementation that you have; I would expect that the results would be similar (handle times up to 2900 AD or so).

It would be a good idea to move to a more current version of the compiler; the versions I tested with can be downloaded from the links indicated earlier.
Sorry for the late reply.

Thank you for your very detailed explaination and code factorization: very useful and informative.

I will try as soon as possible the code.

Kind regards
@JLBorges

Hello,

tried the code above.
Still have problem with "throw std::out_of_range( "time is out of range" ) ;" the error say that out_of_range is not part of std.

Btw, replacing the "throw std::out_of_range( "time is out of range" ) ;" with a simple cout ... the software works with the following output:

"time is out of range
initial: time beyond what the C library can handle gracefully"

then continue to works till

1
2
t.advance_days(-423) ; // back by 423 days
    std::cout << "  -423 days: " << t.current_simulated_time_str() << '\n' ;


if i use the

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    t.advance_months(-142) ; // back by 142 months
    std::cout << "-142 months: " << t.current_simulated_time_str() << '\n' ;

    t.advance_years(75) ; // forward by 75 years
    std::cout << "  +75 years: "<< t.current_simulated_time_str() << '\n' ;

    try
    {
        for( int i = 0 ; i < 10 ; ++i )
        {

            std::cout << " +100 years: " ;
            t.advance_years(100) ; // forward by 100 years
            std::cout << t.current_simulated_time_str() << '\n' ;
        }
    }
    catch( const std::exception& e )
    {
        std::cerr << "*** error: " << e.what() << '\n' ;
    }


the software crash

I'm wondering if my codeblocks + mingw setup works.
Last edited on
> Still have problem with "throw std::out_of_range( "time is out of range" ) ;"
> the error say that out_of_range is not part of std.

Mea culpa; in timer.cpp, we need to add #include <stdexcept>
http://en.cppreference.com/w/cpp/error/out_of_range



> I'm wondering if my codeblocks + mingw setup works.

The results I get with the two versions of MinGW that I have was posted here:
http://www.cplusplus.com/forum/beginner/219322/#msg1011039

If an older version (versions prior to GCC 5.1) of the GNU compiler library is still being used, you should upgrade immediately; older versions of the GNU library would be thoroughly broken in many places.
@JLBorges

Can't find a suitable (ready made) MINGW installation to replace the MINGW directory of codeblocks.
Thank you.

I used this website in the past, but I completely forgot it. Very useful
Pages: 12