Seconds to hours minutes and seconds

Hello Stefan

Well, it is short and readable, and I am not sure if you can gain anything, from adding statements in order to remove the modulus operations, or if that is feasible in the first place, with regards to the adding of statements. :slight_smile:

(Kel’s code wouldn’t need anyting of this anyway, as that code will execute one operation at a time. I was more thinking of formatting a large list of seconds.)

Could you explain why you want to remove the mod(ulus) command? AFAIK it’s a primitive processor instruction supported by Intel which can’t be optimized in any other way. Or does AppleScript calculate the modulo by itself?

Well, if you look beneath the assembler instruction set in general, then a div and modulus, does a lot more than a multiplication. Especially the modulus is a “high cost” operator.

So, I figured, if you have done a div already, then it will be cheaper to subtract the quantity by multiplying the quotient by the divisor.

It is easier to show it by code, what I mean. :slight_smile:

set fmtTime to secondsToHMS from 3719
--> "01:01:59"
on secondsToHMS from theSecs
	set h to theSecs div 3600
	set s to theSecs - h * 3600
	set m to s div 60
	set s to s - m * 60
	
	if h < 10 then
		set h to text -2 thru -1 of ("00" & h)
	else
		set h to h as text
	end if
	
	if m < 10 then set m to text -2 thru -1 of ("00" & m)
	if s < 10 then set s to text -2 thru -1 of ("00" & s)
	
	return (h & ":" & m & ":" & s)
end secondsToHMS

Edit
But I also wrote that the benefit would truly be eaten up the added number of statements, just for the hell of it, I made an optimized version, and it can’t beat Stefan’s for speed, even tough I have inlined his call to the pad handler.

on secondsToHMS from theSecs
	set h to theSecs div hours
	set s to theSecs - h * hours
	set m to s div minutes
	set s to s - m * minutes
	return (text -2 thru -1 of (h + 100 as text) & ":" & text -2 thru -1 of (m + 100 as text) & ":" & (s + 100 as text))
end secondsToHMS

First of all div and mod are done using 1 instruction, div instruction, the divider and remainder are both returned. A distinction between mod en and div is made in higher level programming languages like C and upwards to make programming “easier”. Functions and methods can only return one value so there is an operator mod and div separately which are in fact the same but only returns different results.

Performance-wise there is indeed a difference but how much will you gain? Divisor instruction is considered expensive compared to logical instructions who can even take less than one cycle (there is headroom for a another instruction in the same pipeline and therefore not considered to require a whole cycle). Each line (byte code) that needs to be executed in AppleScript will require hundreds of more cycles of the processor than a single divisor instruction. I mean think about the number of assembler code is required for one single AppleScript line of code (thousands easily). The overhead of AppleScript itself is too large and the netto effect would only be negative.

Hello.

I agree fully. :slight_smile:

I have done the observation/conclusion that you write in the post above. This holds true for the mod operation, it can’t be outperformed by doing a subtraction and a multiplication. There are several other aspects to this, regarding “optimization” in AppleScript the number of statements, that made it a bad idea to reuse previous results of expressions. Which you cove, I also wonder, but I won’t speculate over it, if Stefan’s “one-line” function, really just does all its work on the “call-stack” since it just consists of a return statement.

Every machine, including virtual machines, has a call stack (stack machine) which uses stack pointers and stack frames to make recursive programming available. How AppleScript’s stackmachine exactly works I don’t know and is just speculation. But looking at other programming and scripting language designs in general I don’t think AppleScript is way off or technically can’t. Logically everything between an on and end will be a single stack frame and the blueprint of the handler is copied to the stack and executed when needed. But I don’t think a single or 100 commands inside an AppleScript handler would make a difference in the call stack. The stack frame would grow in size because the sequence of opcodes is obviously longer but it still would be a single item on the call stack.

I was actually thinking more in the lines of the mechanism of tail-recurson, where I believe compilers to optimize away the whole pushing and popping of stack frames, but just let the current stack grow - and shrink, thereby saving lots of calls.

The oppositive of that is of course to inline. I inled Stefan’s handler, with the pad handler, and the optimized version were around 1 second faster when I iterated over both 50000 times.

Enjoy your day.

My mistake :). Still I don’t think anything changes or better said it even applies here. Tail recursion will only apply if the last instruction/command of the subroutine will call to itself. Then the stack frame is not removed and re-allocated again but re-used, if the compiler recognizes tail recursion and optimizes it. I don’t think that applies to Stefan’s script because the the caller and callee are not the same routine.

However to be sure that AppleScript doesn’t optimize tail calls at all I have here a script that proves it:

global currentLevel

set currentLevel to 0
try
	recurse()
end try
set r1 to currentLevel

set currentLevel to 0
try
	tailRecurse()
end try
set r2 to currentLevel

return {r1, r2}

on recurse()
	set currentLevel to currentLevel + 1
	recurse()
	-- to avoud tail call
	void()
end recurse

on tailRecurse()
	set currentLevel to currentLevel + 1
	tailRecurse()
end tailRecurse

on void()
end void

As you can see both recursions break at the same level, the stack size. If tail calls were optimized in AppleScript the number of recursions where much higher for tail recursion, r2 would contain a much higher value before the stack overflow error or run infinite.

However even if it doesn’t apply to StefanK’s example, it’s still an interesting question. :slight_smile:

Hello DJ.

