Subscribe for automatic updates: RSS icon RSS

Login icon Sign in for full access | Help icon Help
Advanced search

Pages: [1]
  Reply  |  Print  
Author Topic: Get current time, in milliseconds  (Read 32245 times)
Anderson P.
Posts: 82


« on: August 04, 2014, 07:06:34 pm »

Hello,

How can i get the number of milliseconds passed since the UNIX epoch (January 1, 1970 UTC) in BDL?

What i need is something like the command "System.currentTimeMillis()" from Java does.

Thank you for your attention.
Sebastien F.
Four Js
Posts: 509


« Reply #1 on: August 05, 2014, 11:33:21 am »

Hello,

With Genero, you typically handle date+time data with DATETIME and INTERVAL types, and you can get the current system time with the CURRENT keyword.

DATETIME values represent a given point in time, while INTERVAL values represent a duration.

You could compute the duration since epoch by substracting DATETIME(1970-01-01) from CURRENT, but CURRENT returns the local system time in the current timezone (not in UTC).

You will have to wait for the next major version of Genero to get UTC to local TZ datetime conversion functions, for now it's only available in the BDL 2.51 version that ships with Genero Mobile V1:

http://www.generomobile.com/online_documentation/fjs-fgl-manual/#c_fgl_ext_util_Datetime_methods.html

You could then do following:
Code
  1. IMPORT util
  2.  
  3. MAIN
  4.    RUN "echo $(($(date +%s%N)/1000000))" -- Linux/Bash!
  5.    DISPLAY unixtime_ms(CURRENT YEAR TO FRACTION(3)) USING "<<<<<<<<<<<<<<<<<<"
  6.    DISPLAY unixtime_ms("1970-01-01 00:00:00.000")   USING "-<<<<<<<<<<<<<<<<<<"
  7. END MAIN
  8.  
  9. -- Converts a datetime to milliseconds since epoch
  10. FUNCTION unixtime_ms(d)
  11.    CONSTANT ms_1970_2000 BIGINT = 946684800000 -- milliseconds from 1970-01-01 to 2000-01-01
  12.    DEFINE d DATETIME YEAR TO FRACTION(3)
  13.    DEFINE d2000 DATETIME YEAR TO FRACTION(3)
  14.    DEFINE sec_2000_now INTERVAL SECOND(9) TO FRACTION(3)
  15.    DEFINE ms_2000_now BIGINT
  16.    DEFINE tmp STRING
  17.  
  18.    LET d2000 = "2000-01-01 00:00:00.000"
  19.    LET sec_2000_now = util.Datetime.toUTC(d) - d2000
  20.    LET tmp = sec_2000_now -- Formatted!
  21.    LET ms_2000_now = (tmp.subString(1,10)*1000) + tmp.subString(12,14)
  22.  
  23.    RETURN (ms_1970_2000 + ms_2000_now)
  24.  
  25. END FUNCTION

Can we have more detais about what you want to do with the current UTC date/time in milliseconds?

Seb
Anderson P.
Posts: 82


« Reply #2 on: August 05, 2014, 01:46:43 pm »

Hello Sebastien,

Thanks for your reply! This solution worked perfectly.

We need this "timestamp" for our e-mail client, Zimbra, pre-authentication process. As described in the link below, we need the current time, in milliseconds to generate the access token.

http://wiki.zimbra.com/wiki/Preauth
Sebastien F.
Four Js
Posts: 509


« Reply #3 on: August 05, 2014, 02:30:48 pm »

Ok fine!

I just realized that the UTC conversion methods are already available in 2.50.

https://4js.com/online_documentation/fjs-fgl-manual-html/?path=fjs-fgl-manual#c_fgl_ext_util_Datetime_methods.html

In fact in BDL 2.51, we have added fromSecondsSinceEpoch / toSecondsSinceEpoch methods...

Note also that there is a more elegant whay to define the year 2000 datetime value (d2000), by using a CONSTANT:

