How so you get the milliseconds from a time value, without first converting it to a string?
- You are here:
- Home
- Q&A
- Q&A
- PowerBuilder
- Fractions of a second from time object
- Anthony Dollarhide
- PowerBuilder
- Wednesday, 14 April 2021 19:59 PM UTC
- Thursday, 15 April 2021 16:01 PM UTC
- PowerBuilder
- # 1
Thank you for all the responses. Just one follow up question, how do you find the structure definition of the PB time object? I need to pass it to external DLLs.
- Thursday, 15 April 2021 04:47 AM UTC
- PowerBuilder
- # 2
Hi, Anthony -
I'll tell you up front that this is a hack. Use at your own risk.
The only way I know to obtain the fractional seconds portion of a PB Time variable (which is an undocumented structure, by the way) without using the String function is to extract the pertinent bytes from the memory occupied by the Time variable.
To do this, you'll need the following two external function declarations:
SUBROUTINE GetAddressOfTimeVar ( &
REF Longptr pDest, &
REF Time pSource, &
Long ByteLen &
) LIBRARY "kernel32.dll" ALIAS FOR "RtlMoveMemory"
SUBROUTINE CopyBytesFromAddress ( &
REF Byte pDest[], &
Longptr pSource, &
Long ByteLen &
) LIBRARY "kernel32.dll" ALIAS FOR "RtlMoveMemory"
The following code snippet illustrates how you can extract the 6-digit fraction of a second (microseconds) from a value stored in a Time variable:
Byte lbyte_array[] // Ext. Fn Declaration says this array is unbounded.
Integer li_rc, li_ndx, li_bitness
Long ll_address_length // The WinAPI function says this needs to a Long.
ULong lul_microseconds
Longptr llptr_time_var_address // 4 bytes long in 32-bit, 8 bytes long in 64-bit.
Time lt_value
Environment lenv
// The length of a memory address depends on execution bitness.
li_rc = GetEnvironment(lenv)
li_bitness = lenv.ProcessBitness // 32 or 64
ll_address_length = li_bitness / 8 // 4 or 8
lt_value = Time('23:59:59.987654') // Test with any time value...
// The microseconds member of a time structure occupies three bytes.
// The receiving byte array MUST be initialized.
For li_ndx = 1 To 3
lbyte_array[li_ndx] = 0
Next
// Obtain the address of the Time variable/structure.
GetAddressOfTimeVar(llptr_time_var_address,lt_value,ll_address_length)
// The fractional seconds are stored in the first three bytes at this address.
CopyBytesFromAddress(lbyte_array,llptr_time_var_address,3)
// Re-construct the microseconds value. In Windows, all integer data types
// are laid out in memory in LittleEndian encoding (low-order byte is first).
lul_microseconds = lbyte_array[3]
lul_microseconds *= 256
lul_microseconds += lbyte_array[2]
lul_microseconds *= 256
lul_microseconds += lbyte_array[1]
// Now that you have microseconds, you can use any/all of the six digits...
MessageBox('Eureka!','Microseconds = ' + String(lul_microseconds))
I hope this meets your needs.
Best regards, John
- Benjamin Gaesslein
- Thursday, 15 April 2021 06:00 AM UTC
-
Helpful Loading... Helpful 0
- Olan Knight
- Thursday, 15 April 2021 15:46 PM UTC
-
Helpful Loading... Helpful 0
- Wednesday, 14 April 2021 23:53 PM UTC
- PowerBuilder
- # 3
The problem for us is that the PowerBuilder strings are not thread safe.
- Miguel Leeuwe
- Thursday, 15 April 2021 00:00 AM UTC
Just curious.
regards
-
Helpful Loading... Helpful 0
- Wednesday, 14 April 2021 23:50 PM UTC
- PowerBuilder
- # 4
Looking at other answers, I might not fully understand the question, but my interpretation is that "You want to 'get' the milliseconds from some time variable in powerscript without having to convert to a string?"
I don't think there's any other way of doing so (as by using the string function). I'm not sure why the string conversion means a problem for you, but I think it might be related to speed issues?
If so, you could write a .net assembly or C, C++ dll and let that one return the value as a number and that will probably speed-up things significantly.
I don't think there's a "time" variable in .Net though, so you'd have to use a datetime with a dummy value for the date part: https://docs.microsoft.com/en-us/dotnet/api/system.datetime.millisecond?view=net-5.0
regards.
- Miguel Leeuwe
- Wednesday, 14 April 2021 23:51 PM UTC
-
Helpful Loading... Helpful 0
- Wednesday, 14 April 2021 23:45 PM UTC
- PowerBuilder
- # 5
There's no getting around the string conversion, but you can do it purely in PowerBuilder without involving a database:
int li_msec
li_msec = integer(string(now(), "fff"))
- Olan Knight
- Thursday, 15 April 2021 15:48 PM UTC
-
Helpful Loading... Helpful 0
- Wednesday, 14 April 2021 22:55 PM UTC
- PowerBuilder
- # 6
Anthony -
As far as I know, that is correct. The ONLY way to get the milliseconds of a time value is to use the time value from a database. Oracle and MySQL both support a millisecond component.
This might be an easy-to-do enhancement request. :)
Later -
Olan
- Wednesday, 14 April 2021 21:10 PM UTC
- PowerBuilder
- # 7
In looking at your response, it looks like there is no way to get the milliseconds by using PowerBuilder only? Is that the case?
- Wednesday, 14 April 2021 20:53 PM UTC
- PowerBuilder
- # 8
Hi Anthony;
How about using a variation of one of these as an SQL computed column?
Example here from MS SS ...
SELECT GETDATE() 'Today',
DATEPART(millisecond,GETDATE()) 'MilliSecond Part'
// - OR -
SELECT GETDATE() 'Today',
DATEPART(ms,GETDATE()) 'MilliSecond Part'
Regards ... Chris
- Page :
- 1
However, you are not allowed to reply to this question.
If your goal is to pass a PB Time value to an external DLL, you can or should, IMHO, deconstruct the hours, minutes, seconds and microseconds into separate values using the Hour(), Minute(), and Second() PowerScript functions along with the technique I've demonstrated to obtain the microseconds - then pass these four individual values to the external DLL.
I guess I'll always be intrigued why it's a problem that "strings aren't thread safe in powerbuilder", but life is too short for me to worry about it.
regards.