B4R Tutorial Getting local date & time (easy and free)

Kevin

Well-Known Member
Licensed User
I saw a post or two about how to get the local date & time with no easy answers. I need this myself for my first Arduino project so I had a look around and found a free API that does just this!

Although they also have a premium API, their free one is generous, allowing 1 request per second. I intend to only check it once or twice a day, myself. I should add that I found this through a 2-year-old post online where one person said it isn't reliable. I've only tested it a few times but so far, so good.

Info and how to sign up: https://timezonedb.com/

Sample URL: http://api.timezonedb.com/v2/get-time-zone?key=YOURAPIKEY&format=xml&by=zone&zone=America/Chicago
(Link will not work as it requires a working API key which I am not sharing. :p

Sample response:

<result>
<status>OK</status>
<message/>
<countryCode>US</countryCode>
<countryName>United States</countryName>
<zoneName>America/Chicago</zoneName>
<abbreviation>CDT</abbreviation>
<gmtOffset>-18000</gmtOffset>
<dst>1</dst>
<dstStart>1520755200</dstStart>
<dstEnd>1541314799</dstEnd>
<nextAbbreviation>CST</nextAbbreviation>
<timestamp>1527884091</timestamp>
<formatted>2018-06-01 20:14:51</formatted>
</result>
Hope this helps someone!

Now a question for Erel or anyone else who would know:

I've read about the need to be efficient in B4R, so how would be the best way to process this response into something usable for updating our internal "clock" on the board?
 

jgmdavies

Member
Licensed User
I see you can also get the result in json format, and restrict the fields returned, e.g.

http://api.timezonedb.com/v2/get-time-zone?key=YOURAPIKEY&format=json&fields=timestamp&by=zone&zone=Europe/London

which gives something like:

{"status":"OK","message":"","timestamp":1527930034}​

or:

http://api.timezonedb.com/v2/get-time-zone?key=YOURAPIKEY&format=json&fields=formatted&by=zone&zone=Europe/London

which gives:

{"status":"OK","message":"","formatted":"2018-06-02 09:03:15"}​

So it should be easy to parse and get what you need.
 

derez

Expert
Licensed User
See this example https://www.b4x.com/android/forum/threads/esp8266-requesting-time-from-a-server.68636/#post-435534, it does not need a special API.
I use time from Italy :
B4X:
Public Sub Connect(u As  Byte)
    Log("Connecting to Italian server...")
    If socket.ConnectHost("time.ien.it",  13) Then
        astream.Initialize(socket.Stream, "astream_NewData", "astream_Error")
        Log("Connected")
    Else
        Log("Failed to connect to Italian server")
        CallSubPlus("Connect", 10000, 0)
    End If
End Sub
I calculate the offset by the dates.
 

Kevin

Well-Known Member
Licensed User
@derez

As I understood it, people were having issues converting the UTC to local while also accounting for Daylight Savings Time which starts to complicate things. Perhaps I'm wrong though - it wouldn't be the first time! :D
 

Kevin

Well-Known Member
Licensed User
Thanks Kevin - looks useful.
What sort of Arduino clock are you using - is it from http://playground.arduino.cc/Code/Time or similar, or are you using an add-on hardware RTC board/shield?
Jim
I was looking at https://www.b4x.com/android/forum/threads/resp8266rtc-a-real-time-clock.72783/ but I don't know if there are better options or not. I'm very new to this stuff.

I was planning on using this on an 8266. Either an 8266-01 or 12E. I am monitoring the weather (via forecast) and controlling a pool heater based on that information.

Edit: Now I am not sure that is the thread I was looking at before. It may have been an older post before better solutions came about. So confused! :confused:
 
Last edited:

Kevin

Well-Known Member
Licensed User
In case anyone is interested in this option, here is some working code. My reason for wanting to use this API as opposed to using the NIST time example or retrieving it from a script on my server is because I felt the better option was to use something that a) would not require me to determine through code if DST is in effect or not and b) would be a bit more universal as opposed to relying on a specialized script on a server that must reside in the correct timezone, else you need to adjust for it, not that it is a bad option. Possibly worth noting is that while I use this option first, I also use the server script option (described in the library noted below) as a backup if for some reason this API fails, though I won't get into that in the sample code.

The code uses the JSON reponse (and the "formatted" field) option as mentioned by jgmdavies above.

I use this in conjunction with the rESP8266rtc library, which suited my needs due to having built-in alarm functions for performing certain tasks at desired times.

