[Tutorial/Code] Adding dynamic amounts of time (minutes) to a 24 hour clock

I’m not sure if this is a usual practice, but I have created some code that others might find useful and wanted to share it - it would also be good for people to check it over and see if you can spot any bugs or edge cases that would break it. This ended up really long, but hopefully it’ll help anyone who just wants to steal my subroutine quickly, but also explain how it works for anyone interested.

The problem I wanted to solve was:
Giving players a range of tasks they can complete, in any order, which take a variable amount of time each (5 mins, 30 mins, an hour and a half, and so on). I wanted to display the current time (e.g. 07:30) and have it update after every task is completed. However, because tasks can be completed in any order, you can not simply update the time to a fixed value. For example:

Time is 07:30. You can:

  1. Eat breakfast (15 minutes)
  2. Have a shower (10 minutes)

If the player selects (1) first then it would say “you ate breakfast, the time is now 07:45”.
If the player THEN selects (2) it would say “you had a shower, the time is now 07:55”

But, if a player were to do (2) first then the shower task should report 07:40 instead of 07:55 (as it takes 10 minutes from 07:30, as it came first). Equally, if (1) happens second, it should report 07:55 instead of 07:40.

I couldn’t find any inbuilt functionality to do this. I did find some threads where others had produced their own subroutines. The first one I looked at did not handle cases where you wanted to add 120 or more minutes in one go. The second one looked complicated and I confess I didn’t try it, as the first method enabled me to figure out how to do it myself.

Edit: I subsequently realised that there was a more elegant solution, which is detailed first. The original method is retained below. I’ll be re-writing this shortly to make it a little easier to follow - currently it is just pasted from my below post.

You still need to define three variables in the startup.txt

*create TotalTime 1420
*create Hour 00
*create Min 00

Here TotalTime is the time, in minutes, you want to start with. 0 for midnight up to 1440 (which is also midnight). For a 7am start, set it to 420.
Hour and Min start as 0 and are what we use to display the time in hours and minutes after we have updated the clock each time.

Then create a new file and insert the following code:

*label clock

*if TotalTime = 1440
    *set TotalTime 0

*label TotalTimeLoop
*if TotalTime > 1440
    *set TotalTime - 1440
    *goto TotalTimeLoop

*set Hour TotalTime / 60