Code
  1. CONSTANT d2000 DATETIME YEAR TO DAY = DATETIME(2000-01-01) YEAR TO DAY

Seb
Reuben B.
Four Js
Posts: 1047


« Reply #4 on: August 06, 2014, 12:12:28 am »

Hi,

Seen as you already had found a method in Java that did what you want, I'll point out that ...

Code
  1. IMPORT JAVA java.lang.System
  2. MAIN
  3.   DISPLAY System.currentTimeMillis()
  4. END MAIN

... would've got you what you wanted as well (although I prefer a 4gl solution where possible).

Seb:

Two things to note.

One: The Java routine returns answer in milliseconds, whereas the new util.Datetime methods http://www.generomobile.com/online_documentation/fjs-fgl-manual/#c_fgl_ext_util_Datetime_toSecondsSinceEpoch.html that are being added to 4gl return an answer in seconds.  Might be worth checking if we lose anything by returning seconds and not milliseconds.  In this case it doesn't look like we do,  however having an answer in milliseconds gives you flexibility you don't have with an answer in seconds.

Two: In your example, you subtracted 2000-01-01 instead of 1970-01-01 to get around the fact that the maximum precision for first qualifier of an INTERVAL is 9.  You can have INTERVAL SECOND(9) TO FRACTION(3) but you can't have INTERVAL SECOND(10) TO FRACTION (3).  The reason for the upper-bound of 9 is probably "historical" or something to do with INTEGER, I wonder if that is still necessary.

Reuben



Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Sebastien F.
Four Js
Posts: 509


« Reply #5 on: August 06, 2014, 10:35:54 am »

Hi Reuben,

1) About using JAVA's System.currentTimeMillis(): loading a JVM just to get this info is to me a lot of overhead.

2) I had the same thought regarding the new util.Datetime methods of 2.51.
You can however very easily add milliseconds from the CURRENT time, even if CURRENT is timezoned (the milliseconds are the same):

Code
  1. IMPORT util
  2. MAIN
  3.    RUN "echo $(($(date +%s%N)/1000000))" -- Linux/Bash!
  4.    DISPLAY millisecondsSinceEpoch(CURRENT) USING "<<<<<<<<<<<<<<<<<<"
  5. END MAIN
  6.  
  7. FUNCTION millisecondsSinceEpoch(dt)
  8.    DEFINE dt DATETIME YEAR TO FRACTION(3)
  9.    DEFINE s, ms BIGINT, tmp STRING
  10.    LET s = util.Datetime.toSecondsSinceEpoch(dt)
  11.    LET tmp = EXTEND(dt, FRACTION TO FRACTION(3)) -- ".fff"
  12.    LET ms = tmp.subString(2,4)
  13.    RETURN (s * 1000) + ms
  14. END FUNCTION

I believe people can deal with this.

3) Regarding the precision of INTERVALs, we use the Informix / ESQL/C interval structure definition: We use it internally to store interval values, so we can pass it directly to the ESQL/C Informix driver without any further conversion as in non-Informix drivers. Thus, we are forced to use the exact Informix definition and precision. Handling values with a larger precision as what the database can store would also lead to overflow errors when used in SQL.

Seb
Anderson P.
Posts: 82


« Reply #6 on: August 06, 2014, 01:32:49 pm »

Hey! It's great to see you guys evolving in this topic...

Here are my considerations as an end user:

1) Also agree that Java must be used only in very special cases, in this case will be necessary to start Java just for a simple command;

2) If possible, i think it will be better to make the "toSecondsSinceEpoch" return in milliseconds once it will be easier to convert from milliseconds to seconds rather than the other way. This kind of precision is useful to us when we want to ensure oneness of files name, as the following case:

Code
  1. let l_time = current year to fraction (5)
  2.  
  3. let l_unic = ga_mem[l_i].empcod using "<<<<", "_", ga_mem[l_i].memcod using "<<<<<<<<<<", "_", l_time[03,04], l_time[06,07], l_time[09,10], l_time[15,16], l_time[18,19], l_time[21,25]
  4.  