This code as-is requires the library listed above as well as the rHttpUtils2 module. Replace YOURAPIKEY with the API key you are given when you sign up for the free API access.

B4X:
Private Sub SyncClock 'Call this to begin the process of setting the clock after the clock has been initialized
    HttpJob.Initialize ("GetTime")
    HttpJob.Download ("http://api.timezonedb.com/v2/get-time-zone?key=YOURAPIKEY&format=json&fields=formatted&by=zone&zone=America/Chicago")
    'If successful, the Job Done sub will send it to the parser sub.  If it fails, it will try SyncClock2 from the script on my website server.
End Sub

Sub JobDone (Job As JobResult)
    Log("*******************************")
    Log("JobName: ", Job.JobName)
    If Job.Success Then
        Log("Response: ", bc.SubString2(Job.Response, 0, Min(200, Job.Response.Length))) 'truncate to 200 characters
        If Job.JobName = "GetTime" Then
            Log ("Job GetTime detected.  Sending to parser.")
            ParseTime (Job.Response)
        End If
    Else 'Job Failed
            Log("Job failed: ", Job.JobName)
            Log("ErrorMessage: ", Job.ErrorMessage)
            Log("Status: ", Job.Status)
            Log(Job.Response)
            If Job.JobName = "GetTime" Then
                SyncClock2 'This is where I use the library's sync from server option
            End If
    End If
End Sub

Private Sub ParseTime (Resp As Object)
    'Sample reponse:  {"status":"OK","message":"","formatted":"2018-06-22 20:13:20"}

    Dim iStart, iEnd As Int, RespParse() As Byte
    iStart = bc.IndexOf(Resp, "formatted")
    iEnd = (bc.IndexOf2(Resp, "}", iStart)) - 1
    RespParse = bc.SubString2 (Resp, iStart + 12, iEnd)
    Log ("Parsed string = ",RespParse)

    Dim cn, yy, mo, dd, hh, mm, ss As Byte
    Dim i As Byte = 0
    For Each s() As Byte In bc.Split(RespParse, " ")
        Select i
            Case 0
                Dim n As Byte = 0
                For Each s2() As Byte In bc.Split(s, "-")
                    Select n
                        Case 0
                            Dim temp() As Byte
                            bc.ArrayCopy (s2, temp)
                            Dim tmp As Int = bc.StringFromBytes(bc.SubString2(temp, 0,2))
                            cn = NumberFormat(tmp + 1,2,0)
                            Dim tmp2 As Int = bc.StringFromBytes(bc.SubString2(temp, 2,4))
                            yy = tmp2
                        Case 1
                            mo = bc.StringFromBytes(s2)
                        Case 2
                            dd = bc.StringFromBytes(s2)
                    End Select
                    n = n + 1
                Next
            Case 1
                Dim n As Byte = 0
                For Each s3() As Byte In bc.Split(s, ":")
                    Select n
                        Case 0
                            hh = bc.StringFromBytes(s3)
                        Case 1
                            mm = bc.StringFromBytes(s3)
                        Case 2
                            ss = bc.StringFromBytes(s3)
                    End Select
                    n = n + 1
                Next
        End Select
        i = i + 1
    Next
    Log ("Century, Year, Month, Day: ", cn, ",", yy, ",", mo,",", dd)
    Log ("hh = ",hh)
    Log ("mm = ",mm)
    Log ("ss = ",ss)

    Dim Stamp() As Byte = Array As Byte(cn, yy, mo, dd, hh, mm, ss, 0)
    rtc.SetClock(Stamp,0,0) 'No offset needed when syncing with this service as it is already adjusted
    rtc.ReadClock(DT)
    Log("RTC: ",rtc.ShortDOW(DT),", ",rtc.ShortDate(DT),", ",rtc.Time24(DT))
    rtc.SetAlarm(1,rtc.Sec2DT(rtc.DT2Sec(DT)+rtc.SECONDS_1HOUR)) 'Alarm 1 is set to sync the clock every hour
End Sub

private Sub rtc_Alarm(index As Byte)
    If index=0 Then 'This is a default alarm that ticks every second if enabled in Initialize
         'tick every second
         rtc.ReadClock(DT)
         Log("RTC: ",rtc.ShortDOW(DT),", ",rtc.ShortDate(DT),", ",rtc.Time24(DT))
         Return
    End If

    Select Case index
        Case 1 'Alarm #1 used to sync clock every hour - Afterwards, it will set this alarm again for an hour later
            Log("AlarmEvent (", index, "): SyncClock called")
            SyncClock
    End Select
End Sub
 
Last edited:
Top