*if Hour < 10
    *set Hour 0 & (Hour#1)

*if Hour > 9
    *set Hour (Hour#1) & (Hour#2)

*set Min (TotalTime modulo 60)

*if Min < 10
    *set Min 0 & Min

*return

You then call this very simply with the following code

*set TotalTime + 15
*gosub_scene clock

If the TotalTime is equal to 1,440 (midnight) we set it to 0 and start counting again. The reason why is explained next.

If the TotalTime is more than 1,440 (24 hours), then we remove 1,440 minutes from it (we loop round and round until the TotalTime is less than 1,440. Functionally we’re removing 24 hours from any total elapsed time of 24 hours or more. This won’t change what ‘time’ it is for any given ‘day’.
7 am is 420 elapsed minutes. If you kept adding minutes until you were at 7am the following day, then TotalTime would be 1,860. We can simply subtract 1,440 to return this value to 420 without changing the resulting time (7am).

This ensures that our next calculations are all kept within the bounds of the 24h clock - making life much simpler.

Then we do our hour calculation. Divde TotalTime by 60 (line 8). This gives us a decimal whereby the number before the decimal point is the time in hours in 24h format. (for example 440 = 7.3333 - and we know that 420 is 7 hours, with the additonal 20 minutes generating the remainder).

So now we just need to extract the 7. This is lines 10+11 and 12+13.
CS let’s us select individual characters from a variable. If the resulting hour is 10 or more (10-24) then we need to select the first two characters (as the variable will be 10.2333, or 16.2492, or whatever). If the resulting hour is less than 10, then we only need to select the first character (0-9), but we want to concatenate a 0 on the front, so that we retain our nice 24h format.

For the minute, we use modulo as discussed and it just returns us the remainder that didn’t divide cleanly into 60 (and therefore is always a number below 60). Again, if it is a single digit, we concatenate a 0 on the front.

What do you need to do? – original method

In your startup.txt you need to define three variables (excuse my capitalisation, I prefer it for variable names):

*create Hour 22
*create Min 00
*create AddMin 0

Hour is in 24 hour format (the leading 0 will be dropped when you alter the hour and I can’t (and haven’t really tried) to figure out how to retain the 0 - so you will end up with 7:00 for 7am).

Min is any number between 00 and 59

AddMin will be set in your code as the number of minutes you wish to add. This can literally be any number you want (I just tried with 14,400 and returned the correct time, i.e. the exact same time)

Then create a new file and insert this code (again, note my capitialisation):

*label Clock

*temp MinDiff 0
*temp TotalMin Min + AddMin

*if TotalMin < 60
    *if TotalMin < 10
        *set Min 0&(Min+AddMin)
    *if TotalMin > 9
        *set Min + AddMin
    *set AddMin 0
    
*if TotalMin = 60
    *set Min 00
    *set Hour + 1
    *set AddMin 0

*if TotalMin > 60
    *set MinDiff 60 - Min
    *set Min 00
    *set Hour + 1
    *set AddMin - MinDiff

    *label MinuteLoop
    *if AddMin < 60
        *if AddMin < 10
            *set Min 0&(Min+AddMin)
        *if AddMin > 9
            *set Min + AddMin
        *set AddMin 0
  
    *if AddMin = 60
        *set Min 00
        *set Hour + 1
        *set AddMin 0
    
    *if AddMin > 60
        *set Hour + 1
        *set AddMin - 60
        *if AddMin > 0
            *goto MinuteLoop

*if AddMin = 0
    *if Hour = 24
        *set Hour 0
    *label HourLoop
    *if Hour > 24
        *set Hour Hour - 24
        *if Hour > 24
            *goto HourLoop
*return

You then add the following code at any point you want to update the clock:

*set AddMin 30
*gosub_scene clock
The time is ${Hour}:${Min}

This will pass in 30 as the number of minutes to add, update the Hour and Min variables accordingly (in the subroutine called ‘clock’. Then the third line just outputs it so you can see - it isn’t required for the code to work.

For those that want a full explanation:
We simply work on the basis that every time we have 60 minutes (Min = 60) then we roll over (set Min to 00 and increase Hour by 1). Then every time we have 24 hours (Hour = 24) we roll over (set Hour to 0).

There is one key variable we need to calculate: ‘TotalMin’ - this is the number of minutes currently on the clock (at 07:30, there are 30 minutes on the clock; Min = 30) PLUS the number of minutes we want to add. We calculate this on line 4.

There are three conditions arising from our TotalMin

  1. TotalMin is LESS than 60 (e.g. it’s 07:30, AddMin = 15, TotalMin = 45) - We’re not going to roll over the hour as the result will be 07:45. Thus we simply add AddMin to Min (lines 6 and 7)

  2. TotalMin is EQUAL to 60 - We’re going to land bang on the hour, with no remainder (07:30, AddMin = 30; result = 08:00) - this is lines 10-12

  3. TotalMin is MORE than 60 - We’re going to have to roll over the hour several times

(1) and (2) are pretty self explanatory. We also set AddMin to 0, so that we know we have updated the clock with all the new minutes. This let’s us *return on line 36 (via updating the hour)
Edit: I had to add more complexity to (1). This was to handle the case where the resulting number of minutes was below 10. Because CS will sum the two numbers (e.g. Min = 05 and AddMin = 2 - this will generate a result of 7, not 07) - you generate an interger and lose your 0 when the result is less than 10. So in these cases we concatenate the result of the sum (7) with a 0 in front and we retain our format.

(3) is more complicated and covers lines 15-34.
Lines 16-19 roll the time up to the next hour and subtract the number of minutes from AddMin. This let’s us deal in whole hours (until we get to the remainder of the minutes).

We then loop through three conditions. That the remaining time to add (AddMin) is less than, equal to, or greater than 60. In the first two conditions we follow (1) and (2) above and that’s us done. Otherwise we increase the hour by 1, decrease AddMin by 60 and then if AddMin is above 0, we loop back and check all three conditions. In this way, we take a number like 142 and add 2 hours (60 * 2 = 120) and then add 22 minutes - AddMin is now equal to 0.

Once AddMin is equal to 0, we update Hour (lines 38-44). If Hour is less than 24, we’re golden. If it is 24, we roll on to 00 (midnight). Finally, if it is > 24 we loop removing 24 from the value until it is <24 again. In this way, we can add 14,400 minutes (10 days) with no problem.

7 Likes

Quite educational! Would storing the “current” time as minutes (single variable), updating it accordingly and then extracting 24-hour periods, hours and remainder minutes with the modulo operator when you need to display the clock achieve the same effect?

1 Like

This post is now redundant as the improved method detailed here has been merged into the OP.

You’re absolutely right, modulo will return us the correct minute remainder from any total ‘current’ time stored as minutes (e.g. 150 modulo 60 = 30, aka: 2 hours and 30 minutes, aka: 02:30).

I started off doing this and hit a little problem. How do I get the hour?
150 / 60 = 2.5. Modulo handles the .5 bit and gives us 30. But how do I get the 2?
I thought about it a bit, decided I wasn’t sure how to do it and so gave up and wrote the above method.

So I’m sat here writing a reply about how you can’t easily get the ‘2’ for the number of hours - and something is really niggling at me - namely, that someone is going to come along and point out how easily you can get the 2. Thus, I went and figured it out and we end up with much simpler code.

So thank you very much for asking the question!

I’m not sure if I can edit my original anymore, so I’ll post it here first.
As my previous code turned out to not be the best solution - do check that this (or that) method meet your needs and don’t generate errors. I can’t guarantee there are no edge cases that will break the system.

You still need to define three variables in the startup.txt

*create TotalTime 1420
*create Hour 00
*create Min 00

Here TotalTime is the time, in minutes, you want to start with. 0 for midnight up to 1440 (which is also midnight). For a 7am start, set it to 420.
Hour and Min start as 0 and are what we use to display the time in hours and minutes after we have updated the clock each time.

Then create a new file and insert the following code:

*label clock

*if TotalTime = 1440
    *set TotalTime 0

*label TotalTimeLoop
*if TotalTime > 1440
    *set TotalTime - 1440
    *goto TotalTimeLoop

*set Hour TotalTime / 60

*if Hour < 10
    *set Hour 0 & (Hour#1)

*if Hour > 9
    *set Hour (Hour#1) & (Hour#2)

*set Min (TotalTime modulo 60)

*if Min < 10
    *set Min 0 & Min

*return

You then call this very simply with the following code

*set TotalTime + 15
*gosub_scene clock

If the TotalTime is equal to 1,440 (midnight) we set it to 0 and start counting again. The reason why is explained next.

If the TotalTime is more than 1,440 (24 hours), then we remove 1,440 minutes from it (we loop round and round until the TotalTime is less than 1,440. Functionally we’re removing 24 hours from any total elapsed time of 24 hours or more. This won’t change what ‘time’ it is for any given ‘day’.
7 am is 420 elapsed minutes. If you kept adding minutes until you were at 7am the following day, then TotalTime would be 1,860. We can simply subtract 1,440 to return this value to 420 without changing the resulting time (7am).

This ensures that our next calculations are all kept within the bounds of the 24h clock - making life much simpler.

Then we do our hour calculation. Divde TotalTime by 60 (line 8). This gives us a decimal whereby the number before the decimal point is the time in hours in 24h format. (for example 440 = 7.3333 - and we know that 420 is 7 hours, with the additonal 20 minutes generating the remainder).

So now we just need to extract the 7. This is lines 10+11 and 12+13.
CS let’s us select individual characters from a variable. If the resulting hour is 10 or more (10-24) then we need to select the first two characters (as the variable will be 10.2333, or 16.2492, or whatever). If the resulting hour is less than 10, then we only need to select the first character (0-9), but we want to concatenate a 0 on the front, so that we retain our nice 24h format.

For the minute, we use modulo as discussed and it just returns us the remainder that didn’t divide cleanly into 60 (and therefore is always a number below 60). Again, if it is a single digit, we concatenate a 0 on the front.

I’ve made your original post a wiki, you should be able to edit it now.

1 Like

Thanks.
I’ll consolidate it (and retain both methods) tomorrow.

You can extract the hours using modulo by calculating (minutes - (minutes modulo 60))/60 — not sure it’s necessarily more elegant, but that’s what I did when I needed a clock subroutine.

Code
*title Clock Subroutine Test
*create time 0

*comment ---------------------------------------------------------------

*label clock_test

*gosub display_12hr_time

*gosub display_24hr_time

*fake_choice
    #Add 1 minute.
        *set time + 1
    #Add 5 minutes.
        *set time + 5
    #Add 10 minutes.
        *set time + 10
    #Add 15 minutes.
        *set time + 15
    #Add 30 minutes.
        *set time + 30
    #Add 4 hours.
        *set time + 240
    #Add 12 hours.
        *set time + 720

*goto clock_test

*comment ---------------------------------------------------------------

*label display_12hr_time
*temp days (time - (time modulo 1440))/1440
*temp minutes ((time - (days*1440)) modulo 60)
*temp hours ((time - (days*1440)) - minutes)/60
It is day ${days}. The time is @{(hours = 0) 12|}@{((hours != 0) and (hours <= 12)) ${hours}|}@{(hours > 12) ${hours-12}|}:@{(minutes < 10) 0|}${minutes} @{((time-(days*1440)) < 720) am|pm}.
*return

*label display_24hr_time
*temp days (time - (time modulo 1440))/1440
*temp minutes ((time - (days*1440)) modulo 60)
*temp hours ((time - (days*1440)) - minutes)/60
It is day ${days}. The time is @{(hours = 0) 0|}${hours}:@{(minutes < 10) 0|}${minutes}.
*return
3 Likes