It is interesting to check out such stuff. :slight_smile: I did write in line of, meaning that the same technique could have been used, but that too was mere speculations. And now you have proved that it isn’t the case anyway. :slight_smile:

I am or I was, more into believing that the compiler copies in a handler into the compiled script where it is called, and then readresses all variable within that handler, (that once the compiler has compiled the code, it works more like a linkage editor), much have happend to AppleScript since I played with such ideas. (Tiger)

Since then I have learnt that it pays off to think that the Applescript compiler is as smart as a 70’s unoptimizing C-compiler. :slight_smile:

Hi everybody,

Very informative as usual. I’ll have to go back and read through all the posts. Been working on the timer and finally got all the logic down. Getting down to the split times, final time, and cleaning up the notification text. I missed a lot of things.

Thanks a lot,
kel

Hi everybody,

A lot of good info from everybody.

What I got is that it’s inconclusive. There are two ways to get the seconds. You can get the seconds through AppleScript or through Objective c with the interval. If you get it through the interval, then you need to convert with Objective C the interval into an integer. On the other hand, if you get the seconds through AppleScript, then you can directly convert through Applescript.

AppleScript is slower when converting seconds to hours, minutes and seconds. So overall what you lose is just the time for using the speed of Objective c when you need to coerce the interval into seconds. How much speed you gain by using AppleScript is unknown and hence it’s a tossup.

Lastly, whatever you are comfortible in using might be the best way to go and you need choose whatever method that is easiest for you. The differences in time are minute (less than a second). But is you want better accuracy, then you might use AppleScript Objective C.

Stefan’s script uses built in AppleScript functions and that is ok if you don’t need millisecond accuracy. That’s a far cry from seconds. That’s the main point I think.

Edited: and so, just get the hours, minutes, and seconds with AppleScript mod and rem if you’re just dealing with seconds. Although, I’m still thinking about that thousands times comment by DJ or was it hundred times.

Think I got it now and again,
kel

Hi Stefan,

And btw nice script. I didn’t know that is you’re writing a string you could start the concatenation with an integer. Let me look at that again!:smiley:

Thanks a lot,
kel

I see now. You did start with a string. Disregard.

Edited: nice puzzle!

Old post, but just wanted to propose a one-line version of Stefan’s solution:

secondsToHMS from 5240 --> "01:27:20"

on secondsToHMS from theSeconds
	tell theSeconds to tell {it div hours, it mod hours div minutes, it mod minutes} to return "" & ((item 1) div 10) & ((item 1) mod 10) & ":" & ((item 2) div 10) & ((item 2) mod 10) & ":" & ((item 3) div 10) & ((item 3) mod 10)
end secondsToHMS

Nice. :slight_smile: How about .

secondsToHMS from 5240 --> "01:27:20"

on secondsToHMS from theSeconds
	tell theSeconds to tell (it div 36000 as text) & (100000 + it div hours mod 10 * 10000 + it mod hours div minutes * 100 + it mod minutes as integer) to return text 1 thru -7 & character -5 & ":" & text -4 thru -3 & ":" & text -2 thru -1
end secondsToHMS

Nigel, your one-liner executes 2.5x faster than mine in a speed test. Sweet!

And these days we can actually fulfil Kel’s original request easily enough:

use AppleScript version "2.5" -- 10.11 or later to use AppleScript dates
use framework "Foundation"
use scripting additions

set startDate to current date
set endDate to startDate + 5240 -- whatever

set df to current application's NSDateComponentsFormatter's new()
df's setAllowedUnits:((current application's NSCalendarUnitHour) + (get current application's NSCalendarUnitMinute) + (get current application's NSCalendarUnitSecond))
df's setUnitsStyle:(current application's NSDateComponentsFormatterUnitsStyleFull)
(df's stringFromDate:startDate toDate:endDate) as text

Yvan should be happy with that :slight_smile:

I thought mine might be slightly faster, but based on theoretical considerations rather than side-by-side speed tests:

[format]
BM NG
10 integer math operations 11 integer math operations
6 implicit coercions to text 2 coercions to text (1 explicit, 1 implicit)
8 text concatentations 6 text concatenations
1 list creation 1 explicit number-to-integer coercion
6 list accesses 4 extractions from text[/format]

There’d be little to chose between them in a real-life situation. :slight_smile:

I was going to criticise the script for not returning the result in the same format, but having read kel’s opening monologue several times, I can’t see anything specifying either a format or three separate figures. :confused:

Interesting script, as always.

And I was expecting that :wink: But in trying to match that format, I hit a bug. As I read the docs, this should do it:

use AppleScript version "2.5" -- 10.11 or later to use AppleScript dates
use framework "Foundation"
use scripting additions

set startDate to current date
set endDate to startDate + 5240 -- whatever

set df to current application's NSDateComponentsFormatter's new()
df's setAllowedUnits:((current application's NSCalendarUnitHour) + (get current application's NSCalendarUnitMinute) + (get current application's NSCalendarUnitSecond))
df's setZeroFormattingBehavior:(current application's NSDateComponentsFormatterZeroFormattingBehaviorPad)
(df's stringFromDate:startDate toDate:endDate) as text

The .h file for NSDateComponentsFormatter says, in part:


But in fact it doesn’t pad the leading value, and instead returns “1:27:20”. (It pads the other values fine.) And that’s when I went back to see exactly what was asked for…