1. Anthony Dollarhide
  2. PowerBuilder
  3. Wednesday, 14 April 2021 19:59 PM UTC

How so you get the milliseconds from a time value, without first converting it to a string?

Anthony Dollarhide Accepted Answer Pending Moderation
  1. Thursday, 15 April 2021 16:01 PM UTC
  2. PowerBuilder
  3. # 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.

Comment
  1. John Fauss
  2. Thursday, 15 April 2021 16:22 PM UTC
I don't know the layout of the entire structure. I was able to determine how the fractional second is stored internally by copying the small chunk of memory used to hold the value of a Time variable and then varying the microseconds portion multiple times until I was confident I had isolated that component. Pure trial and error, determination and a little luck. I'm not even sure I know the memory size of the internal Time structure.

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.
  1. Helpful
  1. Miguel Leeuwe
  2. Thursday, 15 April 2021 16:53 PM UTC
You baffle me John.

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.
  1. Helpful
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Thursday, 15 April 2021 04:47 AM UTC
  2. PowerBuilder
  3. # 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

 

Comment
  1. Benjamin Gaesslein
  2. Thursday, 15 April 2021 06:00 AM UTC
Nice. Is there anything RtlMoveMemory *can't* do? I'm leaning towards "no".
  1. Helpful
  1. Olan Knight
  2. Thursday, 15 April 2021 15:46 PM UTC
Man, that's just sweet! Thank you for sharing, John!
  1. Helpful
There are no comments made yet.
Anthony Dollarhide Accepted Answer Pending Moderation
  1. Wednesday, 14 April 2021 23:53 PM UTC
  2. PowerBuilder
  3. # 3

The problem for us is that the PowerBuilder strings are not thread safe.

Comment
  1. Miguel Leeuwe
  2. Thursday, 15 April 2021 00:00 AM UTC
Please tell us more, are you running some asynchronous processing? Why would the thread safety of powerbuilder be a problem?

Just curious.

regards
  1. Helpful
  1. Miguel Leeuwe
  2. Thursday, 15 April 2021 17:23 PM UTC
Thanks for sharing!
  1. Helpful
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Wednesday, 14 April 2021 23:50 PM UTC
  2. PowerBuilder
  3. # 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.

Comment
  1. Miguel Leeuwe
  2. Wednesday, 14 April 2021 23:51 PM UTC
Of course a C or C++ unmanaged DLL (ATL?) would be fastest and not require the runtime of .net or .net core
  1. Helpful
There are no comments made yet.
Ronnie Po Accepted Answer Pending Moderation
  1. Wednesday, 14 April 2021 23:45 PM UTC
  2. PowerBuilder
  3. # 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"))

 

Comment
  1. Olan Knight
  2. Thursday, 15 April 2021 15:48 PM UTC
I learn something new every day! Thanks, Ronnie!
  1. Helpful
  1. Ronnie Po
  2. Thursday, 15 April 2021 16:38 PM UTC
:^)
  1. Helpful
There are no comments made yet.
Olan Knight Accepted Answer Pending Moderation
  1. Wednesday, 14 April 2021 22:55 PM UTC
  2. PowerBuilder
  3. # 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

Comment
There are no comments made yet.
Anthony Dollarhide Accepted Answer Pending Moderation
  1. Wednesday, 14 April 2021 21:10 PM UTC
  2. PowerBuilder
  3. # 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?

Comment
There are no comments made yet.
Chris Pollach @Appeon Accepted Answer Pending Moderation
  1. Wednesday, 14 April 2021 20:53 PM UTC
  2. PowerBuilder
  3. # 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

Comment
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.