ID:2916778
 
Resolved
time2text() showed altered behavior with the newer runtime libraries when using negative values.
BYOND Version:515.1633
Operating System:Windows 11 Pro 64-bit
Web Browser:Firefox 123.0
Applies to:Dream Daemon
Status: Resolved (515.1634)

This issue has been resolved.
Descriptive Problem Summary:
The time2text proc seems to no longer handle offsets properly in byond 515.1633.
When calling the proc with a negative offset, it only returns something like 00:59:59, seemingly depending on offset.

calling the proc without the offset in both cases (514 and 515) starts with 01:00:00. So the offset is needed to get the time to 00:00:00

Example call without offset: Wed Mar 13 01:00:03 2024

Numbered Steps to Reproduce Problem:
create the proc beneath.
Define an offset like 36000 for the timezoneOffset (-1 hour)
call the proc beneath

Seemingly the proc can't handle the negative numbers anymore because to get beneath the first hour, the wtime - offset is < 0?

Code Snippet (if applicable) to Reproduce Problem:
/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
if(!wtime)
wtime = world.time
return time2text(wtime - GLOB.timezoneOffset, format)


Expected Results:
Like in byond 514: 00:00:0x when called right at round start

Actual Results:
00:59:59

Full result when using a negative offset: Sat Jan 01 00:59:59 2000

Does the problem occur:
Every time? Or how often? every time
In other games? only tested ss13
In other user accounts? yes, server side
On other computers? yes, server side

When does the problem NOT occur?
Rolling back to 514.1589

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked? (Visit http://www.byond.com/download/build to download old versions for testing.)
Worked fine in 514.1589

Workarounds:

</0>
I can confirm above using the below snippet.

#define HOURS *36000

var/global/timezoneOffset

/world
fps = 25

/world/New()
timezoneOffset = get_timezone_offset()
clock()

/proc/clock()
world.log << gameTimestamp()
spawn(10)
clock()

/proc/get_timezone_offset()
var/midnight_gmt_here = text2num(time2text(0,"hh")) HOURS
if(midnight_gmt_here > 12 HOURS)
return 24 HOURS - midnight_gmt_here
else
return midnight_gmt_here

/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
if(!wtime)
wtime = world.time
return time2text(wtime - timezoneOffset, format)


On 514.1589:
https://i.imgur.com/YfJVz3M.png

On 515.1633:
https://i.imgur.com/N2N8VjQ.png

Though I will note we have not noticed any issues with clocks on our live server which is using basically the same code, but it is hosted on linux so this bug may be exclusive to windows.
I can confirm the behavior is different, although it isn't yet clear why, but there's a problem: time2text() is meant to work with world.realtime or world.timeofday, not with world.time which is not a real-time value. world.time is a count of BYOND-standard ticks.

So even though the behavior has changed, I can't actually confirm anything is wrong as such, because your example is working with values that are incorrect.

Update: This might be because of a change in the runtime libraries. Again though I don't have any indication the behavior is actually wrong; it'd need to be tested against valid data.
I mean there isn't anything particularly special about what world.realtime or world.timeofday provide. They just tend to be large positive numbers. The regression is that time2text completely breaks for a negative number whereas before it did not.

To show the lack of difference with more verbose logging of above see:

#define HOURS *36000

var/global/timezoneOffset

/world
fps = 25

/world/New()
spawn(10) // Otherwise happens before connecting
timezoneOffset = get_timezone_offset()
clock()

/proc/clock()
world.log << gameTimestamp()
spawn(10)
clock()

/proc/get_timezone_offset()
var/world_real = world.realtime
world.log << "world.realtime: [world_real] [json_encode(world_real)] \ref[world_real]"
var/world_timeofday = world.timeofday
world.log << "world.timeofday: [world_timeofday] [json_encode(world_timeofday)] \ref[world_timeofday]"

var/text = time2text(0,"hh")
world.log << "get_timezone_offset time2text(0,\"hh\"): [text] [json_encode(text)] \ref[text]"
var/text_num = text2num(text)
world.log << "get_timezone_offset text2num(time2text(0,\"hh\")): [text_num] [json_encode(text_num)] \ref[text_num]"
var/text_num_hours = text_num HOURS
world.log << "get_timezone_offset text2num(time2text(0,\"hh\")) HOURS: [text_num_hours] [json_encode(text_num_hours)] \ref[text_num_hours]"

. = text2num(time2text(0,"hh")) HOURS
world.log << "get_timezone_offset retval: [.] [json_encode(.)] \ref[.]"
if(. > 12 HOURS)
. = 24 HOURS - .
world.log << "get_timezone_offset midnight > 12 HOURS retval: [.] [json_encode(.)] \ref[.]"

/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
if(!wtime)
wtime = world.time
world.log << "gameTimestamp wtime: [wtime] [json_encode(wtime)] \ref[wtime]"
. = time2text(wtime - timezoneOffset, format)
world.log << "gameTimestamp: [.] [json_encode(.)] \ref[.]"

Comparison: https://i.imgur.com/fZFUWXr.png

With a more simplified example:
/world
fps = 25

/world/New()
clock()

/proc/clock()
spawn(10)
var/timestamp = world.time - 100
var/result = time2text(timestamp, "hh:mm:ss")
world.log << "timestamp: [timestamp] result: [result]"
clock()


514.1589:
timestamp: -90 result: 15:59:51
timestamp: -80 result: 15:59:52
timestamp: -70 result: 15:59:53
timestamp: -60 result: 15:59:54
timestamp: -50 result: 15:59:55
timestamp: -40 result: 15:59:56
timestamp: -30 result: 15:59:57
timestamp: -20 result: 15:59:58
timestamp: -10 result: 15:59:59
timestamp: 0 result: 17:00:00
timestamp: 10 result: 17:00:01
timestamp: 20 result: 17:00:02
timestamp: 30 result: 17:00:03
timestamp: 40 result: 17:00:04
timestamp: 50 result: 17:00:05
timestamp: 60 result: 17:00:06
timestamp: 70 result: 17:00:07
timestamp: 80 result: 17:00:08
timestamp: 90 result: 17:00:09


515.1633:

timestamp: -90 result: 15:59:59
timestamp: -80 result: 15:59:59
timestamp: -70 result: 15:59:59
timestamp: -60 result: 15:59:59
timestamp: -50 result: 15:59:59
timestamp: -40 result: 15:59:59
timestamp: -30 result: 15:59:59
timestamp: -20 result: 15:59:59
timestamp: -10 result: 15:59:59
timestamp: 0 result: 17:00:00
timestamp: 10 result: 17:00:01
timestamp: 20 result: 17:00:02
timestamp: 30 result: 17:00:03
timestamp: 40 result: 17:00:04
timestamp: 50 result: 17:00:05
timestamp: 60 result: 17:00:06
timestamp: 70 result: 17:00:07
timestamp: 80 result: 17:00:08
timestamp: 90 result: 17:00:09


I will note in above that it is actually off by an hour during that flip from negative to positive, but at least seconds functioned.

To compensate for the regression, I probably can just add 24 hours to all gameTimestamp calls so it should always be dealing with a positive number if you aren't going to fix the regression.
I believe what happened here is that the new runtimes altered some behavior under the hood. But I was able to make a change to some math in the calling function, and that restored the previous behavior seen in 514. It looks like the new runtimes are just super pissy about negative timestamps.
Lummox JR resolved issue with message:
time2text() showed altered behavior with the newer runtime libraries when using negative values.