In this case, i need to remove the white spaces from current year to fraction (5), a millisecondsSinceEpoch will make things easier for sure!
Sebastien F.
Four Js
Posts: 509


« Reply #7 on: August 06, 2014, 02:35:10 pm »

About (2):

toSecondsFromEpoch() is already GA in FGL 2.51 for GeneroMobile V1.0, so we can't change the unit of the result anymore, we would need to implement 2 new methods to keep backward compatibility.

For what reason do you need unique file names?
Anderson P.
Posts: 82


« Reply #8 on: August 06, 2014, 02:57:16 pm »

Hey Seb,

When we store files in our Informix database we usually insert just the Byte, discarding the filename.

So when the user wants to view the file, we download the Byte to a temporary folder. We name the file with the Current Year to Fraction to avoid that the filename matches the name of another file in this temp folder.
Sebastien F.
Four Js
Posts: 509


« Reply #9 on: August 06, 2014, 03:22:46 pm »

I expected that... And I guess you have to cleanup your temp dir by hand?

What if Genero BDL would provide an API to generate a temp file path for you, and cleanup the temp files when the program ends?
(We discussed internally about this feature)

Code
  1. DEFINE tmpname STRING, mydata BYTE
  2. LET tmpname = os.Path.getTempFileName()
  3. LOCATE mydata IN FILE tmpname
  4. ...
  5.  

Another option could be to get the temp file name created by a LOCATE IN FILE (without filename spec)

Code
  1. DEFINE tmpname STRING, mydata BYTE
  2. LOCATE mydata IN FILE -- (temp file)
  3. LET tmpname = mydata.getFileName()
  4. -- If this is an image data, you can then do:
  5. DISPLAY tmpname TO image_field
  6. ...
  7.  
Sebastien F.
Four Js
Posts: 509


« Reply #10 on: August 06, 2014, 03:50:14 pm »

Precision: In case of image data, you can in fact fetch into a BYTE, and display the BYTE directly to an IMAGE field: The VM handles automatically the display of BYTE images located in files (even if in temporary files).

So you can do:

Code
  1. DEFINE img BYTE
  2. LOCATE img IN FILE
  3. SELECT ... INTO img ...
  4. DISPLAY img TO image_field
  5. ...
  6.  

What type of data contain your files stored in BYTEs?

Doc link:
http://www.generomobile.com/online_documentation/fjs-fgl-manual/#c_fgl_images_dynamic_images.html
Anderson P.
Posts: 82


« Reply #11 on: August 06, 2014, 08:35:33 pm »

Seb,

A function to generate a temp file and erase it after the program execution will be useful for sure.

We name the file with the date because it's necessary in some specific cases. Then we have a script in Linux do clean the files that was created more that 2 days ago.

We also have a function to generate this temporary file name, with the date and user code as follows.

Code
  1. function monta_caminho_temporario(l_nome)
  2.  
  3.    define l_nome    string,
  4.           l_caminho string,
  5.           l_time    char(25)
  6.  
  7.    whenever error call erro_cont
  8.  
  9.    let l_time = current year to fraction (5)
  10.  
  11.    if   l_nome is null
  12.      then
  13.         let l_nome = downshift(gg_sis.sissist) clipped, gg_dmp.dmpcod[4,6]
  14.    end if
  15.  
  16.    let l_caminho = gg_path.tmp,                    # /tmp/
  17.                    l_nome clipped,
  18.                    l_time[03,04],                  # Ano
  19.                    l_time[06,07],                  # Mês
  20.                    l_time[09,10],                  # Dia
  21.                    l_time[12,13],                  # Hora
  22.                    l_time[15,16],                  # Minuto
  23.                    l_time[18,19],                  # Segundo
  24.                    l_time[21,25],                  # Fração
  25.                    gg_usu.usucod using "&&&&&&"    # Usuário
  26.  
  27.    return l_caminho
  28.  
  29. end function
  30.  

Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines