Adding a custom Time Format to the profile look/layout options

Started by Melonking, November 29, 2023, 10:32:26 AM

Previous topic - Next topic

Melonking

Hi, I noticed there is a selection of time formats available to pick from on the profile layout settings; I was wondering if it's possible to add a new custom-scripted time format to this list; in particular, I'd like to allow members to select .beat/swatch time as their default format.

I imagine this would require a custom conversion function to be called; so it may not be as simple as adding a new formatting string.

Any advice on where these formats are stored and if anyone has experience modifying this feature?

Looking at the inspector the format string is simply passed as a value:
<option value="%B %d, %Y, %H:%M:%S">Month Day, Year, HH:MM:SS (24 hour)</option>
So I imagine it might be possible to pass a custom value that triggers a beat time calculation function.

SMF: 2.0.19

Melonking

To answer the simple part of my own question: You add extra lines to the following files to give extra time formats:
Themes/default/languages/Profile.english.php
$txt["timeformat_easy5"] = "DD-MM-YYYY, HH:MM:SS";Sources/Profile-Modify.php
array('format' => '%d-%m-%Y, %H:%M:%S', 'title' => $txt['timeformat_easy5']),Just add extra lines after those timeformat_easy6, timeformat_easy7 etc

I was able to add extra functionality by creating a passthrough function:

Custom time format with added %@ for swatch time.
array('format' => '%d-%m-%Y %@', 'title' => $txt['timeformat_easy6']),
function swatch_strftime($format, $timestamp = null)
{
    $value = strftime($format, $timestamp);

    if (strpos($value, "%@") !== false) {
        $then = new DateTime("@$timestamp");
        $then->add(new DateInterval("PT2H"));
        // Calculate the seconds since midnight e.g. time of day in seconds
        $midnight = clone $then;
        $midnight->setTime(0, 0);
        $seconds = $then->getTimestamp() - $midnight->getTimestamp();
        // Swatch beats in seconds - DO NOT CHANGE
        $swatchBeatInSeconds = 86.4;
        // Calculate beats to two decimal places
        $beat = number_format(round(abs($seconds / $swatchBeatInSeconds), 2), 2);
        $value = str_replace("%@", "@" . $beat, $value);
    }

    return $value;
}

I then replaced all instances of strftime with swatch_strftime - however, the timestamps being passed into this function seem erratic; some of them seem to have totally different timezones from each other. Swatch time does not use timezones, so Im trying to force them all to be UTC+1, but for some reason that requires a 2 hour offset here which makes no sense... (UNIX epoch timestamps should always be UTC) - the $context['current_time'] is wildly different from the forum post time stamps and seems to be pre-offset in a different way.

Sesquipedalian

I promise you nothing.

Sesqu... Sesqui... what?
Sesquipedalian, the best word in the English language.

Melonking

Quote from: Sesquipedalian on November 29, 2023, 12:58:49 PMhttps://www.php.net/manual/en/datetime.settimezone.php is what you need.

The issue with this is that it doesn't have a UTC+1 timezone option; you can do UTC or pick a country in the UTC+1 zone, but as far as I know, if you do that it gets messed up by daylight savings time. The only way is to add 1 hour to UTC.. although that does not explain why I need to add 2 hours here..

Also the member's time-offset option is messing with it too; the timestamps passed to strftime on the topic list are all pre-offset, whereas the ones using the todaytime mod (is ita  mod if its built in?) are not pre-offset.

EDIT:
A slightly modified logic that seems to work a little better and maybe illustrates the last big issue:
if (strpos($value, "%@") !== false) {
        $date = new DateTime("@$timestamp", new DateTimeZone("Europe/Amsterdam"));
        $date->setTimeZone(new DateTimeZone('UTC'));
        // get hours, minutes and seconds
        $hours = $date->format('H');
        $minutes = $date->format('i');
        $seconds = $date->format('s');
        // add hour to get time in Switzerland
        $hours = ($hours == 23) ? 0 : $hours + 1;
        // time in seconds
        $timeInSeconds = ((($hours * 60) + $minutes) * 60) + $seconds;
        // calculate beats to two decimal places
        $beat = number_format(round(abs($timeInSeconds / 86.4), 2), 2);
        $value = str_replace("%@", "@" . $beat, $value);
    }

This works fine if the member's timeoffset is 0 because the server time is Amsterdam, however, if they set a time offer it messes it up. The issue is that there is no way to figure out what the member timezone is and I'm not seeing the time offset in the $context object - is there a way to easily grab someone's offset from anywhere in the forum code?

Sesquipedalian

Etc/GMT-1

Note that the +/- signs for the Etc/GMT* time zones are counterintuitive. Etc/GMT+1 is an hour behind UTC, and Etc/GMT-1 is an hour ahead of UTC.

As for the offset timestamps, that's because you are still running SMF 2.0. SMF 2.0 still uses old legacy code dating from way back before PHP provided proper time zone support. That legacy code provides a fake version of time zone support by manipulating timestamps based on the server time offset and user time offset values stored in the admin centre and each user's profile, respectively. To make your custom format work, you will need to add code to your function to reverse the application of those offset values in order to get back to the true original timestamp.

Or you could make the smart call and upgrade to SMF 2.1, which has real time zone support and doesn't muck around with timestamps at all.
I promise you nothing.

Sesqu... Sesqui... what?
Sesquipedalian, the best word in the English language.

Melonking

Quote from: Sesquipedalian on November 29, 2023, 06:48:49 PMTo make your custom format work, you will need to add code to your function to reverse the application of those offset values in order to get back to the true original timestamp.
This is fine if I can get the offset; is it accessible in any global variable or would I need to add that variable? I can see its popping up in situations like this;
$time = $log_time + ($user_info['time_offset'] + $modSettings['time_offset']) * 3600;but it seems a little inconsistent; I noticed the variable names in $user_info can change depending on context.

Quote from: Sesquipedalian on November 29, 2023, 06:48:49 PMOr you could make the smart call and upgrade to SMF 2.1
I appreciate the help and I appreciate the work that you've all put into the 2.1 version, but I don't appreciate the pushyness I've received about this every time I reach out on this forum.

Switching to 2.1 would require remaking everything from scratch, the mods I use don't exist for 2.1, and the themes don't exist for 2.1; that would be months maybe even years of work. I do not have the time to do this; it's not practical for me to make this switch, so I am going to continue to use 2.0 because it's the best version for me and my use case. When it no longer proves to be viable I will probably close my forum. My use case is different to the average forum, I understand this is perhaps strange and impractical from your perspective; I understand that it's not what you're focused on and that's fine; I'm simply posting here because I think it's useful for anyone searching these questions in the future and because the occasional input others like yourself provide can be really helpful in figuring out an issue  :)

Sesquipedalian

I promise you nothing.

Sesqu... Sesqui... what?
Sesquipedalian, the best word in the English language.

Sesquipedalian

Quote from: Melonking on November 29, 2023, 07:15:16 PMWhen it no longer proves to be viable

That will happen in the not too distant future. SMF 2.0 only works on PHP 8.0 and below, but PHP 8.0 has officially reached "End of Life" status. Hosting providers will probably continue to allow it for a few more years yet, but the writing is on the wall.
I promise you nothing.

Sesqu... Sesqui... what?
Sesquipedalian, the best word in the English language.

Melonking

Quote from: Sesquipedalian on November 29, 2023, 07:50:52 PMHosting providers will probably continue to allow it for a few more years yet, but the writing is on the wall.
Don't worry I know; PHP 8 (Currently in 7.4 I think) is a switchover I plan to do and I think its worth putting in the time to update any custom code that needs it. Thankfully I run my forum on a VPS, so no one is gonna be forcing me to upgrade. Eventually, newer versions of Linux wont work with old PHP versions, and/or the security exploits in them will become impossible to ignore... but I'd still say that's 10 years away; that's a long haul - by then if I'm still around running a 2.0 forum will almost be as retro as running BBS is today :laugh:

Melonking

I can follow this up with some super useful info I've discovered; allll visual time formatting on the forum actually happens in a single function called timeformat in Sources/Subs.php - this is the only function you need to modify to add extra time formats to the forum (assuming you're not using external mods such as calendars/shoutboxes etc)

So in this function, you can pass the un-offset time to your timing function and completely bypass the offset issue ^^ This makes this task way easier and is essential to know - please do not follow my previous advice of replacing all strftime function calls in all sources - this introduces unnecessary instability and bugs!

Advertisement: