B4J Question Detect daylight savings change?

techknight

Well-Known Member
Licensed User
Longtime User
Hey, with long running B4J apps, is it possible to detect when the DST event hits?

For example I have a B4J app that controls external hardware that has its own clock. but I would like to detect the DST change and refresh the clock the time that happens.

any ideas?
 

stevel05

Expert
Licensed User
Longtime User
Can you not just check the external hardware's time against the time reported by the app and change if needed? Assuming that the computer the app is running on syncs to Daylight saving time automatically.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
You could monitor the timezone offset which changes when daylight saving time does:

B4X:
    DateTime.DateFormat = "dd/MM/yyyy"
  
    Log(DateTime.GetTimeZoneOffsetAt(DateTime.DateParse("04/03/2020")))
    Log(DateTime.GetTimeZoneOffsetAt(DateTime.DateParse("01/04/2020")))

(Test data assumes you are in the UK)
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
use a timer of 1 hour and then do an hours(abs(currenttime-previoustime)) and if it's not 1 then the time jumped back or forward.
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
Nice thought. But, what is current time and previous time variables? like currentime = Now?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I am in the USA.

It'll still work the same, just change the test dates to straddle your Daylight saving time change.

Store the current value and each day, test it against a new current value. If it's changed then so has daylight saving time.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
yes. something like this

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Dim prevTime As Long
    Dim currTime As Long
    Dim tmr As Timer
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("main") 'Load the layout file.
    tmr.Initialize("tmrTimeCheck",1000*60*60)
    tmr.Enabled=True
End Sub

Sub tmrTimeCheck_Tick
    currTime=DateTime.Now
    If Floor(Abs(currTime-prevTime)/DateTime.TicksPerHour)<>1 Then
        Log("time changed")
    End If
    prevTime=currTime
End Sub
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
Ahhh nice.

Thanks for the help! ill try both of them and see which one works the best. They both look like viable solutions to my problem.
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
yes. something like this

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Dim prevTime As Long
    Dim currTime As Long
    Dim tmr As Timer
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("main") 'Load the layout file.
    tmr.Initialize("tmrTimeCheck",1000*60*60)
    tmr.Enabled=True
End Sub

Sub tmrTimeCheck_Tick
    currTime=DateTime.Now
    If Floor(Abs(currTime-prevTime)/DateTime.TicksPerHour)<>1 Then
        Log("time changed")
    End If
    prevTime=currTime
End Sub

So i tried this, and it has a small problem. This code keeps triggering as time changed, but I also have the code set to check every 10 seconds instead of 1 hour. Why? because you cant guarantee the code is gonna start exactly at the top of the hour on the hour. so the timing can be severely delayed.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Java knows the time and date of daylight saving changes in advance, so having a timer running all the time to check seems (to me) a bit wasteful.

You could at the start of your program simply create a task on the ScheduledExecutorService that fires when a change occurs and then update the time on the external device.

Just a thought.

code to see the dates & times
B4X:
#if java
import java.util.*;
import java.time.ZoneId;
import java.time.zone.ZoneOffsetTransitionRule;

public static void DST(){
    ZoneId zid = ZoneId.systemDefault();
    
    // 0 is start  1 is end
    ZoneOffsetTransitionRule DSTstarts = zid.getRules().getTransitionRules().get(0);
    ZoneOffsetTransitionRule DSTends = zid.getRules().getTransitionRules().get(1);
    
    // just to see the rules for current year
    //System.out.println(DSTstarts);
    //System.out.println(DSTends);
    
    // pretty print the info
    System.out.println("Daylight saving starts on "+ DSTstarts.getDayOfWeek() + " " + DSTstarts.getDayOfMonthIndicator() +
    " " + DSTstarts.getMonth() + " at " + DSTstarts.getLocalTime() +" clocks goes forward to "+ DSTstarts.getLocalTime().plusHours(1));

    System.out.println("Daylight saving ends on  "+ DSTends.getDayOfWeek() + " " +DSTends.getDayOfMonthIndicator() +
    " " + DSTends.getMonth() + " at " + DSTends.getLocalTime().plusHours(1) +" clocks goes back to "+ DSTends.getLocalTime());
    
}        
#End If
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
So i tried this, and it has a small problem. This code keeps triggering as time changed, but I also have the code set to check every 10 seconds instead of 1 hour.

yeah, I knew it could give problems over time as the timer will trigger miliseconds later each time but you're doing it wrong.

if you check every 10 seconds you need to check for the same time let's say at <hour>:05 and then skip it 5 times so that it doesn't trigger at :05:10, :05:20 etc

otherwise it will work wrong on the other times aswell as the difference in hours will be 0.

by the way the dates when these times change are know so why not put a list with dates in your app and check on that?
in europe they will probably scratch this time changing in a few years.
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Call CheckForTimeZoneChanged when your app starts:
B4X:
Sub CheckForTimeZoneChanges
    Dim CurrentTimeZone As Double = DateTime.GetTimeZoneOffsetAt(DateTime.Now)
    Do While True
        Dim c As Double = DateTime.GetTimeZoneOffsetAt(DateTime.Now)
        If c <> CurrentTimeZone Then
            TimeZoneChanged
            CurrentTimeZone = c        
        End If
        Sleep(1000)
    Loop
End Sub

Sub TimeZoneChanged
    'do whatever you need
End Sub
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
Java knows the time and date of daylight saving changes in advance, so having a timer running all the time to check seems (to me) a bit wasteful.

You could at the start of your program simply create a task on the ScheduledExecutorService that fires when a change occurs and then update the time on the external device.

Just a thought.

code to see the dates & times
B4X:
#if java
import java.util.*;
import java.time.ZoneId;
import java.time.zone.ZoneOffsetTransitionRule;

public static void DST(){
    ZoneId zid = ZoneId.systemDefault();
   
    // 0 is start  1 is end
    ZoneOffsetTransitionRule DSTstarts = zid.getRules().getTransitionRules().get(0);
    ZoneOffsetTransitionRule DSTends = zid.getRules().getTransitionRules().get(1);
   
    // just to see the rules for current year
    //System.out.println(DSTstarts);
    //System.out.println(DSTends);
   
    // pretty print the info
    System.out.println("Daylight saving starts on "+ DSTstarts.getDayOfWeek() + " " + DSTstarts.getDayOfMonthIndicator() +
    " " + DSTstarts.getMonth() + " at " + DSTstarts.getLocalTime() +" clocks goes forward to "+ DSTstarts.getLocalTime().plusHours(1));

    System.out.println("Daylight saving ends on  "+ DSTends.getDayOfWeek() + " " +DSTends.getDayOfMonthIndicator() +
    " " + DSTends.getMonth() + " at " + DSTends.getLocalTime().plusHours(1) +" clocks goes back to "+ DSTends.getLocalTime());
   
}       
#End If

Is there a way that java routine returns back to b4j? or how do you call it? Sorry, I know nothing about java and it kinda blows over my head.
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
Call CheckForTimeZoneChanged when your app starts:
B4X:
Sub CheckForTimeZoneChanges
    Dim CurrentTimeZone As Double = DateTime.GetTimeZoneOffsetAt(DateTime.Now)
    Do While True
        Dim c As Double = DateTime.GetTimeZoneOffsetAt(DateTime.Now)
        If c <> CurrentTimeZone Then
            TimeZoneChanged
            CurrentTimeZone = c       
        End If
        Sleep(1000)
    Loop
End Sub

Sub TimeZoneChanged
    'do whatever you need
End Sub

well now that the DST switch happened, I cant test anymore. But that code worked partly. But it got stuck once the timechange happened. it kept triggering over and over again. and I didnt catch it until the program eventually crashed. Here is my nohup.out dump from when it occured:

1583789829668.png
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Worth adding more logs:
B4X:
Sub CheckForTimeZoneChanges
    Dim CurrentTimeZone As Double = DateTime.GetTimeZoneOffsetAt(DateTime.Now)
    Do While True
        Dim c As Double = DateTime.GetTimeZoneOffsetAt(DateTime.Now)
        If c <> CurrentTimeZone Then
            Log("New time zone: " & c & ", Previous time zone: " & CurrentTimeZone)
            TimeZoneChanged
            CurrentTimeZone = c        
        End If
        Sleep(1000)
    Loop
End Sub
The code looks correct.

You can test it by manually changing the time zone or changing the date and time.
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
I tried to test it previously by changing the date and time, and that wouldn't trigger it. Must be a timezone only thing?
 
Upvote 0
Top